Move linear arithmetic solver to theory::arith::linear (#8628)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Tue, 19 Apr 2022 18:05:12 +0000 (13:05 -0500)
committerGitHub <noreply@github.com>
Tue, 19 Apr 2022 18:05:12 +0000 (18:05 +0000)
This moves the current linear arithmetic solver to src/theory/arith/linear, and inside `cvc5::internal::theory::arith::linear`.

Some code uses internal utilities from `arith::linear`, although this should be minimized.

This is preparation for breaking dependencies with the old code.

112 files changed:
src/CMakeLists.txt
src/preprocessing/passes/pseudo_boolean_processor.cpp
src/theory/arith/approx_simplex.cpp [deleted file]
src/theory/arith/approx_simplex.h [deleted file]
src/theory/arith/arith_ite_utils.cpp
src/theory/arith/arith_rewriter.cpp
src/theory/arith/arith_rewriter.h
src/theory/arith/arith_state.cpp
src/theory/arith/arith_state.h
src/theory/arith/arith_static_learner.cpp [deleted file]
src/theory/arith/arith_static_learner.h [deleted file]
src/theory/arith/arith_utilities.h
src/theory/arith/arithvar.cpp [deleted file]
src/theory/arith/arithvar.h [deleted file]
src/theory/arith/arithvar_node_map.h [deleted file]
src/theory/arith/attempt_solution_simplex.cpp [deleted file]
src/theory/arith/attempt_solution_simplex.h [deleted file]
src/theory/arith/bound_counts.h [deleted file]
src/theory/arith/bound_inference.cpp
src/theory/arith/callbacks.cpp [deleted file]
src/theory/arith/callbacks.h [deleted file]
src/theory/arith/congruence_manager.cpp [deleted file]
src/theory/arith/congruence_manager.h [deleted file]
src/theory/arith/constraint.cpp [deleted file]
src/theory/arith/constraint.h [deleted file]
src/theory/arith/constraint_forward.h [deleted file]
src/theory/arith/cut_log.cpp [deleted file]
src/theory/arith/cut_log.h [deleted file]
src/theory/arith/dio_solver.cpp [deleted file]
src/theory/arith/dio_solver.h [deleted file]
src/theory/arith/dual_simplex.cpp [deleted file]
src/theory/arith/dual_simplex.h [deleted file]
src/theory/arith/error_set.cpp [deleted file]
src/theory/arith/error_set.h [deleted file]
src/theory/arith/fc_simplex.cpp [deleted file]
src/theory/arith/fc_simplex.h [deleted file]
src/theory/arith/infer_bounds.cpp [deleted file]
src/theory/arith/infer_bounds.h [deleted file]
src/theory/arith/linear/approx_simplex.cpp [new file with mode: 0644]
src/theory/arith/linear/approx_simplex.h [new file with mode: 0644]
src/theory/arith/linear/arith_static_learner.cpp [new file with mode: 0644]
src/theory/arith/linear/arith_static_learner.h [new file with mode: 0644]
src/theory/arith/linear/arithvar.cpp [new file with mode: 0644]
src/theory/arith/linear/arithvar.h [new file with mode: 0644]
src/theory/arith/linear/arithvar_node_map.h [new file with mode: 0644]
src/theory/arith/linear/attempt_solution_simplex.cpp [new file with mode: 0644]
src/theory/arith/linear/attempt_solution_simplex.h [new file with mode: 0644]
src/theory/arith/linear/bound_counts.h [new file with mode: 0644]
src/theory/arith/linear/callbacks.cpp [new file with mode: 0644]
src/theory/arith/linear/callbacks.h [new file with mode: 0644]
src/theory/arith/linear/congruence_manager.cpp [new file with mode: 0644]
src/theory/arith/linear/congruence_manager.h [new file with mode: 0644]
src/theory/arith/linear/constraint.cpp [new file with mode: 0644]
src/theory/arith/linear/constraint.h [new file with mode: 0644]
src/theory/arith/linear/constraint_forward.h [new file with mode: 0644]
src/theory/arith/linear/cut_log.cpp [new file with mode: 0644]
src/theory/arith/linear/cut_log.h [new file with mode: 0644]
src/theory/arith/linear/dio_solver.cpp [new file with mode: 0644]
src/theory/arith/linear/dio_solver.h [new file with mode: 0644]
src/theory/arith/linear/dual_simplex.cpp [new file with mode: 0644]
src/theory/arith/linear/dual_simplex.h [new file with mode: 0644]
src/theory/arith/linear/error_set.cpp [new file with mode: 0644]
src/theory/arith/linear/error_set.h [new file with mode: 0644]
src/theory/arith/linear/fc_simplex.cpp [new file with mode: 0644]
src/theory/arith/linear/fc_simplex.h [new file with mode: 0644]
src/theory/arith/linear/infer_bounds.cpp [new file with mode: 0644]
src/theory/arith/linear/infer_bounds.h [new file with mode: 0644]
src/theory/arith/linear/linear_equality.cpp [new file with mode: 0644]
src/theory/arith/linear/linear_equality.h [new file with mode: 0644]
src/theory/arith/linear/matrix.cpp [new file with mode: 0644]
src/theory/arith/linear/matrix.h [new file with mode: 0644]
src/theory/arith/linear/normal_form.cpp [new file with mode: 0644]
src/theory/arith/linear/normal_form.h [new file with mode: 0644]
src/theory/arith/linear/partial_model.cpp [new file with mode: 0644]
src/theory/arith/linear/partial_model.h [new file with mode: 0644]
src/theory/arith/linear/simplex.cpp [new file with mode: 0644]
src/theory/arith/linear/simplex.h [new file with mode: 0644]
src/theory/arith/linear/simplex_update.cpp [new file with mode: 0644]
src/theory/arith/linear/simplex_update.h [new file with mode: 0644]
src/theory/arith/linear/soi_simplex.cpp [new file with mode: 0644]
src/theory/arith/linear/soi_simplex.h [new file with mode: 0644]
src/theory/arith/linear/tableau.cpp [new file with mode: 0644]
src/theory/arith/linear/tableau.h [new file with mode: 0644]
src/theory/arith/linear/tableau_sizes.cpp [new file with mode: 0644]
src/theory/arith/linear/tableau_sizes.h [new file with mode: 0644]
src/theory/arith/linear/theory_arith_private.cpp [new file with mode: 0644]
src/theory/arith/linear/theory_arith_private.h [new file with mode: 0644]
src/theory/arith/linear_equality.cpp [deleted file]
src/theory/arith/linear_equality.h [deleted file]
src/theory/arith/matrix.cpp [deleted file]
src/theory/arith/matrix.h [deleted file]
src/theory/arith/nl/icp/icp_solver.cpp
src/theory/arith/normal_form.cpp [deleted file]
src/theory/arith/normal_form.h [deleted file]
src/theory/arith/partial_model.cpp [deleted file]
src/theory/arith/partial_model.h [deleted file]
src/theory/arith/proof_checker.cpp
src/theory/arith/simplex.cpp [deleted file]
src/theory/arith/simplex.h [deleted file]
src/theory/arith/simplex_update.cpp [deleted file]
src/theory/arith/simplex_update.h [deleted file]
src/theory/arith/soi_simplex.cpp [deleted file]
src/theory/arith/soi_simplex.h [deleted file]
src/theory/arith/tableau.cpp [deleted file]
src/theory/arith/tableau.h [deleted file]
src/theory/arith/tableau_sizes.cpp [deleted file]
src/theory/arith/tableau_sizes.h [deleted file]
src/theory/arith/theory_arith.cpp
src/theory/arith/theory_arith.h
src/theory/arith/theory_arith_private.cpp [deleted file]
src/theory/arith/theory_arith_private.h [deleted file]
src/theory/quantifiers/cegqi/ceg_arith_instantiator.cpp

index 8cf8d516e231248cd31f91080de1bad0128b0f0d..f414db97d4752ab3aaff0270137b7d67906bb2c1 100644 (file)
@@ -349,8 +349,6 @@ libcvc5_add_sources(
   smt/witness_form.h
   smt_util/boolean_simplification.cpp
   smt_util/boolean_simplification.h
-  theory/arith/approx_simplex.cpp
-  theory/arith/approx_simplex.h
   theory/arith/arith_evaluator.cpp
   theory/arith/arith_evaluator.h
   theory/arith/arith_ite_utils.cpp
@@ -365,48 +363,66 @@ libcvc5_add_sources(
   theory/arith/arith_rewriter.h
   theory/arith/arith_state.cpp
   theory/arith/arith_state.h
-  theory/arith/arith_static_learner.cpp
-  theory/arith/arith_static_learner.h
   theory/arith/arith_utilities.cpp
   theory/arith/arith_utilities.h
-  theory/arith/arithvar.cpp
-  theory/arith/arithvar.h
-  theory/arith/attempt_solution_simplex.cpp
-  theory/arith/attempt_solution_simplex.h
-  theory/arith/bound_counts.h
   theory/arith/bound_inference.cpp
   theory/arith/bound_inference.h
   theory/arith/branch_and_bound.cpp
   theory/arith/branch_and_bound.h
-  theory/arith/callbacks.cpp
-  theory/arith/callbacks.h
-  theory/arith/congruence_manager.cpp
-  theory/arith/congruence_manager.h
-  theory/arith/constraint.cpp
-  theory/arith/constraint.h
-  theory/arith/constraint_forward.h
-  theory/arith/cut_log.cpp
-  theory/arith/cut_log.h
   theory/arith/delta_rational.cpp
   theory/arith/delta_rational.h
-  theory/arith/dio_solver.cpp
-  theory/arith/dio_solver.h
-  theory/arith/dual_simplex.cpp
-  theory/arith/dual_simplex.h
   theory/arith/equality_solver.cpp
   theory/arith/equality_solver.h
-  theory/arith/error_set.cpp
-  theory/arith/error_set.h
-  theory/arith/fc_simplex.cpp
-  theory/arith/fc_simplex.h
-  theory/arith/infer_bounds.cpp
-  theory/arith/infer_bounds.h
   theory/arith/inference_manager.cpp
   theory/arith/inference_manager.h
-  theory/arith/linear_equality.cpp
-  theory/arith/linear_equality.h
-  theory/arith/matrix.cpp
-  theory/arith/matrix.h
+  theory/arith/linear/approx_simplex.cpp
+  theory/arith/linear/approx_simplex.h
+  theory/arith/linear/arith_static_learner.cpp
+  theory/arith/linear/arith_static_learner.h
+  theory/arith/linear/arithvar.cpp
+  theory/arith/linear/arithvar.h
+  theory/arith/linear/attempt_solution_simplex.cpp
+  theory/arith/linear/attempt_solution_simplex.h
+  theory/arith/linear/bound_counts.h
+  theory/arith/linear/callbacks.cpp
+  theory/arith/linear/callbacks.h
+  theory/arith/linear/congruence_manager.cpp
+  theory/arith/linear/congruence_manager.h
+  theory/arith/linear/constraint.cpp
+  theory/arith/linear/constraint.h
+  theory/arith/linear/constraint_forward.h
+  theory/arith/linear/cut_log.cpp
+  theory/arith/linear/cut_log.h
+  theory/arith/linear/dio_solver.cpp
+  theory/arith/linear/dio_solver.h
+  theory/arith/linear/dual_simplex.cpp
+  theory/arith/linear/dual_simplex.h
+  theory/arith/linear/error_set.cpp
+  theory/arith/linear/error_set.h
+  theory/arith/linear/fc_simplex.cpp
+  theory/arith/linear/fc_simplex.h
+  theory/arith/linear/infer_bounds.cpp
+  theory/arith/linear/infer_bounds.h
+  theory/arith/linear/linear_equality.cpp
+  theory/arith/linear/linear_equality.h
+  theory/arith/linear/matrix.cpp
+  theory/arith/linear/matrix.h
+  theory/arith/linear/normal_form.cpp
+  theory/arith/linear/normal_form.h
+  theory/arith/linear/partial_model.cpp
+  theory/arith/linear/partial_model.h
+  theory/arith/linear/simplex.cpp
+  theory/arith/linear/simplex.h
+  theory/arith/linear/simplex_update.cpp
+  theory/arith/linear/simplex_update.h
+  theory/arith/linear/soi_simplex.cpp
+  theory/arith/linear/soi_simplex.h
+  theory/arith/linear/tableau.cpp
+  theory/arith/linear/tableau.h
+  theory/arith/linear/tableau_sizes.cpp
+  theory/arith/linear/tableau_sizes.h
+  theory/arith/linear/theory_arith_private.cpp
+  theory/arith/linear/theory_arith_private.h
   theory/arith/nl/coverings_solver.cpp
   theory/arith/nl/coverings_solver.h
   theory/arith/nl/coverings/cdcac.cpp
@@ -487,12 +503,8 @@ libcvc5_add_sources(
   theory/arith/nl/transcendental/transcendental_solver.h
   theory/arith/nl/transcendental/transcendental_state.cpp
   theory/arith/nl/transcendental/transcendental_state.h
-  theory/arith/normal_form.cpp
-  theory/arith/normal_form.h
   theory/arith/operator_elim.cpp
   theory/arith/operator_elim.h
-  theory/arith/partial_model.cpp
-  theory/arith/partial_model.h
   theory/arith/pp_rewrite_eq.cpp
   theory/arith/pp_rewrite_eq.h
   theory/arith/proof_checker.cpp
@@ -506,20 +518,8 @@ libcvc5_add_sources(
   theory/arith/rewriter/rewrite_atom.h
   theory/arith/rewrites.cpp
   theory/arith/rewrites.h
-  theory/arith/simplex.cpp
-  theory/arith/simplex.h
-  theory/arith/simplex_update.cpp
-  theory/arith/simplex_update.h
-  theory/arith/soi_simplex.cpp
-  theory/arith/soi_simplex.h
-  theory/arith/tableau.cpp
-  theory/arith/tableau.h
-  theory/arith/tableau_sizes.cpp
-  theory/arith/tableau_sizes.h
   theory/arith/theory_arith.cpp
   theory/arith/theory_arith.h
-  theory/arith/theory_arith_private.cpp
-  theory/arith/theory_arith_private.h
   theory/arith/theory_arith_type_rules.cpp
   theory/arith/theory_arith_type_rules.h
   theory/arith/type_enumerator.h
index e680dc4b55781eb68cfde7809e476d1a1696d80d..a3ceddeb99c9f0bf07ff8f308cc72c21719dd40d 100644 (file)
@@ -22,7 +22,7 @@
 #include "preprocessing/assertion_pipeline.h"
 #include "preprocessing/preprocessing_pass_context.h"
 #include "theory/arith/arith_utilities.h"
-#include "theory/arith/normal_form.h"
+#include "theory/arith/linear/normal_form.h"
 #include "theory/rewriter.h"
 
 namespace cvc5::internal {
@@ -78,13 +78,13 @@ bool PseudoBooleanProcessor::decomposeAssertion(Node assertion, bool negated)
     return false;
   }
 
-  if (!Polynomial::isMember(l))
+  if (!linear::Polynomial::isMember(l))
   {
     Trace("pbs::rewrites") << "not polynomial" << assertion << std::endl;
     return false;
   }
 
-  Polynomial p = Polynomial::parsePolynomial(l);
+  linear::Polynomial p = linear::Polynomial::parsePolynomial(l);
   clear();
   if (negated)
   {
@@ -112,9 +112,9 @@ bool PseudoBooleanProcessor::decomposeAssertion(Node assertion, bool negated)
   Assert(d_off.value().isIntegral());
 
   int adj = negated ? -1 : 1;
-  for (Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i)
+  for (linear::Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i)
   {
-    Monomial m = *i;
+    linear::Monomial m = *i;
     const Rational& coeff = m.getConstant().getValue();
     if (!(coeff.isOne() || coeff.isNegativeOne()))
     {
@@ -122,7 +122,7 @@ bool PseudoBooleanProcessor::decomposeAssertion(Node assertion, bool negated)
     }
     Assert(coeff.sgn() != 0);
 
-    const VarList& vl = m.getVarList();
+    const linear::VarList& vl = m.getVarList();
     Node v = vl.getNode();
 
     if (!isPseudoBoolean(v))
diff --git a/src/theory/arith/approx_simplex.cpp b/src/theory/arith/approx_simplex.cpp
deleted file mode 100644 (file)
index 0c8f450..0000000
+++ /dev/null
@@ -1,3083 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Aina Niemetz
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-#include "theory/arith/approx_simplex.h"
-
-#include <math.h>
-
-#include <cfloat>
-#include <cmath>
-#include <unordered_set>
-
-#include "base/cvc5config.h"
-#include "base/output.h"
-#include "proof/eager_proof_generator.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/cut_log.h"
-#include "theory/arith/matrix.h"
-#include "theory/arith/normal_form.h"
-
-#ifdef CVC5_USE_GLPK
-#include "theory/arith/partial_model.h"
-#endif
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-struct AuxInfo {
-  TreeLog* tl;
-  int pivotLimit;
-  int branchLimit;
-  int branchDepth;
-  MipResult term; /* terminatation */
-};
-
-enum SlackReplace { SlackUndef=0, SlackLB, SlackUB, SlackVLB, SlackVUB };
-
-std::ostream& operator<<(std::ostream& out, MipResult res){
-  switch(res){
-  case MipUnknown:
-    out << "MipUnknown"; break;
-  case MipBingo:
-    out << "MipBingo"; break;
-  case MipClosed:
-    out << "MipClosed"; break;
-  case BranchesExhausted:
-    out << "BranchesExhausted"; break;
-  case PivotsExhauasted:
-    out << "PivotsExhauasted"; break;
-  case ExecExhausted:
-    out << "ExecExhausted"; break;
-  default:
-    out << "Unexpected Mip Value!"; break;
-  }
-  return out;
-}
-struct VirtualBound {
-  // Either x <= d * y or x >= d * y
-  ArithVar x; // variable being bounded
-  Kind k; // either LEQ or GEQ
-  Rational d; // the multiple on y
-  ArithVar y; // the variable that is the upper bound
-  ConstraintP c; // the original constraint relating x and y
-
-  VirtualBound()
-    : x(ARITHVAR_SENTINEL)
-    , k(kind::UNDEFINED_KIND)
-    , d()
-    , y(ARITHVAR_SENTINEL)
-    , c(NullConstraint)
-  {}
-  VirtualBound(ArithVar toBound, Kind rel, const Rational& coeff, ArithVar bounding, ConstraintP orig)
-    : x(toBound)
-    , k(rel)
-    , d(coeff)
-    , y(bounding)
-    , c(orig)
-  {
-    Assert(k == kind::LEQ || k == kind::GEQ);
-  }
-};
-
-struct CutScratchPad {
-  bool d_failure; // if the construction was unsuccessful
-
-  /* GOMORY CUTS Datastructures */
-  ArithVar d_basic; // a variable that is basic in the approximate solver
-  DenseVector d_tabRow;           // a row in the tableau not including d_basic, equal to 0
-  DenseMap<ConstraintP> d_toBound; // each variable in toBound maps each variable in tabRow to either an upper/lower bound
-
-  /* MIR CUTS Datastructures */
-  DenseMap<SlackReplace> d_slacks;// The x'[i] selected for x[i]
-  DenseMap<VirtualBound> d_vub;   // Virtual upper bounds.
-  DenseMap<VirtualBound> d_vlb;   // Virtual lower bounds.
-  DenseMap<Rational> d_compRanges;
-
-  // a sum of rows in the tableau, with possible replacements for fixed
-  // sum aggLhs[i] x[i] = aggRhs;
-  DenseVector d_agg;
-  // Takes agg and replaces x[i] with a slack variable x'[i]
-  // Takes agg and replaces x[i] with a slack variable x'[i]
-  // sum modLhs[i] x'[i] = modRhs;
-  DenseVector d_mod;
-
-  // Takes mod, and performs c-Mir on it
-  // sum alpha[i] x'[i] <= beta
-  DenseVector d_alpha;
-
-  /* The constructed cut */
-  // sum cut[i] x[i] <= cutRhs
-  DenseVector d_cut;
-  Kind d_cutKind;
-
-  /* The constraints used throughout construction. */
-  std::set<ConstraintP> d_explanation; // use pointer equality
-  CutScratchPad(){
-    clear();
-  }
-  void clear(){
-    d_failure = false;
-    d_basic = ARITHVAR_SENTINEL;
-    d_tabRow.purge();
-    d_toBound.purge();
-
-    d_slacks.purge();
-    d_vub.purge();
-    d_vlb.purge();
-    d_compRanges.purge();
-
-    d_agg.purge();
-    d_mod.purge();
-    d_alpha.purge();
-
-    d_cut.purge();
-    d_cutKind = kind::UNDEFINED_KIND;
-    d_explanation.clear();
-  }
-};
-
-ApproximateStatistics::ApproximateStatistics()
-    : d_branchMaxDepth(
-        smtStatisticsRegistry().registerInt("z::approx::branchMaxDepth")),
-      d_branchesMaxOnAVar(
-          smtStatisticsRegistry().registerInt("z::approx::branchesMaxOnAVar")),
-      d_gaussianElimConstructTime(smtStatisticsRegistry().registerTimer(
-          "z::approx::gaussianElimConstruct::time")),
-      d_gaussianElimConstruct(smtStatisticsRegistry().registerInt(
-          "z::approx::gaussianElimConstruct::calls")),
-      d_averageGuesses(
-          smtStatisticsRegistry().registerAverage("z::approx::averageGuesses"))
-{
-}
-
-ApproximateSimplex::ApproximateSimplex(const ArithVariables& v, TreeLog& l,
-                                       ApproximateStatistics& s)
-  : d_vars(v)
-  , d_log(l)
-  , d_stats(s)
-  , d_pivotLimit(std::numeric_limits<int>::max())
-  , d_branchLimit(std::numeric_limits<int>::max())
-  , d_maxDepth(std::numeric_limits<int>::max())
-{}
-
-void ApproximateSimplex::setPivotLimit(int pl){
-  Assert(pl >= 0);
-  d_pivotLimit = pl;
-}
-
-void ApproximateSimplex::setBranchingDepth(int bd){
-  Assert(bd >= 0);
-  d_maxDepth = bd;
-}
-
-void ApproximateSimplex::setBranchOnVariableLimit(int bl){
-  Assert(bl >= 0);
-  d_branchLimit = bl;
-}
-
-bool ApproximateSimplex::roughlyEqual(double a, double b){
-  if (a == 0){
-    return -SMALL_FIXED_DELTA <= b && b <= SMALL_FIXED_DELTA;
-  }else if (b == 0){
-    return -SMALL_FIXED_DELTA <= a && a <= SMALL_FIXED_DELTA;
-  }else{
-    return std::abs(b/a) <= TOLERENCE && std::abs(a/b) <= TOLERENCE;
-  }
-}
-
-Rational ApproximateSimplex::cfeToRational(const vector<Integer>& exp){
-  if(exp.empty()){
-    return Rational(0);
-  }else{
-    Rational result = exp.back();
-    vector<Integer>::const_reverse_iterator exp_iter = exp.rbegin();
-    vector<Integer>::const_reverse_iterator exp_end = exp.rend();
-    ++exp_iter;
-    while(exp_iter != exp_end){
-      result = result.inverse();
-      const Integer& i = *exp_iter;
-      result += i;
-      ++exp_iter;
-    }
-    return result;
-  }
-}
-std::vector<Integer> ApproximateSimplex::rationalToCfe(const Rational& q, int depth){
-  vector<Integer> mods;
-  if(!q.isZero()){
-    Rational carry = q;
-    for(int i = 0; i <= depth; ++i){
-      Assert(!carry.isZero());
-      mods.push_back(Integer());
-      Integer& back = mods.back();
-      back = carry.floor();
-      Trace("rationalToCfe") << "  cfe["<<i<<"]: " << back << endl;
-      carry -= back;
-      if(carry.isZero()){
-        break;
-      }else if(ApproximateSimplex::roughlyEqual(carry.getDouble(), 0.0)){
-        break;
-      }else{
-        carry = carry.inverse();
-      }
-    }
-  }
-  return mods;
-}
-
-
-Rational ApproximateSimplex::estimateWithCFE(const Rational& r, const Integer& K){
-  Trace("estimateWithCFE") << "estimateWithCFE(" << r << ", " << K << ")" <<endl;
-  // references
-  // page 4: http://carlossicoli.free.fr/C/Cassels_J.W.S.-An_introduction_to_diophantine_approximation-University_Press(1965).pdf
-  // http://en.wikipedia.org/wiki/Continued_fraction
-  Assert(K >= Integer(1));
-  if( r.getDenominator() <= K ){
-    return r;
-  }
-
-  // current numerator and denominator that has not been resolved in the cfe
-  Integer num = r.getNumerator(), den = r.getDenominator();
-  Integer quot,rem;
-
-  unsigned t = 0;
-  // For a sequence of candidate solutions q_t/p_t
-  // we keep only 3 time steps: 0[prev], 1[current], 2[next]
-  // timesteps with a fake timestep 0 (p is 0 and q is 1)
-  // at timestep 1
-  Integer p[3]; // h
-  Integer q[3]; // k
-  // load the first 3 time steps manually
-  p[0] =    0; q[0] = 1; // timestep -2
-  p[1] =    1; q[1] = 0; // timestep -1
-
-  Integer::floorQR(quot, rem, num, den);
-  num = den; den = rem;
-
-  q[2] = q[0] + quot*q[1];
-  p[2] = p[0] + quot*p[1];
-  Trace("estimateWithCFE") <<  "  cfe["<<t<<"]: " << p[2] <<"/"<< q[2] << endl;
-  while( q[2] <= K ){
-    p[0] = p[1]; p[1] = p[2];
-    q[0] = q[1]; q[1] = q[2];
-
-
-    Integer::floorQR(quot, rem, num, den);
-    num = den; den = rem;
-
-    p[2] = p[0]+quot*p[1];
-    q[2] = q[0]+quot*q[1];
-    ++t;
-    Trace("estimateWithCFE") << "  cfe["<<t<<"]: " << p[2] <<"/"<< q[2] << endl;
-  }
-
-  Integer k = (K-q[0]).floorDivideQuotient(q[1]);
-  Rational cand_prev(p[0]+k*p[1], q[0]+k*q[1]);
-  Rational cand_curr(p[1], q[1]);
-  Rational dist_prev = (cand_prev - r).abs();
-  Rational dist_curr = (cand_curr - r).abs();
-  if(dist_prev <= dist_curr){
-    Trace("estimateWithCFE") << cand_prev << " is closer than " << cand_curr << endl;
-    return cand_prev;
-  }else{
-    Trace("estimateWithCFE") << cand_curr << " is closer than " << cand_prev << endl;
-    return cand_curr;
-  }
-}
-
-std::optional<Rational> ApproximateSimplex::estimateWithCFE(double d,
-                                                            const Integer& D)
-{
-  if (std::optional<Rational> from_double = Rational::fromDouble(d))
-  {
-    return estimateWithCFE(*from_double, D);
-  }
-  return std::optional<Rational>();
-}
-
-std::optional<Rational> ApproximateSimplex::estimateWithCFE(double d)
-{
-  return estimateWithCFE(d, Integer(s_defaultMaxDenom));
-}
-
-class ApproxNoOp : public ApproximateSimplex {
-public:
-  ApproxNoOp(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s)
-  : ApproximateSimplex(v,l,s)
-  {}
-  ~ApproxNoOp(){}
-
-  LinResult solveRelaxation() override { return LinUnknown; }
-  Solution extractRelaxation() const override { return Solution(); }
-
-  ArithRatPairVec heuristicOptCoeffs() const override
-  {
-    return ArithRatPairVec();
-  }
-
-  MipResult solveMIP(bool al) override { return MipUnknown; }
-  Solution extractMIP() const override { return Solution(); }
-
-  void setOptCoeffs(const ArithRatPairVec& ref) override {}
-
-  void tryCut(int nid, CutInfo& cut) override {}
-
-  std::vector<const CutInfo*> getValidCuts(const NodeLog& node) override
-  {
-    return std::vector<const CutInfo*>();
-  }
-
-  ArithVar getBranchVar(const NodeLog& nl) const override
-  {
-    return ARITHVAR_SENTINEL;
-  }
-
-  double sumInfeasibilities(bool mip) const override { return 0.0; }
-};
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-
-/* Begin the declaration of GLPK specific code. */
-#ifdef CVC5_USE_GLPK
-extern "C" {
-#include <glpk.h>
-}/* extern "C" */
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-Kind glpk_type_to_kind(int glpk_cut_type){
-  switch(glpk_cut_type){
-  case GLP_LO: return kind::GEQ;
-  case GLP_UP: return kind::LEQ;
-  case GLP_FX: return kind::EQUAL;
-  case GLP_DB:
-  case GLP_FR:
-  default:     return kind::UNDEFINED_KIND;
-  }
-}
-
-class GmiInfo;
-class MirInfo;
-class BranchCutInfo;
-
-class ApproxGLPK : public ApproximateSimplex {
-private:
-  glp_prob* d_inputProb; /* a copy of the input prob */
-  glp_prob* d_realProb;  /* a copy of the real relaxation output */
-  glp_prob* d_mipProb;   /* a copy of the integer prob */
-
-  DenseMap<int> d_colIndices;
-  DenseMap<int> d_rowIndices;
-
-  NodeLog::RowIdMap d_rootRowIds;
-  //DenseMap<ArithVar> d_rowToArithVar;
-  DenseMap<ArithVar> d_colToArithVar;
-
-  bool d_solvedRelaxation;
-  bool d_solvedMIP;
-
-  CutScratchPad d_pad;
-
-  std::vector<Integer> d_denomGuesses;
-
-public:
-  ApproxGLPK(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s);
-  ~ApproxGLPK();
-
-  LinResult solveRelaxation() override;
-  Solution extractRelaxation() const override { return extractSolution(false); }
-
-  ArithRatPairVec heuristicOptCoeffs() const override;
-
-  MipResult solveMIP(bool al) override;
-  Solution extractMIP() const override { return extractSolution(true); }
-  void setOptCoeffs(const ArithRatPairVec& ref) override;
-  std::vector<const CutInfo*> getValidCuts(const NodeLog& nodes) override;
-  ArithVar getBranchVar(const NodeLog& con) const override;
-
-  static void printGLPKStatus(int status, std::ostream& out);
-
-
-private:
- Solution extractSolution(bool mip) const;
- int guessDir(ArithVar v) const;
-
- // get this stuff out of here
- void tryCut(int nid, CutInfo& cut) override;
-
- ArithVar _getArithVar(int nid, int M, int ind) const;
- ArithVar getArithVarFromRow(int nid, int ind) const
- {
-   if (ind >= 0)
-   {
-     const NodeLog& nl = d_log.getNode(nid);
-     return nl.lookupRowId(ind);
-   }
-   return ARITHVAR_SENTINEL;
-  }
-
-  // virtual void mapRowId(int nid, int ind, ArithVar v){
-  //   NodeLog& nl = d_log.getNode(nid);
-  //   nl.mapRowId(ind, v);
-  // }
-  // virtual void applyRowsDeleted(int nid, const RowsDeleted& rd){
-  //   NodeLog& nl = d_log.getNode(nid);
-  //   nl.applyRowsDeleted(rd);
-  // }
-
-  ArithVar getArithVarFromStructural(int ind) const{
-    if(ind >= 0){
-      unsigned u = (unsigned) ind;
-      if(d_colToArithVar.isKey(u)){
-        return d_colToArithVar[u];
-      }
-    }
-    return ARITHVAR_SENTINEL;
-  }
-
-  /**
-   * Attempts to make the row vector vec on the pad.
-   * If this is not in the row span of the original tableau this
-   * raises the failure flag.
-   */
-  bool attemptConstructTableRow(int node, int M, const PrimitiveVec& vec);
-  bool guessCoefficientsConstructTableRow(int node, int M, const PrimitiveVec& vec);
-  bool guessCoefficientsConstructTableRow(int node, int M, const PrimitiveVec& vec, const Integer& D);
-  bool gaussianElimConstructTableRow(int node, int M, const PrimitiveVec& vec);
-
-  /* This is a guess of a vector in the row span of the tableau.
-   * Attempt to cancel out all of the variables.
-   * returns true if this is constructable.
-   */
-  bool guessIsConstructable(const DenseMap<Rational>& guess) const;
-
-  /**
-   * Loads a vector of statuses into a dense map over bounds.
-   * returns true on failure.
-   */
-  bool loadToBound(int node, int M, int len, int* inds, int* statuses,
-                   DenseMap<ConstraintP>& toBound) const;
-
-  /** checks the cut on the pad for whether it is sufficiently similar to cut. */
-  bool checkCutOnPad(int nid, const CutInfo& cut) const;
-
-
-  /** turns the pad into a node and creates an explanation. */
-  //std::pair<Node, Node> makeCutNodes(int nid, const CutInfo& cut) const;
-
-  // true means failure!
-  // BRANCH CUTS
-  bool attemptBranchCut(int nid, const BranchCutInfo& br);
-
-  // GOMORY CUTS
-  bool attemptGmi(int nid, const GmiInfo& gmi);
-  /** tries to turn the information on the pad into a cut. */
-  bool constructGmiCut();
-
-  // MIR CUTS
-  bool attemptMir(int nid, const MirInfo& mir);
-  bool applyCMIRRule(int nid, const MirInfo& mir);
-  bool makeRangeForComplemented(int nid, const MirInfo& mir);
-  bool loadSlacksIntoPad(int nid, const MirInfo& mir);
-  bool loadVirtualBoundsIntoPad(int nid, const MirInfo& mir);
-  bool loadRowSumIntoAgg(int nid, int M, const PrimitiveVec& mir);
-  bool buildModifiedRow(int nid, const MirInfo& mir);
-  bool constructMixedKnapsack();
-  bool replaceSlacksOnCuts();
-  bool loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp);
-
-  double sumInfeasibilities(bool mip) const override
-  {
-    return sumInfeasibilities(mip? d_mipProb : d_realProb);
-  }
-  double sumInfeasibilities(glp_prob* prob, bool mip) const;
-};
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-#endif /*#ifdef CVC5_USE_GLPK */
-/* End the declaration of GLPK specific code. */
-
-/* Begin GPLK/NOGLPK Glue code. */
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-ApproximateSimplex* ApproximateSimplex::mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s){
-#ifdef CVC5_USE_GLPK
-  return new ApproxGLPK(vars, l, s);
-#else
-  return new ApproxNoOp(vars, l, s);
-#endif
-}
-bool ApproximateSimplex::enabled() {
-#ifdef CVC5_USE_GLPK
-  return true;
-#else
-  return false;
-#endif
-}
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-/* End GPLK/NOGLPK Glue code. */
-
-/* Begin GPLK implementation. */
-#ifdef CVC5_USE_GLPK
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-#ifdef CVC5_ASSERTIONS
-static CutInfoKlass fromGlpkClass(int klass){
-  switch(klass){
-  case GLP_RF_GMI: return GmiCutKlass;
-  case GLP_RF_MIR: return MirCutKlass;
-  case GLP_RF_COV:
-  case GLP_RF_CLQ:
-  default:         return UnknownKlass;
-  }
-}
-#endif
-
-ApproxGLPK::ApproxGLPK(const ArithVariables& var,
-                       TreeLog& l,
-                       ApproximateStatistics& s)
-    : ApproximateSimplex(var, l, s),
-      d_inputProb(nullptr),
-      d_realProb(nullptr),
-      d_mipProb(nullptr),
-      d_solvedRelaxation(false),
-      d_solvedMIP(false)
-{
-
-  d_denomGuesses.push_back(Integer(1<<22));
-  d_denomGuesses.push_back(Integer(ApproximateSimplex::s_defaultMaxDenom));
-  d_denomGuesses.push_back(Integer(1ul<<29));
-  d_denomGuesses.push_back(Integer(1ul<<31));
-
-  d_inputProb = glp_create_prob();
-  d_realProb = glp_create_prob();
-  d_mipProb = glp_create_prob();
-  glp_set_obj_dir(d_inputProb, GLP_MAX);
-  glp_set_prob_name(d_inputProb, "ApproximateSimplex::approximateFindModel");
-
-  int numRows = 0;
-  int numCols = 0;
-
-  // Assign each variable to a row and column variable as it appears in the input
-  for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
-    ArithVar v = *vi;
-
-    if(d_vars.isAuxiliary(v)){
-      ++numRows;
-      d_rowIndices.set(v, numRows);
-      //mapRowId(d_log.getRootId(), numRows, v);
-      d_rootRowIds.insert(make_pair(numRows, v));
-      //d_rowToArithVar.set(numRows, v);
-      Trace("approx") << "Row vars: " << v << "<->" << numRows << endl;
-    }else{
-      ++numCols;
-      d_colIndices.set(v, numCols);
-      d_colToArithVar.set(numCols, v);
-      Trace("approx") << "Col vars: " << v << "<->" << numCols << endl;
-    }
-  }
-  Assert(numRows > 0);
-  Assert(numCols > 0);
-
-  glp_add_rows(d_inputProb, numRows);
-  glp_add_cols(d_inputProb, numCols);
-
-  // Assign the upper/lower bounds and types to each variable
-  for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
-    ArithVar v = *vi;
-
-    int type;
-    double lb = 0.0;
-    double ub = 0.0;
-    if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
-      if(d_vars.boundsAreEqual(v)){
-        type = GLP_FX;
-      }else{
-        type = GLP_DB;
-      }
-      lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
-      ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
-    }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
-      type = GLP_UP;
-      ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
-    }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
-      type = GLP_LO;
-      lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
-    }else{
-      type = GLP_FR;
-    }
-
-    if(d_vars.isAuxiliary(v)){
-      int rowIndex = d_rowIndices[v];
-      glp_set_row_bnds(d_inputProb, rowIndex, type, lb, ub);
-    }else{
-      int colIndex = d_colIndices[v];
-      // is input is correct here
-      int kind = d_vars.isInteger(v) ? GLP_IV : GLP_CV;
-      glp_set_col_kind(d_inputProb, colIndex, kind);
-      glp_set_col_bnds(d_inputProb, colIndex, type, lb, ub);
-    }
-  }
-
-  // Count the number of entries
-  int numEntries = 0;
-  for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
-    ArithVar v = *i;
-    Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
-    for (Polynomial::iterator j = p.begin(), end = p.end(); j != end; ++j)
-    {
-      ++numEntries;
-    }
-  }
-
-  int* ia = new int[numEntries+1];
-  int* ja = new int[numEntries+1];
-  double* ar = new double[numEntries+1];
-
-  int entryCounter = 0;
-  for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
-    ArithVar v = *i;
-    int rowIndex = d_rowIndices[v];
-
-    Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
-
-    for (Polynomial::iterator j = p.begin(), end = p.end(); j != end; ++j)
-    {
-      const Monomial& mono = *j;
-      const Constant& constant = mono.getConstant();
-      const VarList& variable = mono.getVarList();
-
-      Node n = variable.getNode();
-
-      Assert(d_vars.hasArithVar(n));
-      ArithVar av = d_vars.asArithVar(n);
-      int colIndex = d_colIndices[av];
-      double coeff = constant.getValue().getDouble();
-
-      ++entryCounter;
-      ia[entryCounter] = rowIndex;
-      ja[entryCounter] = colIndex;
-      ar[entryCounter] = coeff;
-    }
-  }
-  glp_load_matrix(d_inputProb, numEntries, ia, ja, ar);
-
-  delete[] ia;
-  delete[] ja;
-  delete[] ar;
-}
-int ApproxGLPK::guessDir(ArithVar v) const{
-  if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
-    return -1;
-  }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
-    return 1;
-  }else if(!d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
-    return 0;
-  }else{
-    int ubSgn = d_vars.getUpperBound(v).sgn();
-    int lbSgn = d_vars.getLowerBound(v).sgn();
-
-    if(ubSgn != 0 && lbSgn == 0){
-      return -1;
-    }else if(ubSgn == 0 && lbSgn != 0){
-      return 1;
-    }
-
-    return 1;
-  }
-}
-
-ArithRatPairVec ApproxGLPK::heuristicOptCoeffs() const{
-  ArithRatPairVec ret;
-
-  // Strategies are guess:
-  // 1 simple shared "ceiling" variable: danoint, pk1
-  //  x1 >= c, x1 >= tmp1, x1 >= tmp2, ...
-  // 1 large row: fixnet, vpm2, pp08a
-  //  (+ .......... ) <= c
-  // Not yet supported:
-  // 1 complex shared "ceiling" variable: opt1217
-  //  x1 >= c, x1 >= (+ ..... ), x1 >= (+ ..... )
-  //  and all of the ... are the same sign
-
-
-  // Candidates:
-  // 1) Upper and lower bounds are not equal.
-  // 2) The variable is not integer
-  // 3a) For columns look for a ceiling variable
-  // 3B) For rows look for a large row with
-
-  DenseMap<BoundCounts> d_colCandidates;
-  DenseMap<uint32_t> d_rowCandidates;
-
-  double sumRowLength = 0.0;
-  uint32_t maxRowLength = 0;
-  for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
-    ArithVar v = *vi;
-
-    int type;
-    if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
-      if(d_vars.boundsAreEqual(v)){
-        type = GLP_FX;
-      }else{
-        type = GLP_DB;
-      }
-    }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
-      type = GLP_UP;
-    }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
-      type = GLP_LO;
-    }else{
-      type = GLP_FR;
-    }
-
-    if(type != GLP_FX && type != GLP_FR){
-
-      if(d_vars.isAuxiliary(v)){
-        Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
-        uint32_t len = p.size();
-        d_rowCandidates.set(v, len);
-        sumRowLength += len;
-        maxRowLength = std::max(maxRowLength, len);
-      }else if(!d_vars.isInteger(v)){
-        d_colCandidates.set(v, BoundCounts());
-      }
-    }
-  }
-
-  uint32_t maxCount = 0;
-  for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
-    ArithVar v = *i;
-
-    bool lbCap = d_vars.hasLowerBound(v) && !d_vars.hasUpperBound(v);
-    bool ubCap = !d_vars.hasLowerBound(v) && d_vars.hasUpperBound(v);
-
-    if(lbCap || ubCap){
-      ConstraintP b = lbCap ? d_vars.getLowerBoundConstraint(v)
-        : d_vars.getUpperBoundConstraint(v);
-
-      if(!(b->getValue()).noninfinitesimalIsZero()){ continue; }
-
-      Polynomial poly = Polynomial::parsePolynomial(d_vars.asNode(v));
-      if(poly.size() != 2) { continue; }
-
-      Polynomial::iterator j = poly.begin();
-      Monomial first = *j;
-      ++j;
-      Monomial second = *j;
-
-      bool firstIsPos = first.constantIsPositive();
-      bool secondIsPos = second.constantIsPositive();
-
-      if(firstIsPos == secondIsPos){ continue; }
-
-      Monomial pos = firstIsPos == lbCap ? first : second;
-      Monomial neg = firstIsPos != lbCap ? first : second;
-      // pos >= neg
-      VarList p = pos.getVarList();
-      VarList n = neg.getVarList();
-      if(d_vars.hasArithVar(p.getNode())){
-        ArithVar ap = d_vars.asArithVar(p.getNode());
-        if( d_colCandidates.isKey(ap)){
-          BoundCounts bc = d_colCandidates.get(ap);
-          bc = BoundCounts(bc.lowerBoundCount(), bc.upperBoundCount()+1);
-          maxCount = std::max(maxCount, bc.upperBoundCount());
-          d_colCandidates.set(ap, bc);
-        }
-      }
-      if(d_vars.hasArithVar(n.getNode())){
-        ArithVar an = d_vars.asArithVar(n.getNode());
-        if( d_colCandidates.isKey(an)){
-          BoundCounts bc = d_colCandidates.get(an);
-          bc = BoundCounts(bc.lowerBoundCount()+1, bc.upperBoundCount());
-          maxCount = std::max(maxCount, bc.lowerBoundCount());
-          d_colCandidates.set(an, bc);
-        }
-      }
-    }
-  }
-
-  // Attempt row
-  double avgRowLength = d_rowCandidates.size() >= 1 ?
-    ( sumRowLength / d_rowCandidates.size() ) : 0.0;
-
-  // There is a large row among the candidates
-  bool guessARowCandidate = maxRowLength >= (10.0 * avgRowLength);
-
-  double rowLengthReq = (maxRowLength * .9);
-
-  if (guessARowCandidate)
-  {
-    for (ArithVar r : d_rowCandidates)
-    {
-      uint32_t len = d_rowCandidates[r];
-
-      int dir = guessDir(r);
-      if(len >= rowLengthReq){
-        ret.push_back(ArithRatPair(r, Rational(dir)));
-      }
-    }
-  }
-
-  // Attempt columns
-  bool guessAColCandidate = maxCount >= 4;
-  if (guessAColCandidate)
-  {
-    for (ArithVar c : d_colCandidates)
-    {
-      BoundCounts bc = d_colCandidates[c];
-
-      int dir = guessDir(c);
-      double ubScore = double(bc.upperBoundCount()) / maxCount;
-      double lbScore = double(bc.lowerBoundCount()) / maxCount;
-      if(ubScore  >= .9 || lbScore >= .9){
-        ret.push_back(ArithRatPair(c, Rational(c)));
-      }
-    }
-  }
-
-  return ret;
-}
-
-void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){
-  DenseMap<double> nbCoeffs;
-
-  for(ArithRatPairVec::const_iterator i = ref.begin(), iend = ref.end(); i != iend; ++i){
-    ArithVar v = (*i).first;
-    const Rational& q = (*i).second;
-
-    if(d_vars.isAuxiliary(v)){
-      // replace the variable by its definition and multiply by q
-      Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
-      Polynomial pq = p * q;
-
-      for(Polynomial::iterator j = pq.begin(), jend = pq.end(); j != jend; ++j){
-        const Monomial& mono = *j;
-        const Constant& constant = mono.getConstant();
-        const VarList& variable = mono.getVarList();
-
-        Node n = variable.getNode();
-
-        Assert(d_vars.hasArithVar(n));
-        ArithVar av = d_vars.asArithVar(n);
-        int colIndex = d_colIndices[av];
-        double coeff = constant.getValue().getDouble();
-        if(!nbCoeffs.isKey(colIndex)){
-          nbCoeffs.set(colIndex, 0.0);
-        }
-        nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
-      }
-    }else{
-      int colIndex = d_colIndices[v];
-      double coeff = q.getDouble();
-      if(!nbCoeffs.isKey(colIndex)){
-        nbCoeffs.set(colIndex, 0.0);
-      }
-      nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
-    }
-  }
-  for(DenseMap<double>::const_iterator ci =nbCoeffs.begin(), ciend = nbCoeffs.end(); ci != ciend; ++ci){
-    Index colIndex = *ci;
-    double coeff = nbCoeffs[colIndex];
-    glp_set_obj_coef(d_inputProb, colIndex, coeff);
-  }
-}
-
-
-/*
- * rough strategy:
- *  real relaxation
- *   try approximate real optimization of error function
- *   pivot in its basis
- *   update to its assignment
- *   check with FCSimplex
- *  check integer solution
- *   try approximate mixed integer problem
- *   stop at the first feasible point
- *   pivot in its basis
- *   update to its assignment
- *   check with FCSimplex
- */
-
-void ApproxGLPK::printGLPKStatus(int status, std::ostream& out){
-  switch(status){
-  case GLP_OPT:
-    out << "GLP_OPT" << endl;
-    break;
-  case GLP_FEAS:
-    out << "GLP_FEAS" << endl;
-    break;
-  case GLP_INFEAS:
-    out << "GLP_INFEAS" << endl;
-    break;
-  case GLP_NOFEAS:
-    out << "GLP_NOFEAS" << endl;
-    break;
-  case GLP_UNBND:
-    out << "GLP_UNBND" << endl;
-    break;
-  case GLP_UNDEF:
-    out << "GLP_UNDEF" << endl;
-    break;
-  default:
-    out << "Status unknown" << endl;
-    break;
-  }
-}
-
-ApproxGLPK::~ApproxGLPK(){
-  glp_delete_prob(d_inputProb);
-  glp_delete_prob(d_realProb);
-  glp_delete_prob(d_mipProb);
-
-}
-
-ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const
-{
-  Assert(d_solvedRelaxation);
-  Assert(!mip || d_solvedMIP);
-
-  ApproximateSimplex::Solution sol;
-  DenseSet& newBasis = sol.newBasis;
-  DenseMap<DeltaRational>& newValues = sol.newValues;
-
-  glp_prob* prob = mip ? d_mipProb : d_realProb;
-
-  for(ArithVariables::var_iterator i = d_vars.var_begin(), i_end = d_vars.var_end(); i != i_end; ++i){
-    ArithVar vi = *i;
-    bool isAux = d_vars.isAuxiliary(vi);
-    int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi];
-
-    int status = isAux ? glp_get_row_stat(prob, glpk_index)
-      : glp_get_col_stat(prob, glpk_index);
-
-    bool useDefaultAssignment = false;
-
-    switch(status){
-    case GLP_BS:
-      newBasis.add(vi);
-      useDefaultAssignment = true;
-      break;
-    case GLP_NL:
-    case GLP_NS:
-      if(!mip){
-        newValues.set(vi, d_vars.getLowerBound(vi));
-      }else{// intentionally fall through otherwise
-        useDefaultAssignment = true;
-      }
-      break;
-    case GLP_NU:
-      if(!mip){
-        newValues.set(vi, d_vars.getUpperBound(vi));
-      }else {// intentionally fall through otherwise
-        useDefaultAssignment = true;
-      }
-      break;
-    default:
-      {
-        useDefaultAssignment = true;
-      }
-      break;
-    }
-
-    if(useDefaultAssignment){
-
-      double newAssign;
-      if(mip){
-        newAssign = (isAux ? glp_mip_row_val(prob, glpk_index)
-                     :  glp_mip_col_val(prob, glpk_index));
-      }else{
-        newAssign = (isAux ? glp_get_row_prim(prob, glpk_index)
-                     :  glp_get_col_prim(prob, glpk_index));
-      }
-      const DeltaRational& oldAssign = d_vars.getAssignment(vi);
-
-      if (d_vars.hasLowerBound(vi)
-          && roughlyEqual(newAssign,
-                          d_vars.getLowerBound(vi).approx(SMALL_FIXED_DELTA)))
-      {
-
-        newValues.set(vi, d_vars.getLowerBound(vi));
-      }
-      else if (d_vars.hasUpperBound(vi)
-               && roughlyEqual(
-                   newAssign,
-                   d_vars.getUpperBound(vi).approx(SMALL_FIXED_DELTA)))
-      {
-        newValues.set(vi, d_vars.getUpperBound(vi));
-      }
-      else
-      {
-        double rounded = round(newAssign);
-        if (roughlyEqual(newAssign, rounded))
-        {
-          newAssign = rounded;
-        }
-
-        DeltaRational proposal;
-        if (std::optional maybe_new = estimateWithCFE(newAssign))
-        {
-          proposal = *maybe_new;
-        }
-        else
-        {
-          // failed to estimate the old value. defaulting to the current.
-          proposal = d_vars.getAssignment(vi);
-        }
-
-        if (roughlyEqual(newAssign, oldAssign.approx(SMALL_FIXED_DELTA)))
-        {
-          proposal = d_vars.getAssignment(vi);
-        }
-
-        if (d_vars.strictlyLessThanLowerBound(vi, proposal))
-        {
-          proposal = d_vars.getLowerBound(vi);
-        }
-        else if (d_vars.strictlyGreaterThanUpperBound(vi, proposal))
-        {
-          proposal = d_vars.getUpperBound(vi);
-        }
-        newValues.set(vi, proposal);
-      }
-    }
-  }
-  return sol;
-}
-
-double ApproxGLPK::sumInfeasibilities(glp_prob* prob, bool mip) const{
-  /* compute the sum of dual infeasibilities */
-  double infeas = 0.0;
-
-  for(ArithVariables::var_iterator i = d_vars.var_begin(), i_end = d_vars.var_end(); i != i_end; ++i){
-    ArithVar vi = *i;
-    bool isAux = d_vars.isAuxiliary(vi);
-    int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi];
-
-    double newAssign;
-    if(mip){
-      newAssign = (isAux ? glp_mip_row_val(prob, glpk_index)
-                   :  glp_mip_col_val(prob, glpk_index));
-    }else{
-      newAssign = (isAux ? glp_get_row_prim(prob, glpk_index)
-                   :  glp_get_col_prim(prob, glpk_index));
-    }
-
-
-    double ub = isAux ?
-      glp_get_row_ub(prob, glpk_index) : glp_get_col_ub(prob, glpk_index);
-
-    double lb = isAux ?
-      glp_get_row_lb(prob, glpk_index) : glp_get_col_lb(prob, glpk_index);
-
-    if(ub != +DBL_MAX){
-      if(newAssign > ub){
-        double ubinf = newAssign - ub;
-        infeas += ubinf;
-        Trace("approx::soi") << "ub inf" << vi << " " << ubinf << " " << infeas << endl;
-      }
-    }
-    if(lb != -DBL_MAX){
-      if(newAssign < lb){
-        double lbinf = lb - newAssign;
-        infeas  += lbinf;
-
-        Trace("approx::soi") << "lb inf" << vi << " " << lbinf << " " << infeas << endl;
-      }
-    }
-  }
-  return infeas;
-}
-
-LinResult ApproxGLPK::solveRelaxation(){
-  Assert(!d_solvedRelaxation);
-
-  glp_smcp parm;
-  glp_init_smcp(&parm);
-  parm.presolve = GLP_OFF;
-  parm.meth = GLP_PRIMAL;
-  parm.pricing = GLP_PT_PSE;
-  parm.it_lim = d_pivotLimit;
-  parm.msg_lev = GLP_MSG_OFF;
-
-  glp_erase_prob(d_realProb);
-  glp_copy_prob(d_realProb, d_inputProb, GLP_OFF);
-
-  int res = glp_simplex(d_realProb, &parm);
-  switch(res){
-  case 0:
-    {
-      int status = glp_get_status(d_realProb);
-      int iterationcount = glp_get_it_cnt(d_realProb);
-      switch(status){
-      case GLP_OPT:
-      case GLP_FEAS:
-      case GLP_UNBND:
-        d_solvedRelaxation = true;
-        return LinFeasible;
-      case GLP_INFEAS:
-      case GLP_NOFEAS:
-        d_solvedRelaxation = true;
-        return LinInfeasible;
-      default:
-        {
-          if(iterationcount >= d_pivotLimit){
-            return LinExhausted;
-          }
-          return LinUnknown;
-        }
-      }
-    }
-  default:
-    return LinUnknown;
-  }
-}
-
-
-struct MirInfo : public CutInfo {
-
-  /** a sum of input rows. */
-  PrimitiveVec row_sum;
-
-  /* the delta used */
-  double delta;
-
-  /* all of these are length vars == N+M*/
-  int nvars;
-  char* cset;
-  char* subst;
-  int*  vlbRows;
-  int*  vubRows;
-  MirInfo(int execOrd, int ord)
-    : CutInfo(MirCutKlass, execOrd, ord)
-    , nvars(0)
-    , cset(NULL)
-    , subst(NULL)
-    , vlbRows(NULL)
-    , vubRows(NULL)
-  {}
-
-  ~MirInfo(){
-    clearSets();
-  }
-  void clearSets(){
-    if(cset != NULL){
-      delete[] cset;
-      delete[] subst;
-      delete[] vlbRows;
-      delete[] vubRows;
-      cset = NULL;
-      nvars = 0;
-    }
-  }
-  void initSet(){
-    Assert(d_N >= 0);
-    Assert(d_mAtCreation >= 0);
-    clearSets();
-
-    int vars = 1 + d_N + d_mAtCreation;
-
-    cset = new char[1+vars];
-    subst = new char[1+vars];
-    vlbRows = new int[1+vars];
-    vubRows = new int[1+vars];
-  }
-};
-
-struct GmiInfo : public CutInfo {
-  int basic;
-  PrimitiveVec tab_row;
-  int* tab_statuses;
-  /* has the length tab_row.length */
-
-  GmiInfo(int execOrd, int ord)
-    : CutInfo(GmiCutKlass, execOrd, ord)
-    , basic(-1)
-    , tab_row()
-    , tab_statuses(NULL)
-  {
-    Assert(!initialized_tab());
-  }
-
-  ~GmiInfo(){
-    if(initialized_tab()){
-      clear_tab();
-    }
-  }
-
-  bool initialized_tab() const {
-    return tab_statuses != NULL;
-  }
-
-  void init_tab(int N){
-    if(initialized_tab()){
-      clear_tab();
-    }
-    tab_row.setup(N);
-    tab_statuses = new int[1+N];
-  }
-
-  void clear_tab() {
-    delete[] tab_statuses;
-    tab_statuses = NULL;
-    tab_row.clear();
-    basic = -1;
-  }
-};
-
-
-
-static void loadCut(glp_tree *tree, CutInfo* cut){
-  int ord, cut_len, cut_klass;
-  int N, M;
-  int* cut_inds;
-  double* cut_coeffs;
-  int glpk_cut_type;
-  double cut_rhs;
-  glp_prob* lp;
-
-  lp = glp_ios_get_prob(tree);
-  ord = cut->poolOrdinal();
-
-  N = glp_get_num_cols(lp);
-  M = glp_get_num_rows(lp);
-
-  cut->setDimensions(N, M);
-
-
-
-  // Get the cut
-  cut_len = glp_ios_get_cut(tree, ord, NULL, NULL, &cut_klass, NULL, NULL);
-  Assert(fromGlpkClass(cut_klass) == cut->getKlass());
-
-  PrimitiveVec& cut_vec = cut->getCutVector();
-  cut_vec.setup(cut_len);
-  cut_inds = cut_vec.inds;
-  cut_coeffs = cut_vec.coeffs;
-
-  cut_vec.len = glp_ios_get_cut(tree, ord, cut_inds, cut_coeffs, &cut_klass, &glpk_cut_type, &cut_rhs);
-  Assert(fromGlpkClass(cut_klass) == cut->getKlass());
-  Assert(cut_vec.len == cut_len);
-
-  cut->setRhs(cut_rhs);
-
-  cut->setKind( glpk_type_to_kind(glpk_cut_type) );
-}
-
-
-static MirInfo* mirCut(glp_tree *tree, int exec_ord, int cut_ord){
-  Trace("approx::mirCut") << "mirCut()" << exec_ord << endl;
-
-  MirInfo* mir;
-  mir = new MirInfo(exec_ord, cut_ord);
-  loadCut(tree, mir);
-  mir->initSet();
-
-
-  int nrows = glp_ios_cut_get_aux_nrows(tree, cut_ord);
-
-  PrimitiveVec& row_sum = mir->row_sum;
-  row_sum.setup(nrows);
-  glp_ios_cut_get_aux_rows(tree, cut_ord, row_sum.inds, row_sum.coeffs);
-
-  glp_ios_cut_get_mir_cset(tree, cut_ord, mir->cset);
-  mir->delta = glp_ios_cut_get_mir_delta(tree, cut_ord);
-  glp_ios_cut_get_mir_subst(tree, cut_ord, mir->subst);
-  glp_ios_cut_get_mir_virtual_rows(tree, cut_ord, mir->vlbRows, mir->vubRows);
-
-  if(TraceIsOn("approx::mirCut")){
-    Trace("approx::mirCut") << "mir_id: " << exec_ord << endl;
-    row_sum.print(Trace("approx::mirCut"));
-  }
-
-  return mir;
-}
-
-static GmiInfo* gmiCut(glp_tree *tree, int exec_ord, int cut_ord){
-  Trace("approx::gmiCut") << "gmiCut()" << exec_ord << endl;
-
-  int gmi_var;
-  int write_pos;
-  int read_pos;
-  int stat;
-  int ind;
-  int i;
-
-  GmiInfo* gmi;
-  glp_prob* lp;
-
-  gmi = new GmiInfo(exec_ord, cut_ord);
-  loadCut(tree, gmi);
-
-  lp = glp_ios_get_prob(tree);
-
-  int N = gmi->getN();
-  int M = gmi->getMAtCreation();
-
-  // Get the tableau row
-  int nrows CVC5_UNUSED = glp_ios_cut_get_aux_nrows(tree, gmi->poolOrdinal());
-  Assert(nrows == 1);
-  int rows[1+1];
-  glp_ios_cut_get_aux_rows(tree, gmi->poolOrdinal(), rows, NULL);
-  gmi_var = rows[1];
-
-  gmi->init_tab(N);
-  gmi->basic = M+gmi_var;
-
-  Trace("approx::gmiCut")
-    << gmi <<" " << gmi->basic << " "
-    << cut_ord<<" "  << M <<" " << gmi_var << endl;
-
-  PrimitiveVec& tab_row = gmi->tab_row;
-  Trace("approx::gmiCut") << "Is N sufficient here?" << endl;
-  tab_row.len = glp_eval_tab_row(lp, gmi->basic, tab_row.inds, tab_row.coeffs);
-
-  Trace("approx::gmiCut") << "gmi_var " << gmi_var << endl;
-
-  Trace("approx::gmiCut") << "tab_pos " << tab_row.len << endl;
-  write_pos = 1;
-  for(read_pos = 1; read_pos <= tab_row.len; ++read_pos){
-    if (fabs(tab_row.coeffs[read_pos]) < 1e-10){
-    }else{
-      tab_row.coeffs[write_pos] = tab_row.coeffs[read_pos];
-      tab_row.inds[write_pos] = tab_row.inds[read_pos];
-      ++write_pos;
-    }
-  }
-  tab_row.len = write_pos-1;
-  Trace("approx::gmiCut") << "write_pos " << write_pos << endl;
-  Assert(tab_row.len > 0);
-
-  for(i = 1; i <= tab_row.len; ++i){
-    ind = tab_row.inds[i];
-    Trace("approx::gmiCut") << "ind " << i << " " << ind << endl;
-    stat = (ind <= M) ?
-      glp_get_row_stat(lp, ind) : glp_get_col_stat(lp, ind - M);
-
-    Trace("approx::gmiCut") << "ind " << i << " " << ind << " stat " << stat << endl;
-    switch (stat){
-    case GLP_NL:
-    case GLP_NU:
-    case GLP_NS:
-      gmi->tab_statuses[i] = stat;
-      break;
-    case GLP_NF:
-    default:
-      Unreachable();
-    }
-  }
-
-  if(TraceIsOn("approx::gmiCut")){
-    gmi->print(Trace("approx::gmiCut"));
-  }
-  return gmi;
-}
-
-static BranchCutInfo* branchCut(glp_tree *tree, int exec_ord, int br_var, double br_val, bool down_bad){
-  //(tree, br_var, br_val, dn < 0);
-  double rhs;
-  Kind k;
-  if(down_bad){
-    // down branch is infeasible
-    // x <= floor(v) is infeasible
-    // - so x >= ceiling(v) is implied
-    k = kind::GEQ;
-    rhs = std::ceil(br_val);
-  }else{
-    // up branch is infeasible
-    // x >= ceiling(v) is infeasible
-    // - so x <= floor(v) is implied
-    k = kind::LEQ;
-    rhs = std::floor(br_val);
-  }
-  BranchCutInfo* br_cut = new BranchCutInfo(exec_ord, br_var, k, rhs);
-  return br_cut;
-}
-
-static void glpkCallback(glp_tree *tree, void *info){
-  AuxInfo* aux = (AuxInfo*)(info);
-  TreeLog& tl = *(aux->tl);
-
-  int exec = tl.getExecutionOrd();
-  int glpk_node_p = -1;
-  int node_ord = -1;
-
-  if(tl.isActivelyLogging()){
-    switch(glp_ios_reason(tree)){
-    case GLP_LI_DELROW:
-      {
-        glpk_node_p = glp_ios_curr_node(tree);
-        node_ord = glp_ios_node_ord(tree, glpk_node_p);
-
-        int nrows = glp_ios_rows_deleted(tree, NULL);
-        int* num = new int[1+nrows];
-        glp_ios_rows_deleted(tree, num);
-
-        NodeLog& node = tl.getNode(node_ord);
-
-        RowsDeleted* rd = new RowsDeleted(exec, nrows, num);
-
-        node.addCut(rd);
-        delete[] num;
-      }
-      break;
-    case GLP_ICUTADDED:
-      {
-        int cut_ord = glp_ios_pool_size(tree);
-        glpk_node_p = glp_ios_curr_node(tree);
-        node_ord = glp_ios_node_ord(tree, glpk_node_p);
-        Assert(cut_ord > 0);
-        Trace("approx") << "curr node " << glpk_node_p
-                        << " cut ordinal " << cut_ord
-                        << " node depth " << glp_ios_node_level(tree, glpk_node_p)
-                        << endl;
-        int klass;
-        glp_ios_get_cut(tree, cut_ord, NULL, NULL, &klass, NULL, NULL);
-
-        NodeLog& node = tl.getNode(node_ord);
-        switch(klass){
-        case GLP_RF_GMI:
-          {
-            GmiInfo* gmi = gmiCut(tree, exec, cut_ord);
-            node.addCut(gmi);
-          }
-          break;
-        case GLP_RF_MIR:
-          {
-            MirInfo* mir = mirCut(tree, exec, cut_ord);
-            node.addCut(mir);
-          }
-          break;
-        case GLP_RF_COV:
-          Trace("approx") << "GLP_RF_COV" << endl;
-          break;
-        case GLP_RF_CLQ:
-          Trace("approx") << "GLP_RF_CLQ" << endl;
-          break;
-        default:
-          break;
-        }
-      }
-      break;
-    case GLP_ICUTSELECT:
-      {
-        glpk_node_p = glp_ios_curr_node(tree);
-        node_ord = glp_ios_node_ord(tree, glpk_node_p);
-        int cuts = glp_ios_pool_size(tree);
-        int* ords = new int[1+cuts];
-        int* rows = new int[1+cuts];
-        int N = glp_ios_selected_cuts(tree, ords, rows);
-
-        NodeLog& nl = tl.getNode(node_ord);
-        Trace("approx") << glpk_node_p << " " << node_ord << " " << cuts << " " << N << std::endl;
-        for(int i = 1; i <= N; ++i){
-          Trace("approx") << "adding to " << node_ord <<" @ i= " << i
-                          << " ords[i] = " << ords[i]
-                          << " rows[i] = " << rows[i] << endl;
-          nl.addSelected(ords[i], rows[i]);
-        }
-        delete[] ords;
-        delete[] rows;
-        nl.applySelected();
-      }
-    break;
-  case GLP_LI_BRANCH:
-    {
-      // a branch was just made
-      int br_var;
-      int p, dn, up;
-      int p_ord, dn_ord, up_ord;
-      double br_val;
-      br_var = glp_ios_branch_log(tree, &br_val, &p, &dn, &up);
-      p_ord = glp_ios_node_ord(tree, p);
-
-      dn_ord = (dn >= 0) ? glp_ios_node_ord(tree, dn) : -1;
-      up_ord = (up >= 0) ? glp_ios_node_ord(tree, up) : -1;
-
-      Trace("approx::") << "branch: "<< br_var << " "  << br_val << " tree " << p << " " << dn << " " << up << endl;
-      Trace("approx::") << "\t " << p_ord << " " << dn_ord << " " << up_ord << endl;
-      if(dn < 0 && up < 0){
-        Trace("approx::") << "branch close " << exec << endl;
-        NodeLog& node = tl.getNode(p_ord);
-        BranchCutInfo* cut_br = branchCut(tree, exec, br_var, br_val, dn < 0);
-        node.addCut(cut_br);
-        tl.close(p_ord);
-      }else if(dn < 0 || up < 0){
-        Trace("approx::") << "branch cut" << exec << endl;
-        NodeLog& node = tl.getNode(p_ord);
-        BranchCutInfo* cut_br = branchCut(tree, exec, br_var, br_val, dn < 0);
-        node.addCut(cut_br);
-      }else{
-        Trace("approx::") << "normal branch" << endl;
-        tl.branch(p_ord, br_var, br_val, dn_ord, up_ord);
-      }
-    }
-    break;
-    case GLP_LI_CLOSE:
-      {
-        glpk_node_p = glp_ios_curr_node(tree);
-        node_ord = glp_ios_node_ord(tree, glpk_node_p);
-        Trace("approx::") << "close " << glpk_node_p << endl;
-        tl.close(node_ord);
-      }
-      break;
-    default:
-      break;
-    }
-  }
-
-  switch(glp_ios_reason(tree)){
-  case GLP_IBINGO:
-    Trace("approx::") << "bingo" << endl;
-    aux->term = MipBingo;
-    glp_ios_terminate(tree);
-    break;
-  case GLP_ICUTADDED:
-    {
-      tl.addCut();
-    }
-    break;
-  case GLP_LI_BRANCH:
-    {
-      int p, dn, up;
-      int br_var = glp_ios_branch_log(tree, NULL, &p, &dn, &up);
-
-      if(br_var >= 0){
-        unsigned v = br_var;
-        tl.logBranch(v);
-        int depth = glp_ios_node_level(tree, p);
-        unsigned ubl =  (aux->branchLimit) >= 0 ? ((unsigned)(aux->branchLimit)) : 0u;
-        if(tl.numBranches(v) >= ubl || depth >= (aux->branchDepth)){
-          aux->term = BranchesExhausted;
-          glp_ios_terminate(tree);
-        }
-      }
-    }
-    break;
-  case GLP_LI_CLOSE:
-    break;
-  default:
-    {
-      glp_prob* prob = glp_ios_get_prob(tree);
-      int iterationcount = glp_get_it_cnt(prob);
-      if(exec > (aux->pivotLimit)){
-        aux->term = ExecExhausted;
-        glp_ios_terminate(tree);
-      }else if(iterationcount > (aux->pivotLimit)){
-        aux->term = PivotsExhauasted;
-        glp_ios_terminate(tree);
-      }
-    }
-    break;
-  }
-}
-
-std::vector<const CutInfo*> ApproxGLPK::getValidCuts(const NodeLog& con)
-{
-  std::vector<const CutInfo*> proven;
-  int nid = con.getNodeId();
-  for(NodeLog::const_iterator j = con.begin(), jend=con.end(); j!=jend; ++j){
-    CutInfo* cut = *j;
-
-    if(cut->getKlass() != RowsDeletedKlass){
-      if(!cut->reconstructed()){
-        Assert(!cut->reconstructed());
-        tryCut(nid, *cut);
-      }
-    }
-
-    if(cut->proven()){
-      proven.push_back(cut);
-    }
-  }
-  return proven;
-}
-
-ArithVar ApproxGLPK::getBranchVar(const NodeLog& con) const{
-  int br_var = con.branchVariable();
-  return getArithVarFromStructural(br_var);
-}
-
-
-MipResult ApproxGLPK::solveMIP(bool activelyLog){
-  Assert(d_solvedRelaxation);
-  // Explicitly disable presolving
-  // We need the basis thus the presolver must be off!
-  // This is default, but this is just being cautious.
-  AuxInfo aux;
-  aux.pivotLimit = d_pivotLimit;
-  aux.branchLimit = d_branchLimit;
-  aux.branchDepth = d_maxDepth;
-  aux.tl = &d_log;
-  aux.term = MipUnknown;
-
-  d_log.reset(d_rootRowIds);
-  if(activelyLog){
-    d_log.makeActive();
-  }else{
-    d_log.makeInactive();
-  }
-
-  glp_iocp parm;
-  glp_init_iocp(&parm);
-  parm.presolve = GLP_OFF;
-  parm.pp_tech = GLP_PP_NONE;
-  parm.fp_heur = GLP_ON;
-  parm.gmi_cuts = GLP_ON;
-  parm.mir_cuts = GLP_ON;
-  parm.cov_cuts = GLP_ON;
-  parm.cb_func = glpkCallback;
-  parm.cb_info = &aux;
-  parm.msg_lev = GLP_MSG_OFF;
-
-  glp_erase_prob(d_mipProb);
-  glp_copy_prob(d_mipProb, d_realProb, GLP_OFF);
-
-  int res = glp_intopt(d_mipProb, &parm);
-
-  Trace("approx::solveMIP") << "res "<<res<<" aux.term "<< aux.term << endl;
-
-  switch(res){
-  case 0:
-  case GLP_ESTOP:
-    {
-      int status = glp_mip_status(d_mipProb);
-      Trace("approx::") << "status " << status << endl;
-      switch(status){
-      case GLP_OPT:
-      case GLP_FEAS:
-        d_solvedMIP = true;
-        Trace("approx::") << "bingo here!" << endl;
-        return MipBingo;
-      case GLP_NOFEAS:
-        d_solvedMIP = true;
-        return MipClosed;
-      default:
-        if(aux.term == MipBingo){
-          d_solvedMIP = true;
-          Trace("approx::") << "bingo here?" << endl;
-        }
-        return aux.term;
-      }
-    }
-  default:
-    return MipUnknown;
-  }
-}
-
-DeltaRational sumConstraints(const DenseMap<Rational>& xs, const DenseMap<ConstraintP>& cs, bool* anyinf){
-  if(anyinf != NULL){
-    *anyinf = false;
-  }
-
-  DeltaRational beta(0);
-  DenseMap<Rational>::const_iterator iter, end;
-  iter = xs.begin();
-  end = xs.end();
-
-  Trace("approx::sumConstraints") << "sumConstraints";
-  for(; iter != end; ++iter){
-    ArithVar x = *iter;
-    const Rational& psi = xs[x];
-    ConstraintP c = cs[x];
-    Assert(c != NullConstraint);
-
-    const DeltaRational& bound = c->getValue();
-    beta += bound * psi;
-    Trace("approx::sumConstraints") << " +("<<bound << "*" << psi <<")";
-    if(anyinf != NULL ){
-      *anyinf = *anyinf || !bound.infinitesimalIsZero();
-    }
-  }
-  Trace("approx::sumConstraints") << "= " << beta << endl;
-
-  return beta;
-}
-
-// remove fixed variables from the vector
-void removeFixed(const ArithVariables& vars, DenseVector& dv, set<ConstraintP>& exp){
-  DenseMap<Rational>& vec = dv.lhs;
-  Rational& removed = dv.rhs;
-  vector<ArithVar> equal;
-  DenseMap<Rational>::const_iterator vec_iter, vec_end;
-  vec_iter = vec.begin(), vec_end = vec.end();
-  for(; vec_iter != vec_end; ++vec_iter){
-    ArithVar x = *vec_iter;
-    if(vars.boundsAreEqual(x)){
-      equal.push_back(x);
-    }
-  }
-  vector<ArithVar>::const_iterator equal_iter, equal_end;
-  equal_iter = equal.begin(), equal_end = equal.end();
-  for(; equal_iter != equal_end; ++equal_iter){
-    ArithVar x = *equal_iter;
-    Assert(vars.boundsAreEqual(x));
-    const DeltaRational& lb = vars.getLowerBound(x);
-    Assert(lb.infinitesimalIsZero());
-    removed -= (vec[x]) * lb.getNoninfinitesimalPart();
-
-    vec.remove(x);
-
-    std::pair<ConstraintP, ConstraintP> p = vars.explainEqualBounds(x);
-    exp.insert(p.first);
-    Trace("removeFixed") << "remove fixed " << p.first << endl;
-    if(p.second != NullConstraint){
-      exp.insert(p.second);
-      Trace("removeFixed") << "remove fixed " << p.second << endl;
-    }
-  }
-}
-void removeZeroes(DenseMap<Rational>& v){
-  // Remove Slack variables
-  vector<ArithVar> zeroes;
-  DenseMap<Rational>::const_iterator i, iend;
-  for(i = v.begin(), iend = v.end(); i != iend; ++i){
-    ArithVar x = *i;
-    if(v[x].isZero()){
-      zeroes.push_back(x);
-    }
-  }
-
-  vector<ArithVar>::const_iterator j, jend;
-  for(j = zeroes.begin(), jend = zeroes.end(); j != jend; ++j){
-    ArithVar x = *j;
-    v.remove(x);
-  }
-}
-void removeZeroes(DenseVector& v){
-  removeZeroes(v.lhs);
-}
-
-void removeAuxillaryVariables(const ArithVariables& vars, DenseMap<Rational>& vec){
-  // Remove auxillary variables
-  vector<ArithVar> aux;
-  DenseMap<Rational>::const_iterator vec_iter, vec_end;
-  vec_iter = vec.begin(), vec_end = vec.end();
-  for(; vec_iter != vec_end; ++vec_iter){
-    ArithVar x = *vec_iter;
-    if(vars.isAuxiliary(x)){
-      aux.push_back(x);
-    }
-  }
-
-  vector<ArithVar>::const_iterator aux_iter, aux_end;
-  aux_iter = aux.begin(), aux_end = aux.end();
-  for(; aux_iter != aux_end; ++aux_iter){
-    ArithVar s = *aux_iter;
-    Rational& s_coeff = vec.get(s);
-    Assert(vars.isAuxiliary(s));
-    Assert(vars.hasNode(s));
-    Node sAsNode = vars.asNode(s);
-    Polynomial p = Polynomial::parsePolynomial(sAsNode);
-    for(Polynomial::iterator j = p.begin(), p_end=p.end(); j != p_end; ++j){
-      Monomial m = *j;
-      const Rational& ns_coeff = m.getConstant().getValue();
-      Node vl = m.getVarList().getNode();
-      ArithVar ns = vars.asArithVar(vl);
-      Rational prod = s_coeff * ns_coeff;
-      if(vec.isKey(ns)){
-        vec.get(ns) += prod;
-      }else{
-        vec.set(ns, prod);
-      }
-    }
-    s_coeff = Rational(0); // subtract s_coeff * s from vec
-  }
-  removeZeroes(vec);
-}
-
-ArithVar ApproxGLPK::_getArithVar(int nid, int M, int ind) const{
-  if(ind <= 0){
-    return ARITHVAR_SENTINEL;
-  }else if(ind <= M){
-    return getArithVarFromRow(nid, ind);
-  }else{
-    return getArithVarFromStructural(ind - M);
-  }
-}
-
-
-bool ApproxGLPK::guessIsConstructable(const DenseMap<Rational>& guess) const {
-  // basic variable
-  // sum g[i] * x_i
-  DenseMap<Rational> g = guess;
-  removeAuxillaryVariables(d_vars, g);
-
-  if(TraceIsOn("guessIsConstructable")){
-    if(!g.empty()){
-      Trace("approx::guessIsConstructable") << "guessIsConstructable failed " << g.size() << endl;
-      DenseVector::print(Trace("approx::guessIsConstructable"), g);
-      Trace("approx::guessIsConstructable") << endl;
-    }
-  }
-  return g.empty();
-}
-
-bool ApproxGLPK::loadToBound(int nid, int M, int len, int* inds, int* statuses, DenseMap<ConstraintP>& toBound) const{
-  for(int i = 1; i <= len; ++i){
-    int status = statuses[i];
-    int ind = inds[i];
-    ArithVar v = _getArithVar(nid, M, ind);
-    ConstraintP c = NullConstraint;
-    if(v == ARITHVAR_SENTINEL){ return true; }
-
-    switch(status){
-    case GLP_NL:
-      c = d_vars.getLowerBoundConstraint(v);
-      break;
-    case GLP_NU:
-    case GLP_NS: // upper bound sufficies for fixed variables
-      c = d_vars.getUpperBoundConstraint(v);
-      break;
-    case GLP_NF:
-    default:
-      return true;
-    }
-    if(c == NullConstraint){
-      Trace("approx::") << "couldn't find " << v << " @ " << nid << endl;
-      return true;
-    }
-    Assert(c != NullConstraint);
-    toBound.set(v, c);
-  }
-  return false;
-}
-
-bool ApproxGLPK::checkCutOnPad(int nid, const CutInfo& cut) const{
-
-  Trace("approx::checkCutOnPad") << "checkCutOnPad(" << nid <<", " << cut.getId() <<")"<<endl;
-
-  const DenseMap<Rational>& constructedLhs = d_pad.d_cut.lhs;
-  const Rational& constructedRhs = d_pad.d_cut.rhs;
-  std::unordered_set<ArithVar> visited;
-
-  if(constructedLhs.empty()){
-    Trace("approx::checkCutOnPad") << "its empty?" <<endl;
-    return true;
-  }
-  if(cut.getKind() != d_pad.d_cutKind) {
-    Trace("approx::checkCutOnPad") << "rel doesn't match" << endl;
-    return true;
-  }
-
-  const PrimitiveVec& cv = cut.getCutVector();
-  for(int i = 1; i <= cv.len; ++i){
-    int ind = cv.inds[i]; // this is always a structural variable
-    double coeff = cv.coeffs[i];
-
-
-
-    if(!d_colToArithVar.isKey(ind)){ return true; }
-    ArithVar x = d_colToArithVar[ind];
-    //if(x == ARITHVAR_SENTINEL){ return true; }
-    visited.insert(x);
-
-
-    if(!constructedLhs.isKey(x)){
-      if(TraceIsOn("approx::checkCutOnPad")){
-        Trace("approx::checkCutOnPad") << " didn't find key for " << x << std::endl;
-        cut.print(Trace("approx::checkCutOnPad"));
-        Trace("approx::checkCutOnPad") << endl;
-        d_pad.d_cut.print(Trace("approx::checkCutOnPad"));
-        Trace("approx::checkCutOnPad") << endl;
-      }
-      return true;
-    }
-
-    const Rational& onConstructed = constructedLhs[x];
-
-    Trace("approx::checkCutOnPad") << ind << " " << coeff  << " " << endl;
-    Trace("approx::checkCutOnPad") << " " << x << " " << onConstructed << endl;
-
-    if(!roughlyEqual(coeff, onConstructed.getDouble())){
-      Trace("approx::checkCutOnPad") << "coeff failure" << endl;
-      return true;
-    }
-  }
-  if(visited.size() != constructedLhs.size()){
-    Trace("approx::checkCutOnPad") << "size mismatch" << endl;
-    return true;
-  }
-
-
-  if(!roughlyEqual(cut.getRhs(), constructedRhs.getDouble())){
-    Trace("approx::checkCutOnPad")
-      << "norm rhs is off " << cut.getRhs() << " " << constructedRhs << endl;
-    return true;
-  }
-  return false;
-}
-
-
-
-bool ApproxGLPK::attemptBranchCut(int nid, const BranchCutInfo& br_cut){
-  d_pad.clear();
-
-  const PrimitiveVec& cut_vec = br_cut.getCutVector();
-  int structural = cut_vec.inds[1];
-  Assert(roughlyEqual(cut_vec.coeffs[1], +1.0));
-
-  ArithVar x = getArithVarFromStructural(structural);
-  d_pad.d_failure = (x == ARITHVAR_SENTINEL);
-  if(d_pad.d_failure){ return true; }
-
-  Kind brKind = br_cut.getKind();
-
-  d_pad.d_failure = (brKind != kind::LEQ && brKind != kind::GEQ);
-  if(d_pad.d_failure){ return true; }
-
-  d_pad.d_cutKind = brKind;
-  d_pad.d_cut.lhs.set(x, Rational(1));
-
-  Rational& rhs = d_pad.d_cut.rhs;
-  std::optional<Rational> br_cut_rhs = Rational::fromDouble(br_cut.getRhs());
-  if (!br_cut_rhs)
-  {
-    return true;
-  }
-
-  rhs = estimateWithCFE(*br_cut_rhs, Integer(1));
-  d_pad.d_failure = !rhs.isIntegral();
-  if(d_pad.d_failure) { return true; }
-
-  d_pad.d_failure = checkCutOnPad(nid, br_cut);
-  if(d_pad.d_failure){ return true; }
-
-  return false;
-}
-
-bool ApproxGLPK::attemptGmi(int nid, const GmiInfo& gmi){
-  d_pad.clear();
-
-  d_pad.d_cutKind = kind::GEQ;
-
-  int M = gmi.getMAtCreation();
-  ArithVar b = _getArithVar(nid, M, gmi.basic);
-  d_pad.d_failure = (b == ARITHVAR_SENTINEL);
-  if(d_pad.d_failure){ return true; }
-
-  d_pad.d_failure = !d_vars.isIntegerInput(b);
-  if(d_pad.d_failure){ return true; }
-
-  d_pad.d_basic = b;
-
-
-  const PrimitiveVec& tab = gmi.tab_row;
-  d_pad.d_failure = attemptConstructTableRow(nid, M, tab);
-  if(d_pad.d_failure){ return true; }
-
-  int* statuses = gmi.tab_statuses;
-  DenseMap<ConstraintP>& toBound = d_pad.d_toBound;
-  d_pad.d_failure = loadToBound(nid, M, tab.len, tab.inds, statuses, toBound);
-  if(d_pad.d_failure){ return true; }
-
-  d_pad.d_failure = constructGmiCut();
-  if(d_pad.d_failure){ return true; }
-
-  d_pad.d_failure = checkCutOnPad(nid, gmi);
-  if(d_pad.d_failure){ return true; }
-
-  return false;
-}
-
-bool ApproxGLPK::applyCMIRRule(int nid, const MirInfo& mir){
-
-  const DenseMap<Rational>& compRanges = d_pad.d_compRanges;
-
-  DenseMap<Rational>& alpha = d_pad.d_alpha.lhs;
-  Rational& b = d_pad.d_alpha.rhs;
-
-  std::optional<Rational> delta = estimateWithCFE(mir.delta);
-  if (!delta)
-  {
-    return true;
-  }
-
-  d_pad.d_failure = (delta->sgn() <= 0);
-  if(d_pad.d_failure){ return true; }
-
-  Trace("approx::mir") << "applyCMIRRule() " << delta << " " << mir.delta << endl;
-
-  DenseMap<Rational>::const_iterator iter, iend;
-  iter = alpha.begin(), iend = alpha.end();
-  for(; iter != iend; ++iter){
-    ArithVar v = *iter;
-    const Rational& curr = alpha[v];
-    Rational next = curr / *delta;
-    if(compRanges.isKey(v)){
-      b -= curr * compRanges[v];
-      alpha.set(v, - next);
-    }else{
-      alpha.set(v, next);
-    }
-  }
-  b = b / *delta;
-
-  Rational roundB = (b + Rational(1,2)).floor();
-  d_pad.d_failure = (b - roundB).abs() < Rational(1,90);
-  // intensionally more generous than glpk here
-  if(d_pad.d_failure){ return true; }
-
-  Rational one(1);
-  Rational fb = b.floor_frac();
-  Rational one_sub_fb = one - fb;
-  Rational gamma = (one / one_sub_fb);
-
-  DenseMap<Rational>& cut = d_pad.d_cut.lhs;
-  Rational& beta = d_pad.d_cut.rhs;
-
-  iter = alpha.begin(), iend = alpha.end();
-  for(; iter != iend; ++iter){
-    ArithVar v = *iter;
-    const Rational& a_j = alpha[v];
-    if(d_vars.isIntegerInput(v)){
-      Rational floor_aj = a_j.floor();
-      Rational frac_aj = a_j.floor_frac();
-      if(frac_aj <= fb){
-        cut.set(v, floor_aj);
-      }else{
-        Rational tmp =  ((frac_aj - fb) / one_sub_fb);
-        cut.set(v, floor_aj + tmp);
-      }
-    }else{
-      cut.set(v, a_j * gamma);
-    }
-  }
-  beta = b.floor();
-
-  iter = cut.begin(), iend = cut.end();
-  for(; iter != iend; ++iter){
-    ArithVar v = *iter;
-    if(compRanges.isKey(v)){
-      Rational neg = - cut[v];
-      beta += neg * compRanges[v];
-      cut.set(v, neg);
-    }
-  }
-
-  return false;
-}
-
-bool ApproxGLPK::attemptMir(int nid, const MirInfo& mir){
-  d_pad.clear();
-
-  d_pad.d_cutKind = kind::LEQ;
-
-  // virtual bounds must be done before slacks
-  d_pad.d_failure = loadVirtualBoundsIntoPad(nid, mir);
-  if(d_pad.d_failure){ return true; }
-
-  d_pad.d_failure = loadSlacksIntoPad(nid, mir);
-  if(d_pad.d_failure){ return true; }
-
-
-  d_pad.d_failure = loadRowSumIntoAgg(nid, mir.getMAtCreation(), mir.row_sum);
-  if(d_pad.d_failure){ return true; }
-
-  removeFixed(d_vars, d_pad.d_agg, d_pad.d_explanation);
-
-  d_pad.d_failure = buildModifiedRow(nid, mir);
-  if(d_pad.d_failure){ return true; }
-
-  d_pad.d_failure =  constructMixedKnapsack();
-  if(d_pad.d_failure){ return true; }
-
-  d_pad.d_failure = makeRangeForComplemented(nid, mir);
-  if(d_pad.d_failure){ return true; }
-
-  d_pad.d_failure = applyCMIRRule(nid, mir);
-  if(d_pad.d_failure){ return true; }
-
-  d_pad.d_failure = replaceSlacksOnCuts();
-  if(d_pad.d_failure){ return true; }
-
-  removeAuxillaryVariables(d_vars, d_pad.d_cut.lhs);
-
-  d_pad.d_failure = checkCutOnPad(nid, mir);
-  if(d_pad.d_failure){ return true; }
-
-  return false;
-  //return makeCutNodes(nid, mir);
-}
-
-/** Returns true on failure. */
-bool ApproxGLPK::loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp){
-  if(ri <= 0) { return true; }
-
-  Trace("glpk::loadVB") << "loadVB()" << endl;
-
-  ArithVar rowVar = _getArithVar(nid, M, ri);
-  ArithVar contVar = _getArithVar(nid, M, j);
-  if(rowVar == ARITHVAR_SENTINEL){
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " rowVar is ARITHVAR_SENTINEL " << rowVar << endl;
-    return true;
-  }
-  if(contVar == ARITHVAR_SENTINEL){
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " contVar is ARITHVAR_SENTINEL " << contVar
-                          << endl;
-    return true; }
-
-  if(!d_vars.isAuxiliary(rowVar)){
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " rowVar is not auxilliary " << rowVar << endl;
-    return true;
-  }
-  // is integer is correct here
-  if(d_vars.isInteger(contVar)){
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " contVar is integer " << contVar << endl;
-    return true;
-  }
-
-  ConstraintP lb = d_vars.getLowerBoundConstraint(rowVar);
-  ConstraintP ub = d_vars.getUpperBoundConstraint(rowVar);
-
-  if(lb != NullConstraint && ub != NullConstraint){
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " lb and ub are both NULL " << lb << " " << ub
-                          << endl;
-    return true;
-  }
-
-  ConstraintP rcon = lb == NullConstraint ? ub : lb;
-  if(rcon == NullConstraint) {
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " rcon is NULL " << rcon << endl;
-    return true;
-  }
-
-  if(!rcon->getValue().isZero()){
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " rcon value is not 0 " << rcon->getValue()
-                          << endl;
-    return true;
-  }
-
-  if(!d_vars.hasNode(rowVar)){
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " does not have node " << rowVar << endl;
-    return true;
-  }
-
-  Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(rowVar));
-  if (p.size() != 2)
-  {
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " polynomial is not binary: " << p.getNode()
-                          << endl;
-    return true;
-  }
-
-  Monomial first = p.getHead(), second = p.getTail().getHead();
-  Rational c1 = first.getConstant().getValue();
-  Rational c2 = second.getConstant().getValue();
-  Node nx1 = first.getVarList().getNode();
-  Node nx2 = second.getVarList().getNode();
-
-  if(!d_vars.hasArithVar(nx1)) {
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " does not have a variable for nx1: " << nx1
-                          << endl;
-    return true;
-  }
-  if(!d_vars.hasArithVar(nx2)) {
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " does not have a variable for nx2 " << nx2
-                          << endl;
-    return true;
-  }
-  ArithVar x1 = d_vars.asArithVar(nx1), x2 = d_vars.asArithVar(nx2);
-
-  Assert(x1 != x2);
-  Assert(!c1.isZero());
-  Assert(!c2.isZero());
-
-  Trace("glpk::loadVB")
-    << " lb " << lb
-    << " ub " << ub
-    << " rcon " << rcon
-    << " x1 " << x1
-    << " x2 " << x2
-    << " c1 " << c1
-    << " c2 " << c2 << endl;
-
-  ArithVar iv = (x1 == contVar) ? x2 : x1;
-  Rational& cc = (x1 == contVar) ? c1 : c2;
-  Rational& ic = (x1 == contVar) ? c2 : c1;
-
-  Trace("glpk::loadVB")
-    << " cv " << contVar
-    << " cc " << cc
-    << " iv " << iv
-    << " c2 " << ic << endl;
-
-  if(!d_vars.isIntegerInput(iv)){
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " iv is not an integer input variable " << iv
-                          << endl;
-    return true;
-  }
-  // cc * cv + ic * iv <= 0 or
-  // cc * cv + ic * iv <= 0
-
-  if(rcon == ub){ // multiply by -1
-    cc = -cc; ic = - ic;
-  }
-  Trace("glpk::loadVB") << " cv " << contVar
-                        << " cc " << cc
-                        << " iv " << iv
-                        << " c2 " << ic << endl;
-
-  // cc * cv + ic * iv >= 0
-  // cc * cv >= -ic * iv
-  // if cc < 0:
-  //   cv <= -ic/cc * iv
-  // elif cc > 0:
-  //   cv >= -ic/cc * iv
-  Assert(!cc.isZero());
-  Rational d = -ic/cc;
-  Trace("glpk::loadVB") << d << " " << cc.sgn() << endl;
-  bool nowUb = cc.sgn() < 0;
-  if(wantUb != nowUb) {
-    Trace("glpk::loadVB") << "loadVB()"
-                          << " wantUb is not nowUb " << wantUb << " " << nowUb
-                          << endl;
-
-    return true;
-  }
-
-  Kind rel = wantUb ? kind::LEQ : kind::GEQ;
-
-  tmp = VirtualBound(contVar, rel, d, iv, rcon);
-  Trace("glpk::loadVB") << "loadVB()"
-                        << " was successful" << endl;
-  return false;
-}
-
-bool ApproxGLPK::loadVirtualBoundsIntoPad(int nid, const MirInfo& mir){
-  Assert(mir.vlbRows != NULL);
-  Assert(mir.vubRows != NULL);
-
-  int N = mir.getN();
-  int M = mir.getMAtCreation();
-
-  // Load the virtual bounds first
-  VirtualBound tmp;
-  for(int j=1; j <= N+M; ++j){
-    if(!loadVB(nid, M, j, mir.vlbRows[j], false, tmp)){
-      if(d_pad.d_vlb.isKey(tmp.x)){ return true; }
-      d_pad.d_vlb.set(tmp.x, tmp);
-    }else if(mir.vlbRows[j] > 0){
-      Trace("approx::mir") << "expected vlb to work" << endl;
-    }
-    if(!loadVB(nid, M, j, mir.vubRows[j], true, tmp)){
-      if(d_pad.d_vub.isKey(tmp.x)){ return true; }
-      d_pad.d_vub.set(tmp.x, tmp);
-    }else if(mir.vubRows[j] > 0){
-      Trace("approx::mir") << "expected vub to work" << endl;
-    }
-  }
-  return false;
-}
-
-bool ApproxGLPK::loadSlacksIntoPad(int nid, const MirInfo& mir){
-  Assert(mir.vlbRows != NULL);
-  Assert(mir.vubRows != NULL);
-
-  int N = mir.getN();
-  int M = mir.getMAtCreation();
-
-  bool useVB;
-  // Load the virtual bounds first
-  SlackReplace rep;
-  bool lb;
-  ConstraintP b;
-  Trace("approx::mir") << "loadSlacksIntoPad(): N="<<N<<", M=" << M << std::endl;
-  for(int j=1; j <= N+M; ++j){
-    ArithVar v = _getArithVar(nid, M, j);
-    if(v == ARITHVAR_SENTINEL){
-      Trace("approx::mir") << " for: " << j << " no variable" << endl;
-      continue;
-    }
-    rep = SlackUndef;
-    char sub = mir.subst[j];
-    switch(sub){
-    case 'L':
-    case 'U':
-      lb = (sub == 'L');
-      useVB = lb ? (mir.vlbRows[j] > 0) : (mir.vubRows[j] > 0);
-      if(useVB){
-        if(lb ? d_pad.d_vlb.isKey(v) : d_pad.d_vub.isKey(v)){
-          rep = lb ? SlackVLB : SlackVUB;
-        }
-      }else{
-        b = lb ? d_vars.getLowerBoundConstraint(v)
-          : d_vars.getUpperBoundConstraint(v);
-        if(b != NullConstraint){
-          if(b->getValue().infinitesimalIsZero()){
-            rep = lb ? SlackLB : SlackUB;
-          }
-        }
-      }
-
-      Trace("approx::mir") << " for: " << j << ", " << v;
-      Trace("approx::mir") << " " << ((rep != SlackUndef) ? "succ" : "fail") << " ";
-      Trace("approx::mir") << sub << " " << rep << " " << mir.vlbRows[j] << " " << mir.vubRows[j]
-                           << endl;
-      if(rep != SlackUndef){
-        d_pad.d_slacks.set(v,rep);
-      }
-      break;
-    case '?':
-      continue;
-    default:
-      Trace("approx::mir") << " for: " << j << " got subst " << (int)sub << endl;
-      continue;
-    }
-  }
-  return false;
-}
-
-bool ApproxGLPK::replaceSlacksOnCuts(){
-  vector<ArithVar> virtualVars;
-
-  DenseMap<Rational>& cut = d_pad.d_cut.lhs;
-  Rational& cutRhs = d_pad.d_cut.rhs;
-
-  DenseMap<Rational>::const_iterator iter, iend;
-  iter = cut.begin(), iend = cut.end();
-  for(; iter != iend; ++iter){
-    ArithVar x = *iter;
-    SlackReplace rep = d_pad.d_slacks[x];
-    if(d_vars.isIntegerInput(x)){
-      Assert(rep == SlackLB || rep == SlackUB);
-      Rational& a = cut.get(x);
-
-      const DeltaRational& bound = (rep == SlackLB) ?
-        d_vars.getLowerBound(x) : d_vars.getUpperBound(x);
-      Assert(bound.infinitesimalIsZero());
-      Rational prod = a * bound.getNoninfinitesimalPart();
-      if(rep == SlackLB){
-        cutRhs += prod;
-      }else{
-        cutRhs -= prod;
-        a = -a;
-      }
-    }else if(rep == SlackVLB){
-      virtualVars.push_back(d_pad.d_vlb[x].y);
-    }else if(rep == SlackVUB){
-      virtualVars.push_back(d_pad.d_vub[x].y);
-    }
-  }
-
-  for(size_t i = 0; i < virtualVars.size(); ++i){
-    ArithVar x = virtualVars[i];
-    if(!cut.isKey(x)){
-      cut.set(x, Rational(0));
-    }
-  }
-
-  iter = cut.begin(), iend = cut.end();
-  for(; iter != iend; ++iter){
-    ArithVar x = *iter;
-    if(!d_vars.isIntegerInput(x)){
-      SlackReplace rep = d_pad.d_slacks[x];
-      Rational& a = cut.get(x);
-      switch(rep){
-      case SlackLB:
-        {
-          const DeltaRational& bound = d_vars.getLowerBound(x);
-          Assert(bound.infinitesimalIsZero());
-          cutRhs += a * bound.getNoninfinitesimalPart();
-        }
-        break;
-      case SlackUB:
-        {
-          const DeltaRational& bound = d_vars.getUpperBound(x);
-          Assert(bound.infinitesimalIsZero());
-          cutRhs -= a * bound.getNoninfinitesimalPart();
-          a = -a;
-        }
-        break;
-      case SlackVLB:
-      case SlackVUB:
-        {
-          bool lb = (rep == SlackVLB);
-          const VirtualBound& vb = lb ?
-            d_pad.d_vlb[x] : d_pad.d_vub[x];
-          ArithVar y = vb.y;
-          Assert(vb.x == x);
-          Assert(cut.isKey(y));
-          Rational& ycoeff = cut.get(y);
-          if(lb){
-            ycoeff -= a * vb.d;
-          }else{
-            ycoeff += a * vb.d;
-            a = -a;
-          }
-        }
-        break;
-      default:
-        return true;
-      }
-    }
-  }
-  removeZeroes(cut);
-  return false;
-}
-
-bool ApproxGLPK::loadRowSumIntoAgg(int nid, int M, const PrimitiveVec& row_sum){
-  DenseMap<Rational>& lhs = d_pad.d_agg.lhs;
-  d_pad.d_agg.rhs = Rational(0);
-
-  int len = row_sum.len;
-  for(int i = 1; i <= len; ++i){
-    int aux_ind = row_sum.inds[i]; // auxillary index
-    double coeff = row_sum.coeffs[i];
-    ArithVar x = _getArithVar(nid, M, aux_ind);
-    if(x == ARITHVAR_SENTINEL){ return true; }
-    std::optional<Rational> c = estimateWithCFE(coeff);
-    if (!c)
-    {
-      return true;
-    }
-
-    if (lhs.isKey(x))
-    {
-      lhs.get(x) -= *c;
-    }
-    else
-    {
-      lhs.set(x, -(*c));
-    }
-  }
-
-  Trace("approx::mir") << "beg loadRowSumIntoAgg() 1" << endl;
-  if(TraceIsOn("approx::mir")) { DenseVector::print(Trace("approx::mir"), lhs); }
-  removeAuxillaryVariables(d_vars, lhs);
-  Trace("approx::mir") << "end loadRowSumIntoAgg() 1" << endl;
-
-  if(TraceIsOn("approx::mir")){
-    Trace("approx::mir") << "loadRowSumIntoAgg() 2" << endl;
-    DenseVector::print(Trace("approx::mir"), lhs);
-    Trace("approx::mir") << "end loadRowSumIntoAgg() 2" << endl;
-  }
-
-  for(int i = 1; i <= len; ++i){
-    int aux_ind = row_sum.inds[i]; // auxillary index
-    double coeff = row_sum.coeffs[i];
-    ArithVar x = _getArithVar(nid, M, aux_ind);
-    Assert(x != ARITHVAR_SENTINEL);
-    std::optional<Rational> c = estimateWithCFE(coeff);
-    if (!c)
-    {
-      return true;
-    }
-    Assert(!lhs.isKey(x));
-    lhs.set(x, *c);
-  }
-
-  if(TraceIsOn("approx::mir")){
-    Trace("approx::mir") << "loadRowSumIntoAgg() 2" << endl;
-    DenseVector::print(Trace("approx::mir"), lhs);
-    Trace("approx::mir") << "end loadRowSumIntoAgg() 3" << endl;
-  }
-  return false;
-}
-
-bool ApproxGLPK::buildModifiedRow(int nid, const MirInfo& mir){
-  const DenseMap<Rational>& agg = d_pad.d_agg.lhs;
-  const Rational& aggRhs = d_pad.d_agg.rhs;
-  DenseMap<Rational>& mod = d_pad.d_mod.lhs;
-  Rational& modRhs = d_pad.d_mod.rhs;
-
-  Trace("approx::mir")
-    << "buildModifiedRow()"
-    << " |agg|=" << d_pad.d_agg.lhs.size()
-    << " |mod|=" << d_pad.d_mod.lhs.size()
-    << " |slacks|=" << d_pad.d_slacks.size()
-    << " |vlb|=" << d_pad.d_vub.size()
-    << " |vub|=" << d_pad.d_vlb.size() << endl;
-
-  mod.addAll(agg);
-  modRhs = aggRhs;
-  DenseMap<Rational>::const_iterator iter, iend;
-  for(iter = agg.begin(), iend = agg.end(); iter != iend; ++iter){
-    ArithVar x = *iter;
-    const Rational& c = mod[x];
-    if(!d_pad.d_slacks.isKey(x)){
-      Trace("approx::mir") << "missed x: " << x << endl;
-      return true;
-    }
-    SlackReplace rep = d_pad.d_slacks[x];
-    switch(rep){
-    case SlackLB: // skip for now
-    case SlackUB:
-      break;
-    case SlackVLB: /* x[k] = lb[k] * x[kk] + x'[k] */
-    case SlackVUB: /* x[k] = ub[k] * x[kk] - x'[k] */
-      {
-        Assert(!d_vars.isIntegerInput(x));
-        bool ub = (rep == SlackVUB);
-        const VirtualBound& vb =
-          ub ? d_pad.d_vub[x] : d_pad.d_vlb[x];
-        Assert(vb.x == x);
-        ArithVar y = vb.y;
-        Rational prod = c * vb.d;
-        if(mod.isKey(y)){
-          mod.get(x) += prod;
-        }else{
-          mod.set(y, prod);
-        }
-        if(ub){
-          mod.set(x, -c);
-        }
-        Assert(vb.c != NullConstraint);
-        d_pad.d_explanation.insert(vb.c);
-      }
-      break;
-    default:
-      return true;
-    }
-  }
-  removeZeroes(mod); /* if something cancelled we don't want it in the explanation */
-  for(iter = mod.begin(), iend = mod.end(); iter != iend; ++iter){
-    ArithVar x = *iter;
-    if(!d_pad.d_slacks.isKey(x)){  return true; }
-
-    SlackReplace rep = d_pad.d_slacks[x];
-    switch(rep){
-    case SlackLB: /* x = lb + x' */
-    case SlackUB: /* x = ub - x' */
-      {
-        bool ub = (rep == SlackUB);
-        ConstraintP b = ub ?  d_vars.getUpperBoundConstraint(x):
-          d_vars.getLowerBoundConstraint(x);
-
-        Assert(b != NullConstraint);
-        Assert(b->getValue().infinitesimalIsZero());
-        const Rational& c = mod.get(x);
-        modRhs -= c * b->getValue().getNoninfinitesimalPart();
-        if(ub){
-          mod.set(x, -c);
-        }
-        d_pad.d_explanation.insert(b);
-      }
-      break;
-    case SlackVLB: /* handled earlier */
-    case SlackVUB:
-      break;
-    default:
-      return true;
-    }
-  }
-  removeZeroes(mod);
-  return false;
-}
-
-bool ApproxGLPK::makeRangeForComplemented(int nid, const MirInfo& mir){
-  DenseMap<Rational>& alpha = d_pad.d_alpha.lhs;
-  int M = mir.getMAtCreation();
-  int N = mir.getN();
-  DenseMap<Rational>& compRanges = d_pad.d_compRanges;
-
-  int complemented = 0;
-
-  for(int j = 1; j <= M + N; ++j){
-    if(mir.cset[j] != 0){
-      complemented++;
-      ArithVar x = _getArithVar(nid, M, j);
-      if(!alpha.isKey(x)){ return true; }
-      if(!d_vars.isIntegerInput(x)){ return true; }
-      Assert(d_pad.d_slacks.isKey(x));
-      Assert(d_pad.d_slacks[x] == SlackLB || d_pad.d_slacks[x] == SlackUB);
-
-      ConstraintP lb = d_vars.getLowerBoundConstraint(x);
-      ConstraintP ub = d_vars.getUpperBoundConstraint(x);
-
-      if(lb == NullConstraint) { return true; }
-      if(ub == NullConstraint) { return true; }
-
-      if(!lb->getValue().infinitesimalIsZero()){
-        return true;
-      }
-      if(!ub->getValue().infinitesimalIsZero()){
-        return true;
-      }
-
-      const Rational& uval = ub->getValue().getNoninfinitesimalPart();
-      const Rational& lval = lb->getValue().getNoninfinitesimalPart();
-
-      d_pad.d_explanation.insert(lb);
-      d_pad.d_explanation.insert(ub);
-
-      Rational u = uval - lval;
-      // u is the same for both rep == LP and rep == UB
-      if(compRanges.isKey(x)) { return true; }
-      compRanges.set(x,u);
-    }
-  }
-
-  Trace("approx::mir") <<  "makeRangeForComplemented()" << complemented << endl;
-  return false;
-}
-
-
-bool ApproxGLPK::constructMixedKnapsack(){
-  const DenseMap<Rational>& mod = d_pad.d_mod.lhs;
-  const Rational& modRhs = d_pad.d_mod.rhs;
-  DenseMap<Rational>& alpha = d_pad.d_alpha.lhs;
-  Rational& beta = d_pad.d_alpha.rhs;
-
-  Assert(alpha.empty());
-  beta = modRhs;
-
-  unsigned intVars = 0;
-  unsigned remain = 0;
-  unsigned dropped = 0;
-  DenseMap<Rational>::const_iterator iter, iend;
-  for(iter = mod.begin(), iend = mod.end(); iter != iend; ++iter){
-    ArithVar v = *iter;
-    const Rational& c = mod[v];
-    Assert(!c.isZero());
-    if(d_vars.isIntegerInput(v)){
-      intVars++;
-      alpha.set(v, c);
-    }else if(c.sgn() < 0){
-      remain++;
-      alpha.set(v, c);
-    }else{
-      dropped++;
-    }
-  }
-
-  Trace("approx::mir")
-    << "constructMixedKnapsack() "
-    <<" dropped " << dropped
-    <<" remain " << remain
-    <<" intVars " << intVars
-    << endl;
-  return intVars == 0; // if this is 0 we have failed
-}
-
-bool ApproxGLPK::attemptConstructTableRow(int nid, int M, const PrimitiveVec& vec){
-  bool failed = guessCoefficientsConstructTableRow(nid, M, vec);
-  if(failed){
-    failed = gaussianElimConstructTableRow(nid, M, vec);
-  }
-
-  return failed;
-}
-
-bool ApproxGLPK::gaussianElimConstructTableRow(int nid, int M, const PrimitiveVec& vec){
-  TimerStat::CodeTimer codeTimer(d_stats.d_gaussianElimConstructTime);
-  ++d_stats.d_gaussianElimConstruct;
-
-  ArithVar basic = d_pad.d_basic;
-  DenseMap<Rational>& tab = d_pad.d_tabRow.lhs;
-  tab.purge();
-  d_pad.d_tabRow.rhs = Rational(0);
-  Assert(basic != ARITHVAR_SENTINEL);
-  Assert(tab.empty());
-  Assert(d_pad.d_tabRow.rhs.isZero());
-
-  if(d_vars.isAuxiliary(basic)) { return true; }
-
-  if(TraceIsOn("gaussianElimConstructTableRow")){
-    Trace("gaussianElimConstructTableRow") << "1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-    vec.print(Trace("gaussianElimConstructTableRow"));
-    Trace("gaussianElimConstructTableRow") << "match " << basic << "("<<d_vars.asNode(basic)<<")"<<endl;
-  }
-
-  set<ArithVar> onrow;
-  for(int i = 1; i <= vec.len; ++i){
-    int ind = vec.inds[i];
-    ArithVar var = _getArithVar(nid, M, ind);
-    if(var == ARITHVAR_SENTINEL){
-      Trace("gaussianElimConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl;
-      return true;
-    }
-    onrow.insert(var);
-  }
-
-
-  Trace("gaussianElimConstructTableRow") << "2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-
-  Matrix<Rational> A;
-  A.increaseSizeTo(d_vars.getNumberOfVariables());
-  std::vector< std::pair<RowIndex, ArithVar> > rows;
-  // load the rows for auxiliary variables into A
-  for (ArithVar v : onrow)
-  {
-    if(d_vars.isAuxiliary(v)){
-      Assert(d_vars.hasNode(v));
-
-      vector<Rational> coeffs;
-      vector<ArithVar> vars;
-
-      coeffs.push_back(Rational(-1));
-      vars.push_back(v);
-
-      Node n = d_vars.asNode(v);
-      Polynomial p = Polynomial::parsePolynomial(n);
-      Polynomial::iterator j = p.begin(), jend=p.end();
-      for(j=p.begin(), jend=p.end(); j!=jend; ++j){
-        Monomial m = *j;
-        if(m.isConstant()) { return true; }
-        VarList vl = m.getVarList();
-        if(!d_vars.hasArithVar(vl.getNode())){ return true; }
-        ArithVar x = d_vars.asArithVar(vl.getNode());
-        const Rational& q = m.getConstant().getValue();
-        coeffs.push_back(q); vars.push_back(x);
-      }
-      RowIndex rid = A.addRow(coeffs, vars);
-      rows.push_back(make_pair(rid, ARITHVAR_SENTINEL));
-    }
-  }
-  Trace("gaussianElimConstructTableRow") << "3 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-
-  for(size_t i=0; i < rows.size(); ++i){
-    RowIndex rid = rows[i].first;
-    Assert(rows[i].second == ARITHVAR_SENTINEL);
-
-    // substitute previous rows
-    for(size_t j=0; j < i; j++){
-      RowIndex prevRow = rows[j].first;
-      ArithVar other = rows[j].second;
-      Assert(other != ARITHVAR_SENTINEL);
-      const Matrix<Rational>::Entry& e = A.findEntry(rid, other);
-      if(!e.blank()){
-        // r_p : 0 = -1 * other + sum a_i x_i
-        // rid : 0 =  e * other + sum b_i x_i
-        // rid += e * r_p
-        //     : 0 = 0 * other + ...
-        Assert(!e.getCoefficient().isZero());
-
-        Rational cp = e.getCoefficient();
-        Trace("gaussianElimConstructTableRow")
-          << "on " << rid << " subst " << cp << "*" << prevRow << " " << other << endl;
-        A.rowPlusRowTimesConstant(rid, prevRow, cp);
-      }
-    }
-    if(TraceIsOn("gaussianElimConstructTableRow")){
-      A.printMatrix(Trace("gaussianElimConstructTableRow"));
-    }
-
-    // solve the row for anything other than non-basics
-    bool solveForBasic = (i + 1 == rows.size());
-    Rational q;
-    ArithVar s = ARITHVAR_SENTINEL;
-    Matrix<Rational>::RowIterator k = A.getRow(rid).begin();
-    Matrix<Rational>::RowIterator k_end = A.getRow(rid).end();
-    for(; k != k_end; ++k){
-      const Matrix<Rational>::Entry& e = *k;
-      ArithVar colVar = e.getColVar();
-      bool selectColVar = false;
-      if(colVar == basic){
-        selectColVar = solveForBasic;
-      }else if(onrow.find(colVar) == onrow.end()) {
-        selectColVar = true;
-      }
-      if(selectColVar){
-        s = colVar;
-        q = e.getCoefficient();
-      }
-    }
-    if(s == ARITHVAR_SENTINEL || q.isZero()){
-      Trace("gaussianElimConstructTableRow") << "3 fail gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-      return true;
-    }else{
-      // 0 = q * s + sum c_i * x_i
-      Rational mult = -(q.inverse());
-      Trace("gaussianElimConstructTableRow") << "selecting " << s << " : " << mult << endl;
-      Trace("gaussianElimConstructTableRow") << "selecting " << rid << " " << s << endl;
-      //cout << "selecting " << s << " : complexity " << mult.complexity() << " " << mult << endl;
-      //cout << "selecting " << rid << " " << s << endl;
-      A.multiplyRowByConstant(rid, mult);
-      rows[i].second = s;
-    }
-  }
-  Trace("gaussianElimConstructTableRow") << "4 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-
-  if(rows.empty()) {
-    Trace("gaussianElimConstructTableRow") << "4 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-    return true;
-  }
-  RowIndex rid_last = rows.back().first;
-  ArithVar rid_var = rows.back().second;
-  if(rid_var != basic){
-    Trace("gaussianElimConstructTableRow") << "4 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-    return true;
-  }
-
-  Assert(tab.empty());
-
-  Matrix<Rational>::RowIterator k = A.getRow(rid_last).begin();
-  Matrix<Rational>::RowIterator k_end = A.getRow(rid_last).end();
-  for(; k != k_end; ++k){
-    const Matrix<Rational>::Entry& e = *k;
-    tab.set(e.getColVar(), e.getCoefficient());
-  }
-  Trace("gaussianElimConstructTableRow") << "5 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-  if(!tab.isKey(basic)){
-    Trace("gaussianElimConstructTableRow") << "5 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-    return true;
-  }
-  if(tab[basic] != Rational(-1)){
-    Trace("gaussianElimConstructTableRow") << "5 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-    return true;
-  }
-
-  tab.remove(basic);
-  Trace("gaussianElimConstructTableRow") << "6 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-
-  if(vec.len < 0 ){
-    Trace("gaussianElimConstructTableRow") << "6 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-    return true;
-  }
-  if(tab.size() != ((unsigned)vec.len) ) {
-    Trace("gaussianElimConstructTableRow") << "6 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<< tab.size() <<  " " << vec.len << endl;
-    return true;
-  }
-
-  Trace("gaussianElimConstructTableRow") << "7 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-
-  for(int i = 1; i <= vec.len; ++i){
-    int ind = vec.inds[i];
-    double coeff = vec.coeffs[i];
-    ArithVar var = _getArithVar(nid, M, ind);
-    Assert(var != ARITHVAR_SENTINEL);
-    if(!tab.isKey(var)){
-      Trace("gaussianElimConstructTableRow") << "7 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-      return true;
-    }
-
-    double est = tab[var].getDouble();
-
-    if(!ApproximateSimplex::roughlyEqual(coeff, est)){
-      Trace("gaussianElimConstructTableRow") << "7 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"
-           << " boink on " << ind << " " << var << " " << est <<endl;
-      return true;
-    }
-    Trace("gaussianElimConstructTableRow") << var << " cfe " << coeff << endl;
-  }
-
-  Trace("gaussianElimConstructTableRow")
-    << "gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"
-    << " superduper" << endl;
-
-  return false;
-}
-bool ApproxGLPK::guessCoefficientsConstructTableRow(int nid, int M, const PrimitiveVec& vec){
-  for(size_t i=0; i < d_denomGuesses.size(); ++i){
-    const Integer& D = d_denomGuesses[i];
-    if(!guessCoefficientsConstructTableRow(nid, M, vec, D)){
-      d_stats.d_averageGuesses << i+1;
-      Trace("approx::gmi") << "guesseditat " << i << " D=" << D << endl;
-      return false;
-    }
-  }
-  return true;
-}
-bool ApproxGLPK::guessCoefficientsConstructTableRow(int nid, int M, const PrimitiveVec& vec, const Integer& D){
-  ArithVar basic = d_pad.d_basic;
-  DenseMap<Rational>& tab = d_pad.d_tabRow.lhs;
-  tab.purge();
-  d_pad.d_tabRow.rhs = Rational(0);
-  Assert(basic != ARITHVAR_SENTINEL);
-  Assert(tab.empty());
-  Assert(d_pad.d_tabRow.rhs.isZero());
-
-  if(TraceIsOn("guessCoefficientsConstructTableRow")){
-    Trace("guessCoefficientsConstructTableRow")  << "attemptConstructTableRow("<<nid <<", "<< basic<<",...," << D<< ")"<<endl;
-    vec.print(Trace("guessCoefficientsConstructTableRow"));
-    Trace("guessCoefficientsConstructTableRow") << "match " << basic << "("<<d_vars.asNode(basic)<<")"<<endl;
-  }
-
-  tab.set(basic, Rational(-1));
-  for(int i = 1; i <= vec.len; ++i){
-    int ind = vec.inds[i];
-    double coeff = vec.coeffs[i];
-    ArithVar var = _getArithVar(nid, M, ind);
-    if(var == ARITHVAR_SENTINEL){
-      Trace("guessCoefficientsConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl;
-      return true;
-    }
-    Trace("guessCoefficientsConstructTableRow") << "match " << ind << "," << var << "("<<d_vars.asNode(var)<<")"<<endl;
-
-    std::optional<Rational> cfe = estimateWithCFE(coeff, D);
-    if (!cfe)
-    {
-      return true;
-    }
-    tab.set(var, *cfe);
-    Trace("guessCoefficientsConstructTableRow") << var << " cfe " << cfe << endl;
-  }
-  if(!guessIsConstructable(tab)){
-    Trace("guessCoefficientsConstructTableRow") << "failed to construct with " << D  << endl;
-    return true;
-  }
-  tab.remove(basic);
-  return false;
-}
-
-/* Maps an ArithVar to either an upper/lower bound */
-bool ApproxGLPK::constructGmiCut(){
-  const DenseMap<Rational>& tabRow = d_pad.d_tabRow.lhs;
-  const DenseMap<ConstraintP>& toBound = d_pad.d_toBound;
-  DenseMap<Rational>& cut = d_pad.d_cut.lhs;
-  std::set<ConstraintP>& explanation = d_pad.d_explanation;
-  Rational& rhs = d_pad.d_cut.rhs;
-
-  DenseMap<Rational>::const_iterator iter, end;
-  Assert(cut.empty());
-
-  // compute beta for a "fake" assignment
-  bool anyInf;
-  DeltaRational dbeta = sumConstraints(tabRow, toBound, &anyInf);
-  const Rational& beta = dbeta.getNoninfinitesimalPart();
-  Trace("approx::gmi") << dbeta << endl;
-  if(anyInf || beta.isIntegral()){ return true; }
-
-  Rational one = Rational(1);
-  Rational fbeta = beta.floor_frac();
-  rhs = fbeta;
-  Assert(fbeta.sgn() > 0);
-  Assert(fbeta < one);
-  Rational one_sub_fbeta = one - fbeta;
-  for(iter = tabRow.begin(), end = tabRow.end(); iter != end; ++iter){
-    ArithVar x = *iter;
-    const Rational& psi = tabRow[x];
-    ConstraintP c = toBound[x];
-    const Rational& bound = c->getValue().getNoninfinitesimalPart();
-    if(d_vars.boundsAreEqual(x)){
-      // do not add a coefficient
-      // implictly substitute the variable w/ its constraint
-      std::pair<ConstraintP, ConstraintP> exp = d_vars.explainEqualBounds(x);
-      explanation.insert(exp.first);
-      if(exp.second != NullConstraint){
-        explanation.insert(exp.second);
-      }
-    }else if(d_vars.isIntegerInput(x) && psi.isIntegral()){
-      // do not add a coefficient
-      // nothing to explain
-      Trace("approx::gmi") << "skipping " << x << endl;
-    }else{
-      explanation.insert(c);
-      Rational phi;
-      Rational alpha = (c->isUpperBound() ? psi : -psi);
-
-      // x - ub <= 0 and lb - x <= 0
-      if(d_vars.isIntegerInput(x)){
-        Assert(!psi.isIntegral());
-        // alpha = slack_sgn * psi
-        Rational falpha = alpha.floor_frac();
-        Assert(falpha.sgn() > 0);
-        Assert(falpha < one);
-        phi = (falpha <= fbeta) ?
-          falpha : ((fbeta / one_sub_fbeta) * (one - falpha));
-      }else{
-        phi = (alpha >= 0) ?
-          alpha : ((fbeta / one_sub_fbeta) * (- alpha));
-      }
-      Assert(phi.sgn() != 0);
-      if(c->isUpperBound()){
-        cut.set(x, -phi);
-        rhs -= phi * bound;
-      }else{
-        cut.set(x, phi);
-        rhs += phi * bound;
-      }
-    }
-  }
-  if(TraceIsOn("approx::gmi")){
-    Trace("approx::gmi") << "pre removeSlackVariables";
-    d_pad.d_cut.print(Trace("approx::gmi"));
-    Trace("approx::gmi") << endl;
-  }
-  removeAuxillaryVariables(d_vars, cut);
-
-  if(TraceIsOn("approx::gmi")){
-    Trace("approx::gmi") << "post removeAuxillaryVariables";
-    d_pad.d_cut.print(Trace("approx::gmi"));
-    Trace("approx::gmi") << endl;
-  }
-  removeFixed(d_vars, d_pad.d_cut, explanation);
-
-  if(TraceIsOn("approx::gmi")){
-    Trace("approx::gmi") << "post removeFixed";
-    d_pad.d_cut.print(Trace("approx::gmi"));
-    Trace("approx::gmi") << endl;
-  }
-  return false;
-}
-
-void ApproxGLPK::tryCut(int nid, CutInfo& cut)
-{
-  Assert(!cut.reconstructed());
-  Assert(cut.getKlass() != RowsDeletedKlass);
-  bool failure = false;
-  switch(cut.getKlass()){
-  case GmiCutKlass:
-    failure = attemptGmi(nid, static_cast<const GmiInfo&>(cut));
-    break;
-  case MirCutKlass:
-    failure = attemptMir(nid, static_cast<const MirInfo&>(cut));
-    break;
-  case BranchCutKlass:
-    failure = attemptBranchCut(nid, dynamic_cast<const BranchCutInfo&>(cut));
-    break;
-  default:
-    break;
-  }
-  Assert(failure == d_pad.d_failure);
-
-  if(!failure){
-    // move the pad to the cut
-    cut.setReconstruction(d_pad.d_cut);
-
-    if(cut.getKlass() != BranchCutKlass){
-      std::set<ConstraintP>& exp = d_pad.d_explanation;
-      ConstraintCPVec asvec(exp.begin(), exp.end());
-      cut.swapExplanation(asvec);
-    }
-  }else{
-    Trace("approx") << "failure " << cut.getKlass() << endl;
-  }
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-#endif /*#ifdef CVC5_USE_GLPK */
-/* End GPLK implementation. */
diff --git a/src/theory/arith/approx_simplex.h b/src/theory/arith/approx_simplex.h
deleted file mode 100644 (file)
index 0b4bc2c..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <optional>
-#include <vector>
-
-#include "theory/arith/arithvar.h"
-#include "theory/arith/delta_rational.h"
-#include "util/dense_map.h"
-#include "util/rational.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-enum LinResult {
-  LinUnknown,  /* Unknown error */
-  LinFeasible, /* Relaxation is feasible */
-  LinInfeasible,   /* Relaxation is infeasible/all integer branches closed */
-  LinExhausted
-};
-
-enum MipResult {
-  MipUnknown,  /* Unknown error */
-  MipBingo,    /* Integer feasible */
-  MipClosed,   /* All integer branches closed */
-  BranchesExhausted, /* Exhausted number of branches */
-  PivotsExhauasted,  /* Exhausted number of pivots */
-  ExecExhausted      /* Exhausted total operations */
-};
-std::ostream& operator<<(std::ostream& out, MipResult res);
-
-class ApproximateStatistics {
- public:
-  ApproximateStatistics();
-
-  IntStat d_branchMaxDepth;
-  IntStat d_branchesMaxOnAVar;
-
-  TimerStat d_gaussianElimConstructTime;
-  IntStat d_gaussianElimConstruct;
-  AverageStat d_averageGuesses;
-};
-
-
-class NodeLog;
-class TreeLog;
-class ArithVariables;
-class CutInfo;
-
-class ApproximateSimplex{
- public:
-  static bool enabled();
-
-  /**
-   * If glpk is enabled, return a subclass that can do something.
-   * If glpk is disabled, return a subclass that does nothing.
-   */
-  static ApproximateSimplex* mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s);
-  ApproximateSimplex(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s);
-  virtual ~ApproximateSimplex(){}
-
-  /* the maximum pivots allowed in a query. */
-  void setPivotLimit(int pl);
-
-  /* maximum branches allowed on a variable */
-  void setBranchOnVariableLimit(int bl);
-
-  /* maximum branches allowed on a variable */
-  void setBranchingDepth(int bd);
-
-  /** A result is either sat, unsat or unknown.*/
-  struct Solution {
-    DenseSet newBasis;
-    DenseMap<DeltaRational> newValues;
-    Solution() : newBasis(), newValues(){}
-  };
-
-  virtual ArithVar getBranchVar(const NodeLog& nl) const = 0;
-
-  /** Sets a maximization criteria for the approximate solver.*/
-  virtual void setOptCoeffs(const ArithRatPairVec& ref) = 0;
-
-  virtual ArithRatPairVec heuristicOptCoeffs() const = 0;
-
-  virtual LinResult solveRelaxation() = 0;
-  virtual Solution extractRelaxation() const = 0;
-
-  virtual MipResult solveMIP(bool activelyLog) = 0;
-
-  virtual Solution extractMIP() const = 0;
-
-  virtual std::vector<const CutInfo*> getValidCuts(const NodeLog& node) = 0;
-
-  virtual void tryCut(int nid, CutInfo& cut) = 0;
-
-  /** UTILITIES FOR DEALING WITH ESTIMATES */
-
-  static constexpr double SMALL_FIXED_DELTA = .000000001;
-  static constexpr double TOLERENCE = 1 + .000000001;
-
-  /** Returns true if two doubles are roughly equal based on TOLERENCE and SMALL_FIXED_DELTA.*/
-  static bool roughlyEqual(double a, double b);
-
-  /**
-   * Estimates a double as a Rational using continued fraction expansion that
-   * cuts off the estimate once the value is approximately zero.
-   * This is designed for removing rounding artifacts.
-   */
-  static std::optional<Rational> estimateWithCFE(double d);
-  static std::optional<Rational> estimateWithCFE(double d, const Integer& D);
-
-  /**
-   * Converts a rational to a continued fraction expansion representation
-   * using a maximum number of expansions equal to depth as long as the expression
-   * is not roughlyEqual() to 0.
-   */
-  static std::vector<Integer> rationalToCfe(const Rational& q, int depth);
-
-  /** Converts a continued fraction expansion representation to a rational. */
-  static Rational cfeToRational(const std::vector<Integer>& exp);
-
-  /** Estimates a rational as a continued fraction expansion.*/
-  static Rational estimateWithCFE(const Rational& q, const Integer& K);
-
-  virtual double sumInfeasibilities(bool mip) const = 0;
-
- protected:
-  const ArithVariables& d_vars;
-  TreeLog& d_log;
-  ApproximateStatistics& d_stats;
-
-  /* the maximum pivots allowed in a query. */
-  int d_pivotLimit;
-
-  /* maximum branches allowed on a variable */
-  int d_branchLimit;
-
-  /* maxmimum branching depth allowed.*/
-  int d_maxDepth;
-
-  /* Default denominator for diophatine approximation, 2^{26} .*/
-  static constexpr uint64_t s_defaultMaxDenom = (1 << 26);
-};/* class ApproximateSimplex */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
index 6bf79b4ea39c692b6c8ed104015f3c123a097d8c..ee79b837567a8df25761e6645bdcda0ab0ab0668 100644 (file)
@@ -26,7 +26,7 @@
 #include "preprocessing/util/ite_utilities.h"
 #include "smt/env.h"
 #include "theory/arith/arith_utilities.h"
-#include "theory/arith/normal_form.h"
+#include "theory/arith/linear/normal_form.h"
 #include "theory/rewriter.h"
 #include "theory/substitutions.h"
 #include "theory/theory_model.h"
@@ -102,7 +102,7 @@ Node ArithIteUtils::reduceVariablesInItes(Node n){
   default:
   {
     TypeNode tn = n.getType();
-    if (tn.isRealOrInt() && Polynomial::isMember(n))
+    if (tn.isRealOrInt() && linear::Polynomial::isMember(n))
     {
       Node newn = Node::null();
       if(!d_contains.containsTermITE(n)){
@@ -110,12 +110,12 @@ Node ArithIteUtils::reduceVariablesInItes(Node n){
       }else if(n.getNumChildren() > 0){
         newn = applyReduceVariablesInItes(n);
         newn = rewrite(newn);
-        Assert(Polynomial::isMember(newn));
+        Assert(linear::Polynomial::isMember(newn));
       }else{
         newn = n;
       }
       NodeManager* nm = NodeManager::currentNM();
-      Polynomial p = Polynomial::parsePolynomial(newn);
+      linear::Polynomial p = linear::Polynomial::parsePolynomial(newn);
       if(p.isConstant()){
         d_constants[n] = newn;
         d_varParts[n] = nm->mkConstRealOrInt(tn, Rational(0));
@@ -127,7 +127,7 @@ Node ArithIteUtils::reduceVariablesInItes(Node n){
         d_reduceVar[n] = p.getNode();
         return p.getNode();
       }else{
-        Monomial mc = p.getHead();
+        linear::Monomial mc = p.getHead();
         d_constants[n] = mc.getConstant().getNode();
         d_varParts[n] = p.getTail().getNode();
         d_reduceVar[n] = newn;
@@ -444,12 +444,12 @@ bool ArithIteUtils::solveBinOr(TNode binor){
       Node useForCmpL = selectForCmp(otherL);
       Node useForCmpR = selectForCmp(otherR);
 
-      Assert(Polynomial::isMember(sel));
-      Assert(Polynomial::isMember(useForCmpL));
-      Assert(Polynomial::isMember(useForCmpR));
-      Polynomial lside = Polynomial::parsePolynomial( useForCmpL );
-      Polynomial rside = Polynomial::parsePolynomial( useForCmpR );
-      Polynomial diff = lside-rside;
+      Assert(linear::Polynomial::isMember(sel));
+      Assert(linear::Polynomial::isMember(useForCmpL));
+      Assert(linear::Polynomial::isMember(useForCmpR));
+      linear::Polynomial lside = linear::Polynomial::parsePolynomial( useForCmpL );
+      linear::Polynomial rside = linear::Polynomial::parsePolynomial( useForCmpR );
+      linear::Polynomial diff = lside-rside;
 
       Trace("arith::ite") << "diff: " << diff.getNode() << endl;
       if(diff.isConstant()){
index 8add0b5ec44b10d9d6cc092e1ca8d41c1435b72f..b32d488a29fcaae2f7f2925c4a7043c18e8ba4e2 100644 (file)
@@ -28,7 +28,6 @@
 #include "smt/logic_exception.h"
 #include "theory/arith/arith_msum.h"
 #include "theory/arith/arith_utilities.h"
-#include "theory/arith/normal_form.h"
 #include "theory/arith/operator_elim.h"
 #include "theory/arith/rewriter/addition.h"
 #include "theory/arith/rewriter/node_utils.h"
index e74790d26548ccb796af6b6917c4b166d61636e5..0f2a55f842e016d14464d1281fc90aad9f1e6fa2 100644 (file)
@@ -13,7 +13,6 @@
  * Rewriter for the theory of arithmetic.
  *
  * This rewrites to the normal form for arithmetic.
- * See theory/arith/normal_form.h for more information.
  */
 
 #include "cvc5_private.h"
index 86088cc063205b0552d6f08b08f9705cf7ae3ad0..315551b4e0d23febafa4d38a4e2306bd842a308a 100644 (file)
@@ -15,7 +15,7 @@
 
 #include "theory/arith/arith_state.h"
 
-#include "theory/arith/theory_arith_private.h"
+#include "theory/arith/linear/theory_arith_private.h"
 
 namespace cvc5::internal {
 namespace theory {
@@ -31,7 +31,7 @@ bool ArithState::isInConflict() const
   return d_parent->anyConflict() || d_conflict;
 }
 
-void ArithState::setParent(TheoryArithPrivate* p) { d_parent = p; }
+void ArithState::setParent(linear::TheoryArithPrivate* p) { d_parent = p; }
 
 }  // namespace arith
 }  // namespace theory
index 3df2d68b0153395fa321d9446519649a7b454dae..2fffb8cc52f9a5eeabd683d345238e956b3edfcd 100644 (file)
@@ -24,7 +24,9 @@ namespace cvc5::internal {
 namespace theory {
 namespace arith {
 
+namespace linear {
 class TheoryArithPrivate;
+}
 
 /**
  * The arithmetic state.
@@ -44,11 +46,11 @@ class ArithState : public TheoryState
   /** Are we currently in conflict? */
   bool isInConflict() const override;
   /** Set parent */
-  void setParent(TheoryArithPrivate* p);
+  void setParent(linear::TheoryArithPrivate* p);
 
  private:
   /** reference to parent */
-  TheoryArithPrivate* d_parent;
+  linear::TheoryArithPrivate* d_parent;
 };
 
 }  // namespace arith
diff --git a/src/theory/arith/arith_static_learner.cpp b/src/theory/arith/arith_static_learner.cpp
deleted file mode 100644 (file)
index f45f597..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Dejan Jovanovic, Andres Noetzli
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include <vector>
-
-#include "base/output.h"
-#include "expr/node_algorithm.h"
-#include "options/arith_options.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/arith_static_learner.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/normal_form.h"
-#include "theory/rewriter.h"
-
-using namespace std;
-using namespace cvc5::internal::kind;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-ArithStaticLearner::ArithStaticLearner(context::Context* userContext)
-    : d_minMap(userContext), d_maxMap(userContext), d_statistics()
-{
-}
-
-ArithStaticLearner::~ArithStaticLearner(){
-}
-
-ArithStaticLearner::Statistics::Statistics()
-    : d_iteMinMaxApplications(smtStatisticsRegistry().registerInt(
-        "theory::arith::iteMinMaxApplications")),
-      d_iteConstantApplications(smtStatisticsRegistry().registerInt(
-          "theory::arith::iteConstantApplications"))
-{
-}
-
-void ArithStaticLearner::staticLearning(TNode n, NodeBuilder& learned)
-{
-  vector<TNode> workList;
-  workList.push_back(n);
-  TNodeSet processed;
-
-  //Contains an underapproximation of nodes that must hold.
-  TNodeSet defTrue;
-
-  defTrue.insert(n);
-
-  while(!workList.empty()) {
-    n = workList.back();
-
-    bool unprocessedChildren = false;
-    for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
-      if(processed.find(*i) == processed.end()) {
-        // unprocessed child
-        workList.push_back(*i);
-        unprocessedChildren = true;
-      }
-    }
-    if(n.getKind() == AND && defTrue.find(n) != defTrue.end() ){
-      for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
-        defTrue.insert(*i);
-      }
-    }
-
-    if(unprocessedChildren) {
-      continue;
-    }
-
-    workList.pop_back();
-    // has node n been processed in the meantime ?
-    if(processed.find(n) != processed.end()) {
-      continue;
-    }
-    processed.insert(n);
-
-    process(n,learned, defTrue);
-
-  }
-}
-
-void ArithStaticLearner::process(TNode n,
-                                 NodeBuilder& learned,
-                                 const TNodeSet& defTrue)
-{
-  Trace("arith::static") << "===================== looking at " << n << endl;
-
-  switch(n.getKind()){
-  case ITE:
-    if (expr::hasBoundVar(n))
-    {
-      // Unsafe with non-ground ITEs; do nothing
-      Trace("arith::static")
-          << "(potentially) non-ground ITE, ignoring..." << endl;
-      break;
-    }
-
-    if(n[0].getKind() != EQUAL &&
-       isRelationOperator(n[0].getKind())  ){
-      iteMinMax(n, learned);
-    }
-
-    if((d_minMap.find(n[1]) != d_minMap.end() && d_minMap.find(n[2]) != d_minMap.end()) ||
-       (d_maxMap.find(n[1]) != d_maxMap.end() && d_maxMap.find(n[2]) != d_maxMap.end())) {
-      iteConstant(n, learned);
-    }
-    break;
-
-  case CONST_RATIONAL:
-  case CONST_INTEGER:
-    // Mark constants as minmax
-    d_minMap.insert(n, n.getConst<Rational>());
-    d_maxMap.insert(n, n.getConst<Rational>());
-    break;
-  default: // Do nothing
-    break;
-  }
-}
-
-void ArithStaticLearner::iteMinMax(TNode n, NodeBuilder& learned)
-{
-  Assert(n.getKind() == kind::ITE);
-  Assert(n[0].getKind() != EQUAL);
-  Assert(isRelationOperator(n[0].getKind()));
-
-  TNode c = n[0];
-  Kind k = oldSimplifiedKind(c);
-  TNode t = n[1];
-  TNode e = n[2];
-  TNode cleft = (c.getKind() == NOT) ? c[0][0] : c[0];
-  TNode cright = (c.getKind() == NOT) ? c[0][1] : c[1];
-
-  if((t == cright) && (e == cleft)){
-    TNode tmp = t;
-    t = e;
-    e = tmp;
-    k = reverseRelationKind(k);
-  }
-  //(ite (< x y) x y)
-  //(ite (x < y) x y)
-  //(ite (x - y < 0) x y)
-  // ----------------
-  // (ite (x - y < -c) )
-
-  if(t == cleft && e == cright){
-    // t == cleft && e == cright
-    Assert(t == cleft);
-    Assert(e == cright);
-    switch(k){
-    case LT:   // (ite (< x y) x y)
-    case LEQ: { // (ite (<= x y) x y)
-      Node nLeqX = NodeBuilder(LEQ) << n << t;
-      Node nLeqY = NodeBuilder(LEQ) << n << e;
-      Trace("arith::static") << n << "is a min =>"  << nLeqX << nLeqY << endl;
-      learned << nLeqX << nLeqY;
-      ++(d_statistics.d_iteMinMaxApplications);
-      break;
-    }
-    case GT: // (ite (> x y) x y)
-    case GEQ: { // (ite (>= x y) x y)
-      Node nGeqX = NodeBuilder(GEQ) << n << t;
-      Node nGeqY = NodeBuilder(GEQ) << n << e;
-      Trace("arith::static") << n << "is a max =>"  << nGeqX << nGeqY << endl;
-      learned << nGeqX << nGeqY;
-      ++(d_statistics.d_iteMinMaxApplications);
-      break;
-    }
-    default: Unreachable();
-    }
-  }
-}
-
-void ArithStaticLearner::iteConstant(TNode n, NodeBuilder& learned)
-{
-  Assert(n.getKind() == ITE);
-
-  Trace("arith::static") << "iteConstant(" << n << ")" << endl;
-
-  if (d_minMap.find(n[1]) != d_minMap.end() && d_minMap.find(n[2]) != d_minMap.end()) {
-    const DeltaRational& first = d_minMap[n[1]];
-    const DeltaRational& second = d_minMap[n[2]];
-    DeltaRational min = std::min(first, second);
-    CDNodeToMinMaxMap::const_iterator minFind = d_minMap.find(n);
-    if (minFind == d_minMap.end() || (*minFind).second < min) {
-      d_minMap.insert(n, min);
-      NodeManager* nm = NodeManager::currentNM();
-      Node nGeqMin = nm->mkNode(
-          min.getInfinitesimalPart() == 0 ? kind::GEQ : kind::GT,
-          n,
-          nm->mkConstRealOrInt(n.getType(), min.getNoninfinitesimalPart()));
-      learned << nGeqMin;
-      Trace("arith::static") << n << " iteConstant"  << nGeqMin << endl;
-      ++(d_statistics.d_iteConstantApplications);
-    }
-  }
-
-  if (d_maxMap.find(n[1]) != d_maxMap.end() && d_maxMap.find(n[2]) != d_maxMap.end()) {
-    const DeltaRational& first = d_maxMap[n[1]];
-    const DeltaRational& second = d_maxMap[n[2]];
-    DeltaRational max = std::max(first, second);
-    CDNodeToMinMaxMap::const_iterator maxFind = d_maxMap.find(n);
-    if (maxFind == d_maxMap.end() || (*maxFind).second > max) {
-      d_maxMap.insert(n, max);
-      NodeManager* nm = NodeManager::currentNM();
-      Node nLeqMax = nm->mkNode(
-          max.getInfinitesimalPart() == 0 ? kind::LEQ : kind::LT,
-          n,
-          nm->mkConstRealOrInt(n.getType(), max.getNoninfinitesimalPart()));
-      learned << nLeqMax;
-      Trace("arith::static") << n << " iteConstant"  << nLeqMax << endl;
-      ++(d_statistics.d_iteConstantApplications);
-    }
-  }
-}
-
-std::set<Node> listToSet(TNode l){
-  std::set<Node> ret;
-  while(l.getKind() == OR){
-    Assert(l.getNumChildren() == 2);
-    ret.insert(l[0]);
-    l = l[1];
-  }
-  return ret;
-}
-
-void ArithStaticLearner::addBound(TNode n) {
-
-  CDNodeToMinMaxMap::const_iterator minFind = d_minMap.find(n[0]);
-  CDNodeToMinMaxMap::const_iterator maxFind = d_maxMap.find(n[0]);
-
-  Rational constant = n[1].getConst<Rational>();
-  DeltaRational bound = constant;
-
-  switch(Kind k = n.getKind()) {
-    case kind::LT: bound = DeltaRational(constant, -1); CVC5_FALLTHROUGH;
-    case kind::LEQ:
-      if (maxFind == d_maxMap.end() || (*maxFind).second > bound)
-      {
-        d_maxMap.insert(n[0], bound);
-        Trace("arith::static") << "adding bound " << n << endl;
-      }
-      break;
-    case kind::GT: bound = DeltaRational(constant, 1); CVC5_FALLTHROUGH;
-    case kind::GEQ:
-      if (minFind == d_minMap.end() || (*minFind).second < bound)
-      {
-        d_minMap.insert(n[0], bound);
-        Trace("arith::static") << "adding bound " << n << endl;
-      }
-      break;
-    default: Unhandled() << k; break;
-  }
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/arith_static_learner.h b/src/theory/arith/arith_static_learner.h
deleted file mode 100644 (file)
index 1ac8d7c..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Andres Noetzli, Dejan Jovanovic
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H
-#define CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H
-
-#include "context/cdhashmap.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/delta_rational.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::context {
-class Context;
-}
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class ArithStaticLearner {
-private:
-
-  /**
-   * Map from a node to it's minimum and maximum.
-   */
- typedef context::CDHashMap<Node, DeltaRational> CDNodeToMinMaxMap;
- CDNodeToMinMaxMap d_minMap;
- CDNodeToMinMaxMap d_maxMap;
-
-public:
- ArithStaticLearner(context::Context* userContext);
- ~ArithStaticLearner();
- void staticLearning(TNode n, NodeBuilder& learned);
-
- void addBound(TNode n);
-
-private:
- void process(TNode n, NodeBuilder& learned, const TNodeSet& defTrue);
-
- void iteMinMax(TNode n, NodeBuilder& learned);
- void iteConstant(TNode n, NodeBuilder& learned);
-
- /**
-  * These fields are designed to be accessible to ArithStaticLearner methods.
-  */
- class Statistics
- {
-  public:
-   IntStat d_iteMinMaxApplications;
-   IntStat d_iteConstantApplications;
-
-   Statistics();
- };
-
-  Statistics d_statistics;
-
-};/* class ArithStaticLearner */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H */
index 0f85468a28b4e1dfc73cd43dd3c457a3ae053a08..7f4398b9b1f5e6c4c9e0b58c0627e807cca20ea4 100644 (file)
@@ -25,7 +25,6 @@
 #include "context/cdhashset.h"
 #include "expr/node.h"
 #include "expr/subs.h"
-#include "theory/arith/arithvar.h"
 #include "util/dense_map.h"
 #include "util/integer.h"
 #include "util/rational.h"
@@ -39,10 +38,6 @@ typedef std::unordered_set<Node> NodeSet;
 typedef std::unordered_set<TNode> TNodeSet;
 typedef context::CDHashSet<Node> CDNodeSet;
 
-//Maps from Nodes -> ArithVars, and vice versa
-typedef std::unordered_map<Node, ArithVar> NodeToArithVarMap;
-typedef DenseMap<Node> ArithVarToNodeMap;
-
 inline Node mkRationalNode(const Rational& q){
   return NodeManager::currentNM()->mkConst(kind::CONST_RATIONAL, q);
 }
diff --git a/src/theory/arith/arithvar.cpp b/src/theory/arith/arithvar.cpp
deleted file mode 100644 (file)
index d83fe2b..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/arithvar.h"
-#include <limits>
-#include <set>
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-const ArithVar ARITHVAR_SENTINEL = std::numeric_limits<ArithVar>::max();
-
-bool debugIsASet(const std::vector<ArithVar>& variables){
-  std::set<ArithVar> asSet(variables.begin(), variables.end());
-  return asSet.size() == variables.size();
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/arithvar.h b/src/theory/arith/arithvar.h
deleted file mode 100644 (file)
index 33eb196..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Defines ArithVar which is the internal representation of variables in
- * arithmetic
- *
- * This defines ArithVar which is the internal representation of variables in
- * arithmetic. This is a typedef from Index to ArithVar.
- * This file also provides utilities for ArithVars.
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <vector>
-
-#include "util/index.h"
-#include "util/rational.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-typedef Index ArithVar;
-extern const ArithVar ARITHVAR_SENTINEL;
-
-typedef std::vector<ArithVar> ArithVarVec;
-typedef std::pair<ArithVar, Rational> ArithRatPair;
-typedef std::vector< ArithRatPair > ArithRatPairVec;
-
-extern bool debugIsASet(const ArithVarVec& variables);
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/arithvar_node_map.h b/src/theory/arith/arithvar_node_map.h
deleted file mode 100644 (file)
index 85a9627..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Aina Niemetz
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H
-#define CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H
-
-#include "theory/arith/arithvar.h"
-#include "context/context.h"
-#include "context/cdlist.h"
-#include "context/cdhashmap.h"
-#include "context/cdo.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class ArithVarNodeMap {
-private:
-  /**
-   * Bidirectional map between Nodes and ArithVars.
-   */
-  NodeToArithVarMap d_nodeToArithVarMap;
-  ArithVarToNodeMap d_arithVarToNodeMap;
-
-public:
-
-  typedef ArithVarToNodeMap::const_iterator var_iterator;
-
-  ArithVarNodeMap() {}
-
-  inline bool hasArithVar(TNode x) const {
-    return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end();
-  }
-
-  inline bool hasNode(ArithVar a) const {
-    return d_arithVarToNodeMap.isKey(a);
-  }
-
-  inline ArithVar asArithVar(TNode x) const{
-    Assert(hasArithVar(x));
-    Assert((d_nodeToArithVarMap.find(x))->second <= ARITHVAR_SENTINEL);
-    return (d_nodeToArithVarMap.find(x))->second;
-  }
-
-  inline Node asNode(ArithVar a) const{
-    Assert(hasNode(a));
-    return d_arithVarToNodeMap[a];
-  }
-
-  inline void setArithVar(TNode x, ArithVar a){
-    Assert(!hasArithVar(x));
-    Assert(!d_arithVarToNodeMap.isKey(a));
-    d_arithVarToNodeMap.set(a, x);
-    d_nodeToArithVarMap[x] = a;
-  }
-
-  inline void remove(ArithVar x){
-    Assert(hasNode(x));
-    Node node = asNode(x);
-
-    d_nodeToArithVarMap.erase(d_nodeToArithVarMap.find(node));
-    d_arithVarToNodeMap.remove(x);
-  }
-
-  var_iterator var_begin() const {
-    return d_arithVarToNodeMap.begin();
-  }
-  var_iterator var_end() const {
-    return d_arithVarToNodeMap.end();
-  }
-
-};/* class ArithVarNodeMap */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H */
diff --git a/src/theory/arith/attempt_solution_simplex.cpp b/src/theory/arith/attempt_solution_simplex.cpp
deleted file mode 100644 (file)
index ed35dad..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-#include "theory/arith/attempt_solution_simplex.h"
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/error_set.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/tableau.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-AttemptSolutionSDP::AttemptSolutionSDP(Env& env,
-                                       LinearEqualityModule& linEq,
-                                       ErrorSet& errors,
-                                       RaiseConflict conflictChannel,
-                                       TempVarMalloc tvmalloc)
-    : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
-      d_statistics()
-{ }
-
-AttemptSolutionSDP::Statistics::Statistics()
-    : d_searchTime(smtStatisticsRegistry().registerTimer(
-        "theory::arith::attempt::searchTime")),
-      d_queueTime(smtStatisticsRegistry().registerTimer(
-          "theory::arith::attempt::queueTime")),
-      d_conflicts(smtStatisticsRegistry().registerInt(
-          "theory::arith::attempt::conflicts"))
-{
-}
-
-bool AttemptSolutionSDP::matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const{
-  return nv[v] == d_variables.getAssignment(v);
-}
-
-Result::Status AttemptSolutionSDP::attempt(
-    const ApproximateSimplex::Solution& sol)
-{
-  const DenseSet& newBasis = sol.newBasis;
-  const DenseMap<DeltaRational>& newValues = sol.newValues;
-
-  DenseSet needsToBeAdded;
-  for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){
-    ArithVar b = *i;
-    if(!d_tableau.isBasic(b)){
-      needsToBeAdded.add(b);
-    }
-  }
-  DenseMap<DeltaRational>::const_iterator nvi = newValues.begin(), nvi_end = newValues.end();
-  for(; nvi != nvi_end; ++nvi){
-    ArithVar currentlyNb = *nvi;
-    if(!d_tableau.isBasic(currentlyNb)){
-      if(!matchesNewValue(newValues, currentlyNb)){
-        const DeltaRational& newValue = newValues[currentlyNb];
-        Trace("arith::updateMany")
-          << "updateMany:" << currentlyNb << " "
-          << d_variables.getAssignment(currentlyNb) << " to "<< newValue << endl;
-        d_linEq.update(currentlyNb, newValue);
-        Assert(d_variables.assignmentIsConsistent(currentlyNb));
-      }
-    }
-  }
-  d_errorSet.reduceToSignals();
-  d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
-
-  if(processSignals()){
-    Trace("arith::findModel") << "attemptSolution() early conflict" << endl;
-    d_conflictVariables.purge();
-    return Result::UNSAT;
-  }else if(d_errorSet.errorEmpty()){
-    Trace("arith::findModel") << "attemptSolution() fixed itself" << endl;
-    return Result::SAT;
-  }
-
-  while(!needsToBeAdded.empty() && !d_errorSet.errorEmpty()){
-    ArithVar toRemove = ARITHVAR_SENTINEL;
-    ArithVar toAdd = ARITHVAR_SENTINEL;
-    DenseSet::const_iterator i = needsToBeAdded.begin(), i_end = needsToBeAdded.end();
-    for(; toAdd == ARITHVAR_SENTINEL && i != i_end; ++i){
-      ArithVar v = *i;
-
-      Tableau::ColIterator colIter = d_tableau.colIterator(v);
-      for(; !colIter.atEnd(); ++colIter){
-        const Tableau::Entry& entry = *colIter;
-        Assert(entry.getColVar() == v);
-        ArithVar b = d_tableau.rowIndexToBasic(entry.getRowIndex());
-        if(!newBasis.isMember(b)){
-          toAdd = v;
-
-          bool favorBOverToRemove =
-            (toRemove == ARITHVAR_SENTINEL) ||
-            (matchesNewValue(newValues, toRemove) && !matchesNewValue(newValues, b)) ||
-            (d_tableau.basicRowLength(toRemove) > d_tableau.basicRowLength(b));
-
-          if(favorBOverToRemove){
-            toRemove = b;
-          }
-        }
-      }
-    }
-    Assert(toRemove != ARITHVAR_SENTINEL);
-    Assert(toAdd != ARITHVAR_SENTINEL);
-
-    Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl;
-
-    d_linEq.pivotAndUpdate(toRemove, toAdd, newValues[toRemove]);
-
-    Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
-    needsToBeAdded.remove(toAdd);
-
-    bool conflict = processSignals();
-    if(conflict){
-      d_errorSet.reduceToSignals();
-      d_conflictVariables.purge();
-
-      return Result::UNSAT;
-    }
-  }
-  Assert(d_conflictVariables.empty());
-
-  if(d_errorSet.errorEmpty()){
-    return Result::SAT;
-  }else{
-    d_errorSet.reduceToSignals();
-    return Result::UNKNOWN;
-  }
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/attempt_solution_simplex.h b/src/theory/arith/attempt_solution_simplex.h
deleted file mode 100644 (file)
index 43c6f71..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- *
- * This implements the Simplex module for the Simpelx for DPLL(T) decision
- * procedure.
- * See the Simplex for DPLL(T) technical report for more background.(citation?)
- * This shares with the theory a Tableau, and a PartialModel that:
- *  - satisfies the equalities in the Tableau, and
- *  - the assignment for the non-basic variables satisfies their bounds.
- * This is required to either produce a conflict or satisifying PartialModel.
- * Further, we require being told when a basic variable updates its value.
- *
- * During the Simplex search we maintain a queue of variables.
- * The queue is required to contain all of the basic variables that voilate
- * their bounds.
- * As elimination from the queue is more efficient to be done lazily,
- * we do not maintain that the queue of variables needs to be only basic
- * variables or only variables that satisfy their bounds.
- *
- * The simplex procedure roughly follows Alberto's thesis. (citation?)
- * There is one round of selecting using a heuristic pivoting rule.
- * (See PreferenceFunction Documentation for the available options.)
- * The non-basic variable is the one that appears in the fewest pivots.
- * (Bruno says that Leonardo invented this first.)
- * After this, Bland's pivot rule is invoked.
- *
- * During this proccess, we periodically inspect the queue of variables to
- * 1) remove now extraneous extries,
- * 2) detect conflicts that are "waiting" on the queue but may not be detected
- *    by the current queue heuristics, and
- * 3) detect multiple conflicts.
- *
- * Conflicts are greedily slackened to use the weakest bounds that still
- * produce the conflict.
- *
- * Extra things tracked atm: (Subject to change at Tim's whims)
- * - A superset of all of the newly pivoted variables.
- * - A queue of additional conflicts that were discovered by Simplex.
- *   These are theory valid and are currently turned into lemmas
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "theory/arith/approx_simplex.h"
-#include "theory/arith/simplex.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class AttemptSolutionSDP : public SimplexDecisionProcedure {
-public:
- AttemptSolutionSDP(Env& env,
-                    LinearEqualityModule& linEq,
-                    ErrorSet& errors,
-                    RaiseConflict conflictChannel,
-                    TempVarMalloc tvmalloc);
-
- Result::Status attempt(const ApproximateSimplex::Solution& sol);
-
- Result::Status findModel(bool exactResult) override { Unreachable(); }
-
-private:
- bool matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const;
-
- bool processSignals()
- {
-   TimerStat& timer = d_statistics.d_queueTime;
-   IntStat& conflictStat = d_statistics.d_conflicts;
-   return standardProcessSignals(timer, conflictStat);
-  }
-  /** These fields are designed to be accessible to TheoryArith methods. */
-  class Statistics {
-  public:
-    TimerStat d_searchTime;
-    TimerStat d_queueTime;
-    IntStat d_conflicts;
-
-    Statistics();
-  } d_statistics;
-};/* class AttemptSolutionSDP */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/bound_counts.h b/src/theory/arith/bound_counts.h
deleted file mode 100644 (file)
index bef547d..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Clark Barrett
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-#pragma once
-
-#include "base/check.h"
-#include "theory/arith/arithvar.h"
-#include "util/dense_map.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class BoundCounts {
-private:
-  uint32_t d_lowerBoundCount;
-  uint32_t d_upperBoundCount;
-
-public:
-  BoundCounts() : d_lowerBoundCount(0), d_upperBoundCount(0) {}
-  BoundCounts(uint32_t lbs, uint32_t ubs)
-  : d_lowerBoundCount(lbs), d_upperBoundCount(ubs) {}
-
-  bool operator==(BoundCounts bc) const {
-    return d_lowerBoundCount == bc.d_lowerBoundCount
-      && d_upperBoundCount == bc.d_upperBoundCount;
-  }
-  bool operator!=(BoundCounts bc) const {
-    return  d_lowerBoundCount != bc.d_lowerBoundCount
-      || d_upperBoundCount != bc.d_upperBoundCount;
-  }
-  /** This is not a total order! */
-  bool operator>=(BoundCounts bc) const {
-    return d_lowerBoundCount >= bc.d_lowerBoundCount &&
-      d_upperBoundCount >= bc.d_upperBoundCount;
-  }
-
-  inline bool isZero() const{ return d_lowerBoundCount == 0 && d_upperBoundCount == 0; }
-  inline uint32_t lowerBoundCount() const{
-    return d_lowerBoundCount;
-  }
-  inline uint32_t upperBoundCount() const{
-    return d_upperBoundCount;
-  }
-
-  inline BoundCounts operator+(BoundCounts bc) const{
-    return BoundCounts(d_lowerBoundCount + bc.d_lowerBoundCount,
-                       d_upperBoundCount + bc.d_upperBoundCount);
-  }
-
-  inline BoundCounts operator-(BoundCounts bc) const {
-    Assert(*this >= bc);
-    return BoundCounts(d_lowerBoundCount - bc.d_lowerBoundCount,
-                       d_upperBoundCount - bc.d_upperBoundCount);
-  }
-
-
-  inline BoundCounts& operator+=(BoundCounts bc) {
-    d_upperBoundCount += bc.d_upperBoundCount;
-    d_lowerBoundCount += bc.d_lowerBoundCount;
-    return *this;
-  }
-
-  inline BoundCounts& operator-=(BoundCounts bc) {
-    Assert(d_lowerBoundCount >= bc.d_lowerBoundCount);
-    Assert(d_upperBoundCount >= bc.d_upperBoundCount);
-    d_upperBoundCount -= bc.d_upperBoundCount;
-    d_lowerBoundCount -= bc.d_lowerBoundCount;
-
-    return *this;
-  }
-
-  /** Based on the sign coefficient a variable is multiplied by,
-   * the effects the bound counts either has no effect (sgn == 0),
-   * the lower bounds and upper bounds flip (sgn < 0), or nothing (sgn >0).
-   */
-  inline BoundCounts multiplyBySgn(int sgn) const{
-    if(sgn > 0){
-      return *this;
-    }else if(sgn == 0){
-      return BoundCounts(0,0);
-    }else{
-      return BoundCounts(d_upperBoundCount, d_lowerBoundCount);
-    }
-  }
-
-  inline void addInChange(int sgn, BoundCounts before, BoundCounts after){
-    if(before == after){
-      return;
-    }else if(sgn < 0){
-      Assert(d_upperBoundCount >= before.d_lowerBoundCount);
-      Assert(d_lowerBoundCount >= before.d_upperBoundCount);
-      d_upperBoundCount += after.d_lowerBoundCount - before.d_lowerBoundCount;
-      d_lowerBoundCount += after.d_upperBoundCount - before.d_upperBoundCount;
-    }else if(sgn > 0){
-      Assert(d_upperBoundCount >= before.d_upperBoundCount);
-      Assert(d_lowerBoundCount >= before.d_lowerBoundCount);
-      d_upperBoundCount += after.d_upperBoundCount - before.d_upperBoundCount;
-      d_lowerBoundCount += after.d_lowerBoundCount - before.d_lowerBoundCount;
-    }
-  }
-
-  inline void addInSgn(BoundCounts bc, int before, int after){
-    Assert(before != after);
-    Assert(!bc.isZero());
-
-    if(before < 0){
-      d_upperBoundCount -= bc.d_lowerBoundCount;
-      d_lowerBoundCount -= bc.d_upperBoundCount;
-    }else if(before > 0){
-      d_upperBoundCount -= bc.d_upperBoundCount;
-      d_lowerBoundCount -= bc.d_lowerBoundCount;
-    }
-    if(after < 0){
-      d_upperBoundCount += bc.d_lowerBoundCount;
-      d_lowerBoundCount += bc.d_upperBoundCount;
-    }else if(after > 0){
-      d_upperBoundCount += bc.d_upperBoundCount;
-      d_lowerBoundCount += bc.d_lowerBoundCount;
-    }
-  }
-};
-
-class BoundsInfo {
-private:
-
-  /**
-   * x = \sum_{a < 0} a_i i + \sum_{b > 0} b_j j
-   *
-   * AtUpperBound = {assignment(i) = lb(i)} \cup {assignment(j) = ub(j)}
-   * AtLowerBound = {assignment(i) = ub(i)} \cup {assignment(j) = lb(j)}
-   */
-  BoundCounts d_atBounds;
-
-  /** This is for counting how many upper and lower bounds a row has. */
-  BoundCounts d_hasBounds;
-
-public:
-  BoundsInfo() : d_atBounds(), d_hasBounds() {}
-  BoundsInfo(BoundCounts atBounds, BoundCounts hasBounds)
-    : d_atBounds(atBounds), d_hasBounds(hasBounds) {}
-
-  BoundCounts atBounds() const { return d_atBounds; }
-  BoundCounts hasBounds() const { return d_hasBounds; }
-
-  /** This corresponds to adding in another variable to the row. */
-  inline BoundsInfo operator+(const BoundsInfo& bc) const{
-    return BoundsInfo(d_atBounds + bc.d_atBounds,
-                      d_hasBounds + bc.d_hasBounds);
-  }
-  /** This corresponds to removing a variable from the row. */
-  inline BoundsInfo operator-(const BoundsInfo& bc) const {
-    Assert(*this >= bc);
-    return BoundsInfo(d_atBounds - bc.d_atBounds,
-                      d_hasBounds - bc.d_hasBounds);
-  }
-
-  inline BoundsInfo& operator+=(const BoundsInfo& bc) {
-    d_atBounds += bc.d_atBounds;
-    d_hasBounds += bc.d_hasBounds;
-    return (*this);
-  }
-
-  /** Based on the sign coefficient a variable is multiplied by,
-   * the effects the bound counts either has no effect (sgn == 0),
-   * the lower bounds and upper bounds flip (sgn < 0), or nothing (sgn >0).
-   */
-  inline BoundsInfo multiplyBySgn(int sgn) const{
-    return BoundsInfo(d_atBounds.multiplyBySgn(sgn), d_hasBounds.multiplyBySgn(sgn));
-  }
-
-  bool operator==(const BoundsInfo& other) const{
-    return d_atBounds == other.d_atBounds && d_hasBounds == other.d_hasBounds;
-  }
-  bool operator!=(const BoundsInfo& other) const{
-    return !(*this == other);
-  }
-  /** This is not a total order! */
-  bool operator>=(const BoundsInfo& other) const{
-    return d_atBounds >= other.d_atBounds && d_hasBounds >= other.d_hasBounds;
-  }
-  void addInChange(int sgn, const BoundsInfo& before, const BoundsInfo& after){
-    addInAtBoundChange(sgn, before.d_atBounds, after.d_atBounds);
-    addInHasBoundChange(sgn, before.d_hasBounds, after.d_hasBounds);
-  }
-  void addInAtBoundChange(int sgn, BoundCounts before, BoundCounts after){
-    d_atBounds.addInChange(sgn, before, after);
-  }
-  void addInHasBoundChange(int sgn, BoundCounts before, BoundCounts after){
-    d_hasBounds.addInChange(sgn, before, after);
-  }
-
-  inline void addInSgn(const BoundsInfo& bc, int before, int after){
-    if(!bc.d_atBounds.isZero()){ d_atBounds.addInSgn(bc.d_atBounds, before, after);}
-    if(!bc.d_hasBounds.isZero()){ d_hasBounds.addInSgn(bc.d_hasBounds, before, after);}
-  }
-};
-
-/** This is intended to map each row to its relevant bound information. */
-typedef DenseMap<BoundsInfo> BoundInfoMap;
-
-inline std::ostream& operator<<(std::ostream& os, const BoundCounts& bc){
-  os << "[bc " << bc.lowerBoundCount() << ", " << bc.upperBoundCount() << "]";
-  return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, const BoundsInfo& inf){
-  os << "[bi : @ " << inf.atBounds() << ", " << inf.hasBounds() << "]";
-  return os;
-}
-class BoundUpdateCallback {
-public:
-  virtual ~BoundUpdateCallback() {}
-  virtual void operator()(ArithVar v, const BoundsInfo&  up) = 0;
-};
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
index 68b3a7b2a62c7e7ed2c0c3ae410893448b66362e..3ec622dcf363136e53f32934df631de5a5c2c72d 100644 (file)
@@ -16,7 +16,7 @@
 #include "theory/arith/bound_inference.h"
 
 #include "smt/env.h"
-#include "theory/arith/normal_form.h"
+#include "theory/arith/linear/normal_form.h"
 #include "theory/rewriter.h"
 
 using namespace cvc5::internal::kind;
@@ -62,7 +62,7 @@ bool BoundInference::add(const Node& n, bool onlyVariables)
     return false;
   }
   // Parse the node as a comparison
-  auto comp = Comparison::parseNormalForm(tmp);
+  auto comp = linear::Comparison::parseNormalForm(tmp);
   auto dec = comp.decompose(true);
   if (onlyVariables && !std::get<0>(dec).isVariable())
   {
diff --git a/src/theory/arith/callbacks.cpp b/src/theory/arith/callbacks.cpp
deleted file mode 100644 (file)
index fb38ac0..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/callbacks.h"
-
-#include "expr/skolem_manager.h"
-#include "proof/proof_node.h"
-#include "theory/arith/theory_arith_private.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-SetupLiteralCallBack::SetupLiteralCallBack(TheoryArithPrivate& ta)
-  : d_arith(ta)
-{}
-void SetupLiteralCallBack::operator()(TNode lit){
-  TNode atom = (lit.getKind() == kind::NOT) ? lit[0] : lit;
-  if(!d_arith.isSetup(atom)){
-    d_arith.setupAtom(atom);
-  }
-}
-
-DeltaComputeCallback::DeltaComputeCallback(const TheoryArithPrivate& ta)
-  : d_ta(ta)
-{}
-Rational DeltaComputeCallback::operator()() const{
-  return d_ta.deltaValueForTotalOrder();
-}
-
-TempVarMalloc::TempVarMalloc(TheoryArithPrivate& ta)
-: d_ta(ta)
-{}
-ArithVar TempVarMalloc::request(){
-  NodeManager* nm = NodeManager::currentNM();
-  SkolemManager* sm = nm->getSkolemManager();
-  Node skolem = sm->mkDummySkolem("tmpVar", nm->realType());
-  return d_ta.requestArithVar(skolem, false, true);
-}
-void TempVarMalloc::release(ArithVar v){
-  d_ta.releaseArithVar(v);
-}
-
-BasicVarModelUpdateCallBack::BasicVarModelUpdateCallBack(TheoryArithPrivate& ta)
-  : d_ta(ta)
-{}
-void BasicVarModelUpdateCallBack::operator()(ArithVar x){
-  d_ta.signal(x);
-}
-
-RaiseConflict::RaiseConflict(TheoryArithPrivate& ta)
-  : d_ta(ta)
-{}
-
-void RaiseConflict::raiseConflict(ConstraintCP c, InferenceId id) const{
-  Assert(c->inConflict());
-  d_ta.raiseConflict(c, id);
-}
-
-FarkasConflictBuilder::FarkasConflictBuilder(bool produceProofs)
-    : d_farkas(),
-      d_constraints(),
-      d_consequent(NullConstraint),
-      d_consequentSet(false),
-      d_produceProofs(produceProofs)
-{
-  reset();
-}
-
-bool FarkasConflictBuilder::underConstruction() const{
-  return d_consequent != NullConstraint;
-}
-
-bool FarkasConflictBuilder::consequentIsSet() const{
-  return d_consequentSet;
-}
-
-void FarkasConflictBuilder::reset(){
-  d_consequent = NullConstraint;
-  d_constraints.clear();
-  d_consequentSet = false;
-  if (d_produceProofs)
-  {
-    d_farkas.clear();
-  }
-  Assert(!underConstruction());
-}
-
-/* Adds a constraint to the constraint under construction. */
-void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc){
-  Assert(
-      !d_produceProofs
-      || (!underConstruction() && d_constraints.empty() && d_farkas.empty())
-      || (underConstruction() && d_constraints.size() + 1 == d_farkas.size()));
-  Assert(d_produceProofs || d_farkas.empty());
-  Assert(c->isTrue());
-
-  if(d_consequent == NullConstraint){
-    d_consequent = c;
-  } else {
-    d_constraints.push_back(c);
-  }
-  if (d_produceProofs)
-  {
-    d_farkas.push_back(fc);
-  }
-  Assert(!d_produceProofs || d_constraints.size() + 1 == d_farkas.size());
-  Assert(d_produceProofs || d_farkas.empty());
-}
-
-void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc, const Rational& mult){
-  Assert(!mult.isZero());
-  if (d_produceProofs && !mult.isOne())
-  {
-    Rational prod = fc * mult;
-    addConstraint(c, prod);
-  }
-  else
-  {
-    addConstraint(c, fc);
-  }
-}
-
-void FarkasConflictBuilder::makeLastConsequent(){
-  Assert(!d_consequentSet);
-  Assert(underConstruction());
-
-  if(d_constraints.empty()){
-    // no-op
-    d_consequentSet = true;
-  } else {
-    Assert(d_consequent != NullConstraint);
-    ConstraintCP last = d_constraints.back();
-    d_constraints.back() = d_consequent;
-    d_consequent = last;
-    if (d_produceProofs)
-    {
-      std::swap(d_farkas.front(), d_farkas.back());
-    }
-    d_consequentSet = true;
-  }
-
-  Assert(!d_consequent->negationHasProof());
-  Assert(d_consequentSet);
-}
-
-/* Turns the vector under construction into a conflict */
-ConstraintCP FarkasConflictBuilder::commitConflict(){
-  Assert(underConstruction());
-  Assert(!d_constraints.empty());
-  Assert(
-      !d_produceProofs
-      || (!underConstruction() && d_constraints.empty() && d_farkas.empty())
-      || (underConstruction() && d_constraints.size() + 1 == d_farkas.size()));
-  Assert(d_produceProofs || d_farkas.empty());
-  Assert(d_consequentSet);
-
-  ConstraintP not_c = d_consequent->getNegation();
-  RationalVectorCP coeffs = d_produceProofs ? &d_farkas : nullptr;
-  not_c->impliedByFarkas(d_constraints, coeffs, true );
-
-  reset();
-  Assert(!underConstruction());
-  Assert(not_c->inConflict());
-  Assert(!d_consequentSet);
-  return not_c;
-}
-
-RaiseEqualityEngineConflict::RaiseEqualityEngineConflict(TheoryArithPrivate& ta)
-  : d_ta(ta)
-{}
-
-/* If you are not an equality engine, don't use this! */
-void RaiseEqualityEngineConflict::raiseEEConflict(
-    Node n, std::shared_ptr<ProofNode> pf) const
-{
-  d_ta.raiseBlackBoxConflict(n, pf);
-}
-
-BoundCountingLookup::BoundCountingLookup(TheoryArithPrivate& ta)
-: d_ta(ta)
-{}
-
-const BoundsInfo& BoundCountingLookup::boundsInfo(ArithVar basic) const{
-  return d_ta.boundsInfo(basic);
-}
-
-BoundCounts BoundCountingLookup::atBounds(ArithVar basic) const{
-  return boundsInfo(basic).atBounds();
-}
-BoundCounts BoundCountingLookup::hasBounds(ArithVar basic) const {
-  return boundsInfo(basic).hasBounds();
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/callbacks.h b/src/theory/arith/callbacks.h
deleted file mode 100644 (file)
index 976cdf6..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Mathias Preiner, Clark Barrett
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#pragma once
-
-#include "expr/node.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/bound_counts.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/inference_id.h"
-#include "util/rational.h"
-
-namespace cvc5::internal {
-
-class ProofNode;
-
-namespace theory {
-namespace arith {
-
-class TheoryArithPrivate;
-
-/**
- * ArithVarCallBack provides a mechanism for agreeing on callbacks while
- * breaking mutual recursion inclusion order problems.
- */
-class ArithVarCallBack {
-public:
-  virtual ~ArithVarCallBack() {}
-  virtual void operator()(ArithVar x) = 0;
-};
-
-/**
- * Requests arithmetic variables for internal use,
- * and releases arithmetic variables that are no longer being used.
- */
-class ArithVarMalloc {
-public:
-  virtual ~ArithVarMalloc() {}
-  virtual ArithVar request() = 0;
-  virtual void release(ArithVar v) = 0;
-};
-
-class TNodeCallBack {
-public:
-  virtual ~TNodeCallBack() {}
-  virtual void operator()(TNode n) = 0;
-};
-
-class NodeCallBack {
-public:
-  virtual ~NodeCallBack() {}
-  virtual void operator()(Node n) = 0;
-};
-
-class RationalCallBack {
-public:
-  virtual ~RationalCallBack() {}
-  virtual Rational operator()() const = 0;
-};
-
-class SetupLiteralCallBack : public TNodeCallBack {
-private:
-  TheoryArithPrivate& d_arith;
-public:
-  SetupLiteralCallBack(TheoryArithPrivate& ta);
-  void operator()(TNode lit) override;
-};
-
-class DeltaComputeCallback : public RationalCallBack {
-private:
-  const TheoryArithPrivate& d_ta;
-public:
-  DeltaComputeCallback(const TheoryArithPrivate& ta);
-  Rational operator()() const override;
-};
-
-class BasicVarModelUpdateCallBack : public ArithVarCallBack{
-private:
-  TheoryArithPrivate& d_ta;
-public:
-  BasicVarModelUpdateCallBack(TheoryArithPrivate& ta);
-  void operator()(ArithVar x) override;
-};
-
-class TempVarMalloc : public ArithVarMalloc {
-private:
-  TheoryArithPrivate& d_ta;
-public:
-  TempVarMalloc(TheoryArithPrivate& ta);
-  ArithVar request() override;
-  void release(ArithVar v) override;
-};
-
-class RaiseConflict {
-private:
-  TheoryArithPrivate& d_ta;
-public:
-  RaiseConflict(TheoryArithPrivate& ta);
-
-  /** Calls d_ta.raiseConflict(c) */
-  void raiseConflict(ConstraintCP c, InferenceId id) const;
-};
-
-class FarkasConflictBuilder {
-private:
-  RationalVector d_farkas;
-  ConstraintCPVec d_constraints;
-  ConstraintCP d_consequent;
-  bool d_consequentSet;
-  bool d_produceProofs;
-
- public:
-
-  /**
-   * Constructs a new FarkasConflictBuilder.
-   */
-  FarkasConflictBuilder(bool produceProofs);
-
-  /**
-   * Adds an antecedent constraint to the conflict under construction
-   * with the farkas coefficient fc * mult.
-   *
-   * The value mult is either 1 or -1.
-   */
-  void addConstraint(ConstraintCP c, const Rational& fc, const Rational& mult);
-
-  /**
-   * Adds an antecedent constraint to the conflict under construction
-   * with the farkas coefficient fc.
-   */
-  void addConstraint(ConstraintCP c, const Rational& fc);
-  
-  /**
-   * Makes the last constraint added the consequent.
-   * Can be done exactly once per reset().
-   */
-  void makeLastConsequent();
-  
-  /**
-   * Turns the antecendents into a proof of the negation of one of the
-   * antecedents.
-   *
-   * The buffer is no longer underConstruction afterwards.
-   *
-   * precondition:
-   * - At least two constraints have been asserted.
-   * - makeLastConsequent() has been called.
-   *
-   * postcondition: The returned constraint is in conflict.
-   */
-  ConstraintCP commitConflict();
-
-  /** Returns true if a conflict has been pushed back since the last reset. */
-  bool underConstruction() const;
-  
-  /** Returns true if the consequent has been set since the last reset. */
-  bool consequentIsSet() const;
-
-  /** Resets the state of the buffer. */
-  void reset();
-};
-
-
-class RaiseEqualityEngineConflict {
-private:
-  TheoryArithPrivate& d_ta;
-  
-public:
-  RaiseEqualityEngineConflict(TheoryArithPrivate& ta);
-
-  /* If you are not an equality engine, don't use this!
-   *
-   * The proof should prove that `n` is a conflict.
-   * */
-  void raiseEEConflict(Node n, std::shared_ptr<ProofNode> pf) const;
-};
-
-class BoundCountingLookup {
-private:
-  TheoryArithPrivate& d_ta;
-public:
-  BoundCountingLookup(TheoryArithPrivate& ta);
-  const BoundsInfo& boundsInfo(ArithVar basic) const;
-  BoundCounts atBounds(ArithVar basic) const;
-  BoundCounts hasBounds(ArithVar basic) const;
-};
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/congruence_manager.cpp b/src/theory/arith/congruence_manager.cpp
deleted file mode 100644 (file)
index a9a0962..0000000
+++ /dev/null
@@ -1,715 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Alex Ozdemir, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/congruence_manager.h"
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "proof/proof_node.h"
-#include "proof/proof_node_manager.h"
-#include "smt/env.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/partial_model.h"
-#include "theory/ee_setup_info.h"
-#include "theory/rewriter.h"
-#include "theory/uf/equality_engine.h"
-#include "theory/uf/proof_equality_engine.h"
-
-using namespace cvc5::internal::kind;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-ArithCongruenceManager::ArithCongruenceManager(
-    Env& env,
-    ConstraintDatabase& cd,
-    SetupLiteralCallBack setup,
-    const ArithVariables& avars,
-    RaiseEqualityEngineConflict raiseConflict)
-    : EnvObj(env),
-      d_inConflict(context()),
-      d_raiseConflict(raiseConflict),
-      d_notify(*this),
-      d_keepAlive(context()),
-      d_propagatations(context()),
-      d_explanationMap(context()),
-      d_constraintDatabase(cd),
-      d_setupLiteral(setup),
-      d_avariables(avars),
-      d_ee(nullptr),
-      d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
-                                           : nullptr),
-      // Construct d_pfGenEe with the SAT context, since its proof include
-      // unclosed assumptions of theory literals.
-      d_pfGenEe(new EagerProofGenerator(
-          d_pnm, context(), "ArithCongruenceManager::pfGenEe")),
-      // Construct d_pfGenEe with the USER context, since its proofs are closed.
-      d_pfGenExplain(new EagerProofGenerator(
-          d_pnm, userContext(), "ArithCongruenceManager::pfGenExplain")),
-      d_pfee(nullptr)
-{
-}
-
-ArithCongruenceManager::~ArithCongruenceManager() {}
-
-bool ArithCongruenceManager::needsEqualityEngine(EeSetupInfo& esi)
-{
-  Assert(!options().arith.arithEqSolver);
-  esi.d_notify = &d_notify;
-  esi.d_name = "arithCong::ee";
-  return true;
-}
-
-void ArithCongruenceManager::finishInit(eq::EqualityEngine* ee)
-{
-  if (options().arith.arithEqSolver)
-  {
-    // use our own copy
-    d_allocEe = std::make_unique<eq::EqualityEngine>(
-        d_env, context(), d_notify, "arithCong::ee", true);
-    d_ee = d_allocEe.get();
-    if (d_pnm != nullptr)
-    {
-      // allocate an internal proof equality engine
-      d_allocPfee = std::make_unique<eq::ProofEqEngine>(d_env, *d_ee);
-      d_ee->setProofEqualityEngine(d_allocPfee.get());
-    }
-  }
-  else
-  {
-    Assert(ee != nullptr);
-    // otherwise, we use the official one
-    d_ee = ee;
-  }
-  // set the congruence kinds on the separate equality engine
-  d_ee->addFunctionKind(kind::NONLINEAR_MULT);
-  d_ee->addFunctionKind(kind::EXPONENTIAL);
-  d_ee->addFunctionKind(kind::SINE);
-  d_ee->addFunctionKind(kind::IAND);
-  d_ee->addFunctionKind(kind::POW2);
-  // the proof equality engine is the one from the equality engine
-  d_pfee = d_ee->getProofEqualityEngine();
-  // have proof equality engine only if proofs are enabled
-  Assert(isProofEnabled() == (d_pfee != nullptr));
-}
-
-ArithCongruenceManager::Statistics::Statistics()
-    : d_watchedVariables(smtStatisticsRegistry().registerInt(
-        "theory::arith::congruence::watchedVariables")),
-      d_watchedVariableIsZero(smtStatisticsRegistry().registerInt(
-          "theory::arith::congruence::watchedVariableIsZero")),
-      d_watchedVariableIsNotZero(smtStatisticsRegistry().registerInt(
-          "theory::arith::congruence::watchedVariableIsNotZero")),
-      d_equalsConstantCalls(smtStatisticsRegistry().registerInt(
-          "theory::arith::congruence::equalsConstantCalls")),
-      d_propagations(smtStatisticsRegistry().registerInt(
-          "theory::arith::congruence::propagations")),
-      d_propagateConstraints(smtStatisticsRegistry().registerInt(
-          "theory::arith::congruence::propagateConstraints")),
-      d_conflicts(smtStatisticsRegistry().registerInt(
-          "theory::arith::congruence::conflicts"))
-{
-}
-
-ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager& acm)
-  : d_acm(acm)
-{}
-
-bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(
-    TNode predicate, bool value)
-{
-  Assert(predicate.getKind() == kind::EQUAL);
-  Trace("arith::congruences")
-      << "ArithCongruenceNotify::eqNotifyTriggerPredicate(" << predicate << ", "
-      << (value ? "true" : "false") << ")" << std::endl;
-  if (value) {
-    return d_acm.propagate(predicate);
-  }
-  return d_acm.propagate(predicate.notNode());
-}
-
-bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) {
-  Trace("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl;
-  if (value) {
-    return d_acm.propagate(t1.eqNode(t2));
-  } else {
-    return d_acm.propagate(t1.eqNode(t2).notNode());
-  }
-}
-void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1, TNode t2) {
-  Trace("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl;
-  d_acm.propagate(t1.eqNode(t2));
-}
-void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t) {
-}
-void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyMerge(TNode t1,
-                                                                  TNode t2)
-{
-}
-void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) {
-}
-
-void ArithCongruenceManager::raiseConflict(Node conflict,
-                                           std::shared_ptr<ProofNode> pf)
-{
-  Assert(!inConflict());
-  Trace("arith::conflict") << "difference manager conflict   " << conflict << std::endl;
-  d_inConflict.raise();
-  d_raiseConflict.raiseEEConflict(conflict, pf);
-}
-bool ArithCongruenceManager::inConflict() const{
-  return d_inConflict.isRaised();
-}
-
-bool ArithCongruenceManager::hasMorePropagations() const {
-  return !d_propagatations.empty();
-}
-const Node ArithCongruenceManager::getNextPropagation() {
-  Assert(hasMorePropagations());
-  Node prop = d_propagatations.front();
-  d_propagatations.dequeue();
-  return prop;
-}
-
-bool ArithCongruenceManager::canExplain(TNode n) const {
-  return d_explanationMap.find(n) != d_explanationMap.end();
-}
-
-Node ArithCongruenceManager::externalToInternal(TNode n) const{
-  Assert(canExplain(n));
-  ExplainMap::const_iterator iter = d_explanationMap.find(n);
-  size_t pos = (*iter).second;
-  return d_propagatations[pos];
-}
-
-void ArithCongruenceManager::pushBack(TNode n){
-  d_explanationMap.insert(n, d_propagatations.size());
-  d_propagatations.enqueue(n);
-
-  ++(d_statistics.d_propagations);
-}
-void ArithCongruenceManager::pushBack(TNode n, TNode r){
-  d_explanationMap.insert(r, d_propagatations.size());
-  d_explanationMap.insert(n, d_propagatations.size());
-  d_propagatations.enqueue(n);
-
-  ++(d_statistics.d_propagations);
-}
-void ArithCongruenceManager::pushBack(TNode n, TNode r, TNode w){
-  d_explanationMap.insert(w, d_propagatations.size());
-  d_explanationMap.insert(r, d_propagatations.size());
-  d_explanationMap.insert(n, d_propagatations.size());
-  d_propagatations.enqueue(n);
-
-  ++(d_statistics.d_propagations);
-}
-
-void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub){
-  Assert(lb->isLowerBound());
-  Assert(ub->isUpperBound());
-  Assert(lb->getVariable() == ub->getVariable());
-  Assert(lb->getValue().sgn() == 0);
-  Assert(ub->getValue().sgn() == 0);
-
-  ++(d_statistics.d_watchedVariableIsZero);
-
-  ArithVar s = lb->getVariable();
-  TNode eq = d_watchedEqualities[s];
-  ConstraintCP eqC = d_constraintDatabase.getConstraint(
-      s, ConstraintType::Equality, lb->getValue());
-  NodeBuilder reasonBuilder(Kind::AND);
-  auto pfLb = lb->externalExplainByAssertions(reasonBuilder);
-  auto pfUb = ub->externalExplainByAssertions(reasonBuilder);
-  Node reason = mkAndFromBuilder(reasonBuilder);
-  std::shared_ptr<ProofNode> pf{};
-  if (isProofEnabled())
-  {
-    pf = d_pnm->mkNode(
-        PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eqC->getProofLiteral()});
-    pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {eq});
-  }
-
-  d_keepAlive.push_back(reason);
-  Trace("arith-ee") << "Asserting an equality on " << s << ", on trichotomy"
-                    << std::endl;
-  Trace("arith-ee") << "  based on " << lb << std::endl;
-  Trace("arith-ee") << "  based on " << ub << std::endl;
-  assertionToEqualityEngine(true, s, reason, pf);
-}
-
-void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq){
-  Trace("arith::cong") << "Cong::watchedVariableIsZero: " << *eq << std::endl;
-
-  Assert(eq->isEquality());
-  Assert(eq->getValue().sgn() == 0);
-
-  ++(d_statistics.d_watchedVariableIsZero);
-
-  ArithVar s = eq->getVariable();
-
-  //Explain for conflict is correct as these proofs are generated
-  //and stored eagerly
-  //These will be safe for propagation later as well
-  NodeBuilder nb(Kind::AND);
-  // An open proof of eq from literals now in reason.
-  if (TraceIsOn("arith::cong"))
-  {
-    eq->printProofTree(Trace("arith::cong"));
-  }
-  auto pf = eq->externalExplainByAssertions(nb);
-  if (isProofEnabled())
-  {
-    pf = d_pnm->mkNode(
-        PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {d_watchedEqualities[s]});
-  }
-  Node reason = mkAndFromBuilder(nb);
-
-  d_keepAlive.push_back(reason);
-  assertionToEqualityEngine(true, s, reason, pf);
-}
-
-void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c){
-  Trace("arith::cong::notzero")
-      << "Cong::watchedVariableCannotBeZero " << *c << std::endl;
-  ++(d_statistics.d_watchedVariableIsNotZero);
-
-  ArithVar s = c->getVariable();
-  Node disEq = d_watchedEqualities[s].negate();
-
-  //Explain for conflict is correct as these proofs are generated and stored eagerly
-  //These will be safe for propagation later as well
-  NodeBuilder nb(Kind::AND);
-  // An open proof of eq from literals now in reason.
-  auto pf = c->externalExplainByAssertions(nb);
-  if (TraceIsOn("arith::cong::notzero"))
-  {
-    Trace("arith::cong::notzero") << "  original proof ";
-    pf->printDebug(Trace("arith::cong::notzero"));
-    Trace("arith::cong::notzero") << std::endl;
-  }
-  Node reason = mkAndFromBuilder(nb);
-  if (isProofEnabled())
-  {
-    if (c->getType() == ConstraintType::Disequality)
-    {
-      Assert(c->getLiteral() == d_watchedEqualities[s].negate());
-      // We have to prove equivalence to the watched disequality.
-      pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {disEq});
-    }
-    else
-    {
-      Trace("arith::cong::notzero")
-          << "  proof modification needed" << std::endl;
-
-      // Four cases:
-      //   c has form x_i = d, d > 0     => multiply c by -1 in Farkas proof
-      //   c has form x_i = d, d > 0     => multiply c by 1 in Farkas proof
-      //   c has form x_i <= d, d < 0     => multiply c by 1 in Farkas proof
-      //   c has form x_i >= d, d > 0     => multiply c by -1 in Farkas proof
-      const bool scaleCNegatively = c->getType() == ConstraintType::LowerBound
-                                    || (c->getType() == ConstraintType::Equality
-                                        && c->getValue().sgn() > 0);
-      const int cSign = scaleCNegatively ? -1 : 1;
-      TNode isZero = d_watchedEqualities[s];
-      TypeNode type = isZero[0].getType();
-      const auto isZeroPf = d_pnm->mkAssume(isZero);
-      const auto nm = NodeManager::currentNM();
-      const auto sumPf =
-          d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
-                        {isZeroPf, pf},
-                        // Trick for getting correct, opposing signs.
-                        {nm->mkConstRealOrInt(type, Rational(-1 * cSign)),
-                         nm->mkConstRealOrInt(type, Rational(cSign))});
-      const auto botPf = d_pnm->mkNode(
-          PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
-      std::vector<Node> assumption = {isZero};
-      pf = d_pnm->mkScope(botPf, assumption, false);
-      Trace("arith::cong::notzero") << "  new proof ";
-      pf->printDebug(Trace("arith::cong::notzero"));
-      Trace("arith::cong::notzero") << std::endl;
-    }
-    Assert(pf->getResult() == disEq);
-  }
-  d_keepAlive.push_back(reason);
-  assertionToEqualityEngine(false, s, reason, pf);
-}
-
-
-bool ArithCongruenceManager::propagate(TNode x){
-  Trace("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x<<")"<<std::endl;
-  if(inConflict()){
-    return true;
-  }
-
-  Node rewritten = rewrite(x);
-
-  //Need to still propagate this!
-  if(rewritten.getKind() == kind::CONST_BOOLEAN){
-    pushBack(x);
-
-    if(rewritten.getConst<bool>()){
-      return true;
-    }else{
-      // x rewrites to false.
-      ++(d_statistics.d_conflicts);
-      TrustNode trn = explainInternal(x);
-      Node conf = flattenAnd(trn.getNode());
-      Trace("arith::congruenceManager") << "rewritten to false "<<x<<" with explanation "<< conf << std::endl;
-      if (isProofEnabled())
-      {
-        auto pf = trn.getGenerator()->getProofFor(trn.getProven());
-        auto confPf = d_pnm->mkNode(
-            PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {conf.negate()});
-        raiseConflict(conf, confPf);
-      }
-      else
-      {
-        raiseConflict(conf);
-      }
-      return false;
-    }
-  }
-
-  Assert(rewritten.getKind() != kind::CONST_BOOLEAN);
-
-  ConstraintP c = d_constraintDatabase.lookup(rewritten);
-  if(c == NullConstraint){
-    //using setup as there may not be a corresponding congruence literal yet
-    d_setupLiteral(rewritten);
-    c = d_constraintDatabase.lookup(rewritten);
-    Assert(c != NullConstraint);
-  }
-
-  Trace("arith::congruenceManager")<< "x is "
-                                   <<  c->hasProof() << " "
-                                   << (x == rewritten) << " "
-                                   << c->canBePropagated() << " "
-                                   << c->negationHasProof() << std::endl;
-
-  if(c->negationHasProof()){
-    TrustNode texpC = explainInternal(x);
-    Node expC = texpC.getNode();
-    ConstraintCP negC = c->getNegation();
-    Node neg = Constraint::externalExplainByAssertions({negC});
-    Node conf = expC.andNode(neg);
-    Node final = flattenAnd(conf);
-
-    ++(d_statistics.d_conflicts);
-    raiseConflict(final);
-    Trace("arith::congruenceManager") << "congruenceManager found a conflict " << final << std::endl;
-    return false;
-  }
-
-  // Cases for propagation
-  // C : c has a proof
-  // S : x == rewritten
-  // P : c can be propagated
-  //
-  // CSP
-  // 000 : propagate x, and mark C it as being explained
-  // 001 : propagate x, and propagate c after marking it as being explained
-  // 01* : propagate x, mark c but do not propagate c
-  // 10* : propagate x, do not mark c and do not propagate c
-  // 11* : drop the constraint, do not propagate x or c
-
-  if(!c->hasProof() && x != rewritten){
-    if(c->assertedToTheTheory()){
-      pushBack(x, rewritten, c->getWitness());
-    }else{
-      pushBack(x, rewritten);
-    }
-
-    c->setEqualityEngineProof();
-    if(c->canBePropagated() && !c->assertedToTheTheory()){
-
-      ++(d_statistics.d_propagateConstraints);
-      c->propagate();
-    }
-  }else if(!c->hasProof() && x == rewritten){
-    if(c->assertedToTheTheory()){
-      pushBack(x, c->getWitness());
-    }else{
-      pushBack(x);
-    }
-    c->setEqualityEngineProof();
-  }else if(c->hasProof() && x != rewritten){
-    if(c->assertedToTheTheory()){
-      pushBack(x);
-    }else{
-      pushBack(x);
-    }
-  }else{
-    Assert(c->hasProof() && x == rewritten);
-  }
-  return true;
-}
-
-void ArithCongruenceManager::explain(TNode literal, std::vector<TNode>& assumptions) {
-  if (literal.getKind() != kind::NOT) {
-    d_ee->explainEquality(literal[0], literal[1], true, assumptions);
-  } else {
-    d_ee->explainEquality(literal[0][0], literal[0][1], false, assumptions);
-  }
-}
-
-void ArithCongruenceManager::enqueueIntoNB(const std::set<TNode> s,
-                                           NodeBuilder& nb)
-{
-  std::set<TNode>::const_iterator it = s.begin();
-  std::set<TNode>::const_iterator it_end = s.end();
-  for(; it != it_end; ++it) {
-    nb << *it;
-  }
-}
-
-TrustNode ArithCongruenceManager::explainInternal(TNode internal)
-{
-  if (isProofEnabled())
-  {
-    return d_pfee->explain(internal);
-  }
-  // otherwise, explain without proof generator
-  Node exp = d_ee->mkExplainLit(internal);
-  return TrustNode::mkTrustPropExp(internal, exp, nullptr);
-}
-
-TrustNode ArithCongruenceManager::explain(TNode external)
-{
-  Trace("arith-ee") << "Ask for explanation of " << external << std::endl;
-  Node internal = externalToInternal(external);
-  Trace("arith-ee") << "...internal = " << internal << std::endl;
-  TrustNode trn = explainInternal(internal);
-  if (isProofEnabled() && trn.getProven()[1] != external)
-  {
-    Assert(trn.getKind() == TrustNodeKind::PROP_EXP);
-    Assert(trn.getProven().getKind() == Kind::IMPLIES);
-    Assert(trn.getGenerator() != nullptr);
-    Trace("arith-ee") << "tweaking proof to prove " << external << " not "
-                      << trn.getProven()[1] << std::endl;
-    std::vector<std::shared_ptr<ProofNode>> assumptionPfs;
-    std::vector<Node> assumptions = andComponents(trn.getNode());
-    assumptionPfs.push_back(trn.toProofNode());
-    for (const auto& a : assumptions)
-    {
-      assumptionPfs.push_back(
-          d_pnm->mkNode(PfRule::TRUE_INTRO, {d_pnm->mkAssume(a)}, {}));
-    }
-    auto litPf = d_pnm->mkNode(
-        PfRule::MACRO_SR_PRED_TRANSFORM, {assumptionPfs}, {external});
-    auto extPf = d_pnm->mkScope(litPf, assumptions);
-    return d_pfGenExplain->mkTrustedPropagation(external, trn.getNode(), extPf);
-  }
-  return trn;
-}
-
-void ArithCongruenceManager::explain(TNode external, NodeBuilder& out)
-{
-  Node internal = externalToInternal(external);
-
-  std::vector<TNode> assumptions;
-  explain(internal, assumptions);
-  std::set<TNode> assumptionSet;
-  assumptionSet.insert(assumptions.begin(), assumptions.end());
-
-  enqueueIntoNB(assumptionSet, out);
-}
-
-void ArithCongruenceManager::addWatchedPair(ArithVar s, TNode x, TNode y){
-  Assert(!isWatchedVariable(s));
-
-  Trace("arith::congruenceManager")
-    << "addWatchedPair(" << s << ", " << x << ", " << y << ")" << std::endl;
-
-
-  ++(d_statistics.d_watchedVariables);
-
-  d_watchedVariables.add(s);
-
-  Node eq = x.eqNode(y);
-  d_watchedEqualities.set(s, eq);
-}
-
-void ArithCongruenceManager::assertLitToEqualityEngine(
-    Node lit, TNode reason, std::shared_ptr<ProofNode> pf)
-{
-  bool isEquality = lit.getKind() != Kind::NOT;
-  Node eq = isEquality ? lit : lit[0];
-  Assert(eq.getKind() == Kind::EQUAL);
-
-  Trace("arith-ee") << "Assert to Eq " << lit << ", reason " << reason
-                    << std::endl;
-  if (isProofEnabled())
-  {
-    if (CDProof::isSame(lit, reason))
-    {
-      Trace("arith-pfee") << "Asserting only, b/c implied by symm" << std::endl;
-      // The equality engine doesn't ref-count for us...
-      d_keepAlive.push_back(eq);
-      d_keepAlive.push_back(reason);
-      d_ee->assertEquality(eq, isEquality, reason);
-    }
-    else if (hasProofFor(lit))
-    {
-      Trace("arith-pfee") << "Skipping b/c already done" << std::endl;
-    }
-    else
-    {
-      setProofFor(lit, pf);
-      Trace("arith-pfee") << "Actually asserting" << std::endl;
-      if (TraceIsOn("arith-pfee"))
-      {
-        Trace("arith-pfee") << "Proof: ";
-        pf->printDebug(Trace("arith-pfee"));
-        Trace("arith-pfee") << std::endl;
-      }
-      // The proof equality engine *does* ref-count for us...
-      d_pfee->assertFact(lit, reason, d_pfGenEe.get());
-    }
-  }
-  else
-  {
-    // The equality engine doesn't ref-count for us...
-    d_keepAlive.push_back(eq);
-    d_keepAlive.push_back(reason);
-    d_ee->assertEquality(eq, isEquality, reason);
-  }
-}
-
-void ArithCongruenceManager::assertionToEqualityEngine(
-    bool isEquality, ArithVar s, TNode reason, std::shared_ptr<ProofNode> pf)
-{
-  Assert(isWatchedVariable(s));
-
-  TNode eq = d_watchedEqualities[s];
-  Assert(eq.getKind() == kind::EQUAL);
-
-  Node lit = isEquality ? Node(eq) : eq.notNode();
-  Trace("arith-ee") << "Assert to Eq " << eq << ", pol " << isEquality
-                    << ", reason " << reason << std::endl;
-  assertLitToEqualityEngine(lit, reason, pf);
-}
-
-bool ArithCongruenceManager::hasProofFor(TNode f) const
-{
-  Assert(isProofEnabled());
-  if (d_pfGenEe->hasProofFor(f))
-  {
-    return true;
-  }
-  Node sym = CDProof::getSymmFact(f);
-  Assert(!sym.isNull());
-  return d_pfGenEe->hasProofFor(sym);
-}
-
-void ArithCongruenceManager::setProofFor(TNode f,
-                                         std::shared_ptr<ProofNode> pf) const
-{
-  Assert(!hasProofFor(f));
-  d_pfGenEe->mkTrustNode(f, pf);
-  Node symF = CDProof::getSymmFact(f);
-  auto symPf = d_pnm->mkNode(PfRule::SYMM, {pf}, {});
-  d_pfGenEe->mkTrustNode(symF, symPf);
-}
-
-void ArithCongruenceManager::equalsConstant(ConstraintCP c){
-  Assert(c->isEquality());
-
-  ++(d_statistics.d_equalsConstantCalls);
-  Trace("equalsConstant") << "equals constant " << c << std::endl;
-
-  ArithVar x = c->getVariable();
-  Node xAsNode = d_avariables.asNode(x);
-  NodeManager* nm = NodeManager::currentNM();
-  Node asRational = nm->mkConstRealOrInt(
-      xAsNode.getType(), c->getValue().getNoninfinitesimalPart());
-
-  // No guarentee this is in normal form!
-  // Note though, that it happens to be in proof normal form!
-  Node eq = xAsNode.eqNode(asRational);
-  d_keepAlive.push_back(eq);
-
-  NodeBuilder nb(Kind::AND);
-  auto pf = c->externalExplainByAssertions(nb);
-  Node reason = mkAndFromBuilder(nb);
-  d_keepAlive.push_back(reason);
-
-  Trace("arith-ee") << "Assert equalsConstant " << eq << ", reason " << reason << std::endl;
-  assertLitToEqualityEngine(eq, reason, pf);
-}
-
-void ArithCongruenceManager::equalsConstant(ConstraintCP lb, ConstraintCP ub){
-  Assert(lb->isLowerBound());
-  Assert(ub->isUpperBound());
-  Assert(lb->getVariable() == ub->getVariable());
-
-  ++(d_statistics.d_equalsConstantCalls);
-  Trace("equalsConstant") << "equals constant " << lb << std::endl
-                          << ub << std::endl;
-
-  ArithVar x = lb->getVariable();
-  NodeBuilder nb(Kind::AND);
-  auto pfLb = lb->externalExplainByAssertions(nb);
-  auto pfUb = ub->externalExplainByAssertions(nb);
-  Node reason = mkAndFromBuilder(nb);
-
-  Node xAsNode = d_avariables.asNode(x);
-  NodeManager* nm = NodeManager::currentNM();
-  Node asRational = nm->mkConstRealOrInt(
-      xAsNode.getType(), lb->getValue().getNoninfinitesimalPart());
-
-  // No guarentee this is in normal form!
-  // Note though, that it happens to be in proof normal form!
-  Node eq = xAsNode.eqNode(asRational);
-  std::shared_ptr<ProofNode> pf;
-  if (isProofEnabled())
-  {
-    pf = d_pnm->mkNode(PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eq});
-  }
-  d_keepAlive.push_back(eq);
-  d_keepAlive.push_back(reason);
-
-  Trace("arith-ee") << "Assert equalsConstant2 " << eq << ", reason " << reason << std::endl;
-
-  assertLitToEqualityEngine(eq, reason, pf);
-}
-
-bool ArithCongruenceManager::isProofEnabled() const { return d_pnm != nullptr; }
-
-std::vector<Node> andComponents(TNode an)
-{
-  auto nm = NodeManager::currentNM();
-  if (an == nm->mkConst(true))
-  {
-    return {};
-  }
-  else if (an.getKind() != Kind::AND)
-  {
-    return {an};
-  }
-  std::vector<Node> a{};
-  a.reserve(an.getNumChildren());
-  a.insert(a.end(), an.begin(), an.end());
-  return a;
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/congruence_manager.h b/src/theory/arith/congruence_manager.h
deleted file mode 100644 (file)
index 1cacb94..0000000
+++ /dev/null
@@ -1,300 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Alex Ozdemir, Tim King, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "context/cdhashmap.h"
-#include "context/cdlist.h"
-#include "context/cdmaybe.h"
-#include "context/cdtrail_queue.h"
-#include "proof/trust_node.h"
-#include "smt/env_obj.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/callbacks.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/uf/equality_engine_notify.h"
-#include "util/dense_map.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::context {
-class Context;
-class UserContext;
-}  // namespace cvc5::context
-
-namespace cvc5::internal {
-
-class ProofNodeManager;
-class EagerProofGenerator;
-
-namespace theory {
-struct EeSetupInfo;
-
-namespace eq {
-class ProofEqEngine;
-class EqualityEngine;
-}
-
-namespace arith {
-
-class ArithVariables;
-
-class ArithCongruenceManager : protected EnvObj
-{
- private:
-  context::CDRaised d_inConflict;
-  RaiseEqualityEngineConflict d_raiseConflict;
-
-  /**
-   * The set of ArithVars equivalent to a pair of terms.
-   * If this is 0 or cannot be 0, this can be signalled.
-   * The pair of terms for the congruence is stored in watched equalities.
-   */
-  DenseSet d_watchedVariables;
-  /** d_watchedVariables |-> (= x y) */
-  ArithVarToNodeMap d_watchedEqualities;
-
-
-  class ArithCongruenceNotify : public eq::EqualityEngineNotify {
-  private:
-    ArithCongruenceManager& d_acm;
-  public:
-    ArithCongruenceNotify(ArithCongruenceManager& acm);
-
-    bool eqNotifyTriggerPredicate(TNode predicate, bool value) override;
-
-    bool eqNotifyTriggerTermEquality(TheoryId tag,
-                                     TNode t1,
-                                     TNode t2,
-                                     bool value) override;
-
-    void eqNotifyConstantTermMerge(TNode t1, TNode t2) override;
-    void eqNotifyNewClass(TNode t) override;
-    void eqNotifyMerge(TNode t1, TNode t2) override;
-    void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) override;
-  };
-  ArithCongruenceNotify d_notify;
-
-  context::CDList<Node> d_keepAlive;
-
-  /** Store the propagations. */
-  context::CDTrailQueue<Node> d_propagatations;
-
-  /* This maps the node a theory engine will request on an explain call to
-   * to its corresponding PropUnit.
-   * This is node is potentially both the propagation or
-   * rewrite(propagation).
-   */
-  typedef context::CDHashMap<Node, size_t> ExplainMap;
-  ExplainMap d_explanationMap;
-
-  ConstraintDatabase& d_constraintDatabase;
-  SetupLiteralCallBack d_setupLiteral;
-
-  const ArithVariables& d_avariables;
-
-  /** The equality engine being used by this class */
-  eq::EqualityEngine* d_ee;
-  /** The equality engine we allocated */
-  std::unique_ptr<eq::EqualityEngine> d_allocEe;
-  /** proof manager */
-  ProofNodeManager* d_pnm;
-  /** A proof generator for storing proofs of facts that are asserted to the EQ
-   * engine. Note that these proofs **are not closed**; they may contain
-   * literals from the explanation of their fact as unclosed assumptions.
-   * This makes these proofs SAT-context depdent.
-   *
-   * This is why this generator is separate from the TheoryArithPrivate
-   * generator, which stores closed proofs.
-   */
-  std::unique_ptr<EagerProofGenerator> d_pfGenEe;
-  /** A proof generator for TrustNodes sent to TheoryArithPrivate.
-   *
-   * When TheoryArithPrivate requests an explanation from
-   * ArithCongruenceManager, it can request a TrustNode for that explanation.
-   * This proof generator is the one used in that TrustNode: it stores the
-   * (closed) proofs of implications proved by the
-   * ArithCongruenceManager/EqualityEngine.
-   *
-   * It is insufficient to just use the ProofGenerator from the ProofEqEngine,
-   * since sometimes the ArithCongruenceManager needs to add some
-   * arith-specific reasoning to extend the proof (e.g. rewriting the result
-   * into a normal form).
-   * */
-  std::unique_ptr<EagerProofGenerator> d_pfGenExplain;
-
-  /** Pointer to the proof equality engine of TheoryArith */
-  theory::eq::ProofEqEngine* d_pfee;
-  /** The proof equality engine we allocated */
-  std::unique_ptr<eq::ProofEqEngine> d_allocPfee;
-
-  /** Raise a conflict node `conflict` to the theory of arithmetic.
-   *
-   * When proofs are enabled, a (closed) proof of the conflict should be
-   * provided.
-   */
-  void raiseConflict(Node conflict, std::shared_ptr<ProofNode> pf = nullptr);
-  /**
-   * Are proofs enabled? This is true if a non-null proof manager was provided
-   * to the constructor of this class.
-   */
-  bool isProofEnabled() const;
-
- public:
-  bool inConflict() const;
-
-  bool hasMorePropagations() const;
-
-  const Node getNextPropagation();
-
-  bool canExplain(TNode n) const;
-
-private:
-  Node externalToInternal(TNode n) const;
-
-  void pushBack(TNode n);
-
-  void pushBack(TNode n, TNode r);
-
-  void pushBack(TNode n, TNode r, TNode w);
-
-  bool propagate(TNode x);
-  void explain(TNode literal, std::vector<TNode>& assumptions);
-
-  /** Assert this literal to the eq engine. Common functionality for
-   *   * assertionToEqualityEngine(..)
-   *   * equalsConstant(c)
-   *   * equalsConstant(lb, ub)
-   * If proof is off, then just asserts.
-   */
-  void assertLitToEqualityEngine(Node lit,
-                                 TNode reason,
-                                 std::shared_ptr<ProofNode> pf);
-  /** This sends a shared term to the uninterpreted equality engine. */
-  void assertionToEqualityEngine(bool eq,
-                                 ArithVar s,
-                                 TNode reason,
-                                 std::shared_ptr<ProofNode> pf);
-
-  /** Check for proof for this or a symmetric fact
-   *
-   * The proof submitted to this method are stored in `d_pfGenEe`, and should
-   * have closure properties consistent with the documentation for that member.
-   *
-   * @returns whether this or a symmetric fact has a proof.
-   */
-  bool hasProofFor(TNode f) const;
-  /**
-   * Sets the proof for this fact and the symmetric one.
-   *
-   * The proof submitted to this method are stored in `d_pfGenEe`, and should
-   * have closure properties consistent with the documentation for that member.
-   * */
-  void setProofFor(TNode f, std::shared_ptr<ProofNode> pf) const;
-
-  /** Dequeues the delay queue and asserts these equalities.*/
-  void enableSharedTerms();
-  void dequeueLiterals();
-
-  void enqueueIntoNB(const std::set<TNode> all, NodeBuilder& nb);
-
-  /**
-   * Determine an explaination for `internal`. That is a conjunction of theory
-   * literals which imply `internal`.
-   *
-   * The TrustNode here is a trusted propagation.
-   */
-  TrustNode explainInternal(TNode internal);
-
- public:
-  ArithCongruenceManager(Env& env,
-                         ConstraintDatabase&,
-                         SetupLiteralCallBack,
-                         const ArithVariables&,
-                         RaiseEqualityEngineConflict raiseConflict);
-  ~ArithCongruenceManager();
-
-  //--------------------------------- initialization
-  /**
-   * Returns true if we need an equality engine, see
-   * Theory::needsEqualityEngine.
-   */
-  bool needsEqualityEngine(EeSetupInfo& esi);
-  /**
-   * Finish initialize. This class is instructed by TheoryArithPrivate to use
-   * the equality engine ee.
-   */
-  void finishInit(eq::EqualityEngine* ee);
-  //--------------------------------- end initialization
-
-  /**
-   * Return the trust node for the explanation of literal. The returned
-   * trust node is generated by the proof equality engine of this class.
-   */
-  TrustNode explain(TNode literal);
-
-  void explain(TNode lit, NodeBuilder& out);
-
-  void addWatchedPair(ArithVar s, TNode x, TNode y);
-
-  inline bool isWatchedVariable(ArithVar s) const {
-    return d_watchedVariables.isMember(s);
-  }
-
-  /** Assert an equality. */
-  void watchedVariableIsZero(ConstraintCP eq);
-
-  /** Assert a conjunction from lb and ub. */
-  void watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub);
-
-  /** Assert that the value cannot be zero. */
-  void watchedVariableCannotBeZero(ConstraintCP c);
-
-  /** Assert that the value cannot be zero. */
-  void watchedVariableCannotBeZero(ConstraintCP c, ConstraintCP d);
-
-
-  /** Assert that the value is congruent to a constant. */
-  void equalsConstant(ConstraintCP eq);
-  void equalsConstant(ConstraintCP lb, ConstraintCP ub);
-
- private:
-  class Statistics {
-  public:
-    IntStat d_watchedVariables;
-    IntStat d_watchedVariableIsZero;
-    IntStat d_watchedVariableIsNotZero;
-
-    IntStat d_equalsConstantCalls;
-
-    IntStat d_propagations;
-    IntStat d_propagateConstraints;
-    IntStat d_conflicts;
-
-    Statistics();
-  } d_statistics;
-
-}; /* class ArithCongruenceManager */
-
-std::vector<Node> andComponents(TNode an);
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/constraint.cpp b/src/theory/arith/constraint.cpp
deleted file mode 100644 (file)
index dc1f430..0000000
+++ /dev/null
@@ -1,2463 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Alex Ozdemir, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-#include "theory/arith/constraint.h"
-
-#include <algorithm>
-#include <ostream>
-#include <unordered_set>
-
-#include "base/output.h"
-#include "options/smt_options.h"
-#include "proof/eager_proof_generator.h"
-#include "proof/proof_node_manager.h"
-#include "smt/env.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/congruence_manager.h"
-#include "theory/arith/normal_form.h"
-#include "theory/arith/partial_model.h"
-#include "theory/builtin/proof_checker.h"
-#include "theory/rewriter.h"
-
-using namespace std;
-using namespace cvc5::internal::kind;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-ConstraintRule::ConstraintRule()
-    : d_constraint(NullConstraint),
-      d_proofType(NoAP),
-      d_antecedentEnd(AntecedentIdSentinel)
-{
-  d_farkasCoefficients = RationalVectorCPSentinel;
-}
-
-ConstraintRule::ConstraintRule(ConstraintP con, ArithProofType pt)
-    : d_constraint(con), d_proofType(pt), d_antecedentEnd(AntecedentIdSentinel)
-{
-  d_farkasCoefficients = RationalVectorCPSentinel;
-}
-ConstraintRule::ConstraintRule(ConstraintP con,
-                               ArithProofType pt,
-                               AntecedentId antecedentEnd)
-    : d_constraint(con), d_proofType(pt), d_antecedentEnd(antecedentEnd)
-{
-  d_farkasCoefficients = RationalVectorCPSentinel;
-}
-
-ConstraintRule::ConstraintRule(ConstraintP con,
-                               ArithProofType pt,
-                               AntecedentId antecedentEnd,
-                               RationalVectorCP coeffs)
-    : d_constraint(con), d_proofType(pt), d_antecedentEnd(antecedentEnd)
-{
-  Assert(con->isProofProducing() || coeffs == RationalVectorCPSentinel);
-  d_farkasCoefficients = coeffs;
-}
-
-/** Given a simplifiedKind this returns the corresponding ConstraintType. */
-//ConstraintType constraintTypeOfLiteral(Kind k);
-ConstraintType Constraint::constraintTypeOfComparison(const Comparison& cmp){
-  Kind k = cmp.comparisonKind();
-  switch(k){
-  case LT:
-  case LEQ:
-    {
-      Polynomial l = cmp.getLeft();
-      if(l.leadingCoefficientIsPositive()){ // (< x c)
-        return UpperBound;
-      }else{
-        return LowerBound; // (< (-x) c)
-      }
-    }
-  case GT:
-  case GEQ:
-    {
-      Polynomial l = cmp.getLeft();
-      if(l.leadingCoefficientIsPositive()){
-        return LowerBound; // (> x c)
-      }else{
-        return UpperBound; // (> (-x) c)
-      }
-    }
-  case EQUAL:
-    return Equality;
-  case DISTINCT:
-    return Disequality;
-  default: Unhandled() << k;
-  }
-}
-
-Constraint::Constraint(ArithVar x,
-                       ConstraintType t,
-                       const DeltaRational& v,
-                       bool produceProofs)
-    : d_variable(x),
-      d_type(t),
-      d_value(v),
-      d_database(NULL),
-      d_literal(Node::null()),
-      d_negation(NullConstraint),
-      d_canBePropagated(false),
-      d_assertionOrder(AssertionOrderSentinel),
-      d_witness(TNode::null()),
-      d_crid(ConstraintRuleIdSentinel),
-      d_split(false),
-      d_variablePosition(),
-      d_produceProofs(produceProofs)
-{
-  Assert(!initialized());
-}
-
-
-std::ostream& operator<<(std::ostream& o, const ArithProofType apt){
-  switch(apt){
-  case NoAP:  o << "NoAP"; break;
-  case AssumeAP:  o << "AssumeAP"; break;
-  case InternalAssumeAP:  o << "InternalAssumeAP"; break;
-  case FarkasAP:  o << "FarkasAP"; break;
-  case TrichotomyAP:  o << "TrichotomyAP"; break;
-  case EqualityEngineAP:  o << "EqualityEngineAP"; break;
-  case IntTightenAP: o << "IntTightenAP"; break;
-  case IntHoleAP: o << "IntHoleAP"; break;
-  default: break;
-  }
-  return o;
-}
-
-std::ostream& operator<<(std::ostream& o, const ConstraintCP c){
-  if(c == NullConstraint){
-    return o << "NullConstraint";
-  }else{
-    return o << *c;
-  }
-}
-
-std::ostream& operator<<(std::ostream& o, const ConstraintP c){
-  if(c == NullConstraint){
-    return o << "NullConstraint";
-  }else{
-    return o << *c;
-  }
-}
-
-std::ostream& operator<<(std::ostream& o, const ConstraintType t){
-  switch(t){
-  case LowerBound:
-    return o << ">=";
-  case UpperBound:
-    return o << "<=";
-  case Equality:
-    return o << "=";
-  case Disequality:
-    return o << "!=";
-  default:
-    Unreachable();
-  }
-}
-
-std::ostream& operator<<(std::ostream& o, const Constraint& c){
-  o << c.getVariable() << ' ' << c.getType() << ' ' << c.getValue();
-  if(c.hasLiteral()){
-    o << "(node " << c.getLiteral() << ')';
-  }
-  return o;
-}
-
-std::ostream& operator<<(std::ostream& o, const ValueCollection& vc){
-  o << "{";
-  bool pending = false;
-  if(vc.hasEquality()){
-    o << "eq: " << vc.getEquality();
-    pending = true;
-  }
-  if(vc.hasLowerBound()){
-    if(pending){
-      o << ", ";
-    }
-    o << "lb: " << vc.getLowerBound();
-    pending = true;
-  }
-  if(vc.hasUpperBound()){
-    if(pending){
-      o << ", ";
-    }
-    o << "ub: " << vc.getUpperBound();
-    pending = true;
-  }
-  if(vc.hasDisequality()){
-    if(pending){
-      o << ", ";
-    }
-    o << "de: " << vc.getDisequality();
-  }
-  return o << "}";
-}
-
-std::ostream& operator<<(std::ostream& o, const ConstraintCPVec& v){
-  o << "[" << v.size() << "x";
-  ConstraintCPVec::const_iterator i, end;
-  for(i=v.begin(), end=v.end(); i != end; ++i){
-    ConstraintCP c = *i;
-    o << ", " << (*c);
-  }
-  o << "]";
-  return o;
-}
-
-ValueCollection::ValueCollection()
-  : d_lowerBound(NullConstraint),
-    d_upperBound(NullConstraint),
-    d_equality(NullConstraint),
-    d_disequality(NullConstraint)
-{}
-
-bool ValueCollection::hasLowerBound() const{
-  return d_lowerBound != NullConstraint;
-}
-
-bool ValueCollection::hasUpperBound() const{
-  return d_upperBound != NullConstraint;
-}
-
-bool ValueCollection::hasEquality() const{
-  return d_equality != NullConstraint;
-}
-
-bool ValueCollection::hasDisequality() const {
-  return d_disequality != NullConstraint;
-}
-
-ConstraintP ValueCollection::getLowerBound() const {
-  Assert(hasLowerBound());
-  return d_lowerBound;
-}
-
-ConstraintP ValueCollection::getUpperBound() const {
-  Assert(hasUpperBound());
-  return d_upperBound;
-}
-
-ConstraintP ValueCollection::getEquality() const {
-  Assert(hasEquality());
-  return d_equality;
-}
-
-ConstraintP ValueCollection::getDisequality() const {
-  Assert(hasDisequality());
-  return d_disequality;
-}
-
-
-void ValueCollection::push_into(std::vector<ConstraintP>& vec) const {
-  Trace("arith::constraint") << "push_into " << *this << endl;
-  if(hasEquality()){
-    vec.push_back(d_equality);
-  }
-  if(hasLowerBound()){
-    vec.push_back(d_lowerBound);
-  }
-  if(hasUpperBound()){
-    vec.push_back(d_upperBound);
-  }
-  if(hasDisequality()){
-    vec.push_back(d_disequality);
-  }
-}
-
-ValueCollection ValueCollection::mkFromConstraint(ConstraintP c){
-  ValueCollection ret;
-  Assert(ret.empty());
-  switch(c->getType()){
-  case LowerBound:
-    ret.d_lowerBound = c;
-    break;
-  case UpperBound:
-    ret.d_upperBound = c;
-    break;
-  case Equality:
-    ret.d_equality = c;
-    break;
-  case Disequality:
-    ret.d_disequality = c;
-    break;
-  default:
-    Unreachable();
-  }
-  return ret;
-}
-
-bool ValueCollection::hasConstraintOfType(ConstraintType t) const{
-  switch(t){
-  case LowerBound:
-    return hasLowerBound();
-  case UpperBound:
-    return hasUpperBound();
-  case Equality:
-    return hasEquality();
-  case Disequality:
-    return hasDisequality();
-  default:
-    Unreachable();
-  }
-}
-
-ArithVar ValueCollection::getVariable() const{
-  Assert(!empty());
-  return nonNull()->getVariable();
-}
-
-const DeltaRational& ValueCollection::getValue() const{
-  Assert(!empty());
-  return nonNull()->getValue();
-}
-
-void ValueCollection::add(ConstraintP c){
-  Assert(c != NullConstraint);
-
-  Assert(empty() || getVariable() == c->getVariable());
-  Assert(empty() || getValue() == c->getValue());
-
-  switch(c->getType()){
-  case LowerBound:
-    Assert(!hasLowerBound());
-    d_lowerBound = c;
-    break;
-  case Equality:
-    Assert(!hasEquality());
-    d_equality = c;
-    break;
-  case UpperBound:
-    Assert(!hasUpperBound());
-    d_upperBound = c;
-    break;
-  case Disequality:
-    Assert(!hasDisequality());
-    d_disequality = c;
-    break;
-  default:
-    Unreachable();
-  }
-}
-
-ConstraintP ValueCollection::getConstraintOfType(ConstraintType t) const{
-  switch(t){
-    case LowerBound: Assert(hasLowerBound()); return d_lowerBound;
-    case Equality: Assert(hasEquality()); return d_equality;
-    case UpperBound: Assert(hasUpperBound()); return d_upperBound;
-    case Disequality: Assert(hasDisequality()); return d_disequality;
-    default: Unreachable();
-  }
-}
-
-void ValueCollection::remove(ConstraintType t){
-  switch(t){
-  case LowerBound:
-    Assert(hasLowerBound());
-    d_lowerBound = NullConstraint;
-    break;
-  case Equality:
-    Assert(hasEquality());
-    d_equality = NullConstraint;
-    break;
-  case UpperBound:
-    Assert(hasUpperBound());
-    d_upperBound = NullConstraint;
-    break;
-  case Disequality:
-    Assert(hasDisequality());
-    d_disequality = NullConstraint;
-    break;
-  default:
-    Unreachable();
-  }
-}
-
-bool ValueCollection::empty() const{
-  return
-    !(hasLowerBound() ||
-      hasUpperBound() ||
-      hasEquality() ||
-      hasDisequality());
-}
-
-ConstraintP ValueCollection::nonNull() const{
-  //This can be optimized by caching, but this is not necessary yet!
-  /* "Premature optimization is the root of all evil." */
-  if(hasLowerBound()){
-    return d_lowerBound;
-  }else if(hasUpperBound()){
-    return d_upperBound;
-  }else if(hasEquality()){
-    return d_equality;
-  }else if(hasDisequality()){
-    return d_disequality;
-  }else{
-    return NullConstraint;
-  }
-}
-
-bool Constraint::initialized() const {
-  return d_database != NULL;
-}
-
-const ConstraintDatabase& Constraint::getDatabase() const{
-  Assert(initialized());
-  return *d_database;
-}
-
-void Constraint::initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, ConstraintP negation){
-  Assert(!initialized());
-  d_database = db;
-  d_variablePosition = v;
-  d_negation = negation;
-}
-
-Constraint::~Constraint() {
-  // Call this instead of safeToGarbageCollect()
-  Assert(!contextDependentDataIsSet());
-
-  if(initialized()){
-    ValueCollection& vc =  d_variablePosition->second;
-    Trace("arith::constraint") << "removing" << vc << endl;
-
-    vc.remove(getType());
-
-    if(vc.empty()){
-      Trace("arith::constraint") << "erasing" << vc << endl;
-      SortedConstraintMap& perVariable = d_database->getVariableSCM(getVariable());
-      perVariable.erase(d_variablePosition);
-    }
-
-    if(hasLiteral()){
-      d_database->d_nodetoConstraintMap.erase(getLiteral());
-    }
-  }
-}
-
-const ConstraintRule& Constraint::getConstraintRule() const {
-  Assert(hasProof());
-  return d_database->d_watches->d_constraintProofs[d_crid];
-}
-
-const ValueCollection& Constraint::getValueCollection() const{
-  return d_variablePosition->second;
-}
-
-
-ConstraintP Constraint::getCeiling() {
-  Trace("getCeiling") << "Constraint_::getCeiling on " << *this << endl;
-  Assert(getValue().getInfinitesimalPart().sgn() > 0);
-
-  const DeltaRational ceiling(getValue().ceiling());
-  return d_database->getConstraint(getVariable(), getType(), ceiling);
-}
-
-ConstraintP Constraint::getFloor() {
-  Assert(getValue().getInfinitesimalPart().sgn() < 0);
-
-  const DeltaRational floor(Rational(getValue().floor()));
-  return d_database->getConstraint(getVariable(), getType(), floor);
-}
-
-void Constraint::setCanBePropagated() {
-  Assert(!canBePropagated());
-  d_database->pushCanBePropagatedWatch(this);
-}
-
-void Constraint::setAssertedToTheTheory(TNode witness, bool nowInConflict) {
-  Assert(hasLiteral());
-  Assert(!assertedToTheTheory());
-  Assert(negationHasProof() == nowInConflict);
-  d_database->pushAssertionOrderWatch(this, witness);
-
-  if(TraceIsOn("constraint::conflictCommit") && nowInConflict ){
-    Trace("constraint::conflictCommit") << "inConflict@setAssertedToTheTheory";
-    Trace("constraint::conflictCommit") << "\t" << this << std::endl;
-    Trace("constraint::conflictCommit") << "\t" << getNegation() << std::endl;
-    Trace("constraint::conflictCommit") << "\t" << getNegation()->externalExplainByAssertions() << std::endl;
-
-  }
-}
-
-bool Constraint::satisfiedBy(const DeltaRational& dr) const {
-  switch(getType()){
-  case LowerBound:
-    return getValue() <= dr;
-  case Equality:
-    return getValue() == dr;
-  case UpperBound:
-    return getValue() >= dr;
-  case Disequality:
-    return getValue() != dr;
-  }
-  Unreachable();
-}
-
-bool Constraint::isInternalAssumption() const {
-  return getProofType() == InternalAssumeAP;
-}
-
-TrustNode Constraint::externalExplainByAssertions() const
-{
-  NodeBuilder nb(kind::AND);
-  auto pfFromAssumptions = externalExplain(nb, AssertionOrderSentinel);
-  Node exp = mkAndFromBuilder(nb);
-  if (d_database->isProofEnabled())
-  {
-    std::vector<Node> assumptions;
-    if (exp.getKind() == Kind::AND)
-    {
-      assumptions.insert(assumptions.end(), exp.begin(), exp.end());
-    }
-    else
-    {
-      assumptions.push_back(exp);
-    }
-    auto pf = d_database->d_pnm->mkScope(pfFromAssumptions, assumptions);
-    return d_database->d_pfGen->mkTrustedPropagation(
-        getLiteral(), NodeManager::currentNM()->mkAnd(assumptions), pf);
-  }
-  return TrustNode::mkTrustPropExp(getLiteral(), exp);
-}
-
-bool Constraint::isAssumption() const {
-  return getProofType() == AssumeAP;
-}
-
-bool Constraint::hasEqualityEngineProof() const {
-  return getProofType() == EqualityEngineAP;
-}
-
-bool Constraint::hasFarkasProof() const {
-  return getProofType() == FarkasAP;
-}
-
-bool Constraint::hasSimpleFarkasProof() const
-{
-  Trace("constraints::hsfp") << "hasSimpleFarkasProof " << this << std::endl;
-  if (!hasFarkasProof())
-  {
-    Trace("constraints::hsfp") << "There is no simple Farkas proof because "
-                                  "there is no farkas proof."
-                               << std::endl;
-    return false;
-  }
-
-  // For each antecdent ...
-  AntecedentId i = getConstraintRule().d_antecedentEnd;
-  for (ConstraintCP a = d_database->getAntecedent(i); a != NullConstraint;
-       a = d_database->getAntecedent(--i))
-  {
-    // ... that antecdent must be an assumption OR a tightened assumption ...
-    if (a->isPossiblyTightenedAssumption())
-    {
-      continue;
-    }
-
-    // ... otherwise, we do not have a simple Farkas proof.
-    if (TraceIsOn("constraints::hsfp"))
-    {
-      Trace("constraints::hsfp") << "There is no simple Farkas proof b/c there "
-                                    "is an antecdent w/ rule ";
-      a->getConstraintRule().print(Trace("constraints::hsfp"), d_produceProofs);
-      Trace("constraints::hsfp") << std::endl;
-    }
-
-    return false;
-  }
-  return true;
-}
-
-bool Constraint::isPossiblyTightenedAssumption() const
-{
-  // ... that antecdent must be an assumption ...
-
-  if (isAssumption()) return true;
-  if (!hasIntTightenProof()) return false;
-  if (getConstraintRule().d_antecedentEnd == AntecedentIdSentinel) return false;
-  return d_database->getAntecedent(getConstraintRule().d_antecedentEnd)
-      ->isAssumption();
-}
-
-bool Constraint::hasIntTightenProof() const {
-  return getProofType() == IntTightenAP;
-}
-
-bool Constraint::hasIntHoleProof() const {
-  return getProofType() == IntHoleAP;
-}
-
-bool Constraint::hasTrichotomyProof() const {
-  return getProofType() == TrichotomyAP;
-}
-
-void Constraint::printProofTree(std::ostream& out, size_t depth) const
-{
-  if (d_produceProofs)
-  {
-    const ConstraintRule& rule = getConstraintRule();
-    out << std::string(2 * depth, ' ') << "* " << getVariable() << " [";
-    out << getProofLiteral();
-    if (assertedToTheTheory())
-    {
-      out << " | wit: " << getWitness();
-    }
-    out << "]" << ' ' << getType() << ' ' << getValue() << " ("
-        << getProofType() << ")";
-    if (getProofType() == FarkasAP)
-    {
-      out << " [";
-      bool first = true;
-      for (const auto& coeff : *rule.d_farkasCoefficients)
-      {
-        if (not first)
-        {
-          out << ", ";
-        }
-        first = false;
-        out << coeff;
-      }
-      out << "]";
-    }
-    out << endl;
-
-    for (AntecedentId i = rule.d_antecedentEnd; i != AntecedentIdSentinel; --i)
-    {
-      ConstraintCP antecdent = d_database->getAntecedent(i);
-      if (antecdent == NullConstraint)
-      {
-        break;
-      }
-      antecdent->printProofTree(out, depth + 1);
-    }
-    return;
-  }
-  out << "Cannot print proof. This is not a proof build." << endl;
-}
-
-bool Constraint::sanityChecking(Node n) const {
-  Comparison cmp = Comparison::parseNormalForm(n);
-  Kind k = cmp.comparisonKind();
-  Polynomial pleft = cmp.normalizedVariablePart();
-  Assert(k == EQUAL || k == DISTINCT || pleft.leadingCoefficientIsPositive());
-  Assert(k != EQUAL || Monomial::isMember(n[0]));
-  Assert(k != DISTINCT || Monomial::isMember(n[0][0]));
-
-  TNode left = pleft.getNode();
-  DeltaRational right = cmp.normalizedDeltaRational();
-
-  const ArithVariables& avariables = d_database->getArithVariables();
-
-  Trace("Constraint::sanityChecking") << cmp.getNode() << endl;
-  Trace("Constraint::sanityChecking") << k << endl;
-  Trace("Constraint::sanityChecking") << pleft.getNode() << endl;
-  Trace("Constraint::sanityChecking") << left << endl;
-  Trace("Constraint::sanityChecking") << right << endl;
-  Trace("Constraint::sanityChecking") << getValue() << endl;
-  Trace("Constraint::sanityChecking") << avariables.hasArithVar(left) << endl;
-  Trace("Constraint::sanityChecking") << avariables.asArithVar(left) << endl;
-  Trace("Constraint::sanityChecking") << getVariable() << endl;
-
-
-  if(avariables.hasArithVar(left) &&
-     avariables.asArithVar(left) == getVariable() &&
-     getValue() == right){
-    switch(getType()){
-    case LowerBound:
-    case UpperBound:
-      //Be overapproximate
-      return k == GT || k == GEQ ||k == LT || k == LEQ;
-    case Equality:
-      return k == EQUAL;
-    case Disequality:
-      return k == DISTINCT;
-    default:
-      Unreachable();
-    }
-  }else{
-    return false;
-  }
-}
-
-ConstraintCP ConstraintDatabase::getAntecedent (AntecedentId p) const {
-  Assert(p < d_antecedents.size());
-  return d_antecedents[p];
-}
-
-void ConstraintRule::print(std::ostream& out, bool produceProofs) const
-{
-  RationalVectorCP coeffs = produceProofs ? d_farkasCoefficients : nullptr;
-  out << "{ConstraintRule, ";
-  out << d_constraint << std::endl;
-  out << "d_proofType= " << d_proofType << ", " << std::endl;
-  out << "d_antecedentEnd= "<< d_antecedentEnd << std::endl;
-
-  if (d_constraint != NullConstraint && d_antecedentEnd != AntecedentIdSentinel)
-  {
-    const ConstraintDatabase& database = d_constraint->getDatabase();
-
-    size_t coeffIterator = (coeffs != RationalVectorCPSentinel) ? coeffs->size()-1 : 0;
-    AntecedentId p = d_antecedentEnd;
-    // must have at least one antecedent
-    ConstraintCP antecedent = database.getAntecedent(p);
-    while(antecedent != NullConstraint){
-      if(coeffs != RationalVectorCPSentinel){
-        out << coeffs->at(coeffIterator);
-      } else {
-        out << "_";
-      }
-      out << " * (" << *antecedent << ")" << std::endl;
-
-      Assert((coeffs == RationalVectorCPSentinel) || coeffIterator > 0);
-      --p;
-      coeffIterator = (coeffs != RationalVectorCPSentinel) ? coeffIterator-1 : 0;
-      antecedent = database.getAntecedent(p);
-    }
-    if(coeffs != RationalVectorCPSentinel){
-      out << coeffs->front();
-    } else {
-      out << "_";
-    }
-    out << " * (" << *(d_constraint->getNegation()) << ")";
-    out << " [not d_constraint] " << endl;
-  }
-  out << "}";
-}
-
-bool Constraint::wellFormedFarkasProof() const {
-  Assert(hasProof());
-
-  const ConstraintRule& cr = getConstraintRule();
-  if(cr.d_constraint != this){ return false; }
-  if(cr.d_proofType != FarkasAP){ return false; }
-
-  AntecedentId p = cr.d_antecedentEnd;
-
-  // must have at least one antecedent
-  ConstraintCP antecedent = d_database->d_antecedents[p];
-  if(antecedent  == NullConstraint) { return false; }
-
-  if (!d_produceProofs)
-  {
-    return cr.d_farkasCoefficients == RationalVectorCPSentinel;
-  }
-  Assert(d_produceProofs);
-
-  if(cr.d_farkasCoefficients == RationalVectorCPSentinel){ return false; }
-  if(cr.d_farkasCoefficients->size() < 2){ return false; }
-
-  const ArithVariables& vars = d_database->getArithVariables();
-
-  DeltaRational rhs(0);
-  Node lhs = Polynomial::mkZero().getNode();
-
-  RationalVector::const_iterator coeffIterator = cr.d_farkasCoefficients->end()-1;
-  RationalVector::const_iterator coeffBegin = cr.d_farkasCoefficients->begin();
-
-  while(antecedent != NullConstraint){
-    Assert(lhs.isNull() || Polynomial::isMember(lhs));
-
-    const Rational& coeff = *coeffIterator;
-    int coeffSgn = coeff.sgn();
-
-    rhs += antecedent->getValue() * coeff;
-
-    ArithVar antVar = antecedent->getVariable();
-    if(!lhs.isNull() && vars.hasNode(antVar)){
-      Node antAsNode = vars.asNode(antVar);
-      if(Polynomial::isMember(antAsNode)){
-        Polynomial lhsPoly = Polynomial::parsePolynomial(lhs);
-        Polynomial antPoly = Polynomial::parsePolynomial(antAsNode);
-        Polynomial sum = lhsPoly + (antPoly * coeff);
-        lhs = sum.getNode();
-      }else{
-        lhs = Node::null();
-      }
-    } else {
-      lhs = Node::null();
-    }
-    Trace("constraints::wffp") << "running sum: " << lhs << " <= " << rhs << endl;
-
-    switch( antecedent->getType() ){
-    case LowerBound:
-      // fc[l] < 0, therefore return false if coeffSgn >= 0
-      if(coeffSgn >= 0){ return false; }
-      break;
-    case UpperBound:
-      // fc[u] > 0, therefore return false if coeffSgn <= 0
-      if(coeffSgn <= 0){ return false; }
-      break;
-    case Equality:
-      if(coeffSgn == 0) { return false; }
-      break;
-    case Disequality:
-    default:
-      return false;
-    }
-
-    if(coeffIterator == coeffBegin){ return false; }
-    --coeffIterator;
-    --p;
-    antecedent = d_database->d_antecedents[p];
-  }
-  if(coeffIterator != coeffBegin){ return false; }
-
-  const Rational& firstCoeff = (*coeffBegin);
-  int firstCoeffSgn = firstCoeff.sgn();
-  rhs += (getNegation()->getValue()) * firstCoeff;
-  if(!lhs.isNull() && vars.hasNode(getVariable())){
-    Node firstAsNode = vars.asNode(getVariable());
-    if(Polynomial::isMember(firstAsNode)){
-      Polynomial lhsPoly = Polynomial::parsePolynomial(lhs);
-      Polynomial firstPoly = Polynomial::parsePolynomial(firstAsNode);
-      Polynomial sum = lhsPoly + (firstPoly * firstCoeff);
-      lhs = sum.getNode();
-    }else{
-      lhs = Node::null();
-    }
-  }else{
-    lhs = Node::null();
-  }
-
-  switch( getNegation()->getType() ){
-  case LowerBound:
-    // fc[l] < 0, therefore return false if coeffSgn >= 0
-    if(firstCoeffSgn >= 0){ return false; }
-    break;
-  case UpperBound:
-    // fc[u] > 0, therefore return false if coeffSgn <= 0
-    if(firstCoeffSgn <= 0){ return false; }
-    break;
-  case Equality:
-    if(firstCoeffSgn == 0) { return false; }
-    break;
-  case Disequality:
-  default:
-    return false;
-  }
-  Trace("constraints::wffp") << "final sum: " << lhs << " <= " << rhs << endl;
-  // 0 = lhs <= rhs < 0
-  return (lhs.isNull() || (Constant::isMember(lhs) && Constant(lhs).isZero()))
-         && rhs.sgn() < 0;
-}
-
-ConstraintP Constraint::makeNegation(ArithVar v,
-                                     ConstraintType t,
-                                     const DeltaRational& r,
-                                     bool produceProofs)
-{
-  switch(t){
-  case LowerBound:
-    {
-      Assert(r.infinitesimalSgn() >= 0);
-      if(r.infinitesimalSgn() > 0){
-        Assert(r.getInfinitesimalPart() == 1);
-        // make (not (v > r)), which is (v <= r)
-        DeltaRational dropInf(r.getNoninfinitesimalPart(), 0);
-        return new Constraint(v, UpperBound, dropInf, produceProofs);
-      }else{
-        Assert(r.infinitesimalSgn() == 0);
-        // make (not (v >= r)), which is (v < r)
-        DeltaRational addInf(r.getNoninfinitesimalPart(), -1);
-        return new Constraint(v, UpperBound, addInf, produceProofs);
-      }
-    }
-  case UpperBound:
-    {
-      Assert(r.infinitesimalSgn() <= 0);
-      if(r.infinitesimalSgn() < 0){
-        Assert(r.getInfinitesimalPart() == -1);
-        // make (not (v < r)), which is (v >= r)
-        DeltaRational dropInf(r.getNoninfinitesimalPart(), 0);
-        return new Constraint(v, LowerBound, dropInf, produceProofs);
-      }else{
-        Assert(r.infinitesimalSgn() == 0);
-        // make (not (v <= r)), which is (v > r)
-        DeltaRational addInf(r.getNoninfinitesimalPart(), 1);
-        return new Constraint(v, LowerBound, addInf, produceProofs);
-      }
-    }
-    case Equality: return new Constraint(v, Disequality, r, produceProofs);
-    case Disequality: return new Constraint(v, Equality, r, produceProofs);
-    default: Unreachable(); return NullConstraint;
-  }
-}
-
-ConstraintDatabase::ConstraintDatabase(Env& env,
-                                       const ArithVariables& avars,
-                                       ArithCongruenceManager& cm,
-                                       RaiseConflict raiseConflict,
-                                       EagerProofGenerator* pfGen)
-    : EnvObj(env),
-      d_varDatabases(),
-      d_toPropagate(context()),
-      d_antecedents(context(), false),
-      d_watches(new Watches(context(), userContext())),
-      d_avariables(avars),
-      d_congruenceManager(cm),
-      d_pfGen(pfGen),
-      d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
-                                           : nullptr),
-      d_raiseConflict(raiseConflict),
-      d_one(1),
-      d_negOne(-1)
-{
-}
-
-SortedConstraintMap& ConstraintDatabase::getVariableSCM(ArithVar v) const{
-  Assert(variableDatabaseIsSetup(v));
-  return d_varDatabases[v]->d_constraints;
-}
-
-void ConstraintDatabase::pushSplitWatch(ConstraintP c){
-  Assert(!c->d_split);
-  c->d_split = true;
-  d_watches->d_splitWatches.push_back(c);
-}
-
-
-void ConstraintDatabase::pushCanBePropagatedWatch(ConstraintP c){
-  Assert(!c->d_canBePropagated);
-  c->d_canBePropagated = true;
-  d_watches->d_canBePropagatedWatches.push_back(c);
-}
-
-void ConstraintDatabase::pushAssertionOrderWatch(ConstraintP c, TNode witness){
-  Assert(!c->assertedToTheTheory());
-  c->d_assertionOrder = d_watches->d_assertionOrderWatches.size();
-  c->d_witness = witness;
-  d_watches->d_assertionOrderWatches.push_back(c);
-}
-
-
-void ConstraintDatabase::pushConstraintRule(const ConstraintRule& crp){
-  ConstraintP c = crp.d_constraint;
-  Assert(c->d_crid == ConstraintRuleIdSentinel);
-  Assert(!c->hasProof());
-  c->d_crid = d_watches->d_constraintProofs.size();
-  d_watches->d_constraintProofs.push_back(crp);
-}
-
-ConstraintP ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r){
-  //This must always return a constraint.
-
-  SortedConstraintMap& scm = getVariableSCM(v);
-  pair<SortedConstraintMapIterator, bool> insertAttempt;
-  insertAttempt = scm.insert(make_pair(r, ValueCollection()));
-
-  SortedConstraintMapIterator pos = insertAttempt.first;
-  ValueCollection& vc = pos->second;
-  if(vc.hasConstraintOfType(t)){
-    return vc.getConstraintOfType(t);
-  }else{
-    ConstraintP c = new Constraint(v, t, r, options().smt.produceProofs);
-    ConstraintP negC =
-        Constraint::makeNegation(v, t, r, options().smt.produceProofs);
-
-    SortedConstraintMapIterator negPos;
-    if(t == Equality || t == Disequality){
-      negPos = pos;
-    }else{
-      pair<SortedConstraintMapIterator, bool> negInsertAttempt;
-      negInsertAttempt = scm.insert(make_pair(negC->getValue(), ValueCollection()));
-      Assert(negInsertAttempt.second
-             || !negInsertAttempt.first->second.hasConstraintOfType(
-                 negC->getType()));
-      negPos = negInsertAttempt.first;
-    }
-
-    c->initialize(this, pos, negC);
-    negC->initialize(this, negPos, c);
-
-    vc.add(c);
-    negPos->second.add(negC);
-
-    return c;
-  }
-}
-
-ConstraintP ConstraintDatabase::ensureConstraint(ValueCollection& vc, ConstraintType t){
-  if(vc.hasConstraintOfType(t)){
-    return vc.getConstraintOfType(t);
-  }else{
-    return getConstraint(vc.getVariable(), t, vc.getValue());
-  }
-}
-
-bool ConstraintDatabase::emptyDatabase(const std::vector<PerVariableDatabase>& vec){
-  std::vector<PerVariableDatabase>::const_iterator first = vec.begin();
-  std::vector<PerVariableDatabase>::const_iterator last = vec.end();
-  return std::find_if(first, last, PerVariableDatabase::IsEmpty) == last;
-}
-
-ConstraintDatabase::~ConstraintDatabase(){
-  delete d_watches;
-
-  std::vector<ConstraintP> constraintList;
-
-  while(!d_varDatabases.empty()){
-    PerVariableDatabase* back = d_varDatabases.back();
-
-    SortedConstraintMap& scm = back->d_constraints;
-    SortedConstraintMapIterator i = scm.begin(), i_end = scm.end();
-    for(; i != i_end; ++i){
-      (i->second).push_into(constraintList);
-    }
-    while(!constraintList.empty()){
-      ConstraintP c = constraintList.back();
-      constraintList.pop_back();
-      delete c;
-    }
-    Assert(scm.empty());
-    d_varDatabases.pop_back();
-    delete back;
-  }
-
-  Assert(d_nodetoConstraintMap.empty());
-}
-
-ConstraintDatabase::Statistics::Statistics()
-    : d_unatePropagateCalls(smtStatisticsRegistry().registerInt(
-        "theory::arith::cd::unatePropagateCalls")),
-      d_unatePropagateImplications(smtStatisticsRegistry().registerInt(
-          "theory::arith::cd::unatePropagateImplications"))
-{
-}
-
-void ConstraintDatabase::deleteConstraintAndNegation(ConstraintP c){
-  Assert(c->safeToGarbageCollect());
-  ConstraintP neg = c->getNegation();
-  Assert(neg->safeToGarbageCollect());
-  delete c;
-  delete neg;
-}
-
-void ConstraintDatabase::addVariable(ArithVar v){
-  if(d_reclaimable.isMember(v)){
-    SortedConstraintMap& scm = getVariableSCM(v);
-
-    std::vector<ConstraintP> constraintList;
-
-    for(SortedConstraintMapIterator i = scm.begin(), end = scm.end(); i != end; ++i){
-      (i->second).push_into(constraintList);
-    }
-    while(!constraintList.empty()){
-      ConstraintP c = constraintList.back();
-      constraintList.pop_back();
-      Assert(c->safeToGarbageCollect());
-      delete c;
-    }
-    Assert(scm.empty());
-
-    d_reclaimable.remove(v);
-  }else{
-    Trace("arith::constraint") << "about to fail" << v << " " << d_varDatabases.size() << endl;
-    Assert(v == d_varDatabases.size());
-    d_varDatabases.push_back(new PerVariableDatabase(v));
-  }
-}
-
-void ConstraintDatabase::removeVariable(ArithVar v){
-  Assert(!d_reclaimable.isMember(v));
-  d_reclaimable.add(v);
-}
-
-bool Constraint::safeToGarbageCollect() const{
-  // Do not call during destructor as getNegation() may be Null by this point
-  Assert(getNegation() != NullConstraint);
-  return !contextDependentDataIsSet() && ! getNegation()->contextDependentDataIsSet();
-}
-
-bool Constraint::contextDependentDataIsSet() const{
-  return hasProof() || isSplit() || canBePropagated() || assertedToTheTheory();
-}
-
-TrustNode Constraint::split()
-{
-  Assert(isEquality() || isDisequality());
-
-  bool isEq = isEquality();
-
-  ConstraintP eq = isEq ? this : d_negation;
-  ConstraintP diseq = isEq ? d_negation : this;
-
-  TNode eqNode = eq->getLiteral();
-  Assert(eqNode.getKind() == kind::EQUAL);
-  TNode lhs = eqNode[0];
-  TNode rhs = eqNode[1];
-
-  Node leqNode = NodeBuilder(kind::LEQ) << lhs << rhs;
-  Node ltNode = NodeBuilder(kind::LT) << lhs << rhs;
-  Node gtNode = NodeBuilder(kind::GT) << lhs << rhs;
-  Node geqNode = NodeBuilder(kind::GEQ) << lhs << rhs;
-
-  Node lemma = NodeBuilder(OR) << leqNode << geqNode;
-
-  TrustNode trustedLemma;
-  if (d_database->isProofEnabled())
-  {
-    TypeNode type = lhs.getType();
-    // Farkas proof that this works.
-    auto nm = NodeManager::currentNM();
-    auto nLeqPf = d_database->d_pnm->mkAssume(leqNode.negate());
-    auto gtPf = d_database->d_pnm->mkNode(
-        PfRule::MACRO_SR_PRED_TRANSFORM, {nLeqPf}, {gtNode});
-    auto nGeqPf = d_database->d_pnm->mkAssume(geqNode.negate());
-    auto ltPf = d_database->d_pnm->mkNode(
-        PfRule::MACRO_SR_PRED_TRANSFORM, {nGeqPf}, {ltNode});
-    auto sumPf =
-        d_database->d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
-                                  {gtPf, ltPf},
-                                  {nm->mkConstRealOrInt(type, Rational(-1)),
-                                   nm->mkConstRealOrInt(type, Rational(1))});
-    auto botPf = d_database->d_pnm->mkNode(
-        PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
-    std::vector<Node> a = {leqNode.negate(), geqNode.negate()};
-    auto notAndNotPf = d_database->d_pnm->mkScope(botPf, a);
-    // No need to ensure that the expected node aggrees with `a` because we are
-    // not providing an expected node.
-    auto orNotNotPf =
-        d_database->d_pnm->mkNode(PfRule::NOT_AND, {notAndNotPf}, {});
-    auto orPf = d_database->d_pnm->mkNode(
-        PfRule::MACRO_SR_PRED_TRANSFORM, {orNotNotPf}, {lemma});
-    trustedLemma = d_database->d_pfGen->mkTrustNode(lemma, orPf);
-  }
-  else
-  {
-    trustedLemma = TrustNode::mkTrustLemma(lemma);
-  }
-
-  eq->d_database->pushSplitWatch(eq);
-  diseq->d_database->pushSplitWatch(diseq);
-
-  return trustedLemma;
-}
-
-bool ConstraintDatabase::hasLiteral(TNode literal) const {
-  return lookup(literal) != NullConstraint;
-}
-
-ConstraintP ConstraintDatabase::addLiteral(TNode literal){
-  Assert(!hasLiteral(literal));
-  bool isNot = (literal.getKind() == NOT);
-  Node atomNode = (isNot ? literal[0] : literal);
-  Node negationNode  = atomNode.notNode();
-
-  Assert(!hasLiteral(atomNode));
-  Assert(!hasLiteral(negationNode));
-  Comparison posCmp = Comparison::parseNormalForm(atomNode);
-
-  ConstraintType posType = Constraint::constraintTypeOfComparison(posCmp);
-
-  Polynomial nvp = posCmp.normalizedVariablePart();
-  ArithVar v = d_avariables.asArithVar(nvp.getNode());
-
-  DeltaRational posDR = posCmp.normalizedDeltaRational();
-
-  ConstraintP posC =
-      new Constraint(v, posType, posDR, options().smt.produceProofs);
-
-  Trace("arith::constraint") << "addliteral( literal ->" << literal << ")" << endl;
-  Trace("arith::constraint") << "addliteral( posC ->" << posC << ")" << endl;
-
-  SortedConstraintMap& scm = getVariableSCM(posC->getVariable());
-  pair<SortedConstraintMapIterator, bool> insertAttempt;
-  insertAttempt = scm.insert(make_pair(posC->getValue(), ValueCollection()));
-
-  SortedConstraintMapIterator posI = insertAttempt.first;
-  // If the attempt succeeds, i points to a new empty ValueCollection
-  // If the attempt fails, i points to a pre-existing ValueCollection
-
-  if(posI->second.hasConstraintOfType(posC->getType())){
-    //This is the situation where the ConstraintP exists, but
-    //the literal has not been  associated with it.
-    ConstraintP hit = posI->second.getConstraintOfType(posC->getType());
-    Trace("arith::constraint") << "hit " << hit << endl;
-    Trace("arith::constraint") << "posC " << posC << endl;
-
-    delete posC;
-
-    hit->setLiteral(atomNode);
-    hit->getNegation()->setLiteral(negationNode);
-    return isNot ? hit->getNegation(): hit;
-  }else{
-    Comparison negCmp = Comparison::parseNormalForm(negationNode);
-
-    ConstraintType negType = Constraint::constraintTypeOfComparison(negCmp);
-    DeltaRational negDR = negCmp.normalizedDeltaRational();
-
-    ConstraintP negC =
-        new Constraint(v, negType, negDR, options().smt.produceProofs);
-
-    SortedConstraintMapIterator negI;
-
-    if(posC->isEquality()){
-      negI = posI;
-    }else{
-      Assert(posC->isLowerBound() || posC->isUpperBound());
-
-      pair<SortedConstraintMapIterator, bool> negInsertAttempt;
-      negInsertAttempt = scm.insert(make_pair(negC->getValue(), ValueCollection()));
-
-      Trace("nf::tmp") << "sdhjfgdhjkldfgljkhdfg" << endl;
-      Trace("nf::tmp") << negC << endl;
-      Trace("nf::tmp") << negC->getValue() << endl;
-
-      //This should always succeed as the DeltaRational for the negation is unique!
-      Assert(negInsertAttempt.second);
-
-      negI = negInsertAttempt.first;
-    }
-
-    (posI->second).add(posC);
-    (negI->second).add(negC);
-
-    posC->initialize(this, posI, negC);
-    negC->initialize(this, negI, posC);
-
-    posC->setLiteral(atomNode);
-    negC->setLiteral(negationNode);
-
-    return isNot ? negC : posC;
-  }
-}
-
-
-ConstraintP ConstraintDatabase::lookup(TNode literal) const{
-  NodetoConstraintMap::const_iterator iter = d_nodetoConstraintMap.find(literal);
-  if(iter == d_nodetoConstraintMap.end()){
-    return NullConstraint;
-  }else{
-    return iter->second;
-  }
-}
-
-void Constraint::setAssumption(bool nowInConflict){
-  Trace("constraints::pf") << "setAssumption(" << this << ")" << std::endl;
-  Assert(!hasProof());
-  Assert(negationHasProof() == nowInConflict);
-  Assert(hasLiteral());
-  Assert(assertedToTheTheory());
-
-  d_database->pushConstraintRule(ConstraintRule(this, AssumeAP));
-
-  Assert(inConflict() == nowInConflict);
-  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
-    Trace("constraint::conflictCommit") << "inConflict@setAssumption " << this << std::endl;
-  }
-}
-
-void Constraint::tryToPropagate(){
-  Assert(hasProof());
-  Assert(!isAssumption());
-  Assert(!isInternalAssumption());
-
-  if(canBePropagated() && !assertedToTheTheory() && !isAssumption() && !isInternalAssumption()){
-    propagate();
-  }
-}
-
-void Constraint::propagate(){
-  Assert(hasProof());
-  Assert(canBePropagated());
-  Assert(!assertedToTheTheory());
-  Assert(!isAssumption());
-  Assert(!isInternalAssumption());
-
-  d_database->d_toPropagate.push(this);
-}
-
-
-/*
- * Example:
- *    x <= a and a < b
- * |= x <= b
- * ---
- *  1*(x <= a) + (-1)*(x > b) => (0 <= a-b)
- */
-void Constraint::impliedByUnate(ConstraintCP imp, bool nowInConflict){
-  Trace("constraints::pf") << "impliedByUnate(" << this << ", " << *imp << ")" << std::endl;
-  Assert(!hasProof());
-  Assert(imp->hasProof());
-  Assert(negationHasProof() == nowInConflict);
-
-  d_database->d_antecedents.push_back(NullConstraint);
-  d_database->d_antecedents.push_back(imp);
-
-  AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
-
-  RationalVectorP coeffs;
-  if (d_produceProofs)
-  {
-    std::pair<int, int> sgns = unateFarkasSigns(getNegation(), imp);
-
-    Rational first(sgns.first);
-    Rational second(sgns.second);
-
-    coeffs = new RationalVector();
-    coeffs->push_back(first);
-    coeffs->push_back(second);
-  }
-  else
-  {
-    coeffs = RationalVectorPSentinel;
-  }
-  // no need to delete coeffs the memory is owned by ConstraintRule
-  d_database->pushConstraintRule(ConstraintRule(this, FarkasAP, antecedentEnd, coeffs));
-
-  Assert(inConflict() == nowInConflict);
-  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
-    Trace("constraint::conflictCommit") << "inConflict@impliedByUnate " << this << std::endl;
-  }
-
-  if(TraceIsOn("constraints::wffp") && !wellFormedFarkasProof()){
-    getConstraintRule().print(Trace("constraints::wffp"), d_produceProofs);
-  }
-  Assert(wellFormedFarkasProof());
-}
-
-void Constraint::impliedByTrichotomy(ConstraintCP a, ConstraintCP b, bool nowInConflict){
-  Trace("constraints::pf") << "impliedByTrichotomy(" << this << ", " << *a << ", ";
-  Trace("constraints::pf") << *b << ")" << std::endl;
-  Assert(!hasProof());
-  Assert(negationHasProof() == nowInConflict);
-  Assert(a->hasProof());
-  Assert(b->hasProof());
-
-  d_database->d_antecedents.push_back(NullConstraint);
-  d_database->d_antecedents.push_back(a);
-  d_database->d_antecedents.push_back(b);
-
-  AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
-  d_database->pushConstraintRule(ConstraintRule(this, TrichotomyAP, antecedentEnd));
-
-  Assert(inConflict() == nowInConflict);
-  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
-    Trace("constraint::conflictCommit") << "inConflict@impliedByTrichotomy " << this << std::endl;
-  }
-}
-
-
-bool Constraint::allHaveProof(const ConstraintCPVec& b){
-  for(ConstraintCPVec::const_iterator i=b.begin(), i_end=b.end(); i != i_end; ++i){
-    ConstraintCP cp = *i;
-    if(! (cp->hasProof())){ return false; }
-  }
-  return true;
-}
-
-void Constraint::impliedByIntTighten(ConstraintCP a, bool nowInConflict){
-  Trace("constraints::pf") << "impliedByIntTighten(" << this << ", " << *a << ")" << std::endl;
-  Assert(!hasProof());
-  Assert(negationHasProof() == nowInConflict);
-  Assert(a->hasProof());
-  Trace("pf::arith") << "impliedByIntTighten(" << this << ", " << a << ")"
-                     << std::endl;
-
-  d_database->d_antecedents.push_back(NullConstraint);
-  d_database->d_antecedents.push_back(a);
-  AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
-  d_database->pushConstraintRule(ConstraintRule(this, IntTightenAP, antecedentEnd));
-
-  Assert(inConflict() == nowInConflict);
-  if(inConflict()){
-    Trace("constraint::conflictCommit") << "inConflict impliedByIntTighten" << this << std::endl;
-  }
-}
-
-void Constraint::impliedByIntHole(ConstraintCP a, bool nowInConflict){
-  Trace("constraints::pf") << "impliedByIntHole(" << this << ", " << *a << ")" << std::endl;
-  Assert(!hasProof());
-  Assert(negationHasProof() == nowInConflict);
-  Assert(a->hasProof());
-  Trace("pf::arith") << "impliedByIntHole(" << this << ", " << a << ")"
-                     << std::endl;
-
-  d_database->d_antecedents.push_back(NullConstraint);
-  d_database->d_antecedents.push_back(a);
-  AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
-  d_database->pushConstraintRule(ConstraintRule(this, IntHoleAP, antecedentEnd));
-
-  Assert(inConflict() == nowInConflict);
-  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
-    Trace("constraint::conflictCommit") << "inConflict impliedByIntHole" << this << std::endl;
-  }
-}
-
-void Constraint::impliedByIntHole(const ConstraintCPVec& b, bool nowInConflict){
-  Trace("constraints::pf") << "impliedByIntHole(" << this;
-  if (TraceIsOn("constraints::pf")) {
-    for (const ConstraintCP& p : b)
-    {
-      Trace("constraints::pf") << ", " << p;
-    }
-  }
-  Trace("constraints::pf") << ")" << std::endl;
-
-  Assert(!hasProof());
-  Assert(negationHasProof() == nowInConflict);
-  Assert(allHaveProof(b));
-
-  CDConstraintList& antecedents = d_database->d_antecedents;
-  antecedents.push_back(NullConstraint);
-  for(ConstraintCPVec::const_iterator i=b.begin(), i_end=b.end(); i != i_end; ++i){
-    antecedents.push_back(*i);
-  }
-  AntecedentId antecedentEnd = antecedents.size() - 1;
-
-  d_database->pushConstraintRule(ConstraintRule(this, IntHoleAP, antecedentEnd));
-
-  Assert(inConflict() == nowInConflict);
-  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
-    Trace("constraint::conflictCommit") << "inConflict@impliedByIntHole[vec] " << this << std::endl;
-  }
-}
-
-/*
- * If proofs are off, coeffs == RationalVectorSentinal.
- * If proofs are on,
- *   coeffs != RationalVectorSentinal,
- *   coeffs->size() = a.size() + 1,
- *   for i in [0,a.size) : coeff[i] corresponds to a[i], and
- *   coeff.back() corresponds to the current constraint.
- */
-void Constraint::impliedByFarkas(const ConstraintCPVec& a, RationalVectorCP coeffs, bool nowInConflict){
-  Trace("constraints::pf") << "impliedByFarkas(" << this;
-  if (TraceIsOn("constraints::pf")) {
-    for (const ConstraintCP& p : a)
-    {
-      Trace("constraints::pf") << ", " << p;
-    }
-  }
-  Trace("constraints::pf") << ", <coeffs>";
-  Trace("constraints::pf") << ")" << std::endl;
-  Assert(!hasProof());
-  Assert(negationHasProof() == nowInConflict);
-  Assert(allHaveProof(a));
-
-  Assert(d_produceProofs == (coeffs != RationalVectorCPSentinel));
-  Assert(!d_produceProofs || coeffs->size() == a.size() + 1);
-
-  Assert(a.size() >= 1);
-
-  d_database->d_antecedents.push_back(NullConstraint);
-  for(ConstraintCPVec::const_iterator i = a.begin(), end = a.end(); i != end; ++i){
-    ConstraintCP c_i = *i;
-    Assert(c_i->hasProof());
-    d_database->d_antecedents.push_back(c_i);
-  }
-  AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
-
-  RationalVectorCP coeffsCopy;
-  if (d_produceProofs)
-  {
-    Assert(coeffs != RationalVectorCPSentinel);
-    coeffsCopy = new RationalVector(*coeffs);
-  }
-  else
-  {
-    coeffsCopy = RationalVectorCPSentinel;
-  }
-  d_database->pushConstraintRule(ConstraintRule(this, FarkasAP, antecedentEnd, coeffsCopy));
-
-  Assert(inConflict() == nowInConflict);
-  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
-    Trace("constraint::conflictCommit") << "inConflict@impliedByFarkas " << this << std::endl;
-  }
-  if(TraceIsOn("constraints::wffp") && !wellFormedFarkasProof()){
-    getConstraintRule().print(Trace("constraints::wffp"), d_produceProofs);
-  }
-  Assert(wellFormedFarkasProof());
-}
-
-
-void Constraint::setInternalAssumption(bool nowInConflict){
-  Trace("constraints::pf") << "setInternalAssumption(" << this;
-  Trace("constraints::pf") << ")" << std::endl;
-  Assert(!hasProof());
-  Assert(negationHasProof() == nowInConflict);
-  Assert(!assertedToTheTheory());
-
-  d_database->pushConstraintRule(ConstraintRule(this, InternalAssumeAP));
-
-  Assert(inConflict() == nowInConflict);
-  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
-    Trace("constraint::conflictCommit") << "inConflict@setInternalAssumption " << this << std::endl;
-  }
-}
-
-
-void Constraint::setEqualityEngineProof(){
-  Trace("constraints::pf") << "setEqualityEngineProof(" << this;
-  Trace("constraints::pf") << ")" << std::endl;
-  Assert(truthIsUnknown());
-  Assert(hasLiteral());
-  d_database->pushConstraintRule(ConstraintRule(this, EqualityEngineAP));
-}
-
-
-SortedConstraintMap& Constraint::constraintSet() const{
-  Assert(d_database->variableDatabaseIsSetup(d_variable));
-  return (d_database->d_varDatabases[d_variable])->d_constraints;
-}
-
-bool Constraint::antecentListIsEmpty() const{
-  Assert(hasProof());
-  return d_database->d_antecedents[getEndAntecedent()] == NullConstraint;
-}
-
-bool Constraint::antecedentListLengthIsOne() const {
-  Assert(hasProof());
-  return !antecentListIsEmpty() &&
-    d_database->d_antecedents[getEndAntecedent()-1] == NullConstraint;
-}
-
-Node Constraint::externalImplication(const ConstraintCPVec& b) const{
-  Assert(hasLiteral());
-  Node antecedent = externalExplainByAssertions(b);
-  Node implied = getLiteral();
-  return antecedent.impNode(implied);
-}
-
-
-Node Constraint::externalExplainByAssertions(const ConstraintCPVec& b){
-  return externalExplain(b, AssertionOrderSentinel);
-}
-
-TrustNode Constraint::externalExplainForPropagation(TNode lit) const
-{
-  Assert(hasProof());
-  Assert(!isAssumption());
-  Assert(!isInternalAssumption());
-  NodeBuilder nb(Kind::AND);
-  auto pfFromAssumptions = externalExplain(nb, d_assertionOrder);
-  Node n = mkAndFromBuilder(nb);
-  if (d_database->isProofEnabled())
-  {
-    std::vector<Node> assumptions;
-    if (n.getKind() == Kind::AND)
-    {
-      assumptions.insert(assumptions.end(), n.begin(), n.end());
-    }
-    else
-    {
-      assumptions.push_back(n);
-    }
-    if (getProofLiteral() != lit)
-    {
-      pfFromAssumptions = d_database->d_pnm->mkNode(
-          PfRule::MACRO_SR_PRED_TRANSFORM, {pfFromAssumptions}, {lit});
-    }
-    auto pf = d_database->d_pnm->mkScope(pfFromAssumptions, assumptions);
-    return d_database->d_pfGen->mkTrustedPropagation(
-        lit, NodeManager::currentNM()->mkAnd(assumptions), pf);
-  }
-  else
-  {
-    return TrustNode::mkTrustPropExp(lit, n);
-  }
-}
-
-TrustNode Constraint::externalExplainConflict() const
-{
-  Trace("pf::arith::explain") << this << std::endl;
-  Assert(inConflict());
-  NodeBuilder nb(kind::AND);
-  auto pf1 = externalExplainByAssertions(nb);
-  auto not2 = getNegation()->getProofLiteral().negate();
-  auto pf2 = getNegation()->externalExplainByAssertions(nb);
-  Node n = mkAndFromBuilder(nb);
-  if (d_database->isProofEnabled())
-  {
-    auto pfNot2 = d_database->d_pnm->mkNode(
-        PfRule::MACRO_SR_PRED_TRANSFORM, {pf1}, {not2});
-    std::vector<Node> lits;
-    if (n.getKind() == Kind::AND)
-    {
-      lits.insert(lits.end(), n.begin(), n.end());
-    }
-    else
-    {
-      lits.push_back(n);
-    }
-    if (TraceIsOn("arith::pf::externalExplainConflict"))
-    {
-      Trace("arith::pf::externalExplainConflict") << "Lits:" << std::endl;
-      for (const auto& l : lits)
-      {
-        Trace("arith::pf::externalExplainConflict") << "  : " << l << std::endl;
-      }
-    }
-    std::vector<Node> contraLits = {getProofLiteral(),
-                                    getNegation()->getProofLiteral()};
-    auto bot =
-        not2.getKind() == Kind::NOT
-            ? d_database->d_pnm->mkNode(PfRule::CONTRA, {pf2, pfNot2}, {})
-            : d_database->d_pnm->mkNode(PfRule::CONTRA, {pfNot2, pf2}, {});
-    if (TraceIsOn("arith::pf::tree"))
-    {
-      Trace("arith::pf::tree") << *this << std::endl;
-      Trace("arith::pf::tree") << *getNegation() << std::endl;
-      Trace("arith::pf::tree") << "\n\nTree:\n";
-      printProofTree(Trace("arith::pf::tree"));
-      getNegation()->printProofTree(Trace("arith::pf::tree"));
-    }
-    auto confPf = d_database->d_pnm->mkScope(bot, lits);
-    return d_database->d_pfGen->mkTrustNode(
-        NodeManager::currentNM()->mkAnd(lits), confPf, true);
-  }
-  else
-  {
-    return TrustNode::mkTrustConflict(n);
-  }
-}
-
-struct ConstraintCPHash {
-  /* Todo replace with an id */
-  size_t operator()(ConstraintCP c) const{
-    Assert(sizeof(ConstraintCP) > 0);
-    return ((size_t)c)/sizeof(ConstraintCP);
-  }
-};
-
-void Constraint::assertionFringe(ConstraintCPVec& v){
-  unordered_set<ConstraintCP, ConstraintCPHash> visited;
-  size_t writePos = 0;
-
-  if(!v.empty()){
-    const ConstraintDatabase* db = v.back()->d_database;
-    const CDConstraintList& antecedents = db->d_antecedents;
-    for(size_t i = 0; i < v.size(); ++i){
-      ConstraintCP vi = v[i];
-      if(visited.find(vi) == visited.end()){
-        Assert(vi->hasProof());
-        visited.insert(vi);
-        if(vi->onFringe()){
-          v[writePos] = vi;
-          writePos++;
-        }else{
-          Assert(vi->hasTrichotomyProof() || vi->hasFarkasProof()
-                 || vi->hasIntHoleProof() || vi->hasIntTightenProof());
-          AntecedentId p = vi->getEndAntecedent();
-
-          ConstraintCP antecedent = antecedents[p];
-          while(antecedent != NullConstraint){
-            v.push_back(antecedent);
-            --p;
-            antecedent = antecedents[p];
-          }
-        }
-      }
-    }
-    v.resize(writePos);
-  }
-}
-
-void Constraint::assertionFringe(ConstraintCPVec& o, const ConstraintCPVec& i){
-  o.insert(o.end(), i.begin(), i.end());
-  assertionFringe(o);
-}
-
-Node Constraint::externalExplain(const ConstraintCPVec& v, AssertionOrder order){
-  NodeBuilder nb(kind::AND);
-  ConstraintCPVec::const_iterator i, end;
-  for(i = v.begin(), end = v.end(); i != end; ++i){
-    ConstraintCP v_i = *i;
-    v_i->externalExplain(nb, order);
-  }
-  return mkAndFromBuilder(nb);
-}
-
-std::shared_ptr<ProofNode> Constraint::externalExplain(
-    NodeBuilder& nb, AssertionOrder order) const
-{
-  if (TraceIsOn("pf::arith::explain"))
-  {
-    this->printProofTree(Trace("arith::pf::tree"));
-    Trace("pf::arith::explain") << "Explaining: " << this << " with rule ";
-    getConstraintRule().print(Trace("pf::arith::explain"), d_produceProofs);
-    Trace("pf::arith::explain") << std::endl;
-  }
-  Assert(hasProof());
-  Assert(!isAssumption() || assertedToTheTheory());
-  Assert(!isInternalAssumption());
-  std::shared_ptr<ProofNode> pf{};
-
-  ProofNodeManager* pnm = d_database->d_pnm;
-
-  if (assertedBefore(order))
-  {
-    Trace("pf::arith::explain") << "  already asserted" << std::endl;
-    nb << getWitness();
-    if (d_database->isProofEnabled())
-    {
-      pf = pnm->mkAssume(getWitness());
-      // If the witness and literal differ, prove the difference through a
-      // rewrite.
-      if (getWitness() != getProofLiteral())
-      {
-        pf = pnm->mkNode(
-            PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {getProofLiteral()});
-      }
-    }
-  }
-  else if (hasEqualityEngineProof())
-  {
-    Trace("pf::arith::explain") << "  going to ee:" << std::endl;
-    TrustNode exp = d_database->eeExplain(this);
-    if (d_database->isProofEnabled())
-    {
-      Assert(exp.getProven().getKind() == Kind::IMPLIES);
-      std::vector<std::shared_ptr<ProofNode>> hypotheses;
-      hypotheses.push_back(exp.getGenerator()->getProofFor(exp.getProven()));
-      if (exp.getNode().getKind() == Kind::AND)
-      {
-        for (const auto& h : exp.getNode())
-        {
-          hypotheses.push_back(
-              pnm->mkNode(PfRule::TRUE_INTRO, {pnm->mkAssume(h)}, {}));
-        }
-      }
-      else
-      {
-        hypotheses.push_back(pnm->mkNode(
-            PfRule::TRUE_INTRO, {pnm->mkAssume(exp.getNode())}, {}));
-      }
-      pf = pnm->mkNode(
-          PfRule::MACRO_SR_PRED_TRANSFORM, {hypotheses}, {getProofLiteral()});
-    }
-    Trace("pf::arith::explain")
-        << "    explanation: " << exp.getNode() << std::endl;
-    if (exp.getNode().getKind() == Kind::AND)
-    {
-      nb.append(exp.getNode().begin(), exp.getNode().end());
-    }
-    else
-    {
-      nb << exp.getNode();
-    }
-  }
-  else
-  {
-    Trace("pf::arith::explain") << "  recursion!" << std::endl;
-    Assert(!isAssumption());
-    AntecedentId p = getEndAntecedent();
-    ConstraintCP antecedent = d_database->d_antecedents[p];
-    std::vector<std::shared_ptr<ProofNode>> children;
-
-    while (antecedent != NullConstraint)
-    {
-      Trace("pf::arith::explain") << "Explain " << antecedent << std::endl;
-      auto pn = antecedent->externalExplain(nb, order);
-      if (d_database->isProofEnabled())
-      {
-        children.push_back(pn);
-      }
-      --p;
-      antecedent = d_database->d_antecedents[p];
-    }
-
-    if (d_database->isProofEnabled())
-    {
-      switch (getProofType())
-      {
-        case ArithProofType::AssumeAP:
-        case ArithProofType::EqualityEngineAP:
-        {
-          Unreachable() << "These should be handled above";
-          break;
-        }
-        case ArithProofType::FarkasAP:
-        {
-          // Per docs in constraint.h,
-          // the 0th farkas coefficient is for the negation of the deduced
-          // constraint the 1st corresponds to the last antecedent the nth
-          // corresponds to the first antecedent Then, the farkas coefficients
-          // and the antecedents are in the same order.
-
-          // Enumerate child proofs (negation included) in d_farkasCoefficients
-          // order
-          Node plit = getNegation()->getProofLiteral();
-          std::vector<std::shared_ptr<ProofNode>> farkasChildren;
-          farkasChildren.push_back(pnm->mkAssume(plit));
-          farkasChildren.insert(
-              farkasChildren.end(), children.rbegin(), children.rend());
-
-          NodeManager* nm = NodeManager::currentNM();
-
-          // Enumerate d_farkasCoefficients as nodes.
-          std::vector<Node> farkasCoeffs;
-          TypeNode type = plit[0].getType();
-          for (Rational r : *getFarkasCoefficients())
-          {
-            farkasCoeffs.push_back(nm->mkConstRealOrInt(type, Rational(r)));
-          }
-
-          // Apply the scaled-sum rule.
-          std::shared_ptr<ProofNode> sumPf = pnm->mkNode(
-              PfRule::MACRO_ARITH_SCALE_SUM_UB, farkasChildren, farkasCoeffs);
-
-          // Provable rewrite the result
-          auto botPf = pnm->mkNode(
-              PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
-
-          // Scope out the negated constraint, yielding a proof of the
-          // constraint.
-          std::vector<Node> assump{plit};
-          auto maybeDoubleNotPf = pnm->mkScope(botPf, assump, false);
-
-          // No need to ensure that the expected node aggrees with `assump`
-          // because we are not providing an expected node.
-          //
-          // Prove that this is the literal (may need to clean a double-not)
-          pf = pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
-                           {maybeDoubleNotPf},
-                           {getProofLiteral()});
-
-          break;
-        }
-        case ArithProofType::IntTightenAP:
-        {
-          if (isUpperBound())
-          {
-            pf = pnm->mkNode(
-                PfRule::INT_TIGHT_UB, children, {}, getProofLiteral());
-          }
-          else if (isLowerBound())
-          {
-            pf = pnm->mkNode(
-                PfRule::INT_TIGHT_LB, children, {}, getProofLiteral());
-          }
-          else
-          {
-            Unreachable();
-          }
-          break;
-        }
-        case ArithProofType::IntHoleAP:
-        {
-          Node t =
-              builtin::BuiltinProofRuleChecker::mkTheoryIdNode(THEORY_ARITH);
-          pf = pnm->mkNode(PfRule::THEORY_INFERENCE,
-                           children,
-                           {getProofLiteral(), t},
-                           getProofLiteral());
-          break;
-        }
-        case ArithProofType::TrichotomyAP:
-        {
-          pf = pnm->mkNode(PfRule::ARITH_TRICHOTOMY,
-                           children,
-                           {getProofLiteral()},
-                           getProofLiteral());
-          break;
-        }
-        case ArithProofType::InternalAssumeAP:
-        case ArithProofType::NoAP:
-        default:
-        {
-          Unreachable() << getProofType()
-                        << " should not be visible in explanation";
-          break;
-        }
-      }
-    }
-  }
-  return pf;
-}
-
-Node Constraint::externalExplainByAssertions(ConstraintCP a, ConstraintCP b){
-  NodeBuilder nb(kind::AND);
-  a->externalExplainByAssertions(nb);
-  b->externalExplainByAssertions(nb);
-  return nb;
-}
-
-Node Constraint::externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c){
-  NodeBuilder nb(kind::AND);
-  a->externalExplainByAssertions(nb);
-  b->externalExplainByAssertions(nb);
-  c->externalExplainByAssertions(nb);
-  return nb;
-}
-
-ConstraintP Constraint::getStrictlyWeakerLowerBound(bool hasLiteral, bool asserted) const {
-  Assert(initialized());
-  Assert(!asserted || hasLiteral);
-
-  SortedConstraintMapConstIterator i = d_variablePosition;
-  const SortedConstraintMap& scm = constraintSet();
-  SortedConstraintMapConstIterator i_begin = scm.begin();
-  while(i != i_begin){
-    --i;
-    const ValueCollection& vc = i->second;
-    if(vc.hasLowerBound()){
-      ConstraintP weaker = vc.getLowerBound();
-
-      // asserted -> hasLiteral
-      // hasLiteral -> weaker->hasLiteral()
-      // asserted -> weaker->assertedToTheTheory()
-      if((!hasLiteral || (weaker->hasLiteral())) &&
-         (!asserted || ( weaker->assertedToTheTheory()))){
-        return weaker;
-      }
-    }
-  }
-  return NullConstraint;
-}
-
-ConstraintP Constraint::getStrictlyWeakerUpperBound(bool hasLiteral, bool asserted) const {
-  SortedConstraintMapConstIterator i = d_variablePosition;
-  const SortedConstraintMap& scm = constraintSet();
-  SortedConstraintMapConstIterator i_end = scm.end();
-
-  ++i;
-  for(; i != i_end; ++i){
-    const ValueCollection& vc = i->second;
-    if(vc.hasUpperBound()){
-      ConstraintP weaker = vc.getUpperBound();
-      if((!hasLiteral || (weaker->hasLiteral())) &&
-         (!asserted || ( weaker->assertedToTheTheory()))){
-        return weaker;
-      }
-    }
-  }
-
-  return NullConstraint;
-}
-
-ConstraintP ConstraintDatabase::getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const {
-  Assert(variableDatabaseIsSetup(v));
-  Assert(t == UpperBound || t == LowerBound);
-
-  SortedConstraintMap& scm = getVariableSCM(v);
-  if(t == UpperBound){
-    SortedConstraintMapConstIterator i = scm.lower_bound(r);
-    SortedConstraintMapConstIterator i_end = scm.end();
-    Assert(i == i_end || r <= i->first);
-    for(; i != i_end; i++){
-      Assert(r <= i->first);
-      const ValueCollection& vc = i->second;
-      if(vc.hasUpperBound()){
-        return vc.getUpperBound();
-      }
-    }
-    return NullConstraint;
-  }else{
-    Assert(t == LowerBound);
-    if(scm.empty()){
-      return NullConstraint;
-    }else{
-      SortedConstraintMapConstIterator i = scm.lower_bound(r);
-      SortedConstraintMapConstIterator i_begin = scm.begin();
-      SortedConstraintMapConstIterator i_end = scm.end();
-      Assert(i == i_end || r <= i->first);
-
-      int fdj = 0;
-
-      if(i == i_end){
-        --i;
-        Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
-      }else if( (i->first) > r){
-        if(i == i_begin){
-          return NullConstraint;
-        }else{
-          --i;
-          Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
-        }
-      }
-
-      do{
-        Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
-        Assert(r >= i->first);
-        const ValueCollection& vc = i->second;
-
-        if(vc.hasLowerBound()){
-          return vc.getLowerBound();
-        }
-
-        if(i == i_begin){
-          break;
-        }else{
-          --i;
-        }
-      }while(true);
-      return NullConstraint;
-    }
-  }
-}
-TrustNode ConstraintDatabase::eeExplain(const Constraint* const c) const
-{
-  Assert(c->hasLiteral());
-  return d_congruenceManager.explain(c->getLiteral());
-}
-
-void ConstraintDatabase::eeExplain(ConstraintCP c, NodeBuilder& nb) const
-{
-  Assert(c->hasLiteral());
-  // NOTE: this is not a recommended method since it ignores proofs
-  d_congruenceManager.explain(c->getLiteral(), nb);
-}
-
-bool ConstraintDatabase::variableDatabaseIsSetup(ArithVar v) const {
-  return v < d_varDatabases.size();
-}
-
-ConstraintDatabase::Watches::Watches(context::Context* satContext,
-                                     context::Context* userContext)
-    : d_constraintProofs(satContext),
-      d_canBePropagatedWatches(satContext),
-      d_assertionOrderWatches(satContext),
-      d_splitWatches(userContext)
-{}
-
-
-void Constraint::setLiteral(Node n) {
-  Trace("arith::constraint") << "Mapping " << *this << " to " << n << std::endl;
-  Assert(Comparison::isNormalAtom(n));
-  Assert(!hasLiteral());
-  Assert(sanityChecking(n));
-  d_literal = n;
-  NodetoConstraintMap& map = d_database->d_nodetoConstraintMap;
-  Assert(map.find(n) == map.end());
-  map.insert(make_pair(d_literal, this));
-}
-
-Node Constraint::getProofLiteral() const
-{
-  Assert(d_database != nullptr);
-  Assert(d_database->d_avariables.hasNode(d_variable));
-  Node varPart = d_database->d_avariables.asNode(d_variable);
-  Kind cmp;
-  bool neg = false;
-  switch (d_type)
-  {
-    case ConstraintType::UpperBound:
-    {
-      if (d_value.infinitesimalIsZero())
-      {
-        cmp = Kind::LEQ;
-      }
-      else
-      {
-        cmp = Kind::LT;
-      }
-      break;
-    }
-    case ConstraintType::LowerBound:
-    {
-      if (d_value.infinitesimalIsZero())
-      {
-        cmp = Kind::GEQ;
-      }
-      else
-      {
-        cmp = Kind::GT;
-      }
-      break;
-    }
-    case ConstraintType::Equality:
-    {
-      cmp = Kind::EQUAL;
-      break;
-    }
-    case ConstraintType::Disequality:
-    {
-      cmp = Kind::EQUAL;
-      neg = true;
-      break;
-    }
-    default: Unreachable() << d_type;
-  }
-  NodeManager* nm = NodeManager::currentNM();
-  Node constPart = nm->mkConstRealOrInt(
-      varPart.getType(), Rational(d_value.getNoninfinitesimalPart()));
-  Node posLit = nm->mkNode(cmp, varPart, constPart);
-  return neg ? posLit.negate() : posLit;
-}
-
-void ConstraintDatabase::proveOr(std::vector<TrustNode>& out,
-                                 ConstraintP a,
-                                 ConstraintP b,
-                                 bool negateSecond) const
-{
-  Node la = a->getLiteral();
-  Node lb = b->getLiteral();
-  Node orN = (la < lb) ? la.orNode(lb) : lb.orNode(la);
-  if (isProofEnabled())
-  {
-    Assert(b->getNegation()->getType() != ConstraintType::Disequality);
-    auto nm = NodeManager::currentNM();
-    Node alit = a->getNegation()->getProofLiteral();
-    TypeNode type = alit[0].getType();
-    auto pf_neg_la = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
-                                   {d_pnm->mkAssume(la.negate())},
-                                   {alit});
-    Node blit = b->getNegation()->getProofLiteral();
-    auto pf_neg_lb = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
-                                   {d_pnm->mkAssume(lb.negate())},
-                                   {blit});
-    int sndSign = negateSecond ? -1 : 1;
-    auto bot_pf = d_pnm->mkNode(
-        PfRule::MACRO_SR_PRED_TRANSFORM,
-        {d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
-                       {pf_neg_la, pf_neg_lb},
-                       {nm->mkConstRealOrInt(type, Rational(-1 * sndSign)),
-                        nm->mkConstRealOrInt(type, Rational(sndSign))})},
-        {nm->mkConst(false)});
-    std::vector<Node> as;
-    std::transform(orN.begin(), orN.end(), std::back_inserter(as), [](Node n) {
-      return n.negate();
-    });
-    // No need to ensure that the expected node aggrees with `as` because we
-    // are not providing an expected node.
-    auto pf = d_pnm->mkNode(
-        PfRule::MACRO_SR_PRED_TRANSFORM,
-        {d_pnm->mkNode(PfRule::NOT_AND, {d_pnm->mkScope(bot_pf, as)}, {})},
-        {orN});
-    out.push_back(d_pfGen->mkTrustNode(orN, pf));
-  }
-  else
-  {
-    out.push_back(TrustNode::mkTrustLemma(orN));
-  }
-}
-
-void ConstraintDatabase::implies(std::vector<TrustNode>& out,
-                                 ConstraintP a,
-                                 ConstraintP b) const
-{
-  Node la = a->getLiteral();
-  Node lb = b->getLiteral();
-
-  Node neg_la = (la.getKind() == kind::NOT)? la[0] : la.notNode();
-
-  Assert(lb != neg_la);
-  Assert(b->getNegation()->getType() == ConstraintType::LowerBound
-         || b->getNegation()->getType() == ConstraintType::UpperBound);
-  proveOr(out,
-          a->getNegation(),
-          b,
-          b->getNegation()->getType() == ConstraintType::LowerBound);
-}
-
-void ConstraintDatabase::mutuallyExclusive(std::vector<TrustNode>& out,
-                                           ConstraintP a,
-                                           ConstraintP b) const
-{
-  Node la = a->getLiteral();
-  Node lb = b->getLiteral();
-
-  Node neg_la = la.negate();
-  Node neg_lb = lb.negate();
-  proveOr(out, a->getNegation(), b->getNegation(), true);
-}
-
-void ConstraintDatabase::outputUnateInequalityLemmas(
-    std::vector<TrustNode>& out, ArithVar v) const
-{
-  SortedConstraintMap& scm = getVariableSCM(v);
-  SortedConstraintMapConstIterator scm_iter = scm.begin();
-  SortedConstraintMapConstIterator scm_end = scm.end();
-  ConstraintP prev = NullConstraint;
-  //get transitive unates
-  //Only lower bounds or upperbounds should be done.
-  for(; scm_iter != scm_end; ++scm_iter){
-    const ValueCollection& vc = scm_iter->second;
-    if(vc.hasUpperBound()){
-      ConstraintP ub = vc.getUpperBound();
-      if(ub->hasLiteral()){
-        if(prev != NullConstraint){
-          implies(out, prev, ub);
-        }
-        prev = ub;
-      }
-    }
-  }
-}
-
-void ConstraintDatabase::outputUnateEqualityLemmas(std::vector<TrustNode>& out,
-                                                   ArithVar v) const
-{
-  vector<ConstraintP> equalities;
-
-  SortedConstraintMap& scm = getVariableSCM(v);
-  SortedConstraintMapConstIterator scm_iter = scm.begin();
-  SortedConstraintMapConstIterator scm_end = scm.end();
-
-  for(; scm_iter != scm_end; ++scm_iter){
-    const ValueCollection& vc = scm_iter->second;
-    if(vc.hasEquality()){
-      ConstraintP eq = vc.getEquality();
-      if(eq->hasLiteral()){
-        equalities.push_back(eq);
-      }
-    }
-  }
-
-  vector<ConstraintP>::const_iterator i, j, eq_end = equalities.end();
-  for(i = equalities.begin(); i != eq_end; ++i){
-    ConstraintP at_i = *i;
-    for(j= i + 1; j != eq_end; ++j){
-      ConstraintP at_j = *j;
-
-      mutuallyExclusive(out, at_i, at_j);
-    }
-  }
-
-  for(i = equalities.begin(); i != eq_end; ++i){
-    ConstraintP eq = *i;
-    const ValueCollection& vc = eq->getValueCollection();
-    Assert(vc.hasEquality() && vc.getEquality()->hasLiteral());
-
-    bool hasLB = vc.hasLowerBound() && vc.getLowerBound()->hasLiteral();
-    bool hasUB = vc.hasUpperBound() && vc.getUpperBound()->hasLiteral();
-
-    ConstraintP lb = hasLB ?
-      vc.getLowerBound() : eq->getStrictlyWeakerLowerBound(true, false);
-    ConstraintP ub = hasUB ?
-      vc.getUpperBound() : eq->getStrictlyWeakerUpperBound(true, false);
-
-    if(hasUB && hasLB && !eq->isSplit()){
-      out.push_back(eq->split());
-    }
-    if(lb != NullConstraint){
-      implies(out, eq, lb);
-    }
-    if(ub != NullConstraint){
-      implies(out, eq, ub);
-    }
-  }
-}
-
-void ConstraintDatabase::outputUnateEqualityLemmas(
-    std::vector<TrustNode>& lemmas) const
-{
-  for(ArithVar v = 0, N = d_varDatabases.size(); v < N; ++v){
-    outputUnateEqualityLemmas(lemmas, v);
-  }
-}
-
-void ConstraintDatabase::outputUnateInequalityLemmas(
-    std::vector<TrustNode>& lemmas) const
-{
-  for(ArithVar v = 0, N = d_varDatabases.size(); v < N; ++v){
-    outputUnateInequalityLemmas(lemmas, v);
-  }
-}
-
-bool ConstraintDatabase::handleUnateProp(ConstraintP ant, ConstraintP cons){
-  if(cons->negationHasProof()){
-    Trace("arith::unate") << "handleUnate: " << ant << " implies " << cons << endl;
-    cons->impliedByUnate(ant, true);
-    d_raiseConflict.raiseConflict(cons, InferenceId::ARITH_CONF_UNATE_PROP);
-    return true;
-  }else if(!cons->isTrue()){
-    ++d_statistics.d_unatePropagateImplications;
-    Trace("arith::unate") << "handleUnate: " << ant << " implies " << cons << endl;
-    cons->impliedByUnate(ant, false);
-    cons->tryToPropagate();
-    return false;
-  } else {
-    return false;
-  }
-}
-
-void ConstraintDatabase::unatePropLowerBound(ConstraintP curr, ConstraintP prev){
-  Trace("arith::unate") << "unatePropLowerBound " << curr << " " << prev << endl;
-  Assert(curr != prev);
-  Assert(curr != NullConstraint);
-  bool hasPrev = ! (prev == NullConstraint);
-  Assert(!hasPrev || curr->getValue() > prev->getValue());
-
-  ++d_statistics.d_unatePropagateCalls;
-
-  const SortedConstraintMap& scm = curr->constraintSet();
-  const SortedConstraintMapConstIterator scm_begin = scm.begin();
-  SortedConstraintMapConstIterator scm_i = curr->d_variablePosition;
-
-  //Ignore the first ValueCollection
-  // NOPE: (>= p c) then (= p c) NOPE
-  // NOPE: (>= p c) then (not (= p c)) NOPE
-
-  while(scm_i != scm_begin){
-    --scm_i; // move the iterator back
-
-    const ValueCollection& vc = scm_i->second;
-
-    //If it has the previous element, do nothing and stop!
-    if(hasPrev &&
-       vc.hasConstraintOfType(prev->getType())
-       && vc.getConstraintOfType(prev->getType()) == prev){
-      break;
-    }
-
-    //Don't worry about implying the negation of upperbound.
-    //These should all be handled by propagating the LowerBounds!
-    if(vc.hasLowerBound()){
-      ConstraintP lb = vc.getLowerBound();
-      if(handleUnateProp(curr, lb)){ return; }
-    }
-    if(vc.hasDisequality()){
-      ConstraintP dis = vc.getDisequality();
-      if(handleUnateProp(curr, dis)){ return; }
-    }
-  }
-}
-
-void ConstraintDatabase::unatePropUpperBound(ConstraintP curr, ConstraintP prev){
-  Trace("arith::unate") << "unatePropUpperBound " << curr << " " << prev << endl;
-  Assert(curr != prev);
-  Assert(curr != NullConstraint);
-  bool hasPrev = ! (prev == NullConstraint);
-  Assert(!hasPrev || curr->getValue() < prev->getValue());
-
-  ++d_statistics.d_unatePropagateCalls;
-
-  const SortedConstraintMap& scm = curr->constraintSet();
-  const SortedConstraintMapConstIterator scm_end = scm.end();
-  SortedConstraintMapConstIterator scm_i = curr->d_variablePosition;
-  ++scm_i;
-  for(; scm_i != scm_end; ++scm_i){
-    const ValueCollection& vc = scm_i->second;
-
-    //If it has the previous element, do nothing and stop!
-    if(hasPrev &&
-       vc.hasConstraintOfType(prev->getType()) &&
-       vc.getConstraintOfType(prev->getType()) == prev){
-      break;
-    }
-    //Don't worry about implying the negation of upperbound.
-    //These should all be handled by propagating the UpperBounds!
-    if(vc.hasUpperBound()){
-      ConstraintP ub = vc.getUpperBound();
-      if(handleUnateProp(curr, ub)){ return; }
-    }
-    if(vc.hasDisequality()){
-      ConstraintP dis = vc.getDisequality();
-      if(handleUnateProp(curr, dis)){ return; }
-    }
-  }
-}
-
-void ConstraintDatabase::unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB){
-  Trace("arith::unate") << "unatePropEquality " << curr << " " << prevLB << " " << prevUB << endl;
-  Assert(curr != prevLB);
-  Assert(curr != prevUB);
-  Assert(curr != NullConstraint);
-  bool hasPrevLB = ! (prevLB == NullConstraint);
-  bool hasPrevUB = ! (prevUB == NullConstraint);
-  Assert(!hasPrevLB || curr->getValue() >= prevLB->getValue());
-  Assert(!hasPrevUB || curr->getValue() <= prevUB->getValue());
-
-  ++d_statistics.d_unatePropagateCalls;
-
-  const SortedConstraintMap& scm = curr->constraintSet();
-  SortedConstraintMapConstIterator scm_curr = curr->d_variablePosition;
-  SortedConstraintMapConstIterator scm_last = hasPrevUB ? prevUB->d_variablePosition : scm.end();
-  SortedConstraintMapConstIterator scm_i;
-  if(hasPrevLB){
-    scm_i = prevLB->d_variablePosition;
-    if(scm_i != scm_curr){ // If this does not move this past scm_curr, move it one forward
-      ++scm_i;
-    }
-  }else{
-    scm_i = scm.begin();
-  }
-
-  for(; scm_i != scm_curr; ++scm_i){
-    // between the previous LB and the curr
-    const ValueCollection& vc = scm_i->second;
-
-    //Don't worry about implying the negation of upperbound.
-    //These should all be handled by propagating the LowerBounds!
-    if(vc.hasLowerBound()){
-      ConstraintP lb = vc.getLowerBound();
-      if(handleUnateProp(curr, lb)){ return; }
-    }
-    if(vc.hasDisequality()){
-      ConstraintP dis = vc.getDisequality();
-      if(handleUnateProp(curr, dis)){ return; }
-    }
-  }
-  Assert(scm_i == scm_curr);
-  if(!hasPrevUB || scm_i != scm_last){
-    ++scm_i;
-  } // hasPrevUB implies scm_i != scm_last
-
-  for(; scm_i != scm_last; ++scm_i){
-    // between the curr and the previous UB imply the upperbounds and disequalities.
-    const ValueCollection& vc = scm_i->second;
-
-    //Don't worry about implying the negation of upperbound.
-    //These should all be handled by propagating the UpperBounds!
-    if(vc.hasUpperBound()){
-      ConstraintP ub = vc.getUpperBound();
-      if(handleUnateProp(curr, ub)){ return; }
-    }
-    if(vc.hasDisequality()){
-      ConstraintP dis = vc.getDisequality();
-      if(handleUnateProp(curr, dis)){ return; }
-    }
-  }
-}
-
-std::pair<int, int> Constraint::unateFarkasSigns(ConstraintCP ca, ConstraintCP cb){
-  ConstraintType a = ca->getType();
-  ConstraintType b = cb->getType();
-
-  Assert(a != Disequality);
-  Assert(b != Disequality);
-
-  int a_sgn = (a == LowerBound) ? -1 : ((a == UpperBound) ? 1 : 0);
-  int b_sgn = (b == LowerBound) ? -1 : ((b == UpperBound) ? 1 : 0);
-
-  if(a_sgn == 0 && b_sgn == 0){
-    Assert(a == Equality);
-    Assert(b == Equality);
-    Assert(ca->getValue() != cb->getValue());
-    if(ca->getValue() < cb->getValue()){
-      a_sgn = 1;
-      b_sgn = -1;
-    }else{
-      a_sgn = -1;
-      b_sgn = 1;
-    }
-  }else if(a_sgn == 0){
-    Assert(b_sgn != 0);
-    Assert(a == Equality);
-    a_sgn = -b_sgn;
-  }else if(b_sgn == 0){
-    Assert(a_sgn != 0);
-    Assert(b == Equality);
-    b_sgn = -a_sgn;
-  }
-  Assert(a_sgn != 0);
-  Assert(b_sgn != 0);
-
-  Trace("arith::unateFarkasSigns") << "Constraint::unateFarkasSigns("<<a <<", " << b << ") -> "
-                                   << "("<<a_sgn<<", "<< b_sgn <<")"<< endl;
-  return make_pair(a_sgn, b_sgn);
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/constraint.h b/src/theory/arith/constraint.h
deleted file mode 100644 (file)
index f59e8ea..0000000
+++ /dev/null
@@ -1,1267 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Alex Ozdemir, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Defines Constraint and ConstraintDatabase which is the internal
- * representation of variables in arithmetic
- *
- * This file defines Constraint and ConstraintDatabase.
- * A Constraint is the internal representation of literals in TheoryArithmetic.
- * Constraints are fundamentally a triple:
- *  - ArithVar associated with the constraint,
- *  - a DeltaRational value,
- *  - and a ConstraintType.
- *
- * Literals:
- *   The constraint may also keep track of a node corresponding to the
- *   Constraint.
- *   This can be accessed by getLiteral() in O(1) if it has been set.
- *   This node must be in normal form and may be used for communication with
- *   the TheoryEngine.
- *
- * In addition, Constraints keep track of the following:
- *  - A Constraint that is the negation of the Constraint.
- *  - An iterator into a set of Constraints for the ArithVar sorted by
- *    DeltaRational value.
- *  - A context dependent internal proof of the node that can be used for
- *    explanations.
- *  - Whether an equality/disequality has been split in the user context via a
- *    lemma.
- *  - Whether a constraint, be be used in explanations sent to the context
- *
- * Looking up constraints:
- *  - All of the Constraints with associated nodes in the ConstraintDatabase
- *    can be accessed via a single hashtable lookup until the Constraint is
- *    removed.
- *  - Nodes that have not been associated to a constraints can be
- *    inserted/associated to existing nodes in O(log n) time.
- *
- * Implications:
- *  - A Constraint can be used to find unate implications.
- *  - A unate implication is an implication based purely on the ArithVar
- *    matching  and the DeltaRational value.
- *    (implies (<= x c) (<= x d)) given c <= d
- *  - This is done using the iterator into the sorted set of constraints.
- *  - Given a tight constraint and previous tightest constraint, this will
- *    efficiently propagate internally.
- *
- * Additing and Removing Constraints
- *  - Adding Constraints takes O(log n) time where n is the number of
- *    constraints associated with the ArithVar.
- *  - Removing Constraints takes O(1) time.
- *
- * Internals:
- *  - Constraints are pointers to ConstraintValues.
- *  - Undefined Constraints are NullConstraint.
- *
- * Assumption vs. Assertion:
- * - An assertion is anything on the theory d_fact queue.
- *   This includes any thing propagated and returned to the fact queue.
- *   These can be used in external conflicts and propagations of earlier
- *   proofs.
- * - An assumption is anything on the theory d_fact queue that has no further
- *   explanation i.e. this theory did not propagate it.
- * - To set something an assumption, first set it as being as assertion.
- * - Internal assumptions have no explanations and must be regressed out of the
- *   proof.
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__CONSTRAINT_H
-#define CVC5__THEORY__ARITH__CONSTRAINT_H
-
-#include <unordered_map>
-#include <vector>
-
-#include "base/configuration_private.h"
-#include "context/cdlist.h"
-#include "context/cdqueue.h"
-#include "expr/node.h"
-#include "proof/trust_node.h"
-#include "smt/env_obj.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/callbacks.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/arith/delta_rational.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::context {
-class Context;
-}
-namespace cvc5::internal {
-
-class ProofNodeManager;
-class EagerProofGenerator;
-
-namespace theory {
-
-namespace arith {
-
-class Comparison;
-class ArithCongruenceManager;
-class ArithVariables;
-
-/**
- * Logs the types of different proofs.
- * Current, proof types:
- * - NoAP             : This constraint is not known to be true.
- * - AssumeAP         : This is an input assertion. There is no proof.
- *                    : Something can be both asserted and have a proof.
- * - InternalAssumeAP : An internal assumption. This has no guarantee of having an external proof.
- *                    : This must be removed by regression.
- * - FarkasAP         : A proof with Farka's coefficients, i.e.
- *                    :  \sum lambda_i ( asNode(x_i) <= c_i  ) |= 0 < 0
- *                    : If proofs are on, coefficients will be logged.
- *                    : If proofs are off, coefficients will not be logged.
- *                    : A unate implication is a FarkasAP.
- * - TrichotomyAP     : This is any entailment using (x<= a and x >=a) => x = a
- *                    : Equivalently, (x > a or x < a or x = a)
- *                    : There are 3 candidate ways this can propagate:
- *                    :   !(x > a) and !(x = a) => x < a
- *                    :   !(x < a) and !(x = a) => x > a
- *                    :   !(x > a) and !(x < a) => x = a
- * - EqualityEngineAP : This is propagated by the equality engine.
- *                    : Consult this for the proof.
- * - IntTightenAP     : This is indicates that a bound involving integers was tightened.
- *                    : e.g. i < 5.5 became i <= 5, when i is an integer.
- * - IntHoleAP        : This is currently a catch-all for all integer specific reason.
- */
-enum ArithProofType
-  { NoAP,
-    AssumeAP,
-    InternalAssumeAP,
-    FarkasAP,
-    TrichotomyAP,
-    EqualityEngineAP,
-    IntTightenAP,
-    IntHoleAP};
-
-/**
- * The types of constraints.
- * The convex constraints are the constraints are LowerBound, Equality,
- * and UpperBound.
- */
-enum ConstraintType {LowerBound, Equality, UpperBound, Disequality};
-
-typedef context::CDList<ConstraintCP> CDConstraintList;
-
-typedef std::unordered_map<Node, ConstraintP> NodetoConstraintMap;
-
-typedef size_t ConstraintRuleID;
-static constexpr ConstraintRuleID ConstraintRuleIdSentinel =
-    std::numeric_limits<ConstraintRuleID>::max();
-
-typedef size_t AntecedentId;
-static constexpr AntecedentId AntecedentIdSentinel =
-    std::numeric_limits<AntecedentId>::max();
-
-typedef size_t AssertionOrder;
-static constexpr AssertionOrder AssertionOrderSentinel =
-    std::numeric_limits<AssertionOrder>::max();
-
-/**
- * A ValueCollection binds together convex constraints that have the same
- * DeltaRational value.
- */
-class ValueCollection {
-private:
-
-  ConstraintP d_lowerBound;
-  ConstraintP d_upperBound;
-  ConstraintP d_equality;
-  ConstraintP d_disequality;
-
-public:
-  ValueCollection();
-
-  static ValueCollection mkFromConstraint(ConstraintP c);
-
-  bool hasLowerBound() const;
-  bool hasUpperBound() const;
-  bool hasEquality() const;
-  bool hasDisequality() const;
-
-  bool hasConstraintOfType(ConstraintType t) const;
-
-  ConstraintP getLowerBound() const;
-  ConstraintP getUpperBound() const;
-  ConstraintP getEquality() const;
-  ConstraintP getDisequality() const;
-
-  ConstraintP getConstraintOfType(ConstraintType t) const;
-
-  /** Returns true if any of the constraints are non-null. */
-  bool empty() const;
-
-  /**
-   * Remove the constraint of the type t from the collection.
-   * Returns true if the ValueCollection is now empty.
-   * If true is returned, d_value is now NULL.
-   */
-  void remove(ConstraintType t);
-
-  /**
-   * Adds a constraint to the set.
-   * The collection must not have a constraint of that type already.
-   */
-  void add(ConstraintP c);
-
-  void push_into(std::vector<ConstraintP>& vec) const;
-
-  ConstraintP nonNull() const;
-
-  ArithVar getVariable() const;
-  const DeltaRational& getValue() const;
-};
-
-/**
- * A Map of ValueCollections sorted by the associated DeltaRational values.
- *
- * Discussion:
- * While it is more natural to consider this a set, this cannot be a set as in
- * sets the type of both iterator and const_iterator in sets are
- * "constant iterators".  We require iterators that dereference to
- * ValueCollection&.
- *
- * See:
- * http://gcc.gnu.org/onlinedocs/libstdc++/ext/lwg-defects.html#103
- */
-typedef std::map<DeltaRational, ValueCollection> SortedConstraintMap;
-typedef SortedConstraintMap::iterator SortedConstraintMapIterator;
-typedef SortedConstraintMap::const_iterator SortedConstraintMapConstIterator;
-
-/** A Pair associating a variables and a Sorted ConstraintSet. */
-struct PerVariableDatabase{
-  ArithVar d_var;
-  SortedConstraintMap d_constraints;
-
-  // x ? c_1, x ? c_2, x ? c_3, ...
-  // where ? is a non-empty subset of {lb, ub, eq}
-  // c_1 < c_2 < c_3 < ...
-
-  PerVariableDatabase(ArithVar v) : d_var(v), d_constraints() {}
-
-  bool empty() const {
-    return d_constraints.empty();
-  }
-
-  static bool IsEmpty(const PerVariableDatabase& p){
-    return p.empty();
-  }
-};
-
-/**
- * If proofs are on, there is a vector of rationals for farkas coefficients.
- * This is the owner of the memory for the vector, and calls delete upon
- * cleanup.
- *
- */
-struct ConstraintRule {
-  ConstraintP d_constraint;
-  ArithProofType d_proofType;
-  AntecedentId d_antecedentEnd;
-
-  /**
-   * In this comment, we abbreviate ConstraintDatabase::d_antecedents
-   * and d_farkasCoefficients as ans and fc.
-   *
-   * This list is always empty if proofs are not enabled.
-   *
-   * If proofs are enabled, the proof of constraint c at p in ans[p] of length n
-   * is (NullConstraint, ans[p-(n-1)], ... , ans[p-1], ans[p])
-   *
-   * Farkas' proofs show a contradiction with the negation of c, c_not =
-   * c->getNegation().
-   *
-   * We treat the position for NullConstraint (p-n) as the position for the
-   * farkas coefficient for so we pretend c_not is ans[p-n]. So this correlation
-   * for the constraints we are going to use: (c_not, ans[p-n+(1)], ... ,
-   * ans[p-n+(n-1)], ans[p-n+(n)]) With the coefficients at positions: (fc[0],
-   * fc[1)], ... fc[n])
-   *
-   * The index of the constraints in the proof are {i | i <= 0 <= n] } (with
-   * c_not being p-n). Partition the indices into L, U, and E, the lower bounds,
-   * the upper bounds and equalities.
-   *
-   * We standardize the proofs to be upper bound oriented following the
-   * convention: A x <= b with the proof witness of the form (lambda) Ax <=
-   * (lambda) b and lambda >= 0.
-   *
-   * To accomplish this cleanly, the fc coefficients must be negative for lower
-   * bounds. The signs of equalities can be either positive or negative.
-   *
-   * Thus the proof corresponds to (with multiplication over inequalities):
-   *    \sum_{u in U} fc[u] ans[p-n+u] + \sum_{e in E} fc[e] ans[p-n+e]
-   *  + \sum_{l in L} fc[l] ans[p-n+l]
-   * |= 0 < 0
-   * where fc[u] > 0, fc[l] < 0, and fc[e] != 0 (i.e. it can be either +/-).
-   *
-   * There is no requirement that the proof is minimal.
-   * We do however use all of the constraints by requiring non-zero
-   * coefficients.
-   */
-  RationalVectorCP d_farkasCoefficients;
-
-  ConstraintRule();
-  ConstraintRule(ConstraintP con, ArithProofType pt);
-  ConstraintRule(ConstraintP con,
-                 ArithProofType pt,
-                 AntecedentId antecedentEnd);
-  ConstraintRule(ConstraintP con,
-                 ArithProofType pt,
-                 AntecedentId antecedentEnd,
-                 RationalVectorCP coeffs);
-
-  void print(std::ostream& out, bool produceProofs) const;
-}; /* class ConstraintRule */
-
-class Constraint {
-
-  friend class ConstraintDatabase;
-
- public:
-  /**
-   * This begins construction of a minimal constraint.
-   *
-   * This should only be called by ConstraintDatabase.
-   *
-   * Because of circular dependencies a Constraint is not fully valid until
-   * initialize has been called on it.
-   */
-  Constraint(ArithVar x,
-             ConstraintType t,
-             const DeltaRational& v,
-             bool produceProofs);
-
-  /**
-   * Destructor for a constraint.
-   * This should only be called if safeToGarbageCollect() is true.
-   */
-  ~Constraint();
-
-  static ConstraintType constraintTypeOfComparison(const Comparison& cmp);
-
-  inline ConstraintType getType() const {
-    return d_type;
-  }
-
-  inline ArithVar getVariable() const {
-    return d_variable;
-  }
-
-  const DeltaRational& getValue() const {
-    return d_value;
-  }
-
-  inline ConstraintP getNegation() const {
-    return d_negation;
-  }
-
-  bool isEquality() const{
-    return d_type == Equality;
-  }
-  bool isDisequality() const{
-    return d_type == Disequality;
-  }
-  bool isLowerBound() const{
-    return d_type == LowerBound;
-  }
-  bool isUpperBound() const{
-    return d_type == UpperBound;
-  }
-  bool isStrictUpperBound() const{
-    Assert(isUpperBound());
-    return getValue().infinitesimalSgn() < 0;
-  }
-
-  bool isStrictLowerBound() const{
-    Assert(isLowerBound());
-    return getValue().infinitesimalSgn() > 0;
-  }
-
-  bool isSplit() const {
-    return d_split;
-  }
-
-  /**
-   * Splits the node in the user context.
-   * Returns a lemma that is assumed to be true for the rest of the user context.
-   * Constraint must be an equality or disequality.
-   */
-  TrustNode split();
-
-  bool canBePropagated() const {
-    return d_canBePropagated;
-  }
-  void setCanBePropagated();
-
-  /**
-   * Light wrapper for calling setCanBePropagated(),
-   * on this and this->d_negation.
-   */
-  void setPreregistered(){
-    setCanBePropagated();
-    d_negation->setCanBePropagated();
-  }
-
-  bool assertedToTheTheory() const {
-    Assert((d_assertionOrder < AssertionOrderSentinel) != d_witness.isNull());
-    return d_assertionOrder < AssertionOrderSentinel;
-  }
-  TNode getWitness() const {
-    Assert(assertedToTheTheory());
-    return d_witness;
-  }
-
-  bool assertedBefore(AssertionOrder time) const {
-    return d_assertionOrder < time;
-  }
-
-  /**
-   * Sets the witness literal for a node being on the assertion stack.
-   *
-   * If the negation of the node is true, inConflict must be true.
-   * If the negation of the node is false, inConflict must be false.
-   * Hence, negationHasProof() == inConflict.
-   *
-   * This replaces:
-   *   void setAssertedToTheTheory(TNode witness);
-   *   void setAssertedToTheTheoryWithNegationTrue(TNode witness);
-   */
-  void setAssertedToTheTheory(TNode witness, bool inConflict);
-
-  bool hasLiteral() const {
-    return !d_literal.isNull();
-  }
-
-  void setLiteral(Node n);
-
-  Node getLiteral() const {
-    Assert(hasLiteral());
-    return d_literal;
-  }
-
-  /** Gets a literal in the normal form suitable for proofs.
-   * That is, (sum of non-const monomials) >< const.
-   *
-   * This is a sister method to `getLiteral`, which returns a normal form
-   * literal, suitable for external solving use.
-   */
-  Node getProofLiteral() const;
-
-  /**
-   * Set the node as having a proof and being an assumption.
-   * The node must be assertedToTheTheory().
-   *
-   * Precondition: negationHasProof() == inConflict.
-   *
-   * Replaces:
-   *  selfExplaining().
-   *  selfExplainingWithNegationTrue().
-   */
-  void setAssumption(bool inConflict);
-
-  /** Returns true if the node is an assumption.*/
-  bool isAssumption() const;
-
-  /** Whether we produce proofs */
-  bool isProofProducing() const { return d_produceProofs; }
-
-  /** Set the constraint to have an EqualityEngine proof. */
-  void setEqualityEngineProof();
-  bool hasEqualityEngineProof() const;
-
-  /** Returns true if the node has a Farkas' proof. */
-  bool hasFarkasProof() const;
-
-  /**
-   * @brief Returns whether this constraint is provable using a Farkas
-   * proof applied to (possibly tightened) input assertions.
-   *
-   * An example of a constraint that has a simple Farkas proof:
-   *    x <= 0 proven from x + y <= 0 and x - y <= 0.
-   *
-   * An example of another constraint that has a simple Farkas proof:
-   *    x <= 0 proven from x + y <= 0 and x - y <= 0.5 for integers x, y
-   *       (integer bound-tightening is applied first!).
-   *
-   * An example of a constraint that might be proven **without** a simple
-   * Farkas proof:
-   *    x < 0 proven from not(x == 0) and not(x > 0).
-   *
-   * This could be proven internally by the arithmetic theory using
-   * `TrichotomyAP` as the proof type.
-   *
-   */
-  bool hasSimpleFarkasProof() const;
-  /**
-   * Returns whether this constraint is an assumption or a tightened
-   * assumption.
-   */
-  bool isPossiblyTightenedAssumption() const;
-
-  /** Returns true if the node has a int bound tightening proof. */
-  bool hasIntTightenProof() const;
-
-  /** Returns true if the node has a int hole proof. */
-  bool hasIntHoleProof() const;
-
-  /** Returns true if the node has a trichotomy proof. */
-  bool hasTrichotomyProof() const;
-
-  void printProofTree(std::ostream & out, size_t depth = 0) const;
-
-  /**
-   * A sets the constraint to be an internal assumption.
-   *
-   * This does not need to have a witness or an associated literal.
-   * This is always itself in the explanation fringe for both conflicts
-   * and propagation.
-   * This cannot be converted back into a Node conflict or explanation.
-   *
-   * This cannot have a proof or be asserted to the theory!
-   *
-   */
-  void setInternalAssumption(bool inConflict);
-  bool isInternalAssumption() const;
-
-  /**
-   * Returns a explanation of the constraint that is appropriate for conflicts.
-   *
-   * This is not appropriate for propagation!
-   *
-   * This is the minimum fringe of the implication tree s.t.
-   * every constraint is assertedToTheTheory() or hasEqualityEngineProof().
-   */
-  TrustNode externalExplainByAssertions() const;
-
-  /**
-   * Writes an explanation of a constraint into the node builder.
-   * Pushes back an explanation that is acceptable to send to the sat solver.
-   * nb is assumed to be an AND.
-   *
-   * This is the minimum fringe of the implication tree s.t.
-   * every constraint is assertedToTheTheory() or hasEqualityEngineProof().
-   *
-   * This is not appropriate for propagation!
-   * Use explainForPropagation() instead.
-   */
-  std::shared_ptr<ProofNode> externalExplainByAssertions(NodeBuilder& nb) const
-  {
-    return externalExplain(nb, AssertionOrderSentinel);
-  }
-
-  /* Equivalent to calling externalExplainByAssertions on all constraints in b */
-  static Node externalExplainByAssertions(const ConstraintCPVec& b);
-  static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b);
-  static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c);
-
-  /**
-   * This is the minimum fringe of the implication tree s.t. every constraint is
-   * - assertedToTheTheory(),
-   * - isInternalDecision() or
-   * - hasEqualityEngineProof().
-   */
-  static void assertionFringe(ConstraintCPVec& v);
-  static void assertionFringe(ConstraintCPVec& out, const ConstraintCPVec& in);
-
-  /** The fringe of a farkas' proof. */
-  bool onFringe() const {
-    return assertedToTheTheory() || isInternalAssumption() || hasEqualityEngineProof();
-  }
-
-  /**
-   * Returns an explanation of a propagation by the ConstraintDatabase.
-   * The constraint must have a proof.
-   * The constraint cannot be an assumption.
-   *
-   * This is the minimum fringe of the implication tree (excluding the
-   * constraint itself) s.t. every constraint is assertedToTheTheory() or
-   * hasEqualityEngineProof().
-   *
-   * All return conjuncts were asserted before this constraint.
-   *
-   * Requires the given node to rewrite to the canonical literal for this
-   * constraint.
-   *
-   * @params n the literal to prove
-   *           n must rewrite to the constraint's canonical literal
-   *
-   * @returns a trust node of the form:
-   *         (=> explanation n)
-   */
-  TrustNode externalExplainForPropagation(TNode n) const;
-
-  /**
-   * Explain the constraint and its negation in terms of assertions.
-   * The constraint must be in conflict.
-   */
-  TrustNode externalExplainConflict() const;
-
-  /** The constraint is known to be true. */
-  inline bool hasProof() const {
-    return d_crid != ConstraintRuleIdSentinel;
-  }
-
-  /** The negation of the constraint is known to hold. */
-  inline bool negationHasProof() const {
-    return d_negation->hasProof();
-  }
-
-  /** Neither the contraint has a proof nor the negation has a proof.*/
-  bool truthIsUnknown() const {
-    return !hasProof() && !negationHasProof();
-  }
-
-  /** This is a synonym for hasProof(). */
-  inline bool isTrue() const {
-    return hasProof();
-  }
-
-  /** Both the constraint and its negation are true. */
-  inline bool inConflict() const {
-    return hasProof() && negationHasProof();
-  }
-
-  /**
-   * Returns the constraint that corresponds to taking
-   *    x r ceiling(getValue()) where r is the node's getType().
-   * Esstentially this is an up branch.
-   */
-  ConstraintP getCeiling();
-
-  /**
-   * Returns the constraint that corresponds to taking
-   *    x r floor(getValue()) where r is the node's getType().
-   * Esstentially this is a down branch.
-   */
-  ConstraintP getFloor();
-
-  static ConstraintP makeNegation(ArithVar v,
-                                  ConstraintType t,
-                                  const DeltaRational& r,
-                                  bool produceProofs);
-
-  const ValueCollection& getValueCollection() const;
-
-
-  ConstraintP getStrictlyWeakerUpperBound(bool hasLiteral, bool mustBeAsserted) const;
-  ConstraintP getStrictlyWeakerLowerBound(bool hasLiteral, bool mustBeAsserted) const;
-
-  /**
-   * Marks a the constraint c as being entailed by a.
-   * The Farkas proof 1*(a) + -1 (c) |= 0<0
-   *
-   * After calling impliedByUnate(), the caller should either raise a conflict
-   * or try call tryToPropagate().
-   */
-  void impliedByUnate(ConstraintCP a, bool inConflict);
-
-  /**
-   * Marks a the constraint c as being entailed by a.
-   * The reason has to do with integer bound tightening.
-   *
-   * After calling impliedByIntTighten(), the caller should either raise a conflict
-   * or try call tryToPropagate().
-   */
-  void impliedByIntTighten(ConstraintCP a, bool inConflict);
-
-  /**
-   * Marks a the constraint c as being entailed by a.
-   * The reason has to do with integer reasoning.
-   *
-   * After calling impliedByIntHole(), the caller should either raise a conflict
-   * or try call tryToPropagate().
-   */
-  void impliedByIntHole(ConstraintCP a, bool inConflict);
-
-  /**
-   * Marks a the constraint c as being entailed by a.
-   * The reason has to do with integer reasoning.
-   *
-   * After calling impliedByIntHole(), the caller should either raise a conflict
-   * or try call tryToPropagate().
-   */
-  void impliedByIntHole(const ConstraintCPVec& b, bool inConflict);
-
-  /**
-   * This is a lemma of the form:
-   *   x < d or x = d or x > d
-   * The current constraint c is one of the above constraints and {a,b}
-   * are the negation of the other two constraints.
-   *
-   * Preconditions:
-   * - negationHasProof() == inConflict.
-   *
-   * After calling impliedByTrichotomy(), the caller should either raise a conflict
-   * or try call tryToPropagate().
-   */
-  void impliedByTrichotomy(ConstraintCP a, ConstraintCP b, bool inConflict);
-
-  /**
-   * Marks the node as having a Farkas proof.
-   *
-   * Preconditions:
-   * - coeffs == NULL if proofs are off.
-   * - See the comments for ConstraintRule for the form of coeffs when
-   *   proofs are on.
-   * - negationHasProof() == inConflict.
-   *
-   * After calling impliedByFarkas(), the caller should either raise a conflict
-   * or try call tryToPropagate().
-   */
-  void impliedByFarkas(const ConstraintCPVec& b, RationalVectorCP coeffs, bool inConflict);
-
-  /**
-   * Generates an implication node, B => getLiteral(),
-   * where B is the result of externalExplainByAssertions(b).
-   * Does not guarantee b is the explanation of the constraint.
-   */
-  Node externalImplication(const ConstraintCPVec& b) const;
-
-  /**
-   * Returns true if the variable is assigned the value dr,
-   * the constraint would be satisfied.
-   */
-  bool satisfiedBy(const DeltaRational& dr) const;
-
-  /**
-   * The node must have a proof already and be eligible for propagation!
-   * You probably want to call tryToPropagate() instead.
-   *
-   * Preconditions:
-   * - hasProof()
-   * - canBePropagated()
-   * - !assertedToTheTheory()
-   */
-  void propagate();
-
-  /**
-   * If the constraint
-   *   canBePropagated() and
-   *   !assertedToTheTheory(),
-   * the constraint is added to the database's propagation queue.
-   *
-   * Precondition:
-   * - hasProof()
-   */
-  void tryToPropagate();
-
-  /**
-   * Returns a reference to the containing database.
-   * Precondition: the constraint must be initialized.
-   */
-  const ConstraintDatabase& getDatabase() const;
-
-  /** Returns the constraint rule at the position. */
-  const ConstraintRule& getConstraintRule() const;
-
- private:
-  /**  Returns true if the constraint has been initialized. */
-  bool initialized() const;
-
-  /**
-   * This initializes the fields that cannot be set in the constructor due to
-   * circular dependencies.
-   */
-  void initialize(ConstraintDatabase* db,
-                  SortedConstraintMapIterator v,
-                  ConstraintP negation);
-
-  class ConstraintRuleCleanup
-  {
-   public:
-    inline void operator()(ConstraintRule* crp)
-    {
-      Assert(crp != NULL);
-      ConstraintP constraint = crp->d_constraint;
-      Assert(constraint->d_crid != ConstraintRuleIdSentinel);
-      constraint->d_crid = ConstraintRuleIdSentinel;
-      if (constraint->isProofProducing())
-      {
-        if (crp->d_farkasCoefficients != RationalVectorCPSentinel)
-        {
-          delete crp->d_farkasCoefficients;
-        }
-      }
-    }
-  };
-
-  class CanBePropagatedCleanup
-  {
-   public:
-    inline void operator()(ConstraintP* p)
-    {
-      ConstraintP constraint = *p;
-      Assert(constraint->d_canBePropagated);
-      constraint->d_canBePropagated = false;
-    }
-  };
-
-  class AssertionOrderCleanup
-  {
-   public:
-    inline void operator()(ConstraintP* p)
-    {
-      ConstraintP constraint = *p;
-      Assert(constraint->assertedToTheTheory());
-      constraint->d_assertionOrder = AssertionOrderSentinel;
-      constraint->d_witness = TNode::null();
-      Assert(!constraint->assertedToTheTheory());
-    }
-  };
-
-  class SplitCleanup
-  {
-   public:
-    inline void operator()(ConstraintP* p)
-    {
-      ConstraintP constraint = *p;
-      Assert(constraint->d_split);
-      constraint->d_split = false;
-    }
-  };
-
-  /**
-   * Returns true if the node is safe to garbage collect.
-   * Both it and its negation must have no context dependent data set.
-   */
-  bool safeToGarbageCollect() const;
-
-  /**
-   * Returns true if the constraint has no context dependent data set.
-   */
-  bool contextDependentDataIsSet() const;
-
-  /**
-   * Returns true if the node correctly corresponds to the constraint that is
-   * being set.
-   */
-  bool sanityChecking(Node n) const;
-
-  /** Returns a reference to the map for d_variable. */
-  SortedConstraintMap& constraintSet() const;
-
-  /** Returns coefficients for the proofs for farkas cancellation. */
-  static std::pair<int, int> unateFarkasSigns(ConstraintCP a, ConstraintCP b);
-
-  Node externalExplain(AssertionOrder order) const;
-  /**
-   * Returns an explanation of that was assertedBefore(order).
-   * The constraint must have a proof.
-   * The constraint cannot be selfExplaining().
-   *
-   * This is the minimum fringe of the implication tree
-   * s.t. every constraint is assertedBefore(order) or hasEqualityEngineProof().
-   */
-  std::shared_ptr<ProofNode> externalExplain(NodeBuilder& nb,
-                                             AssertionOrder order) const;
-
-  static Node externalExplain(const ConstraintCPVec& b, AssertionOrder order);
-
-  inline ArithProofType getProofType() const {
-    return getConstraintRule().d_proofType;
-  }
-
-  inline AntecedentId getEndAntecedent() const {
-    return getConstraintRule().d_antecedentEnd;
-  }
-
-  inline RationalVectorCP getFarkasCoefficients() const
-  {
-    return d_produceProofs ? getConstraintRule().d_farkasCoefficients : nullptr;
-  }
-
-  /**
-   * The proof of the node is empty.
-   * The proof must be a special proof. Either
-   *   isSelfExplaining() or
-   *    hasEqualityEngineProof()
-   */
-  bool antecentListIsEmpty() const;
-
-  bool antecedentListLengthIsOne() const;
-
-  /** Return true if every element in b has a proof. */
-  static bool allHaveProof(const ConstraintCPVec& b);
-
-  /** Precondition: hasFarkasProof()
-   * Computes the combination implied by the farkas coefficients. Sees if it is
-   * a contradiction.
-   */
-
-  bool wellFormedFarkasProof() const;
-
-  /** The ArithVar associated with the constraint. */
-  const ArithVar d_variable;
-
-  /** The type of the Constraint. */
-  const ConstraintType d_type;
-
-  /** The DeltaRational value with the constraint. */
-  const DeltaRational d_value;
-
-  /** A pointer to the associated database for the Constraint. */
-  ConstraintDatabase* d_database;
-
-  /**
-   * The node to be communicated with the TheoryEngine.
-   *
-   * This is not context dependent, but may be set once.
-   *
-   * This must be set if the constraint canBePropagated().
-   * This must be set if the constraint assertedToTheTheory().
-   * Otherwise, this may be null().
-   */
-  Node d_literal;
-
-  /** Pointer to the negation of the Constraint. */
-  ConstraintP d_negation;
-
-  /**
-   * This is true if the associated node can be propagated.
-   *
-   * This should be enabled if the node has been preregistered.
-   *
-   * Sat Context Dependent.
-   * This is initially false.
-   */
-  bool d_canBePropagated;
-
-  /**
-   * This is the order the constraint was asserted to the theory.
-   * If this has been set, the node can be used in conflicts.
-   * If this is c.d_assertedOrder < d.d_assertedOrder, then c can be used in the
-   * explanation of d.
-   *
-   * This should be set after the literal is dequeued by Theory::get().
-   *
-   * Sat Context Dependent.
-   * This is initially AssertionOrderSentinel.
-   */
-  AssertionOrder d_assertionOrder;
-
-  /**
-   * This is guaranteed to be on the fact queue.
-   * For example if x + y = x + 1 is on the fact queue, then use this
-   */
-  TNode d_witness;
-
-  /**
-   * The position of the constraint in the constraint rule id.
-   *
-   * Sat Context Dependent.
-   * This is initially
-   */
-  ConstraintRuleID d_crid;
-
-  /**
-   * True if the equality has been split.
-   * Only meaningful if ConstraintType == Equality.
-   *
-   * User Context Dependent.
-   * This is initially false.
-   */
-  bool d_split;
-
-  /**
-   * Position in sorted constraint set for the variable.
-   * Unset if d_type is Disequality.
-   */
-  SortedConstraintMapIterator d_variablePosition;
-
-  /** Whether to produce proofs, */
-  bool d_produceProofs;
-
-}; /* class ConstraintValue */
-
-std::ostream& operator<<(std::ostream& o, const Constraint& c);
-std::ostream& operator<<(std::ostream& o, const ConstraintP c);
-std::ostream& operator<<(std::ostream& o, const ConstraintCP c);
-std::ostream& operator<<(std::ostream& o, const ConstraintType t);
-std::ostream& operator<<(std::ostream& o, const ValueCollection& c);
-std::ostream& operator<<(std::ostream& o, const ConstraintCPVec& v);
-std::ostream& operator<<(std::ostream& o, const ArithProofType);
-
-class ConstraintDatabase : protected EnvObj
-{
- private:
-  /**
-   * The map from ArithVars to their unique databases.
-   * When the vector changes size, we cannot allow the maps to move so this
-   * is a vector of pointers.
-   */
-  std::vector<PerVariableDatabase*> d_varDatabases;
-
-  SortedConstraintMap& getVariableSCM(ArithVar v) const;
-
-  /** Maps literals to constraints.*/
-  NodetoConstraintMap d_nodetoConstraintMap;
-
-  /**
-   * A queue of propagated constraints.
-   * ConstraintCP are pointers.
-   * The elements of the queue do not require destruction.
-   */
-  context::CDQueue<ConstraintCP> d_toPropagate;
-
-  /**
-   * Proofs are lists of valid constraints terminated by the first null
-   * sentinel value in the proof list.
-   * We abbreviate d_antecedents as ans in the comment.
-   *
-   * The proof at p in ans[p] of length n is
-   *  (NullConstraint, ans[p-(n-1)], ... , ans[p-1], ans[p])
-   *
-   * The proof at p corresponds to the conjunction:
-   *  (and x_i)
-   *
-   * So the proof of a Constraint c corresponds to the horn clause:
-   *  (implies (and x_i) c)
-   * where (and x_i) is the proof at c.d_crid d_antecedentEnd.
-   *
-   * Constraints are pointers so this list is designed not to require any destruction.
-   */
-  CDConstraintList d_antecedents;
-
-  typedef context::CDList<ConstraintRule, Constraint::ConstraintRuleCleanup>
-      ConstraintRuleList;
-  typedef context::CDList<ConstraintP, Constraint::CanBePropagatedCleanup>
-      CBPList;
-  typedef context::CDList<ConstraintP, Constraint::AssertionOrderCleanup>
-      AOList;
-  typedef context::CDList<ConstraintP, Constraint::SplitCleanup> SplitList;
-
-  /**
-   * The watch lists are collected together as they need to be garbage collected
-   * carefully.
-   */
-  struct Watches{
-
-    /**
-     * Contains the exact list of constraints that have a proof.
-     * Upon pop, this unsets d_crid to NoAP.
-     *
-     * The index in this list is the proper ordering of the proofs.
-     */
-    ConstraintRuleList d_constraintProofs;
-
-    /**
-     * Contains the exact list of constraints that can be used for propagation.
-     */
-    CBPList d_canBePropagatedWatches;
-
-    /**
-     * Contains the exact list of constraints that have been asserted to the theory.
-     */
-    AOList d_assertionOrderWatches;
-
-
-    /**
-     * Contains the exact list of atoms that have been preregistered.
-     * This is a pointer as it must be destroyed before the elements of
-     * d_varDatabases.
-     */
-    SplitList d_splitWatches;
-    Watches(context::Context* satContext, context::Context* userContext);
-  };
-  Watches* d_watches;
-
-  void pushSplitWatch(ConstraintP c);
-  void pushCanBePropagatedWatch(ConstraintP c);
-  void pushAssertionOrderWatch(ConstraintP c, TNode witness);
-
-  /** Assumes that antecedents have already been pushed. */
-  void pushConstraintRule(const ConstraintRule& crp);
-
-  /** Returns true if all of the entries of the vector are empty. */
-  static bool emptyDatabase(const std::vector<PerVariableDatabase>& vec);
-
-  /** Map from nodes to arithvars. */
-  const ArithVariables& d_avariables;
-
-  const ArithVariables& getArithVariables() const{
-    return d_avariables;
-  }
-
-  ArithCongruenceManager& d_congruenceManager;
-
-  /** Owned by the TheoryArithPrivate, used here. */
-  EagerProofGenerator* d_pfGen;
-  /** Owned by the TheoryArithPrivate, used here. */
-  ProofNodeManager* d_pnm;
-
-  RaiseConflict d_raiseConflict;
-
-
-  const Rational d_one;
-  const Rational d_negOne;
-
-  friend class Constraint;
-
- public:
-  ConstraintDatabase(Env& env,
-                     const ArithVariables& variables,
-                     ArithCongruenceManager& dm,
-                     RaiseConflict conflictCallBack,
-                     EagerProofGenerator* pfGen);
-
-  ~ConstraintDatabase();
-
-  /** Adds a literal to the database. */
-  ConstraintP addLiteral(TNode lit);
-
-  /**
-   * If hasLiteral() is true, returns the constraint.
-   * Otherwise, returns NullConstraint.
-   */
-  ConstraintP lookup(TNode literal) const;
-
-  /**
-   * Returns true if the literal has been added to the database.
-   * This is a hash table lookup.
-   * It does not look in the database for an equivalent corresponding constraint.
-   */
-  bool hasLiteral(TNode literal) const;
-
-  bool hasMorePropagations() const{
-    return !d_toPropagate.empty();
-  }
-
-  ConstraintCP nextPropagation(){
-    Assert(hasMorePropagations());
-
-    ConstraintCP p = d_toPropagate.front();
-    d_toPropagate.pop();
-
-    return p;
-  }
-
-  void addVariable(ArithVar v);
-  bool variableDatabaseIsSetup(ArithVar v) const;
-  void removeVariable(ArithVar v);
-
-  /** Get an explanation and proof for this constraint from the equality engine
-   */
-  TrustNode eeExplain(ConstraintCP c) const;
-  /** Get an explanation for this constraint from the equality engine */
-  void eeExplain(ConstraintCP c, NodeBuilder& nb) const;
-
-  /**
-   * Returns a constraint with the variable v, the constraint type t, and a value
-   * dominated by r (explained below) if such a constraint exists in the database.
-   * If no such constraint exists, NullConstraint is returned.
-   *
-   * t must be either UpperBound or LowerBound.
-   * The returned value v is dominated:
-   *  If t is UpperBound, r <= v
-   *  If t is LowerBound, r >= v
-   *
-   * variableDatabaseIsSetup(v) must be true.
-   */
-  ConstraintP getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const;
-
-  /** Returns the constraint, if it exists */
-  ConstraintP lookupConstraint(ArithVar v, ConstraintType t, const DeltaRational& r) const;
-
-  /**
-   * Returns a constraint with the variable v, the constraint type t and the value r.
-   * If there is such a constraint in the database already, it is returned.
-   * If there is no such constraint, this constraint is added to the database.
-   *
-   */
-  ConstraintP getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r);
-
-  /**
-   * Returns a constraint of the given type for the value and variable
-   * for the given ValueCollection, vc.
-   * This is made if there is no such constraint.
-   */
-  ConstraintP ensureConstraint(ValueCollection& vc, ConstraintType t);
-
-
-  void deleteConstraintAndNegation(ConstraintP c);
-
-  /** Given constraints `a` and `b` such that `a OR b` by unate reasoning,
-   *  adds a TrustNode to `out` which proves `a OR b` as a lemma.
-   *
-   *  Example: `x <= 5` OR `5 <= x`.
-   */
-  void proveOr(std::vector<TrustNode>& out,
-               ConstraintP a,
-               ConstraintP b,
-               bool negateSecond) const;
-  /** Given constraints `a` and `b` such that `a` implies `b` by unate
-   * reasoning, adds a TrustNode to `out` which proves `-a OR b` as a lemma.
-   *
-   *  Example: `x >= 5` -> `x >= 4`.
-   */
-  void implies(std::vector<TrustNode>& out, ConstraintP a, ConstraintP b) const;
-  /** Given constraints `a` and `b` such that `not(a AND b)` by unate reasoning,
-   *  adds a TrustNode to `out` which proves `-a OR -b` as a lemma.
-   *
-   *  Example: `x >= 4` -> `x <= 3`.
-   */
-  void mutuallyExclusive(std::vector<TrustNode>& out,
-                         ConstraintP a,
-                         ConstraintP b) const;
-
-  /**
-   * Outputs a minimal set of unate implications onto the vector for the variable.
-   * This outputs lemmas of the general forms
-   *     (= p c) implies (<= p d) for c < d, or
-   *     (= p c) implies (not (= p d)) for c != d.
-   */
-  void outputUnateEqualityLemmas(std::vector<TrustNode>& lemmas) const;
-  void outputUnateEqualityLemmas(std::vector<TrustNode>& lemmas,
-                                 ArithVar v) const;
-
-  /**
-   * Outputs a minimal set of unate implications onto the vector for the variable.
-   *
-   * If ineqs is true, this outputs lemmas of the general form
-   *     (<= p c) implies (<= p d) for c < d.
-   */
-  void outputUnateInequalityLemmas(std::vector<TrustNode>& lemmas) const;
-  void outputUnateInequalityLemmas(std::vector<TrustNode>& lemmas,
-                                   ArithVar v) const;
-
-  void unatePropLowerBound(ConstraintP curr, ConstraintP prev);
-  void unatePropUpperBound(ConstraintP curr, ConstraintP prev);
-  void unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB);
-
-  /** AntecendentID must be in range. */
-  ConstraintCP getAntecedent(AntecedentId p) const;
-
-  bool isProofEnabled() const { return d_pnm != nullptr; }
-
- private:
-  /** returns true if cons is now in conflict. */
-  bool handleUnateProp(ConstraintP ant, ConstraintP cons);
-
-  DenseSet d_reclaimable;
-
-  class Statistics {
-  public:
-    IntStat d_unatePropagateCalls;
-    IntStat d_unatePropagateImplications;
-
-    Statistics();
-  } d_statistics;
-
-}; /* ConstraintDatabase */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__CONSTRAINT_H */
diff --git a/src/theory/arith/constraint_forward.h b/src/theory/arith/constraint_forward.h
deleted file mode 100644 (file)
index 5b700ce..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Aina Niemetz
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Forward declarations of the ConstraintValue and ConstraintDatabase
- * classes.
- *
- * This is the forward declarations of the ConstraintValue and
- * ConstraintDatabase and the typedef for Constraint.
- * This is used to break circular dependencies and minimize interaction
- * between header files.
- */
-
-#ifndef CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H
-#define CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H
-
-#include <vector>
-
-#include "cvc5_private.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class Constraint;
-typedef Constraint* ConstraintP;
-typedef const Constraint* ConstraintCP;
-
-static constexpr ConstraintP NullConstraint = nullptr;
-
-class ConstraintDatabase;
-
-typedef std::vector<ConstraintCP> ConstraintCPVec;
-
-typedef std::vector<Rational> RationalVector;
-typedef RationalVector* RationalVectorP;
-typedef const RationalVector* RationalVectorCP;
-static constexpr RationalVectorCP RationalVectorCPSentinel = nullptr;
-static constexpr RationalVectorP RationalVectorPSentinel = nullptr;
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H */
diff --git a/src/theory/arith/cut_log.cpp b/src/theory/arith/cut_log.cpp
deleted file mode 100644 (file)
index 0fc239e..0000000
+++ /dev/null
@@ -1,711 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Andrew Reynolds, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/cut_log.h"
-
-#include <limits.h>
-#include <math.h>
-
-#include <cmath>
-#include <iomanip>
-#include <map>
-
-#include "base/cvc5config.h"
-#include "base/output.h"
-#include "theory/arith/approx_simplex.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/normal_form.h"
-#include "util/ostream_util.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-NodeLog::const_iterator NodeLog::begin() const { return d_cuts.begin(); }
-NodeLog::const_iterator NodeLog::end() const { return d_cuts.end(); }
-
-NodeLog& TreeLog::getNode(int nid) {
-  ToNodeMap::iterator i = d_toNode.find(nid);
-  Assert(i != d_toNode.end());
-  return (*i).second;
-}
-
-TreeLog::const_iterator TreeLog::begin() const { return d_toNode.begin(); }
-TreeLog::const_iterator TreeLog::end() const { return d_toNode.end(); }
-
-int TreeLog::getExecutionOrd(){
-  int res = next_exec_ord;
-  ++next_exec_ord;
-  return res;
-}
-void TreeLog::makeInactive(){  d_active = false; }
-void TreeLog::makeActive(){  d_active = true; }
-bool TreeLog::isActivelyLogging() const { return d_active; }
-
-
-PrimitiveVec::PrimitiveVec()
-  : len(0)
-  , inds(NULL)
-  , coeffs(NULL)
-{}
-
-PrimitiveVec::~PrimitiveVec(){
-  clear();
-}
-bool PrimitiveVec::initialized() const {
-  return inds != NULL;
-}
-void PrimitiveVec::clear() {
-  if(initialized()){
-    delete[] inds;
-    delete[] coeffs;
-    len = 0;
-    inds = NULL;
-    coeffs = NULL;
-  }
-}
-void PrimitiveVec::setup(int l){
-  Assert(!initialized());
-  len = l;
-  inds = new int[1+len];
-  coeffs = new double[1+len];
-}
-void PrimitiveVec::print(std::ostream& out) const{
-  Assert(initialized());
-  StreamFormatScope scope(out);
-
-  out << len << " " << std::setprecision(15);
-  for(int i = 1; i <= len; ++i){
-    out << "["<< inds[i] <<", " << coeffs[i]<<"]";
-  }
-}
-std::ostream& operator<<(std::ostream& os, const PrimitiveVec& pv){
-  pv.print(os);
-  return os;
-}
-
-CutInfo::CutInfo(CutInfoKlass kl, int eid, int o)
-    : d_klass(kl),
-      d_execOrd(eid),
-      d_poolOrd(o),
-      d_cutType(kind::UNDEFINED_KIND),
-      d_cutRhs(),
-      d_cutVec(),
-      d_mAtCreation(-1),
-      d_N(-1),
-      d_rowId(-1),
-      d_exactPrecision(nullptr),
-      d_explanation(nullptr)
-{}
-
-CutInfo::~CutInfo(){
-}
-
-int CutInfo::getId() const {
-  return d_execOrd;
-}
-
-int CutInfo::getRowId() const{
-  return d_rowId;
-}
-
-void CutInfo::setRowId(int rid){
-  d_rowId = rid;
-}
-
-void CutInfo::print(ostream& out) const{
-  out << "[CutInfo " << d_execOrd << " " << d_poolOrd
-      << " " << d_klass << " " << d_cutType << " " << d_cutRhs
-      << " ";
-  d_cutVec.print(out);
-  out << "]" << endl;
-}
-
-PrimitiveVec& CutInfo::getCutVector(){
-  return d_cutVec;
-}
-
-const PrimitiveVec& CutInfo::getCutVector() const{
-  return d_cutVec;
-}
-
-// void CutInfo::init_cut(int l){
-//   cut_vec.setup(l);
-// }
-
-Kind CutInfo::getKind() const{
-  return d_cutType;
-}
-
-void CutInfo::setKind(Kind k){
-  Assert(k == kind::LEQ || k == kind::GEQ);
-  d_cutType = k;
-}
-
-double CutInfo::getRhs() const{
-  return d_cutRhs;
-}
-
-void CutInfo::setRhs(double r){
-  d_cutRhs = r;
-}
-
-bool CutInfo::reconstructed() const { return d_exactPrecision != nullptr; }
-
-CutInfoKlass CutInfo::getKlass() const{
-  return d_klass;
-}
-
-int CutInfo::poolOrdinal() const{
-  return d_poolOrd;
-}
-
-void CutInfo::setDimensions(int N, int M){
-  d_mAtCreation = M;
-  d_N = N;
-}
-
-int CutInfo::getN() const{
-  return d_N;
-}
-
-int CutInfo::getMAtCreation() const{
-  return d_mAtCreation;
-}
-
-/* Returns true if the cut has an explanation. */
-bool CutInfo::proven() const { return d_explanation != nullptr; }
-
-bool CutInfo::operator<(const CutInfo& o) const{
-  return d_execOrd < o.d_execOrd;
-}
-
-
-void CutInfo::setReconstruction(const DenseVector& ep){
-  Assert(!reconstructed());
-  d_exactPrecision.reset(new DenseVector(ep));
-}
-
-void CutInfo::setExplanation(const ConstraintCPVec& ex){
-  Assert(reconstructed());
-  if (d_explanation == nullptr)
-  {
-    d_explanation.reset(new ConstraintCPVec(ex));
-  }
-  else
-  {
-    *d_explanation = ex;
-  }
-}
-
-void CutInfo::swapExplanation(ConstraintCPVec& ex){
-  Assert(reconstructed());
-  Assert(!proven());
-  if (d_explanation == nullptr)
-  {
-    d_explanation.reset(new ConstraintCPVec());
-  }
-  d_explanation->swap(ex);
-}
-
-const DenseVector& CutInfo::getReconstruction() const {
-  Assert(reconstructed());
-  return *d_exactPrecision;
-}
-
-void CutInfo::clearReconstruction(){
-  if(proven()){
-    d_explanation = nullptr;
-  }
-
-  if(reconstructed()){
-    d_exactPrecision = nullptr;
-  }
-
-  Assert(!reconstructed());
-  Assert(!proven());
-}
-
-const ConstraintCPVec& CutInfo::getExplanation() const {
-  Assert(proven());
-  return *d_explanation;
-}
-
-std::ostream& operator<<(std::ostream& os, const CutInfo& ci){
-  ci.print(os);
-  return os;
-}
-
-std::ostream& operator<<(std::ostream& out, CutInfoKlass kl){
-  switch(kl){
-  case MirCutKlass:
-    out << "MirCutKlass"; break;
-  case GmiCutKlass:
-    out << "GmiCutKlass"; break;
-  case BranchCutKlass:
-    out << "BranchCutKlass"; break;
-  case RowsDeletedKlass:
-    out << "RowDeletedKlass"; break;
-  case UnknownKlass:
-    out << "UnknownKlass"; break;
-  default:
-    out << "unexpected CutInfoKlass"; break;
-  }
-  return out;
-}
-bool NodeLog::isBranch() const{
-  return d_brVar >= 0;
-}
-
-NodeLog::NodeLog()
-  : d_nid(-1)
-  , d_parent(NULL)
-  , d_tl(NULL)
-  , d_cuts()
-  , d_rowIdsSelected()
-  , d_stat(Open)
-  , d_brVar(-1)
-  , d_brVal(0.0)
-  , d_downId(-1)
-  , d_upId(-1)
-  , d_rowId2ArithVar()
-{}
-
-NodeLog::NodeLog(TreeLog* tl, int node, const RowIdMap& m)
-  : d_nid(node)
-  , d_parent(NULL)
-  , d_tl(tl)
-  , d_cuts()
-  , d_rowIdsSelected()
-  , d_stat(Open)
-  , d_brVar(-1)
-  , d_brVal(0.0)
-  , d_downId(-1)
-  , d_upId(-1)
-  , d_rowId2ArithVar(m)
-{}
-
-NodeLog::NodeLog(TreeLog* tl, NodeLog* parent, int node)
-  : d_nid(node)
-  , d_parent(parent)
-  , d_tl(tl)
-  , d_cuts()
-  , d_rowIdsSelected()
-  , d_stat(Open)
-  , d_brVar(-1)
-  , d_brVal(0.0)
-  , d_downId(-1)
-  , d_upId(-1)
-  , d_rowId2ArithVar()
-{}
-
-NodeLog::~NodeLog(){
-  CutSet::iterator i = d_cuts.begin(), iend = d_cuts.end();
-  for(; i != iend; ++i){
-    CutInfo* c = *i;
-    delete c;
-  }
-  d_cuts.clear();
-  Assert(d_cuts.empty());
-}
-
-std::ostream& operator<<(std::ostream& os, const NodeLog& nl){
-  nl.print(os);
-  return os;
-}
-
-void NodeLog::copyParentRowIds() {
-  Assert(d_parent != NULL);
-  d_rowId2ArithVar = d_parent->d_rowId2ArithVar;
-}
-
-int NodeLog::branchVariable() const {
-  return d_brVar;
-}
-double NodeLog::branchValue() const{
-  return d_brVal;
-}
-int NodeLog::getNodeId() const {
-  return d_nid;
-}
-int NodeLog::getDownId() const{
-  return d_downId;
-}
-int NodeLog::getUpId() const{
-  return d_upId;
-}
-void NodeLog::addSelected(int ord, int sel){
-  Assert(d_rowIdsSelected.find(ord) == d_rowIdsSelected.end());
-  d_rowIdsSelected[ord] = sel;
-  Trace("approx::nodelog") << "addSelected("<< ord << ", "<< sel << ")" << endl;
-}
-void NodeLog::applySelected() {
-  CutSet::iterator iter = d_cuts.begin(), iend = d_cuts.end(), todelete;
-  while(iter != iend){
-    CutInfo* curr = *iter;
-    int poolOrd = curr->poolOrdinal();
-    if(curr->getRowId() >= 0 ){
-      // selected previously, kip
-      ++iter;
-    }else if(curr->getKlass() == RowsDeletedKlass){
-      // skip
-      ++iter;
-    }else if(curr->getKlass() == BranchCutKlass){
-      // skip
-      ++iter;
-    }else if(d_rowIdsSelected.find(poolOrd) == d_rowIdsSelected.end()){
-      todelete = iter;
-      ++iter;
-      d_cuts.erase(todelete);
-      delete curr;
-    }else{
-      Trace("approx::nodelog") << "applySelected " << curr->getId() << " " << poolOrd << "->" << d_rowIdsSelected[poolOrd] << endl;
-      curr->setRowId( d_rowIdsSelected[poolOrd] );
-      ++iter;
-    }
-  }
-  d_rowIdsSelected.clear();
-}
-
-void NodeLog::applyRowsDeleted(const RowsDeleted& rd) {
-  std::map<int, CutInfo*> currInOrd; //sorted
-
-  const PrimitiveVec& cv = rd.getCutVector();
-  std::vector<int> sortedRemoved (cv.inds+1, cv.inds+cv.len+1);
-  sortedRemoved.push_back(INT_MAX);
-  std::sort(sortedRemoved.begin(), sortedRemoved.end());
-
-  if(TraceIsOn("approx::nodelog")){
-    Trace("approx::nodelog") << "Removing #" << sortedRemoved.size()<< "...";
-    for(unsigned k = 0; k<sortedRemoved.size(); k++){
-      Trace("approx::nodelog") << ", " << sortedRemoved[k];
-    }
-    Trace("approx::nodelog") << endl;
-    Trace("approx::nodelog") << "cv.len" << cv.len  << endl;
-  }
-
-  int min = sortedRemoved.front();
-
-  CutSet::iterator iter = d_cuts.begin(), iend = d_cuts.end();
-  while(iter != iend){
-    CutInfo* curr= *iter;
-    if(curr->getId() < rd.getId()){
-      if(d_rowId2ArithVar.find(curr->getRowId()) != d_rowId2ArithVar.end()){
-        if(curr->getRowId() >= min){
-          currInOrd.insert(make_pair(curr->getRowId(), curr));
-        }
-      }
-    }
-    ++iter;
-  }
-
-  RowIdMap::const_iterator i, end;
-  i=d_rowId2ArithVar.begin(), end = d_rowId2ArithVar.end();
-  for(; i != end; ++i){
-    int key = (*i).first;
-    if(key >= min){
-      if(currInOrd.find(key) == currInOrd.end()){
-        CutInfo* null = NULL;
-        currInOrd.insert(make_pair(key, null));
-      }
-    }
-  }
-
-
-
-  std::map<int, CutInfo*>::iterator j, jend;
-
-  int posInSorted = 0;
-  for(j = currInOrd.begin(), jend=currInOrd.end(); j!=jend; ++j){
-    int origOrd = (*j).first;
-    ArithVar v = d_rowId2ArithVar[origOrd];
-    int headRemovedOrd = sortedRemoved[posInSorted];
-    while(headRemovedOrd < origOrd){
-      ++posInSorted;
-      headRemovedOrd  = sortedRemoved[posInSorted];
-    }
-    // headRemoveOrd >= origOrd
-    Assert(headRemovedOrd >= origOrd);
-
-    CutInfo* ci = (*j).second;
-    if(headRemovedOrd == origOrd){
-
-      if(ci == NULL){
-        Trace("approx::nodelog") << "deleting from above because of " << rd << endl;
-        Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
-        d_rowId2ArithVar.erase(origOrd);
-      }else{
-        Trace("approx::nodelog") << "deleting " << ci << " because of " << rd << endl;
-        Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
-        d_rowId2ArithVar.erase(origOrd);
-        ci->setRowId(-1);
-      }
-    }else{
-      Assert(headRemovedOrd > origOrd);
-      // headRemoveOrd > origOrd
-      int newOrd = origOrd - posInSorted;
-      Assert(newOrd > 0);
-      if(ci == NULL){
-        Trace("approx::nodelog") << "shifting above down due to " << rd << endl;
-        Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
-        Trace("approx::nodelog") << "now have " << newOrd << " <-> " << v << endl;
-        d_rowId2ArithVar.erase(origOrd);
-        mapRowId(newOrd, v);
-      }else{
-        Trace("approx::nodelog") << "shifting " << ci << " down due to " << rd << endl;
-        Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
-        Trace("approx::nodelog") << "now have " << newOrd << " <-> " << v << endl;
-        ci->setRowId(newOrd);
-        d_rowId2ArithVar.erase(origOrd);
-        mapRowId(newOrd, v);
-      }
-    }
-  }
-
-}
-
-// void NodeLog::adjustRowId(CutInfo& ci, const RowsDeleted& rd) {
-//   int origRowId = ci.getRowId();
-//   int newRowId = ci.getRowId();
-//   ArithVar v = d_rowId2ArithVar[origRowId];
-
-//   const PrimitiveVec& cv = rd.getCutVector();
-
-//   for(int j = 1, N = cv.len; j <= N; j++){
-//     int ind = cv.inds[j];
-//     if(ind == origRowId){
-//       newRowId = -1;
-//       break;
-//     }else if(ind < origRowId){
-//       newRowId--;
-//     }
-//   }
-
-//   if(newRowId < 0){
-//     cout << "deleting " << ci << " because of " << rd << endl;
-//     cout << "had " << origRowId << " <-> " << v << endl;
-//     d_rowId2ArithVar.erase(origRowId);
-//     ci.setRowId(-1);
-//   }else if(newRowId != origRowId){
-//     cout << "adjusting " << ci << " because of " << rd << endl;
-//     cout << "had " << origRowId << " <-> " << v << endl;
-//     cout << "now have " << newRowId << " <-> " << v << endl;
-//     d_rowId2ArithVar.erase(origRowId);
-//     ci.setRowId(newRowId);
-//     mapRowId(newRowId, v);
-//   }else{
-//     cout << "row id unchanged " << ci << " because of " << rd << endl;
-//   }
-// }
-
-
-ArithVar NodeLog::lookupRowId(int rowId) const{
-  RowIdMap::const_iterator i = d_rowId2ArithVar.find(rowId);
-  if(i == d_rowId2ArithVar.end()){
-    return ARITHVAR_SENTINEL;
-  }else{
-    return (*i).second;
-  }
-}
-
-void NodeLog::mapRowId(int rowId, ArithVar v){
-  Assert(lookupRowId(rowId) == ARITHVAR_SENTINEL);
-  Trace("approx::nodelog")
-    << "On " << getNodeId()
-    << " adding row id " << rowId << " <-> " << v << endl;
-  d_rowId2ArithVar[rowId] = v;
-}
-
-
-
-void NodeLog::addCut(CutInfo* ci){
-  Assert(ci != NULL);
-  d_cuts.insert(ci);
-}
-
-void NodeLog::print(ostream& o) const{
-  o << "[n" << getNodeId();
-  for(const_iterator iter = begin(), iend = end(); iter != iend; ++iter ){
-    CutInfo* cut = *iter;
-    o << ", " << cut->poolOrdinal();
-    if(cut->getRowId() >= 0){
-      o << " " << cut->getRowId();
-    }
-  }
-  o << "]" << std::endl;
-}
-
-void NodeLog::closeNode(){
-  Assert(d_stat == Open);
-  d_stat = Closed;
-}
-
-void NodeLog::setBranch(int br, double val, int d, int u){
-  Assert(d_stat == Open);
-  d_brVar = br;
-  d_brVal = val;
-  d_downId = d;
-  d_upId = u;
-  d_stat = Branched;
-}
-
-TreeLog::TreeLog()
-  : next_exec_ord(0)
-  , d_toNode()
-  , d_branches()
-  , d_numCuts(0)
-  , d_active(false)
-{
-  NodeLog::RowIdMap empty;
-  reset(empty);
-}
-
-int TreeLog::getRootId() const{
-  return 1;
-}
-
-NodeLog& TreeLog::getRootNode(){
-  return getNode(getRootId());
-}
-
-void TreeLog::clear(){
-  next_exec_ord = 0;
-  d_toNode.clear();
-  d_branches.purge();
-
-  d_numCuts = 0;
-
-  // add root
-}
-
-void TreeLog::reset(const NodeLog::RowIdMap& m){
-  clear();
-  d_toNode.insert(make_pair(getRootId(), NodeLog(this, getRootId(), m)));
-}
-
-void TreeLog::addCut(){ d_numCuts++; }
-uint32_t TreeLog::cutCount() const { return d_numCuts; }
-void TreeLog::logBranch(uint32_t x){
-  d_branches.add(x);
-}
-uint32_t TreeLog::numBranches(uint32_t x){
-  return d_branches.count(x);
-}
-
-void TreeLog::branch(int nid, int br, double val, int dn, int up){
-  NodeLog& nl = getNode(nid);
-  nl.setBranch(br, val, dn, up);
-
-  d_toNode.insert(make_pair(dn, NodeLog(this, &nl, dn)));
-  d_toNode.insert(make_pair(up, NodeLog(this, &nl, up)));
-}
-
-void TreeLog::close(int nid){
-  NodeLog& nl = getNode(nid);
-  nl.closeNode();
-}
-
-
-
-// void TreeLog::applySelected() {
-//   std::map<int, NodeLog>::iterator iter, end;
-//   for(iter = d_toNode.begin(), end = d_toNode.end(); iter != end; ++iter){
-//     NodeLog& onNode = (*iter).second;
-//     //onNode.applySelected();
-//   }
-// }
-
-void TreeLog::print(ostream& o) const{
-  o << "TreeLog: " << d_toNode.size() << std::endl;
-  for(const_iterator iter = begin(), iend = end(); iter != iend; ++iter){
-    const NodeLog& onNode = (*iter).second;
-    onNode.print(o);
-  }
-}
-
-void TreeLog::applyRowsDeleted(int nid, const RowsDeleted& rd){
-  NodeLog& nl = getNode(nid);
-  nl.applyRowsDeleted(rd);
-}
-
-void TreeLog::mapRowId(int nid, int ind, ArithVar v){
-  NodeLog& nl = getNode(nid);
-  nl.mapRowId(ind, v);
-}
-
-void DenseVector::purge() {
-  lhs.purge();
-  rhs = Rational(0);
-}
-
-RowsDeleted::RowsDeleted(int execOrd, int nrows, const int num[])
-  : CutInfo(RowsDeletedKlass, execOrd, 0)
-{
-  d_cutVec.setup(nrows);
-  for(int j=1; j <= nrows; j++){
-    d_cutVec.coeffs[j] = 0;
-    d_cutVec.inds[j] = num[j];
-  }
-}
-
-BranchCutInfo::BranchCutInfo(int execOrd, int br, Kind dir, double val)
-  : CutInfo(BranchCutKlass, execOrd, 0)
-{
-  d_cutVec.setup(1);
-  d_cutVec.inds[1] = br;
-  d_cutVec.coeffs[1] = +1.0;
-  d_cutRhs = val;
-  d_cutType = dir;
-}
-
-void TreeLog::printBranchInfo(ostream& os) const{
-  uint32_t total = 0;
-  DenseMultiset::const_iterator iter = d_branches.begin(),  iend = d_branches.end();
-  for(; iter != iend; ++iter){
-    uint32_t el = *iter;
-    total += el;
-  }
-  os << "printBranchInfo() : " << total << endl;
-  iter = d_branches.begin(),  iend = d_branches.end();
-  for(; iter != iend; ++iter){
-    uint32_t el = *iter;
-    os << "["<<el <<", " << d_branches.count(el) << "]";
-  }
-  os << endl;
-}
-
-
-void DenseVector::print(std::ostream& os) const {
-  os << rhs << " + ";
-  print(os, lhs);
-}
-void DenseVector::print(ostream& out, const DenseMap<Rational>& v){
-  out << "[DenseVec len " <<  v.size();
-  DenseMap<Rational>::const_iterator iter, end;
-  for(iter = v.begin(), end = v.end(); iter != end; ++iter){
-    ArithVar x = *iter;
-    out << ", "<< x << " " << v[x];
-  }
-  out << "]";
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/cut_log.h b/src/theory/arith/cut_log.h
deleted file mode 100644 (file)
index 2cf3eac..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Morgan Deters, Kshitij Bansal
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <map>
-#include <memory>
-#include <set>
-#include <unordered_map>
-
-#include "expr/kind.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/constraint_forward.h"
-#include "util/dense_map.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/** A low level vector of indexed doubles. */
-struct PrimitiveVec {
-  int len;
-  int* inds;
-  double* coeffs;
-  PrimitiveVec();
-  ~PrimitiveVec();
-  bool initialized() const;
-  void clear();
-  void setup(int l);
-  void print(std::ostream& out) const;
-};
-std::ostream& operator<<(std::ostream& os, const PrimitiveVec& pv);
-
-struct DenseVector {
-  DenseMap<Rational> lhs;
-  Rational rhs;
-  void purge();
-  void print(std::ostream& os) const;
-
-  static void print(std::ostream& os, const DenseMap<Rational>& lhs);
-};
-
-/** The different kinds of cuts. */
-enum CutInfoKlass{ MirCutKlass, GmiCutKlass, BranchCutKlass,
-                   RowsDeletedKlass,
-                   UnknownKlass};
-std::ostream& operator<<(std::ostream& os, CutInfoKlass kl);
-
-/** A general class for describing a cut. */
-class CutInfo {
-protected:
-  CutInfoKlass d_klass;
-  int d_execOrd;
-
-  int d_poolOrd;    /* cut's ordinal in the current node pool */
-  Kind d_cutType;   /* Lowerbound, upperbound or undefined. */
-  double d_cutRhs; /* right hand side of the cut */
-  PrimitiveVec d_cutVec; /* vector of the cut */
-
-  /**
-   * The number of rows at the time the cut was made.
-   * This is required to descramble indices after the fact!
-   */
-  int d_mAtCreation;
-
-  /** This is the number of structural variables. */
-  int d_N;
-
-  /** if selected, make this non-zero */
-  int d_rowId;
-
-  /* If the cut has been successfully created,
-   * the cut is stored in exact precision in d_exactPrecision.
-   * If the cut has not yet been proven, this is null.
-   */
-  std::unique_ptr<DenseVector> d_exactPrecision;
-
-  std::unique_ptr<ConstraintCPVec> d_explanation;
-
- public:
-  CutInfo(CutInfoKlass kl, int cutid, int ordinal);
-
-  virtual ~CutInfo();
-
-  int getId() const;
-
-  int getRowId() const;
-  void setRowId(int rid);
-
-  void print(std::ostream& out) const;
-  //void init_cut(int l);
-  PrimitiveVec& getCutVector();
-  const PrimitiveVec& getCutVector() const;
-
-  Kind getKind() const;
-  void setKind(Kind k);
-
-
-  void setRhs(double r);
-  double getRhs() const;
-
-  CutInfoKlass getKlass() const;
-  int poolOrdinal() const;
-
-  void setDimensions(int N, int M);
-  int getN() const;
-  int getMAtCreation() const;
-
-  bool operator<(const CutInfo& o) const;
-
-  /* Returns true if the cut was successfully made in exact precision.*/
-  bool reconstructed() const;
-
-  /* Returns true if the cut has an explanation. */
-  bool proven() const;
-
-  void setReconstruction(const DenseVector& ep);
-  void setExplanation(const ConstraintCPVec& ex);
-  void swapExplanation(ConstraintCPVec& ex);
-
-  const DenseVector& getReconstruction() const;
-  const ConstraintCPVec& getExplanation() const;
-
-  void clearReconstruction();
-};
-std::ostream& operator<<(std::ostream& os, const CutInfo& ci);
-
-class BranchCutInfo : public CutInfo {
-public:
-  BranchCutInfo(int execOrd, int br,  Kind dir, double val);
-};
-
-class RowsDeleted : public CutInfo {
-public:
-  RowsDeleted(int execOrd, int nrows, const int num[]);
-};
-
-class TreeLog;
-
-class NodeLog {
-private:
-  int d_nid;
-  NodeLog* d_parent; /* If null this is the root */
-  TreeLog* d_tl;     /* TreeLog containing the node. */
-
-  struct CmpCutPointer{
-    int operator()(const CutInfo* a, const CutInfo* b) const{
-      return *a < *b;
-    }
-  };
-  typedef std::set<CutInfo*, CmpCutPointer> CutSet;
-  CutSet d_cuts;
-  std::map<int, int> d_rowIdsSelected;
-
-  enum Status {Open, Closed, Branched};
-  Status d_stat;
-
-  int d_brVar; // branching variable
-  double d_brVal;
-  int d_downId;
-  int d_upId;
-
-public:
-  typedef std::unordered_map<int, ArithVar> RowIdMap;
-private:
-  RowIdMap d_rowId2ArithVar;
-
-public:
-  NodeLog(); /* default constructor. */
-  NodeLog(TreeLog* tl, int node, const RowIdMap& m); /* makes a root node. */
-  NodeLog(TreeLog* tl, NodeLog* parent, int node);/* makes a non-root node. */
-
-  ~NodeLog();
-
-  int getNodeId() const;
-  void addSelected(int ord, int sel);
-  void applySelected();
-  void addCut(CutInfo* ci);
-  void print(std::ostream& o) const;
-
-  bool isRoot() const;
-  const NodeLog& getParent() const;
-
-  void copyParentRowIds();
-
-  bool isBranch() const;
-  int branchVariable() const;
-  double branchValue() const;
-
-  typedef CutSet::const_iterator const_iterator;
-  const_iterator begin() const;
-  const_iterator end() const;
-
-  void setBranch(int br, double val, int dn, int up);
-  void closeNode();
-
-  int getDownId() const;
-  int getUpId() const;
-
-  /**
-   * Looks up a row id to the appropriate arith variable.
-   * Be careful these are deleted in context during replay!
-   * failure returns ARITHVAR_SENTINEL */
-  ArithVar lookupRowId(int rowId) const;
-
-  /**
-   * Maps a row id to an arithvar.
-   * Be careful these are deleted in context during replay!
-   */
-  void mapRowId(int rowid, ArithVar v);
-  void applyRowsDeleted(const RowsDeleted& rd);
-
-};
-std::ostream& operator<<(std::ostream& os, const NodeLog& nl);
-
-class TreeLog {
-private:
-  int next_exec_ord;
-  typedef std::map<int, NodeLog> ToNodeMap;
-  ToNodeMap d_toNode;
-  DenseMultiset d_branches;
-
-  uint32_t d_numCuts;
-
-  bool d_active;
-
-public:
-  TreeLog();
-
-  NodeLog& getNode(int nid);
-  void branch(int nid, int br, double val, int dn, int up);
-  void close(int nid);
-
-  //void applySelected();
-  void print(std::ostream& o) const;
-
-  typedef ToNodeMap::const_iterator const_iterator;
-  const_iterator begin() const;
-  const_iterator end() const;
-
-  int getExecutionOrd();
-
-  void reset(const NodeLog::RowIdMap& m);
-
-  // Applies rd tp to the node with id nid
-  void applyRowsDeleted(int nid, const RowsDeleted& rd);
-
-  // Synonym for getNode(nid).mapRowId(ind, v)
-  void mapRowId(int nid, int ind, ArithVar v);
-
-private:
-  void clear();
-
-public:
-  void makeInactive();
-  void makeActive();
-
-  bool isActivelyLogging() const;
-
-  void addCut();
-  uint32_t cutCount() const;
-
-  void logBranch(uint32_t x);
-  uint32_t numBranches(uint32_t x);
-
-  int getRootId() const;
-
-  uint32_t numNodes() const{
-    return d_toNode.size();
-  }
-
-  NodeLog& getRootNode();
-  void printBranchInfo(std::ostream& os) const;
-};
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/dio_solver.cpp b/src/theory/arith/dio_solver.cpp
deleted file mode 100644 (file)
index 8e5f636..0000000
+++ /dev/null
@@ -1,832 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Diophantine equation solver
- *
- * A Diophantine equation solver for the theory of arithmetic.
- */
-#include "theory/arith/dio_solver.h"
-
-#include <iostream>
-
-#include "base/output.h"
-#include "expr/skolem_manager.h"
-#include "options/arith_options.h"
-#include "smt/env.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/partial_model.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-inline Node makeIntegerVariable(){
-  NodeManager* nm = NodeManager::currentNM();
-  SkolemManager* sm = nm->getSkolemManager();
-  return sm->mkDummySkolem("intvar",
-                           nm->integerType(),
-                           "is an integer variable created by the dio solver");
-}
-
-DioSolver::DioSolver(Env& env)
-    : EnvObj(env),
-      d_lastUsedProofVariable(context(), 0),
-      d_inputConstraints(context()),
-      d_nextInputConstraintToEnqueue(context(), 0),
-      d_trail(context()),
-      d_subs(context()),
-      d_currentF(),
-      d_savedQueue(context()),
-      d_savedQueueIndex(context(), 0),
-      d_conflictIndex(context()),
-      d_maxInputCoefficientLength(context(), 0),
-      d_usedDecomposeIndex(context(), false),
-      d_lastPureSubstitution(context(), 0),
-      d_pureSubstitionIter(context(), 0),
-      d_decompositionLemmaQueue(context())
-{
-}
-
-DioSolver::Statistics::Statistics()
-    : d_conflictCalls(smtStatisticsRegistry().registerInt(
-        "theory::arith::dio::conflictCalls")),
-      d_cutCalls(
-          smtStatisticsRegistry().registerInt("theory::arith::dio::cutCalls")),
-      d_cuts(smtStatisticsRegistry().registerInt("theory::arith::dio::cuts")),
-      d_conflicts(
-          smtStatisticsRegistry().registerInt("theory::arith::dio::conflicts")),
-      d_conflictTimer(smtStatisticsRegistry().registerTimer(
-          "theory::arith::dio::conflictTimer")),
-      d_cutTimer(
-          smtStatisticsRegistry().registerTimer("theory::arith::dio::cutTimer"))
-{
-}
-
-bool DioSolver::queueConditions(TrailIndex t){
-  Trace("queueConditions") << !inConflict() << std::endl;
-  Trace("queueConditions") << gcdIsOne(t) << std::endl;
-  Trace("queueConditions") << !debugAnySubstitionApplies(t) << std::endl;
-  Trace("queueConditions") << !triviallySat(t) << std::endl;
-  Trace("queueConditions") << !triviallyUnsat(t) << std::endl;
-
-  return
-    !inConflict() &&
-    gcdIsOne(t) &&
-    !debugAnySubstitionApplies(t) &&
-    !triviallySat(t) &&
-    !triviallyUnsat(t);
-}
-
-size_t DioSolver::allocateProofVariable() {
-  Assert(d_lastUsedProofVariable <= d_proofVariablePool.size());
-  if(d_lastUsedProofVariable == d_proofVariablePool.size()){
-    Assert(d_lastUsedProofVariable == d_proofVariablePool.size());
-    Node intVar = makeIntegerVariable();
-    d_proofVariablePool.push_back(Variable(intVar));
-  }
-  size_t res = d_lastUsedProofVariable;
-  d_lastUsedProofVariable = d_lastUsedProofVariable + 1;
-  return res;
-}
-
-
-Node DioSolver::nextPureSubstitution(){
-  Assert(hasMorePureSubstitutions());
-  SubIndex curr = d_pureSubstitionIter;
-  d_pureSubstitionIter = d_pureSubstitionIter + 1;
-
-  Assert(d_subs[curr].d_fresh.isNull());
-  Variable v = d_subs[curr].d_eliminated;
-
-  SumPair sp = d_trail[d_subs[curr].d_constraint].d_eq;
-  Polynomial p = sp.getPolynomial();
-  Constant c = -sp.getConstant();
-  Polynomial cancelV = p + Polynomial::mkPolynomial(v);
-  Node eq = NodeManager::currentNM()->mkNode(kind::EQUAL, v.getNode(), cancelV.getNode());
-  return eq;
-}
-
-
-bool DioSolver::debugEqualityInInputEquations(Node eq){
-  typedef context::CDList<InputConstraint>::const_iterator const_iterator;
-  const_iterator i=d_inputConstraints.begin(), end = d_inputConstraints.end();
-  for(; i != end; ++i){
-    Node reason_i = (*i).d_reason;
-    if(eq == reason_i){
-      return true;
-    }
-  }
-  return false;
-}
-
-
-void DioSolver::pushInputConstraint(const Comparison& eq, Node reason){
-  Assert(!debugEqualityInInputEquations(reason));
-  Assert(eq.debugIsIntegral());
-  Assert(eq.getNode().getKind() == kind::EQUAL);
-
-  SumPair sp = eq.toSumPair();
-  if(sp.isNonlinear()){
-    return;
-  }
-
-
-
-  uint32_t length = sp.maxLength();
-  if(length > d_maxInputCoefficientLength){
-    d_maxInputCoefficientLength = length;
-  }
-
-  size_t varIndex = allocateProofVariable();
-  Variable proofVariable(d_proofVariablePool[varIndex]);
-  //Variable proofVariable(makeIntegerVariable());
-
-  TrailIndex posInTrail = d_trail.size();
-  Trace("dio::pushInputConstraint") << "pushInputConstraint @ " << posInTrail
-                                    << " " << eq.getNode()
-                                    << " " << reason << endl;
-  d_trail.push_back(Constraint(sp,Polynomial::mkPolynomial(proofVariable)));
-
-  size_t posInConstraintList = d_inputConstraints.size();
-  d_inputConstraints.push_back(InputConstraint(reason, posInTrail));
-
-  d_varToInputConstraintMap[proofVariable.getNode()] = posInConstraintList;
-}
-
-
-DioSolver::TrailIndex DioSolver::scaleEqAtIndex(DioSolver::TrailIndex i, const Integer& g){
-  Assert(g != 0);
-  Constant invg = Constant::mkConstant(Rational(Integer(1),g));
-  const SumPair& sp = d_trail[i].d_eq;
-  const Polynomial& proof = d_trail[i].d_proof;
-
-  SumPair newSP = sp * invg;
-  Polynomial newProof = proof * invg;
-
-  Assert(newSP.isIntegral());
-  Assert(newSP.gcd() == 1);
-
-  TrailIndex j = d_trail.size();
-
-  d_trail.push_back(Constraint(newSP, newProof));
-
-  Trace("arith::dio") << "scaleEqAtIndex(" << i <<","<<g<<")"<<endl;
-  Trace("arith::dio") << "derived "<< newSP.getNode()
-                      <<" with proof " << newProof.getNode() << endl;
-  return j;
-}
-
-Node DioSolver::proveIndex(TrailIndex i){
-  Assert(inRange(i));
-  const Polynomial& proof = d_trail[i].d_proof;
-  Assert(!proof.isConstant());
-
-  NodeBuilder nb(kind::AND);
-  for(Polynomial::iterator iter = proof.begin(), end = proof.end(); iter!= end; ++iter){
-    Monomial m = (*iter);
-    Assert(!m.isConstant());
-    VarList vl = m.getVarList();
-    Assert(vl.singleton());
-    Variable v = vl.getHead();
-
-    Node input = proofVariableToReason(v);
-    if(input.getKind() == kind::AND){
-      for(Node::iterator input_iter = input.begin(), input_end = input.end(); input_iter != input_end; ++input_iter){
-       Node inputChild = *input_iter;
-       nb << inputChild;
-      }
-    }else{
-      nb << input;
-    }
-  }
-
-  Node result = (nb.getNumChildren() == 1) ? nb[0] : (Node)nb;
-  Trace("arith::dio") << "Proof at " << i << " is "
-                      << d_trail[i].d_eq.getNode() << endl
-                      << d_trail[i].d_proof.getNode() << endl
-                      << " which becomes " << result << endl;
-  return result;
-}
-
-bool DioSolver::anyCoefficientExceedsMaximum(TrailIndex j) const{
-  uint32_t length = d_trail[j].d_eq.maxLength();
-  uint32_t nmonos = d_trail[j].d_eq.getPolynomial().numMonomials();
-
-  bool result =
-    nmonos >= 2 &&
-    length > d_maxInputCoefficientLength + MAX_GROWTH_RATE;
-  if(TraceIsOn("arith::dio::max") && result){
-
-    const SumPair& eq = d_trail[j].d_eq;
-    const Polynomial& proof = d_trail[j].d_proof;
-
-    Trace("arith::dio::max") << "about to drop:" << std::endl;
-    Trace("arith::dio::max") << "d_trail[" << j << "].d_eq = " << eq.getNode() << std::endl;
-    Trace("arith::dio::max") << "d_trail[" << j << "].d_proof = " << proof.getNode() << std::endl;
-  }
-  return result;
-}
-
-void DioSolver::enqueueInputConstraints(){
-  Assert(d_currentF.empty());
-  while(d_savedQueueIndex < d_savedQueue.size()){
-    d_currentF.push_back(d_savedQueue[d_savedQueueIndex]);
-    d_savedQueueIndex = d_savedQueueIndex + 1;
-  }
-
-  while(d_nextInputConstraintToEnqueue < d_inputConstraints.size()  && !inConflict()){
-    size_t curr = d_nextInputConstraintToEnqueue;
-    d_nextInputConstraintToEnqueue = d_nextInputConstraintToEnqueue + 1;
-
-    TrailIndex i = d_inputConstraints[curr].d_trailPos;
-    TrailIndex j = applyAllSubstitutionsToIndex(i);
-
-    if(!triviallySat(j)){
-      if(triviallyUnsat(j)){
-        raiseConflict(j);
-      }else{
-        TrailIndex k = reduceByGCD(j);
-
-        if(!inConflict()){
-          if(triviallyUnsat(k)){
-            raiseConflict(k);
-          }else if(!(triviallySat(k) || anyCoefficientExceedsMaximum(k))){
-            pushToQueueBack(k);
-          }
-        }
-      }
-    }
-  }
-}
-
-
-/*TODO Currently linear in the size of the Queue
- *It is not clear if am O(log n) strategy would be better.
- *Right before this in the algorithm is a substitution which could potentially
- *effect the key values of everything in the queue.
- */
-void DioSolver::moveMinimumByAbsToQueueFront(){
-  Assert(!queueEmpty());
-
-  //Select the minimum element.
-  size_t indexInQueue = 0;
-  Monomial minMonomial = d_trail[d_currentF[indexInQueue]].d_minimalMonomial;
-
-  size_t N = d_currentF.size();
-  for(size_t i=1; i < N; ++i){
-    Monomial curr = d_trail[d_currentF[i]].d_minimalMonomial;
-    if(curr.absCmp(minMonomial) < 0){
-      indexInQueue = i;
-      minMonomial = curr;
-    }
-  }
-
-  TrailIndex tmp = d_currentF[indexInQueue];
-  d_currentF[indexInQueue] = d_currentF.front();
-  d_currentF.front() = tmp;
-}
-
-bool DioSolver::queueEmpty() const{
-  return d_currentF.empty();
-}
-
-Node DioSolver::columnGcdIsOne() const{
-  std::unordered_map<Node, Integer> gcdMap;
-
-  std::deque<TrailIndex>::const_iterator iter, end;
-  for(iter = d_currentF.begin(), end = d_currentF.end(); iter != end; ++iter){
-    TrailIndex curr = *iter;
-    Polynomial p = d_trail[curr].d_eq.getPolynomial();
-    Polynomial::iterator monoIter = p.begin(), monoEnd = p.end();
-    for(; monoIter != monoEnd; ++monoIter){
-      Monomial m = *monoIter;
-      VarList vl = m.getVarList();
-      Node vlNode = vl.getNode();
-
-      Constant c = m.getConstant();
-      Integer zc = c.getValue().getNumerator();
-      if(gcdMap.find(vlNode) == gcdMap.end()){
-        // have not seen vl yet.
-        // gcd is c
-        Assert(c.isIntegral());
-        Assert(!m.absCoefficientIsOne());
-        gcdMap.insert(make_pair(vlNode, zc.abs()));
-      }else{
-        const Integer& currentGcd = gcdMap[vlNode];
-        Integer newGcd = currentGcd.gcd(zc);
-        if(newGcd == 1){
-          return vlNode;
-        }else{
-          gcdMap[vlNode] = newGcd;
-        }
-      }
-    }
-  }
-  return Node::null();
-}
-
-void DioSolver::saveQueue(){
-  std::deque<TrailIndex>::const_iterator iter, end;
-  for(iter = d_currentF.begin(), end = d_currentF.end(); iter != end; ++iter){
-    d_savedQueue.push_back(*iter);
-  }
-}
-
-DioSolver::TrailIndex DioSolver::impliedGcdOfOne(){
-  Node canReduce = columnGcdIsOne();
-  if(canReduce.isNull()){
-    return 0;
-  }else{
-    VarList vl = VarList::parseVarList(canReduce);
-
-    TrailIndex current;
-    Integer currentCoeff, currentGcd;
-
-    //step 1 find the first equation containing vl
-    //Set current and currentCoefficient
-    std::deque<TrailIndex>::const_iterator iter, end;
-    for(iter = d_currentF.begin(), end = d_currentF.end(); true; ++iter){
-      Assert(iter != end);
-      current = *iter;
-      Constant coeff = d_trail[current].d_eq.getPolynomial().getCoefficient(vl);
-      if(!coeff.isZero()){
-        currentCoeff = coeff.getValue().getNumerator();
-        currentGcd = currentCoeff.abs();
-
-        ++iter;
-        break;
-      }
-    }
-
-    //For the rest of the equations keep reducing until the coefficient is one
-    for(; iter != end; ++iter){
-      Trace("arith::dio") << "next round : " << currentCoeff << " " << currentGcd << endl;
-      TrailIndex inQueue = *iter;
-      Constant iqc = d_trail[inQueue].d_eq.getPolynomial().getCoefficient(vl);
-      if(!iqc.isZero()){
-        Integer inQueueCoeff = iqc.getValue().getNumerator();
-
-        //mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, mpz_t a, mpz_t b);
-        Integer g, s, t;
-        // g = a*s + b*t
-        Integer::extendedGcd(g, s, t, currentCoeff, inQueueCoeff);
-
-        Trace("arith::dio") << "extendedReduction : " << endl;
-        Trace("arith::dio") << g << " = " << s <<"*"<< currentCoeff << " + " << t <<"*"<< inQueueCoeff << endl;
-
-        Assert(g <= currentGcd);
-        if(g < currentGcd){
-          if(s.sgn() == 0){
-            Trace("arith::dio") << "extendedReduction drop" << endl;
-            Assert(inQueueCoeff.divides(currentGcd));
-            current = *iter;
-            currentCoeff = inQueueCoeff;
-            currentGcd = inQueueCoeff.abs();
-          }else{
-
-            Trace("arith::dio") << "extendedReduction combine" << endl;
-            TrailIndex next = combineEqAtIndexes(current, s, inQueue, t);
-
-            Assert(d_trail[next]
-                       .d_eq.getPolynomial()
-                       .getCoefficient(vl)
-                       .getValue()
-                       .getNumerator()
-                   == g);
-
-            current = next;
-            currentCoeff = g;
-            currentGcd = g;
-            if(currentGcd == 1){
-              return current;
-            }
-          }
-        }
-      }
-    }
-    //This is not reachble as it is assured that the gcd of the column is 1
-    Unreachable();
-  }
-}
-
-bool DioSolver::processEquations(bool allowDecomposition){
-  Assert(!inConflict());
-
-  enqueueInputConstraints();
-  while(! queueEmpty() && !inConflict()){
-    moveMinimumByAbsToQueueFront();
-
-    TrailIndex minimum = d_currentF.front();
-    TrailIndex reduceIndex;
-
-    Assert(inRange(minimum));
-    Assert(!inConflict());
-
-    Trace("arith::dio") << "processEquations " << minimum << " : " << d_trail[minimum].d_eq.getNode() << endl;
-
-    Assert(queueConditions(minimum));
-
-    bool canDirectlySolve = d_trail[minimum].d_minimalMonomial.absCoefficientIsOne();
-
-    std::pair<SubIndex, TrailIndex> p;
-    if(canDirectlySolve){
-      d_currentF.pop_front();
-      p = solveIndex(minimum);
-      reduceIndex = minimum;
-    }else{
-      TrailIndex implied = impliedGcdOfOne();
-
-      if(implied != 0){
-        p = solveIndex(implied);
-        reduceIndex = implied;
-      }else if(allowDecomposition){
-        d_currentF.pop_front();
-        p = decomposeIndex(minimum);
-        reduceIndex = minimum;
-      }else {
-        // Cannot make progress without decomposeIndex
-        saveQueue();
-        break;
-      }
-    }
-
-    SubIndex subIndex = p.first;
-    TrailIndex next = p.second;
-    subAndReduceCurrentFByIndex(subIndex);
-
-    if(next != reduceIndex){
-      if(triviallyUnsat(next)){
-        raiseConflict(next);
-      }else if(! triviallySat(next) ){
-        pushToQueueBack(next);
-      }
-    }
-  }
-
-  d_currentF.clear();
-  return inConflict();
-}
-
-Node DioSolver::processEquationsForConflict(){
-  TimerStat::CodeTimer codeTimer(d_statistics.d_conflictTimer);
-  ++(d_statistics.d_conflictCalls);
-
-  Assert(!inConflict());
-  if(processEquations(true)){
-    ++(d_statistics.d_conflicts);
-    return proveIndex(getConflictIndex());
-  }else{
-    return Node::null();
-  }
-}
-
-SumPair DioSolver::processEquationsForCut(){
-  TimerStat::CodeTimer codeTimer(d_statistics.d_cutTimer);
-  ++(d_statistics.d_cutCalls);
-
-  Assert(!inConflict());
-  if(processEquations(true)){
-    ++(d_statistics.d_cuts);
-    return purifyIndex(getConflictIndex());
-  }else{
-    return SumPair::mkZero();
-  }
-}
-
-
-SumPair DioSolver::purifyIndex(TrailIndex i){
-  // TODO: "This uses the substitution trail to reverse the substitutions from the sum term. Using the proof term should be more efficient."
-
-  SumPair curr = d_trail[i].d_eq;
-
-  Constant negOne = Constant::mkConstant(-1);
-
-  for(uint32_t revIter = d_subs.size(); revIter > 0; --revIter){
-    uint32_t i2 = revIter - 1;
-    Node freshNode = d_subs[i2].d_fresh;
-    if(freshNode.isNull()){
-      continue;
-    }else{
-      Variable var(freshNode);
-      Polynomial vsum = curr.getPolynomial();
-
-      Constant a = vsum.getCoefficient(VarList(var));
-      if(!a.isZero()){
-        const SumPair& sj = d_trail[d_subs[i2].d_constraint].d_eq;
-        Assert(sj.getPolynomial().getCoefficient(VarList(var)).isOne());
-        SumPair newSi = (curr * negOne) + (sj * a);
-        Assert(newSi.getPolynomial().getCoefficient(VarList(var)).isZero());
-        curr = newSi;
-      }
-    }
-  }
-  return curr;
-}
-
-DioSolver::TrailIndex DioSolver::combineEqAtIndexes(DioSolver::TrailIndex i, const Integer& q, DioSolver::TrailIndex j, const Integer& r){
-  Constant cq = Constant::mkConstant(q);
-  Constant cr = Constant::mkConstant(r);
-
-  const SumPair& si = d_trail[i].d_eq;
-  const SumPair& sj = d_trail[j].d_eq;
-
-  Trace("arith::dio") << "combineEqAtIndexes(" << i <<","<<q<<","<<j<<","<<r<<")"<<endl;
-  Trace("arith::dio") << "d_facts[i] = " << si.getNode() << endl
-                      << "d_facts[j] = " << sj.getNode() << endl;
-
-
-  SumPair newSi = (si * cq) + (sj * cr);
-
-
-  const Polynomial& pi = d_trail[i].d_proof;
-  const Polynomial& pj = d_trail[j].d_proof;
-  Polynomial newPi = (pi * cq) + (pj * cr);
-
-  TrailIndex k = d_trail.size();
-  d_trail.push_back(Constraint(newSi, newPi));
-
-
-  Trace("arith::dio") << "derived "<< newSi.getNode()
-                      <<" with proof " << newPi.getNode() << endl;
-
-  return k;
-
-}
-
-void DioSolver::printQueue(){
-  Trace("arith::dio") << "DioSolver::printQueue()" << endl;
-  for(TrailIndex i = 0, last = d_trail.size(); i < last; ++i){
-    Trace("arith::dio") << "d_trail[i].d_eq = " << d_trail[i].d_eq.getNode() << endl;
-    Trace("arith::dio") << "d_trail[i].d_proof = " << d_trail[i].d_proof.getNode() << endl;
-  }
-
-  Trace("arith::dio") << "DioSolver::printSubs()" << endl;
-  for(SubIndex si=0, sN=d_subs.size(); si < sN; ++si){
-    Trace("arith::dio") << "d_subs[i] = {"
-                        << "d_fresh="<< d_subs[si].d_fresh <<","
-                        << "d_eliminated="<< d_subs[si].d_eliminated.getNode() <<","
-                        << "d_constraint="<< d_subs[si].d_constraint <<"}" << endl;
-    Trace("arith::dio") << "d_trail[d_subs[i].d_constraint].d_eq="
-                        << d_trail[d_subs[si].d_constraint].d_eq.getNode() << endl;
-  }
-}
-
-DioSolver::TrailIndex DioSolver::applyAllSubstitutionsToIndex(DioSolver::TrailIndex trailIndex){
-  TrailIndex currentIndex = trailIndex;
-  for(SubIndex subIter = 0, siEnd = d_subs.size(); subIter < siEnd; ++subIter){
-    currentIndex = applySubstitution(subIter, currentIndex);
-  }
-  return currentIndex;
-}
-
-bool DioSolver::debugSubstitutionApplies(DioSolver::SubIndex si, DioSolver::TrailIndex ti){
-  Variable var = d_subs[si].d_eliminated;
-
-  const SumPair& curr = d_trail[ti].d_eq;
-  Polynomial vsum = curr.getPolynomial();
-
-  Constant a = vsum.getCoefficient(VarList(var));
-  return !a.isZero();
-}
-
-bool DioSolver::debugAnySubstitionApplies(DioSolver::TrailIndex i){
-  for(SubIndex subIter = 0, siEnd = d_subs.size(); subIter < siEnd; ++subIter){
-    if(debugSubstitutionApplies(subIter, i)){
-      return true;
-    }
-  }
-  return false;
-}
-
-std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::solveIndex(DioSolver::TrailIndex i){
-  const SumPair& si = d_trail[i].d_eq;
-
-  Trace("arith::dio") << "before solveIndex("<<i<<":"<<si.getNode()<< ")" << endl;
-
-#ifdef CVC5_ASSERTIONS
-  const Polynomial& p = si.getPolynomial();
-#endif
-
-  Assert(p.isIntegral());
-
-  Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
-  const Monomial av = d_trail[i].d_minimalMonomial;
-
-  VarList vl = av.getVarList();
-  Assert(vl.singleton());
-  Variable var = vl.getHead();
-  Constant a = av.getConstant();
-  Integer a_abs = a.getValue().getNumerator().abs();
-
-  Assert(a_abs == 1);
-
-  TrailIndex ci = !a.isNegative() ? scaleEqAtIndex(i, Integer(-1)) : i;
-
-  SubIndex subBy = d_subs.size();
-  d_subs.push_back(Substitution(Node::null(), var, ci));
-
-  Trace("arith::dio") << "after solveIndex " <<  d_trail[ci].d_eq.getNode() << " for " << av.getNode() << endl;
-  Assert(d_trail[ci].d_eq.getPolynomial().getCoefficient(vl)
-         == Constant::mkConstant(-1));
-
-  return make_pair(subBy, i);
-}
-
-std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::decomposeIndex(DioSolver::TrailIndex i){
-  const SumPair& si = d_trail[i].d_eq;
-
-  d_usedDecomposeIndex = true;
-
-  Trace("arith::dio") << "before decomposeIndex("<<i<<":"<<si.getNode()<< ")" << endl;
-
-#ifdef CVC5_ASSERTIONS
-  const Polynomial& p = si.getPolynomial();
-#endif
-
-  Assert(p.isIntegral());
-
-  Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
-  const Monomial& av = d_trail[i].d_minimalMonomial;
-
-  VarList vl = av.getVarList();
-  Assert(vl.singleton());
-  Variable var = vl.getHead();
-  Constant a = av.getConstant();
-  Integer a_abs = a.getValue().getNumerator().abs();
-
-  Assert(a_abs > 1);
-
-  //It is not sufficient to reduce the case where abs(a) == 1 to abs(a) > 1.
-  //We need to handle both cases seperately to ensure termination.
-  Node qr = SumPair::computeQR(si, a.getValue().getNumerator());
-
-  Assert(qr.getKind() == kind::ADD);
-  Assert(qr.getNumChildren() == 2);
-  SumPair q = SumPair::parseSumPair(qr[0]);
-  SumPair r = SumPair::parseSumPair(qr[1]);
-
-  Assert(q.getPolynomial().getCoefficient(vl) == Constant::mkConstant(1));
-
-  Assert(!r.isZero());
-  Node freshNode = makeIntegerVariable();
-  Variable fresh(freshNode);
-  SumPair fresh_one=SumPair::mkSumPair(fresh);
-  SumPair fresh_a = fresh_one * a;
-
-  SumPair newSI = SumPair(fresh_one) - q;
-  // this normalizes the coefficient of var to -1
-
-
-  TrailIndex ci = d_trail.size();
-  d_trail.push_back(Constraint(newSI, Polynomial::mkZero()));
-  // no longer reference av safely!
-  addTrailElementAsLemma(ci);
-
-  Trace("arith::dio") << "Decompose ci(" << ci <<":" <<  d_trail[ci].d_eq.getNode()
-                      << ") for " << d_trail[i].d_minimalMonomial.getNode() << endl;
-  Assert(d_trail[ci].d_eq.getPolynomial().getCoefficient(vl)
-         == Constant::mkConstant(-1));
-
-  SumPair newFact = r + fresh_a;
-
-  TrailIndex nextIndex = d_trail.size();
-  d_trail.push_back(Constraint(newFact, d_trail[i].d_proof));
-
-  SubIndex subBy = d_subs.size();
-  d_subs.push_back(Substitution(freshNode, var, ci));
-
-  Trace("arith::dio") << "Decompose nextIndex " <<  d_trail[nextIndex].d_eq.getNode() << endl;
-  return make_pair(subBy, nextIndex);
-}
-
-
-DioSolver::TrailIndex DioSolver::applySubstitution(DioSolver::SubIndex si, DioSolver::TrailIndex ti){
-  Variable var = d_subs[si].d_eliminated;
-  TrailIndex subIndex = d_subs[si].d_constraint;
-
-  const SumPair& curr = d_trail[ti].d_eq;
-  Polynomial vsum = curr.getPolynomial();
-
-  Constant a = vsum.getCoefficient(VarList(var));
-  Assert(a.isIntegral());
-  if(!a.isZero()){
-    Integer one(1);
-    TrailIndex afterSub = combineEqAtIndexes(ti, one, subIndex, a.getValue().getNumerator());
-    Assert(d_trail[afterSub]
-               .d_eq.getPolynomial()
-               .getCoefficient(VarList(var))
-               .isZero());
-    return afterSub;
-  }else{
-    return ti;
-  }
-}
-
-
-DioSolver::TrailIndex DioSolver::reduceByGCD(DioSolver::TrailIndex ti){
-  const SumPair& sp = d_trail[ti].d_eq;
-  Polynomial vsum = sp.getPolynomial();
-  Constant c = sp.getConstant();
-
-  Trace("arith::dio") << "reduceByGCD " << vsum.getNode() << endl;
-  Assert(!vsum.isConstant());
-  Integer g = vsum.gcd();
-  Assert(g >= 1);
-  Trace("arith::dio") << "gcd("<< vsum.getNode() <<")=" << g << " " << c.getValue() << endl;
-  if(g.divides(c.getValue().getNumerator())){
-    if(g > 1){
-      return scaleEqAtIndex(ti, g);
-    }else{
-      return ti;
-    }
-  }else{
-    raiseConflict(ti);
-    return ti;
-  }
-}
-
-bool DioSolver::triviallySat(TrailIndex i){
-  const SumPair& eq = d_trail[i].d_eq;
-  if(eq.isConstant()){
-    return eq.getConstant().isZero();
-  }else{
-    return false;
-  }
-}
-
-bool DioSolver::triviallyUnsat(DioSolver::TrailIndex i){
-  const SumPair& eq = d_trail[i].d_eq;
-  if(eq.isConstant()){
-    return !eq.getConstant().isZero();
-  }else{
-    return false;
-  }
-}
-
-
-bool DioSolver::gcdIsOne(DioSolver::TrailIndex i){
-  const SumPair& eq = d_trail[i].d_eq;
-  return eq.gcd() == Integer(1);
-}
-
-void DioSolver::subAndReduceCurrentFByIndex(DioSolver::SubIndex subIndex){
-  size_t N = d_currentF.size();
-
-  size_t readIter = 0, writeIter = 0;
-  for(; readIter < N && !inConflict(); ++readIter){
-    TrailIndex curr = d_currentF[readIter];
-    TrailIndex nextTI = applySubstitution(subIndex, curr);
-    if(nextTI == curr){
-      d_currentF[writeIter] = curr;
-      ++writeIter;
-    }else{
-      Assert(nextTI != curr);
-
-      if(triviallyUnsat(nextTI)){
-        raiseConflict(nextTI);
-      }else if(!triviallySat(nextTI)){
-        TrailIndex nextNextTI = reduceByGCD(nextTI);
-
-        if(!(inConflict() || anyCoefficientExceedsMaximum(nextNextTI))){
-          Assert(queueConditions(nextNextTI));
-          d_currentF[writeIter] = nextNextTI;
-          ++writeIter;
-        }
-      }
-    }
-  }
-  if(!inConflict() && writeIter < N){
-    d_currentF.resize(writeIter);
-  }
-}
-
-void DioSolver::addTrailElementAsLemma(TrailIndex i) {
-  if (options().arith.exportDioDecompositions)
-  {
-    d_decompositionLemmaQueue.push(i);
-  }
-}
-
-Node DioSolver::trailIndexToEquality(TrailIndex i) const {
-  const SumPair& sp = d_trail[i].d_eq;
-  Node n = sp.getNode();
-  Node zero =
-      NodeManager::currentNM()->mkConstRealOrInt(n.getType(), Rational(0));
-  Node eq = n.eqNode(zero);
-  return eq;
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/dio_solver.h b/src/theory/arith/dio_solver.h
deleted file mode 100644 (file)
index 1ac2eaa..0000000
+++ /dev/null
@@ -1,424 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Morgan Deters, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * A Diophantine equation solver for the theory of arithmetic.
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__DIO_SOLVER_H
-#define CVC5__THEORY__ARITH__DIO_SOLVER_H
-
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "context/cdlist.h"
-#include "context/cdmaybe.h"
-#include "context/cdo.h"
-#include "context/cdqueue.h"
-#include "smt/env_obj.h"
-#include "theory/arith/normal_form.h"
-#include "util/rational.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::context {
-class Context;
-}
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class DioSolver : protected EnvObj
-{
- private:
-  typedef size_t TrailIndex;
-  typedef size_t InputConstraintIndex;
-  typedef size_t SubIndex;
-
-  std::vector<Variable> d_proofVariablePool;
-  /** Sat context dependent. */
-  context::CDO<size_t> d_lastUsedProofVariable;
-
-  /**
-   * The set of input constraints is stored in a CDList.
-   * Each constraint point to an element of the trail.
-   */
-  struct InputConstraint {
-    Node d_reason;
-    TrailIndex d_trailPos;
-    InputConstraint(Node reason, TrailIndex pos) : d_reason(reason), d_trailPos(pos) {}
-  };
-  context::CDList<InputConstraint> d_inputConstraints;
-
-  /**
-   * This is the next input constraint to handle.
-   */
-  context::CDO<size_t> d_nextInputConstraintToEnqueue;
-
-  /**
-   * We maintain a map from the variables associated with proofs to an input constraint.
-   * These variables can then be used in polynomial manipulations.
-   */
-  typedef std::unordered_map<Node, InputConstraintIndex>
-      NodeToInputConstraintIndexMap;
-  NodeToInputConstraintIndexMap d_varToInputConstraintMap;
-
-  Node proofVariableToReason(const Variable& v) const{
-    Assert(d_varToInputConstraintMap.find(v.getNode())
-           != d_varToInputConstraintMap.end());
-    InputConstraintIndex pos = (*(d_varToInputConstraintMap.find(v.getNode()))).second;
-    Assert(pos < d_inputConstraints.size());
-    return d_inputConstraints[pos].d_reason;
-  }
-
-  /**
-   * The main work horse of the algorithm, the trail of constraints.
-   * Each constraint is a SumPair that implicitly represents an equality against 0.
-   *   d_trail[i].d_eq = (+ c (+ [(* coeff var)])) representing (+ [(* coeff var)]) = -c
-   * Each constraint has a proof in terms of a linear combination of the input constraints.
-   *   d_trail[i].d_proof
-   *
-   * Each Constraint also a monomial in d_eq.getPolynomial()
-   * of minimal absolute value by the coefficients.
-   * d_trail[i].d_minimalMonomial
-   *
-   * See Alberto's paper for how linear proofs are maintained for the abstract
-   * state machine in rules (7), (8) and (9).
-   */
-  struct Constraint {
-    SumPair d_eq;
-    Polynomial d_proof;
-    Monomial d_minimalMonomial;
-    Constraint(const SumPair& eq, const Polynomial& p) :
-      d_eq(eq), d_proof(p), d_minimalMonomial(d_eq.getPolynomial().selectAbsMinimum())
-    {}
-  };
-  context::CDList<Constraint> d_trail;
-
-  // /** Compare by d_minimal. */
-  // struct TrailMinimalCoefficientOrder {
-  //   const context::CDList<Constraint>& d_trail;
-  //   TrailMinimalCoefficientOrder(const context::CDList<Constraint>&
-  //   trail):
-  //     d_trail(trail)
-  //   {}
-
-  //   bool operator()(TrailIndex i, TrailIndex j){
-  //     return d_trail[i].d_minimalMonomial.absLessThan(d_trail[j].d_minimalMonomial);
-  //   }
-  // };
-
-  /**
-   * A substitution is stored as a constraint in the trail together with
-   * the variable to be eliminated, and a fresh variable if one was introduced.
-   * The variable d_subs[i].d_eliminated is substituted using the implicit equality in
-   * d_trail[d_subs[i].d_constraint]
-   *  - d_subs[i].d_eliminated is normalized to have coefficient -1 in
-   *    d_trail[d_subs[i].d_constraint].
-   *  - d_subs[i].d_fresh is either Node::null() or it is variable it is normalized
-   *    to have coefficient 1 in d_trail[d_subs[i].d_constraint].
-   */
-  struct Substitution {
-    Node d_fresh;
-    Variable d_eliminated;
-    TrailIndex d_constraint;
-    Substitution(Node f, const Variable& e, TrailIndex c) :
-      d_fresh(f), d_eliminated(e), d_constraint(c)
-    {}
-  };
-  context::CDList<Substitution> d_subs;
-
-  /**
-   * This is the queue of constraints to be processed in the current context level.
-   * This is to be empty upon entering solver and cleared upon leaving the solver.
-   *
-   * All elements in currentF:
-   * - are fully substituted according to d_subs.
-   * - !isConstant().
-   * - If the element is (+ constant (+ [(* coeff var)] )), then the gcd(coeff) = 1
-   */
-  std::deque<TrailIndex> d_currentF;
-  context::CDList<TrailIndex> d_savedQueue;
-  context::CDO<size_t> d_savedQueueIndex;
-  context::CDMaybe<TrailIndex> d_conflictIndex;
-
-  /**
-   * Drop derived constraints with a coefficient length larger than
-   * the maximum input constraints length than 2**MAX_GROWTH_RATE.
-   */
-  context::CDO<uint32_t> d_maxInputCoefficientLength;
-  static constexpr uint32_t MAX_GROWTH_RATE = 3;
-
-  /** Returns true if the element on the trail should be dropped.*/
-  bool anyCoefficientExceedsMaximum(TrailIndex j) const;
-
-  /**
-   * Is true if decomposeIndex has been used in this context.
-   */
-  context::CDO<bool> d_usedDecomposeIndex;
-
-  context::CDO<SubIndex> d_lastPureSubstitution;
-  context::CDO<SubIndex> d_pureSubstitionIter;
-
-  /**
-   * Decomposition lemma queue.
-   */
-  context::CDQueue<TrailIndex> d_decompositionLemmaQueue;
-
- public:
-  /** Construct a Diophantine equation solver with the given context. */
- DioSolver(Env& env);
-
- /** Returns true if the substitutions use no new variables. */
- bool hasMorePureSubstitutions() const
- {
-   return d_pureSubstitionIter < d_lastPureSubstitution;
-  }
-
-  Node nextPureSubstitution();
-
-  /**
-   * Adds an equality to the queue of the DioSolver.
-   * orig is blamed in a conflict.
-   * orig can either be of the form (= p c) or (and ub lb).
-   * where ub is either (leq p c) or (not (> p [- c 1])), and
-   * where lb is either (geq p c) or (not (< p [+ c 1]))
-   *
-   * If eq cannot be used, this constraint is dropped.
-   */
-  void pushInputConstraint(const Comparison& eq, Node reason);
-
-  /**
-   * Processes the queue looking for any conflict.
-   * If a conflict is found, this returns conflict.
-   * Otherwise, it returns null.
-   * The conflict is guarenteed to be over literals given in addEquality.
-   */
-  Node processEquationsForConflict();
-
-  /**
-   * Processes the queue looking for an integer unsatisfiable cutting plane.
-   * If such a plane is found this returns an entailed plane using no
-   * fresh variables.
-   */
-  SumPair processEquationsForCut();
-
-private:
-  /** Returns true if the TrailIndex refers to a element in the trail. */
-  bool inRange(TrailIndex i) const{
-    return i < d_trail.size();
-  }
-
-  Node columnGcdIsOne() const;
-
-
-  /**
-   * Returns true if the context dependent flag for conflicts
-   * has been raised.
-   */
-  bool inConflict() const { return d_conflictIndex.isSet(); }
-
-  /** Raises a conflict at the index ti. */
-  void raiseConflict(TrailIndex ti){
-    Assert(!inConflict());
-    d_conflictIndex.set(ti);
-  }
-
-  /** Returns the conflict index. */
-  TrailIndex getConflictIndex() const{
-    Assert(inConflict());
-    return d_conflictIndex.get();
-  }
-
-  /**
-   * Allocates a "unique" proof variable.
-   * This variable is fresh with respect to the context.
-   * Returns index of the variable in d_variablePool;
-   */
-  size_t allocateProofVariable();
-
-
-  /** Empties the unproccessed input constraints into the queue. */
-  void enqueueInputConstraints();
-
-  /**
-   * Returns true if an input equality is in the map.
-   * This is expensive and is only for debug assertions.
-   */
-  bool debugEqualityInInputEquations(Node eq);
-
-  /** Applies the substitution at subIndex to currentF. */
-  void subAndReduceCurrentFByIndex(SubIndex d_subIndex);
-
-  /**
-   * Takes as input a TrailIndex i and an integer that divides d_trail[i].d_eq, and
-   * returns a TrailIndex j s.t.
-   *   d_trail[j].d_eq = (1/g) d_trail[i].d_eq
-   * and
-   *   d_trail[j].d_proof = (1/g) d_trail[i].d_proof.
-   *
-   * g must be non-zero.
-   *
-   * This corresponds to an application of Alberto's rule (7).
-   */
-  TrailIndex scaleEqAtIndex(TrailIndex i, const Integer& g);
-
-
-  /**
-   * Takes as input TrailIndex's i and j and Integer's q and r and a TrailIndex k s.t.
-   *   d_trail[k].d_eq == d_trail[i].d_eq * q + d_trail[j].d_eq * r
-   * and
-   *   d_trail[k].d_proof == d_trail[i].d_proof * q + d_trail[j].d_proof * r
-   *
-   * This corresponds to an application of Alberto's rule (8).
-   */
-  TrailIndex combineEqAtIndexes(TrailIndex i, const Integer& q, TrailIndex j, const Integer& r);
-
-  /**
-   * Decomposes the equation at index ti of trail by the variable
-   * with the lowest coefficient.
-   * This corresponds to an application of Alberto's rule (9).
-   *
-   * Returns a pair of a SubIndex and a TrailIndex.
-   * The SubIndex is the index of a newly introduced substition.
-   */
-  std::pair<SubIndex, TrailIndex> decomposeIndex(TrailIndex ti);
-
-  /** Solves the index at ti for the value in minimumMonomial. */
-  std::pair<SubIndex, TrailIndex> solveIndex(TrailIndex ti);
-
-  /** Prints the queue for debugging purposes to Trace("arith::dio"). */
-  void printQueue();
-
-  /**
-   * Exhaustively applies all substitutions discovered to an element of the trail.
-   * Returns a TrailIndex corresponding to the substitutions being applied.
-   */
-  TrailIndex applyAllSubstitutionsToIndex(TrailIndex i);
-
-  /**
-   * Applies a substitution to an element in the trail.
-   */
-  TrailIndex applySubstitution(SubIndex s, TrailIndex i);
-
-  /**
-   * Reduces the trail node at i by the gcd of the variables.
-   * Returns the new trail element.
-   *
-   * This raises the conflict flag if unsat is detected.
-   */
-  TrailIndex reduceByGCD(TrailIndex i);
-
-  /**
-   * Returns true if i'th element in the trail is trivially true.
-   * (0 = 0)
-   */
-  bool triviallySat(TrailIndex t);
-
-  /**
-   * Returns true if i'th element in the trail is trivially unsatisfiable.
-   * (1 = 0)
-   */
-  bool triviallyUnsat(TrailIndex t);
-
-  /** Returns true if the gcd of the i'th element of the trail is 1.*/
-  bool gcdIsOne(TrailIndex t);
-
-  bool debugAnySubstitionApplies(TrailIndex t);
-  bool debugSubstitutionApplies(SubIndex si, TrailIndex ti);
-
-
-  /** Returns true if the queue of nodes to process is empty. */
-  bool queueEmpty() const;
-
-  bool queueConditions(TrailIndex t);
-
-
-  void pushToQueueBack(TrailIndex t){
-    Assert(queueConditions(t));
-    d_currentF.push_back(t);
-  }
-
-  void pushToQueueFront(TrailIndex t){
-    Assert(queueConditions(t));
-    d_currentF.push_front(t);
-  }
-
-  /**
-   * Moves the minimum Constraint by absolute value of the minimum coefficient to
-   * the front of the queue.
-   */
-  void moveMinimumByAbsToQueueFront();
-
-  void saveQueue();
-
-  TrailIndex impliedGcdOfOne();
-
-
-  /**
-   * Processing the current set of equations.
-   *
-   * decomposeIndex() rule is only applied if allowDecomposition is true.
-   */
-  bool processEquations(bool allowDecomposition);
-
-  /**
-   * Constructs a proof from any d_trail[i] in terms of input literals.
-   */
-  Node proveIndex(TrailIndex i);
-
-  /**
-   * Returns the SumPair in d_trail[i].d_eq with all of the fresh variables purified out.
-   */
-  SumPair purifyIndex(TrailIndex i);
-
-public:
-  bool hasMoreDecompositionLemmas() const{
-    return !d_decompositionLemmaQueue.empty();
-  }
-  Node nextDecompositionLemma() {
-    Assert(hasMoreDecompositionLemmas());
-    TrailIndex front = d_decompositionLemmaQueue.front();
-    d_decompositionLemmaQueue.pop();
-    return trailIndexToEquality(front);
-  }
-private:
-  Node trailIndexToEquality(TrailIndex i) const;
-  void addTrailElementAsLemma(TrailIndex i);
-
-public:
-
-  /** These fields are designed to be accessible to TheoryArith methods. */
-  class Statistics {
-  public:
-
-    IntStat d_conflictCalls;
-    IntStat d_cutCalls;
-
-    IntStat d_cuts;
-    IntStat d_conflicts;
-
-    TimerStat d_conflictTimer;
-    TimerStat d_cutTimer;
-
-    Statistics();
-  };
-
-  Statistics d_statistics;
-}; /* class DioSolver */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__DIO_SOLVER_H */
diff --git a/src/theory/arith/dual_simplex.cpp b/src/theory/arith/dual_simplex.cpp
deleted file mode 100644 (file)
index e431191..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- */
-#include "theory/arith/dual_simplex.h"
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "smt/env.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/error_set.h"
-#include "theory/arith/linear_equality.h"
-
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-DualSimplexDecisionProcedure::DualSimplexDecisionProcedure(
-    Env& env,
-    LinearEqualityModule& linEq,
-    ErrorSet& errors,
-    RaiseConflict conflictChannel,
-    TempVarMalloc tvmalloc)
-    : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
-      d_pivotsInRound(),
-      d_statistics(d_pivots)
-{ }
-
-DualSimplexDecisionProcedure::Statistics::Statistics(uint32_t& pivots)
-    : d_statUpdateConflicts(smtStatisticsRegistry().registerInt(
-        "theory::arith::dual::UpdateConflicts")),
-      d_processSignalsTime(smtStatisticsRegistry().registerTimer(
-          "theory::arith::dual::findConflictOnTheQueueTime")),
-      d_simplexConflicts(smtStatisticsRegistry().registerInt(
-          "theory::arith::dual::simplexConflicts")),
-      d_recentViolationCatches(smtStatisticsRegistry().registerInt(
-          "theory::arith::dual::recentViolationCatches")),
-      d_searchTime(smtStatisticsRegistry().registerTimer(
-          "theory::arith::dual::searchTime")),
-      d_finalCheckPivotCounter(
-          smtStatisticsRegistry().registerReference<uint32_t>(
-              "theory::arith::dual::lastPivots", pivots))
-{
-}
-
-Result::Status DualSimplexDecisionProcedure::dualFindModel(bool exactResult)
-{
-  Assert(d_conflictVariables.empty());
-
-  d_pivots = 0;
-
-  if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
-    Trace("arith::findModel") << "dualFindModel() trivial" << endl;
-    return Result::SAT;
-  }
-
-  // We need to reduce this because of
-  d_errorSet.reduceToSignals();
-  d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
-
-  if(processSignals()){
-    d_conflictVariables.purge();
-
-    Trace("arith::findModel") << "dualFindModel() early conflict" << endl;
-    return Result::UNSAT;
-  }else if(d_errorSet.errorEmpty()){
-    Trace("arith::findModel") << "dualFindModel() fixed itself" << endl;
-    Assert(!d_errorSet.moreSignals());
-    return Result::SAT;
-  }
-
-  Trace("arith::findModel") << "dualFindModel() start non-trivial" << endl;
-
-  Result::Status result = Result::UNKNOWN;
-
-  exactResult |= d_varOrderPivotLimit < 0;
-
-  uint32_t checkPeriod = options().arith.arithSimplexCheckPeriod;
-  if (result == Result::UNKNOWN)
-  {
-    uint32_t numDifferencePivots = options().arith.arithHeuristicPivots < 0
-                                       ? d_numVariables + 1
-                                       : options().arith.arithHeuristicPivots;
-    // The signed to unsigned conversion is safe.
-    if(numDifferencePivots > 0){
-
-      d_errorSet.setSelectionRule(d_heuristicRule);
-      if(searchForFeasibleSolution(numDifferencePivots)){
-        result = Result::UNSAT;
-      }
-    }
-  }
-  Assert(!d_errorSet.moreSignals());
-
-  if(!d_errorSet.errorEmpty() && result != Result::UNSAT){
-    if(exactResult){
-      d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
-      while(!d_errorSet.errorEmpty() && result != Result::UNSAT){
-        Assert(checkPeriod > 0);
-        if(searchForFeasibleSolution(checkPeriod)){
-          result = Result::UNSAT;
-        }
-      }
-    }
-    else if (d_varOrderPivotLimit > 0)
-    {
-      d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
-      if (searchForFeasibleSolution(d_varOrderPivotLimit))
-      {
-        result = Result::UNSAT;
-      }
-    }
-  }
-
-  Assert(!d_errorSet.moreSignals());
-  if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
-  {
-    result = Result::SAT;
-  }
-
-  d_pivotsInRound.purge();
-  // ensure that the conflict variable is still in the queue.
-  d_conflictVariables.purge();
-
-  Trace("arith::findModel") << "end findModel() " << result << endl;
-
-  return result;
-}
-
-//corresponds to Check() in dM06
-//template <SimplexDecisionProcedure::PreferenceFunction pf>
-bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingIterations){
-  TimerStat::CodeTimer codeTimer(d_statistics.d_searchTime);
-
-  Trace("arith") << "searchForFeasibleSolution" << endl;
-  Assert(remainingIterations > 0);
-
-  while(remainingIterations > 0 && !d_errorSet.focusEmpty()){
-    if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
-    Assert(d_conflictVariables.empty());
-    ArithVar x_i = d_errorSet.topFocusVariable();
-
-    Trace("arith::update::select") << "selectSmallestInconsistentVar()=" << x_i << endl;
-    if(x_i == ARITHVAR_SENTINEL){
-      Trace("arith::update") << "No inconsistent variables" << endl;
-      return false; //sat
-    }
-
-    --remainingIterations;
-
-    bool useVarOrderPivot =
-        d_pivotsInRound.count(x_i) >= options().arith.arithPivotThreshold;
-    if(!useVarOrderPivot){
-      d_pivotsInRound.add(x_i);
-    }
-
-    Trace("arith::update") << "pivots in rounds: " << d_pivotsInRound.count(x_i)
-                           << " use " << useVarOrderPivot << " threshold "
-                           << options().arith.arithPivotThreshold << std::endl;
-
-    LinearEqualityModule::VarPreferenceFunction pf = useVarOrderPivot ?
-      &LinearEqualityModule::minVarOrder : &LinearEqualityModule::minBoundAndColLength;
-
-    //DeltaRational beta_i = d_variables.getAssignment(x_i);
-    ArithVar x_j = ARITHVAR_SENTINEL;
-
-    int32_t prevErrorSize CVC5_UNUSED = d_errorSet.errorSize();
-
-    if(d_variables.cmpAssignmentLowerBound(x_i) < 0 ){
-      x_j = d_linEq.selectSlackUpperBound(x_i, pf);
-      if(x_j == ARITHVAR_SENTINEL ){
-        Unreachable();
-        // ++(d_statistics.d_statUpdateConflicts);
-        // reportConflict(x_i);
-        // ++(d_statistics.d_simplexConflicts);
-        // Node conflict = d_linEq.generateConflictBelowLowerBound(x_i); //unsat
-        // d_conflictVariable = x_i;
-        // reportConflict(conflict);
-        // return true;
-      }else{
-        const DeltaRational& l_i = d_variables.getLowerBound(x_i);
-        d_linEq.pivotAndUpdate(x_i, x_j, l_i);
-      }
-    }else if(d_variables.cmpAssignmentUpperBound(x_i) > 0){
-      x_j = d_linEq.selectSlackLowerBound(x_i, pf);
-      if(x_j == ARITHVAR_SENTINEL ){
-        Unreachable();
-        // ++(d_statistics.d_statUpdateConflicts);
-        // reportConflict(x_i);
-        // ++(d_statistics.d_simplexConflicts);
-        // Node conflict = d_linEq.generateConflictAboveUpperBound(x_i); //unsat
-        // d_conflictVariable = x_i;
-        // reportConflict(conflict);
-        // return true;
-      }else{
-        const DeltaRational& u_i = d_variables.getUpperBound(x_i);
-        d_linEq.pivotAndUpdate(x_i, x_j, u_i);
-      }
-    }
-    Assert(x_j != ARITHVAR_SENTINEL);
-
-    bool conflict = processSignals();
-    int32_t currErrorSize CVC5_UNUSED = d_errorSet.errorSize();
-    d_pivots++;
-
-    if(TraceIsOn("arith::dual")){
-      Trace("arith::dual")
-        << "#" << d_pivots
-        << " c" << conflict
-        << " d" << (prevErrorSize - currErrorSize)
-        << " f"  << d_errorSet.inError(x_j)
-        << " h" << d_conflictVariables.isMember(x_j)
-        << " " << x_i << "->" << x_j
-        << endl;
-    }
-
-    if(conflict){
-      return true;
-    }
-  }
-  Assert(!d_errorSet.focusEmpty() || d_errorSet.errorEmpty());
-  Assert(remainingIterations == 0 || d_errorSet.focusEmpty());
-  Assert(d_errorSet.noSignals());
-
-  return false;
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/dual_simplex.h b/src/theory/arith/dual_simplex.h
deleted file mode 100644 (file)
index b4df9b4..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- *
- * This implements the Simplex module for the Simpelx for DPLL(T) decision
- * procedure.
- * See the Simplex for DPLL(T) technical report for more background.(citation?)
- * This shares with the theory a Tableau, and a PartialModel that:
- *  - satisfies the equalities in the Tableau, and
- *  - the assignment for the non-basic variables satisfies their bounds.
- * This is required to either produce a conflict or satisifying PartialModel.
- * Further, we require being told when a basic variable updates its value.
- *
- * During the Simplex search we maintain a queue of variables.
- * The queue is required to contain all of the basic variables that voilate
- * their bounds.
- * As elimination from the queue is more efficient to be done lazily,
- * we do not maintain that the queue of variables needs to be only basic
- * variables or only variables that satisfy their bounds.
- *
- * The simplex procedure roughly follows Alberto's thesis. (citation?)
- * There is one round of selecting using a heuristic pivoting rule.
- * (See PreferenceFunction Documentation for the available options.)
- * The non-basic variable is the one that appears in the fewest pivots.
- * (Bruno says that Leonardo invented this first.)
- * After this, Bland's pivot rule is invoked.
- *
- * During this proccess, we periodically inspect the queue of variables to
- * 1) remove now extraneous extries,
- * 2) detect conflicts that are "waiting" on the queue but may not be detected
- *    by the current queue heuristics, and
- * 3) detect multiple conflicts.
- *
- * Conflicts are greedily slackened to use the weakest bounds that still
- * produce the conflict.
- *
- * Extra things tracked atm: (Subject to change at Tim's whims)
- * - A superset of all of the newly pivoted variables.
- * - A queue of additional conflicts that were discovered by Simplex.
- *   These are theory valid and are currently turned into lemmas
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "theory/arith/simplex.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class DualSimplexDecisionProcedure : public SimplexDecisionProcedure{
-public:
- DualSimplexDecisionProcedure(Env& env,
-                              LinearEqualityModule& linEq,
-                              ErrorSet& errors,
-                              RaiseConflict conflictChannel,
-                              TempVarMalloc tvmalloc);
-
- Result::Status findModel(bool exactResult) override
- {
-   return dualFindModel(exactResult);
-  }
-
-private:
-
-  /**
-   * Maps a variable to how many times they have been used as a pivot in the
-   * simplex search.
-   */
-  DenseMultiset d_pivotsInRound;
-
-  Result::Status dualFindModel(bool exactResult);
-
-  /**
-   * This is the main simplex for DPLL(T) loop.
-   * It runs for at most maxIterations.
-   *
-   * Returns true iff it has found a conflict.
-   * d_conflictVariable will be set and the conflict for this row is reported.
-   */
-  bool searchForFeasibleSolution(uint32_t maxIterations);
-  
-
-  bool processSignals(){
-    TimerStat &timer = d_statistics.d_processSignalsTime;
-    IntStat& conflictStat  = d_statistics.d_recentViolationCatches;
-    return standardProcessSignals(timer, conflictStat);
-  }
-  /** These fields are designed to be accessible to TheoryArith methods. */
-  class Statistics {
-  public:
-    IntStat d_statUpdateConflicts;
-    TimerStat d_processSignalsTime;
-    IntStat d_simplexConflicts;
-    IntStat d_recentViolationCatches;
-    TimerStat d_searchTime;
-
-    ReferenceStat<uint32_t> d_finalCheckPivotCounter;
-
-    Statistics(uint32_t& pivots);
-  } d_statistics;
-};/* class DualSimplexDecisionProcedure */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/error_set.cpp b/src/theory/arith/error_set.cpp
deleted file mode 100644 (file)
index 8a904b5..0000000
+++ /dev/null
@@ -1,490 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Andres Noetzli, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/error_set.h"
-
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-ErrorInformation::ErrorInformation()
-    : d_variable(ARITHVAR_SENTINEL),
-      d_violated(NullConstraint),
-      d_sgn(0),
-      d_relaxed(false),
-      d_inFocus(false),
-      d_handle(),
-      d_amount(nullptr),
-      d_metric(0)
-{
-  Trace("arith::error::mem")
-      << "def constructor " << d_variable << " " << d_amount.get() << endl;
-}
-
-ErrorInformation::ErrorInformation(ArithVar var, ConstraintP vio, int sgn)
-    : d_variable(var),
-      d_violated(vio),
-      d_sgn(sgn),
-      d_relaxed(false),
-      d_inFocus(false),
-      d_handle(),
-      d_amount(nullptr),
-      d_metric(0)
-{
-  Assert(debugInitialized());
-  Trace("arith::error::mem")
-      << "constructor " << d_variable << " " << d_amount.get() << endl;
-}
-
-
-ErrorInformation::~ErrorInformation() {
-  Assert(d_relaxed != true);
-  if (d_amount != nullptr)
-  {
-    Trace("arith::error::mem") << d_amount.get() << endl;
-    Trace("arith::error::mem")
-        << "destroy " << d_variable << " " << d_amount.get() << endl;
-    d_amount = nullptr;
-  }
-}
-
-ErrorInformation::ErrorInformation(const ErrorInformation& ei)
-  : d_variable(ei.d_variable)
-  , d_violated(ei.d_violated)
-  , d_sgn(ei.d_sgn)
-  , d_relaxed(ei.d_relaxed)
-  , d_inFocus(ei.d_inFocus)
-  , d_handle(ei.d_handle)
-  , d_metric(0)
-{
-  if (ei.d_amount == nullptr)
-  {
-    d_amount = nullptr;
-  }
-  else
-  {
-    d_amount = std::make_unique<DeltaRational>(*ei.d_amount);
-  }
-  Trace("arith::error::mem")
-      << "copy const " << d_variable << " " << d_amount.get() << endl;
-}
-
-ErrorInformation& ErrorInformation::operator=(const ErrorInformation& ei){
-  d_variable = ei.d_variable;
-  d_violated = ei.d_violated;
-  d_sgn = ei.d_sgn;
-  d_relaxed = (ei.d_relaxed);
-  d_inFocus = (ei.d_inFocus);
-  d_handle = (ei.d_handle);
-  d_metric = ei.d_metric;
-  if (d_amount != nullptr && ei.d_amount != nullptr)
-  {
-    Trace("arith::error::mem")
-        << "assignment assign " << d_variable << " " << d_amount.get() << endl;
-    *d_amount = *ei.d_amount;
-  }
-  else if (ei.d_amount != nullptr)
-  {
-    d_amount = std::make_unique<DeltaRational>(*ei.d_amount);
-    Trace("arith::error::mem")
-        << "assignment alloc " << d_variable << " " << d_amount.get() << endl;
-  }
-  else if (d_amount != nullptr)
-  {
-    Trace("arith::error::mem")
-        << "assignment release " << d_variable << " " << d_amount.get() << endl;
-    d_amount = nullptr;
-  }
-  else
-  {
-    d_amount = nullptr;
-  }
-  return *this;
-}
-
-void ErrorInformation::reset(ConstraintP c, int sgn){
-  Assert(!isRelaxed());
-  Assert(c != NullConstraint);
-  d_violated = c;
-  d_sgn = sgn;
-
-  if (d_amount != nullptr)
-  {
-    Trace("arith::error::mem")
-        << "reset " << d_variable << " " << d_amount.get() << endl;
-    d_amount = nullptr;
-  }
-}
-
-void ErrorInformation::setAmount(const DeltaRational& am){
-  if (d_amount == nullptr)
-  {
-    d_amount = std::make_unique<DeltaRational>();
-    Trace("arith::error::mem")
-        << "setAmount " << d_variable << " " << d_amount.get() << endl;
-  }
-  (*d_amount) = am;
-}
-
-ErrorSet::Statistics::Statistics()
-    : d_enqueues(
-        smtStatisticsRegistry().registerInt("theory::arith::pqueue::enqueues")),
-      d_enqueuesCollection(smtStatisticsRegistry().registerInt(
-          "theory::arith::pqueue::enqueuesCollection")),
-      d_enqueuesDiffMode(smtStatisticsRegistry().registerInt(
-          "theory::arith::pqueue::enqueuesDiffMode")),
-      d_enqueuesVarOrderMode(smtStatisticsRegistry().registerInt(
-          "theory::arith::pqueue::enqueuesVarOrderMode")),
-      d_enqueuesCollectionDuplicates(smtStatisticsRegistry().registerInt(
-          "theory::arith::pqueue::enqueuesCollectionDuplicates")),
-      d_enqueuesVarOrderModeDuplicates(smtStatisticsRegistry().registerInt(
-          "theory::arith::pqueue::enqueuesVarOrderModeDuplicates"))
-{
-}
-
-ErrorSet::ErrorSet(ArithVariables& vars,
-                   TableauSizes tabSizes,
-                   BoundCountingLookup lookups)
-    : d_variables(vars),
-      d_errInfo(),
-      d_selectionRule(options::ErrorSelectionRule::VAR_ORDER),
-      d_focus(ComparatorPivotRule(this, d_selectionRule)),
-      d_outOfFocus(),
-      d_signals(),
-      d_tableauSizes(tabSizes),
-      d_boundLookup(lookups)
-{}
-
-options::ErrorSelectionRule ErrorSet::getSelectionRule() const
-{
-  return d_selectionRule;
-}
-
-void ErrorSet::recomputeAmount(ErrorInformation& ei,
-                               options::ErrorSelectionRule rule)
-{
-  switch(rule){
-    case options::ErrorSelectionRule::MINIMUM_AMOUNT:
-    case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
-      ei.setAmount(computeDiff(ei.getVariable()));
-      break;
-    case options::ErrorSelectionRule::SUM_METRIC:
-      ei.setMetric(sumMetric(ei.getVariable()));
-      break;
-    case options::ErrorSelectionRule::VAR_ORDER:
-      // do nothing
-      break;
-  }
-}
-
-void ErrorSet::setSelectionRule(options::ErrorSelectionRule rule)
-{
-  if(rule != getSelectionRule()){
-    FocusSet into(ComparatorPivotRule(this, rule));
-    FocusSet::const_iterator iter = d_focus.begin();
-    FocusSet::const_iterator i_end = d_focus.end();
-    for(; iter != i_end; ++iter){
-      ArithVar v = *iter;
-      ErrorInformation& ei = d_errInfo.get(v);
-      if(ei.inFocus()){
-        recomputeAmount(ei, rule);
-        FocusSetHandle handle = into.push(v);
-        ei.setHandle(handle);
-      }
-    }
-    d_focus.swap(into);
-    d_selectionRule = rule;
-  }
-  Assert(getSelectionRule() == rule);
-}
-
-ComparatorPivotRule::ComparatorPivotRule(const ErrorSet* es,
-                                         options::ErrorSelectionRule r)
-    : d_errorSet(es), d_rule(r)
-{}
-
-bool ComparatorPivotRule::operator()(ArithVar v, ArithVar u) const {
-  switch(d_rule){
-    case options::ErrorSelectionRule::VAR_ORDER:
-      // This needs to be the reverse of the minVariableOrder
-      return v > u;
-    case options::ErrorSelectionRule::SUM_METRIC:
-    {
-      uint32_t v_metric = d_errorSet->getMetric(v);
-      uint32_t u_metric = d_errorSet->getMetric(u);
-      if(v_metric == u_metric){
-        return v > u;
-      }else{
-        return v_metric > u_metric;
-      }
-    }
-    case options::ErrorSelectionRule::MINIMUM_AMOUNT:
-    {
-      const DeltaRational& vamt = d_errorSet->getAmount(v);
-      const DeltaRational& uamt = d_errorSet->getAmount(u);
-      int cmp = vamt.cmp(uamt);
-      if(cmp == 0){
-        return v > u;
-      }else{
-        return cmp > 0;
-      }
-    }
-    case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
-    {
-      const DeltaRational& vamt = d_errorSet->getAmount(v);
-      const DeltaRational& uamt = d_errorSet->getAmount(u);
-      int cmp = vamt.cmp(uamt);
-      if(cmp == 0){
-        return v > u;
-      }else{
-        return cmp < 0;
-      }
-    }
-  }
-  Unreachable();
-}
-
-void ErrorSet::update(ErrorInformation& ei){
-  if(ei.inFocus()){
-
-    switch(getSelectionRule()){
-      case options::ErrorSelectionRule::MINIMUM_AMOUNT:
-      case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
-        ei.setAmount(computeDiff(ei.getVariable()));
-        d_focus.update(ei.getHandle(), ei.getVariable());
-        break;
-      case options::ErrorSelectionRule::SUM_METRIC:
-        ei.setMetric(sumMetric(ei.getVariable()));
-        d_focus.update(ei.getHandle(), ei.getVariable());
-        break;
-      case options::ErrorSelectionRule::VAR_ORDER:
-        // do nothing
-        break;
-    }
-  }
-}
-
-/** A variable becomes satisfied. */
-void ErrorSet::transitionVariableOutOfError(ArithVar v) {
-  Assert(!inconsistent(v));
-  ErrorInformation& ei = d_errInfo.get(v);
-  Assert(ei.debugInitialized());
-  if(ei.isRelaxed()){
-    ConstraintP viol = ei.getViolated();
-    if(ei.sgn() > 0){
-      d_variables.setLowerBoundConstraint(viol);
-    }else{
-      d_variables.setUpperBoundConstraint(viol);
-    }
-    Assert(!inconsistent(v));
-    ei.setUnrelaxed();
-  }
-  if(ei.inFocus()){
-    d_focus.erase(ei.getHandle());
-    ei.setInFocus(false);
-  }
-  d_errInfo.remove(v);
-}
-
-
-void ErrorSet::transitionVariableIntoError(ArithVar v) {
-  Assert(inconsistent(v));
-  bool vilb = d_variables.cmpAssignmentLowerBound(v) < 0;
-  int sgn = vilb ? 1 : -1;
-  ConstraintP c = vilb ?
-    d_variables.getLowerBoundConstraint(v) : d_variables.getUpperBoundConstraint(v);
-  d_errInfo.set(v, ErrorInformation(v, c, sgn));
-  ErrorInformation& ei = d_errInfo.get(v);
-
-  switch(getSelectionRule()){
-    case options::ErrorSelectionRule::MINIMUM_AMOUNT:
-    case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
-      ei.setAmount(computeDiff(v));
-      break;
-    case options::ErrorSelectionRule::SUM_METRIC:
-      ei.setMetric(sumMetric(ei.getVariable()));
-      break;
-    case options::ErrorSelectionRule::VAR_ORDER:
-      // do nothing
-      break;
-  }
-  ei.setInFocus(true);
-  FocusSetHandle handle = d_focus.push(v);
-  ei.setHandle(handle);
-}
-
-void ErrorSet::dropFromFocus(ArithVar v) {
-  Assert(inError(v));
-  ErrorInformation& ei = d_errInfo.get(v);
-  Assert(ei.inFocus());
-  d_focus.erase(ei.getHandle());
-  ei.setInFocus(false);
-  d_outOfFocus.push_back(v);
-}
-
-void ErrorSet::addBackIntoFocus(ArithVar v) {
-  Assert(inError(v));
-  ErrorInformation& ei = d_errInfo.get(v);
-  Assert(!ei.inFocus());
-  switch(getSelectionRule()){
-    case options::ErrorSelectionRule::MINIMUM_AMOUNT:
-    case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
-      ei.setAmount(computeDiff(v));
-      break;
-    case options::ErrorSelectionRule::SUM_METRIC:
-      ei.setMetric(sumMetric(v));
-      break;
-    case options::ErrorSelectionRule::VAR_ORDER:
-      // do nothing
-      break;
-  }
-
-  ei.setInFocus(true);
-  FocusSetHandle handle = d_focus.push(v);
-  ei.setHandle(handle);
-}
-
-void ErrorSet::blur(){
-  while(!d_outOfFocus.empty()){
-    ArithVar v = d_outOfFocus.back();
-    d_outOfFocus.pop_back();
-
-    if(inError(v) && !inFocus(v)){
-      addBackIntoFocus(v);
-    }
-  }
-}
-
-
-
-int ErrorSet::popSignal() {
-  ArithVar back = d_signals.back();
-  d_signals.pop_back();
-
-  if(inError(back)){
-    ErrorInformation& ei = d_errInfo.get(back);
-    int prevSgn = ei.sgn();
-    int focusSgn = ei.focusSgn();
-    bool vilb = d_variables.cmpAssignmentLowerBound(back) < 0;
-    bool viub = d_variables.cmpAssignmentUpperBound(back) > 0;
-    if(vilb || viub){
-      Assert(!vilb || !viub);
-      int currSgn = vilb ? 1 : -1;
-      if(currSgn != prevSgn){
-        ConstraintP curr = vilb ?  d_variables.getLowerBoundConstraint(back)
-          : d_variables.getUpperBoundConstraint(back);
-        ei.reset(curr, currSgn);
-      }
-      update(ei);
-    }else{
-      transitionVariableOutOfError(back);
-    }
-    return focusSgn;
-  }else if(inconsistent(back)){
-    transitionVariableIntoError(back);
-  }
-  return 0;
-}
-
-void ErrorSet::clear(){
-  // Nothing should be relaxed!
-  d_signals.clear();
-  d_errInfo.purge();
-  d_focus.clear();
-}
-
-void ErrorSet::clearFocus(){
-  for(ErrorSet::focus_iterator i =focusBegin(), i_end = focusEnd(); i != i_end; ++i){
-    ArithVar f = *i;
-    ErrorInformation& fei = d_errInfo.get(f);
-    fei.setInFocus(false);
-    d_outOfFocus.push_back(f);
-  }
-  d_focus.clear();
-}
-
-void ErrorSet::reduceToSignals(){
-  for(error_iterator ei=errorBegin(), ei_end=errorEnd(); ei != ei_end; ++ei){
-    ArithVar curr = *ei;
-    signalVariable(curr);
-  }
-
-  d_errInfo.purge();
-  d_focus.clear();
-  d_outOfFocus.clear();
-}
-
-DeltaRational ErrorSet::computeDiff(ArithVar v) const{
-  Assert(inconsistent(v));
-  const DeltaRational& beta = d_variables.getAssignment(v);
-  DeltaRational diff = d_variables.cmpAssignmentLowerBound(v) < 0 ?
-    d_variables.getLowerBound(v) - beta:
-    beta - d_variables.getUpperBound(v);
-
-  Assert(diff.sgn() > 0);
-  return diff;
-}
-
-void ErrorSet::debugPrint(std::ostream& out) const {
-  out << "error set debugprint" << endl;
-  for(error_iterator i = errorBegin(), i_end = errorEnd();
-      i != i_end; ++i){
-    ArithVar e = *i;
-    const ErrorInformation& ei = d_errInfo[e];
-    ei.print(out);
-    out << "  ";
-    d_variables.printModel(e, out);
-    out << endl;
-  }
-  out << "focus ";
-  for(focus_iterator i = focusBegin(), i_end = focusEnd();
-      i != i_end; ++i){
-    out << *i << " ";
-  }
-  out << ";" << endl;
-}
-
-void ErrorSet::focusDownToJust(ArithVar v) {
-  clearFocus();
-
-  ErrorInformation& vei = d_errInfo.get(v);
-  vei.setInFocus(true);
-  FocusSetHandle handle = d_focus.push(v);
-  vei.setHandle(handle);
-}
-
-void ErrorSet::pushErrorInto(ArithVarVec& vec) const{
-  for(error_iterator i = errorBegin(), e = errorEnd(); i != e; ++i ){
-    vec.push_back(*i);
-  }
-}
-
-void ErrorSet::pushFocusInto(ArithVarVec& vec) const{
-  for(focus_iterator i = focusBegin(), e = focusEnd(); i != e; ++i ){
-    vec.push_back(*i);
-  }
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/error_set.h b/src/theory/arith/error_set.h
deleted file mode 100644 (file)
index ec61ee0..0000000
+++ /dev/null
@@ -1,421 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Mathias Preiner, Andres Noetzli
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <memory>
-#include <vector>
-
-#include "options/arith_options.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/bound_counts.h"
-#include "theory/arith/callbacks.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/tableau_sizes.h"
-#include "util/bin_heap.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-
-/**
- * The priority queue has 3 different modes of operation:
- * - Collection
- *   This passively collects arithmetic variables that may be inconsistent.
- *   This does not maintain any heap structure.
- *   dequeueInconsistentBasicVariable() does not work in this mode!
- *   Entering this mode requires the queue to be empty.
- *
- * - Difference Queue
- *   This mode uses the difference between a variables and its bound
- *   to determine which to dequeue first.
- *
- * - Variable Order Queue
- *   This mode uses the variable order to determine which ArithVar is dequeued first.
- *
- * The transitions between the modes of operation are:
- *  Collection => Difference Queue
- *  Difference Queue => Variable Order Queue
- *  Difference Queue => Collection (queue must be empty!)
- *  Variable Order Queue => Collection (queue must be empty!)
- *
- * The queue begins in Collection mode.
- */
-
-
-class ErrorSet;
-
-class ComparatorPivotRule {
-private:
-  const ErrorSet* d_errorSet;
-
-  options::ErrorSelectionRule d_rule;
-
- public:
-  ComparatorPivotRule();
-  ComparatorPivotRule(const ErrorSet* es, options::ErrorSelectionRule r);
-
-  bool operator()(ArithVar v, ArithVar u) const;
-  options::ErrorSelectionRule getRule() const { return d_rule; }
-};
-
-// typedef boost::heap::d_ary_heap<
-//   ArithVar,
-//   boost::heap::arity<2>,
-//   boost::heap::compare<ComparatorPivotRule>,
-//   boost::heap::mutable_<true> > FocusSet;
-//
-// typedef FocusSet::handle_type FocusSetHandle;
-
-// typedef CVC5_PB_DS_NAMESPACE::priority_queue<
-//   ArithVar,
-//   ComparatorPivotRule,
-//   CVC5_PB_DS_NAMESPACE::pairing_heap_tag> FocusSet;
-
-// typedef FocusSet::point_iterator FocusSetHandle;
-
-typedef BinaryHeap<ArithVar, ComparatorPivotRule> FocusSet;
-typedef FocusSet::handle FocusSetHandle;
-
-
-class ErrorInformation {
-private:
-  /** The variable that is in error. */
-  ArithVar d_variable;
-
-  /**
-   * The constraint that was violated.
-   * This needs to be saved in case that the
-   * violated constraint
-   */
-  ConstraintP d_violated;
-
-  /**
-   * This is the sgn of the first derivate the variable must move to satisfy
-   * the bound violated.
-   * If d_sgn > 0, then d_violated was a lowerbound.
-   * If d_sgn < 0, then d_violated was an upperbound.
-   */
-  int d_sgn;
-
-  /**
-   * If this is true, then the bound is no longer set on d_variables.
-   * This MUST be undone before this is deleted.
-   */
-  bool d_relaxed;
-
-  /**
-   * If this is true, then the variable is in the focus set and the focus heap.
-   * d_handle is then a reasonable thing to interpret.
-   * If this is false, the variable is somewhere in
-   */
-  bool d_inFocus;
-  FocusSetHandle d_handle;
-
-  /**
-   * Auxillary information for storing the difference between a variable and its bound.
-   * Only set on signals.
-   */
-  std::unique_ptr<DeltaRational> d_amount;
-
-  /** */
-  uint32_t d_metric;
-
-public:
-  ErrorInformation();
-  ErrorInformation(ArithVar var, ConstraintP vio, int sgn);
-  ~ErrorInformation();
-  ErrorInformation(const ErrorInformation& ei);
-  ErrorInformation& operator=(const ErrorInformation& ei);
-
-  void reset(ConstraintP c, int sgn);
-
-  inline ArithVar getVariable() const { return d_variable; }
-
-  bool isRelaxed() const { return d_relaxed; }
-  void setRelaxed()
-  {
-    Assert(!d_relaxed);
-    d_relaxed = true;
-  }
-  void setUnrelaxed()
-  {
-    Assert(d_relaxed);
-    d_relaxed = false;
-  }
-
-  inline int sgn() const { return d_sgn; }
-
-  inline bool inFocus() const { return d_inFocus; }
-  inline int focusSgn() const {
-    return (d_inFocus) ? sgn() : 0;
-  }
-
-  inline void setInFocus(bool inFocus) { d_inFocus = inFocus; }
-
-  const DeltaRational& getAmount() const {
-    Assert(d_amount != nullptr);
-    return *d_amount;
-  }
-
-  void setAmount(const DeltaRational& am);
-  void setMetric(uint32_t m) { d_metric = m; }
-  uint32_t getMetric() const { return d_metric; }
-
-  inline void setHandle(FocusSetHandle h) {
-    Assert(d_inFocus);
-    d_handle = h;
-  }
-  inline const FocusSetHandle& getHandle() const{ return d_handle; }
-
-  inline ConstraintP getViolated() const { return d_violated; }
-
-  bool debugInitialized() const {
-    return
-      d_variable != ARITHVAR_SENTINEL &&
-      d_violated != NullConstraint &&
-      d_sgn != 0;
-  }
-  void print(std::ostream& os) const {
-    os << "{ErrorInfo: " << d_variable
-       << ", " << d_violated
-       << ", " << d_sgn
-       << ", " << d_relaxed
-       << ", " << d_inFocus;
-    if (d_amount == nullptr)
-    {
-      os << "nullptr";
-    }
-    else
-    {
-      os << (*d_amount);
-    }
-    os << "}";
-  }
-};
-
-class ErrorInfoMap : public DenseMap<ErrorInformation> {};
-
-class ErrorSet {
-private:
-  /**
-   * Reference to the arithmetic partial model for checking if a variable
-   * is consistent with its upper and lower bounds.
-   */
-  ArithVariables& d_variables;
-
-  /**
-   * The set of all variables that violate exactly one of their bounds.
-   */
-  ErrorInfoMap d_errInfo;
-
-  options::ErrorSelectionRule d_selectionRule;
-  /**
-   * The ordered heap for the variables that are in ErrorSet.
-   */
-  FocusSet d_focus;
-
-
-  /**
-   * A strict subset of the error set.
-   *   d_outOfFocus \neq d_errInfo.
-   *
-   * Its symbolic complement is Focus.
-   *   d_outOfFocus \intersect Focus == \emptyset
-   *   d_outOfFocus \union Focus == d_errInfo
-   */
-  ArithVarVec d_outOfFocus;
-
-  /**
-   * Before a variable is added to the error set, it is added to the signals list.
-   * A variable may appear on the list multiple times.
-   * This introduces a delay.
-   */
-  ArithVarVec d_signals;
-
-  TableauSizes d_tableauSizes;
-
-  BoundCountingLookup d_boundLookup;
-
-  /**
-   * Computes the difference between the assignment and its bound for x.
-   */
-public:
-  DeltaRational computeDiff(ArithVar x) const;
-private:
- void recomputeAmount(ErrorInformation& ei, options::ErrorSelectionRule r);
-
- void update(ErrorInformation& ei);
- void transitionVariableOutOfError(ArithVar v);
- void transitionVariableIntoError(ArithVar v);
- void addBackIntoFocus(ArithVar v);
-
-public:
-
-  /** The new focus set is the entire error set. */
-  void blur();
-  void dropFromFocus(ArithVar v);
-
-  void dropFromFocusAll(const ArithVarVec& vec) {
-    for(ArithVarVec::const_iterator i = vec.begin(), i_end = vec.end(); i != i_end; ++i){
-      ArithVar v = *i;
-      dropFromFocus(v);
-    }
-  }
-
-  ErrorSet(ArithVariables& var, TableauSizes tabSizes, BoundCountingLookup boundLookup);
-
-  typedef ErrorInfoMap::const_iterator error_iterator;
-  error_iterator errorBegin() const { return d_errInfo.begin(); }
-  error_iterator errorEnd() const { return d_errInfo.end(); }
-
-  bool inError(ArithVar v) const { return d_errInfo.isKey(v); }
-  bool inFocus(ArithVar v) const { return d_errInfo[v].inFocus(); }
-
-  void pushErrorInto(ArithVarVec& vec) const;
-  void pushFocusInto(ArithVarVec& vec) const;
-
-  options::ErrorSelectionRule getSelectionRule() const;
-  void setSelectionRule(options::ErrorSelectionRule rule);
-
-  inline ArithVar topFocusVariable() const{
-    Assert(!focusEmpty());
-    return d_focus.top();
-  }
-
-  inline void signalVariable(ArithVar var){
-    d_signals.push_back(var);
-  }
-
-  inline void signalUnderCnd(ArithVar var, bool b){
-    if(b){ signalVariable(var); }
-  }
-
-  inline bool inconsistent(ArithVar var) const{
-    return !d_variables.assignmentIsConsistent(var) ;
-  }
-  inline void signalIfInconsistent(ArithVar var){
-    signalUnderCnd(var, inconsistent(var));
-  }
-
-  inline bool errorEmpty() const{
-    return d_errInfo.empty();
-  }
-  inline uint32_t errorSize() const{
-    return d_errInfo.size();
-  }
-
-  inline bool focusEmpty() const {
-    return d_focus.empty();
-  }
-  inline uint32_t focusSize() const{
-    return d_focus.size();
-  }
-
-  inline int getSgn(ArithVar x) const {
-    Assert(inError(x));
-    return d_errInfo[x].sgn();
-  }
-  inline int focusSgn(ArithVar v) const {
-    if(inError(v)){
-      return d_errInfo[v].focusSgn();
-    }else{
-      return 0;
-    }
-  }
-
-  void focusDownToJust(ArithVar v);
-
-  void clearFocus();
-
-  /** Clears the set. */
-  void clear();
-  void reduceToSignals();
-
-  bool noSignals() const {
-    return d_signals.empty();
-  }
-  bool moreSignals() const {
-    return !noSignals();
-  }
-  ArithVar topSignal() const {
-    Assert(moreSignals());
-    return d_signals.back();
-  }
-
-  /**
-   * Moves a variable out of the signals.
-   * This moves it into the error set.
-   * Return the previous focus sign.
-   */
-  int popSignal();
-
-  const DeltaRational& getAmount(ArithVar v) const {
-    return d_errInfo[v].getAmount();
-  }
-
-  uint32_t sumMetric(ArithVar a) const{
-    Assert(inError(a));
-    BoundCounts bcs = d_boundLookup.atBounds(a);
-    uint32_t count = getSgn(a) > 0 ? bcs.upperBoundCount() : bcs.lowerBoundCount();
-
-    uint32_t length = d_tableauSizes.getRowLength(a);
-
-    return (length - count);
-  }
-
-  uint32_t getMetric(ArithVar a) const {
-    return d_errInfo[a].getMetric();
-  }
-
-  ConstraintP getViolated(ArithVar a) const {
-    return d_errInfo[a].getViolated();
-  }
-
-
-  typedef FocusSet::const_iterator focus_iterator;
-  focus_iterator focusBegin() const { return d_focus.begin(); }
-  focus_iterator focusEnd() const { return d_focus.end(); }
-
-  void debugPrint(std::ostream& out) const;
-
-private:
-  class Statistics {
-  public:
-    IntStat d_enqueues;
-    IntStat d_enqueuesCollection;
-    IntStat d_enqueuesDiffMode;
-    IntStat d_enqueuesVarOrderMode;
-
-    IntStat d_enqueuesCollectionDuplicates;
-    IntStat d_enqueuesVarOrderModeDuplicates;
-
-    Statistics();
-  };
-
-  Statistics d_statistics;
-};
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/fc_simplex.cpp b/src/theory/arith/fc_simplex.cpp
deleted file mode 100644 (file)
index a4bb076..0000000
+++ /dev/null
@@ -1,787 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T)decision procedure.
- */
-#include "theory/arith/fc_simplex.h"
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/error_set.h"
-#include "util/statistics_stats.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-FCSimplexDecisionProcedure::FCSimplexDecisionProcedure(
-    Env& env,
-    LinearEqualityModule& linEq,
-    ErrorSet& errors,
-    RaiseConflict conflictChannel,
-    TempVarMalloc tvmalloc)
-    : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
-      d_focusSize(0),
-      d_focusErrorVar(ARITHVAR_SENTINEL),
-      d_focusCoefficients(),
-      d_pivotBudget(0),
-      d_prevWitnessImprovement(AntiProductive),
-      d_witnessImprovementInARow(0),
-      d_sgnDisagreements(),
-      d_statistics("theory::arith::FC::", d_pivots)
-{ }
-
-FCSimplexDecisionProcedure::Statistics::Statistics(const std::string& name,
-                                                   uint32_t& pivots)
-    : d_initialSignalsTime(
-        smtStatisticsRegistry().registerTimer(name + "initialProcessTime")),
-      d_initialConflicts(
-          smtStatisticsRegistry().registerInt(name + "UpdateConflicts")),
-      d_fcFoundUnsat(smtStatisticsRegistry().registerInt(name + "FoundUnsat")),
-      d_fcFoundSat(smtStatisticsRegistry().registerInt(name + "FoundSat")),
-      d_fcMissed(smtStatisticsRegistry().registerInt(name + "Missed")),
-      d_fcTimer(smtStatisticsRegistry().registerTimer(name + "Timer")),
-      d_fcFocusConstructionTimer(
-          smtStatisticsRegistry().registerTimer(name + "Construction")),
-      d_selectUpdateForDualLike(smtStatisticsRegistry().registerTimer(
-          name + "selectUpdateForDualLike")),
-      d_selectUpdateForPrimal(smtStatisticsRegistry().registerTimer(
-          name + "selectUpdateForPrimal")),
-      d_finalCheckPivotCounter(
-          smtStatisticsRegistry().registerReference<uint32_t>(
-              name + "lastPivots", pivots))
-{
-}
-
-Result::Status FCSimplexDecisionProcedure::findModel(bool exactResult)
-{
-  Assert(d_conflictVariables.empty());
-  Assert(d_sgnDisagreements.empty());
-
-  d_pivots = 0;
-
-  if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
-    Trace("arith::findModel") << "fcFindModel() trivial" << endl;
-    Assert(d_conflictVariables.empty());
-    return Result::SAT;
-  }
-
-  // We need to reduce this because of
-  d_errorSet.reduceToSignals();
-
-  // We must start tracking NOW
-  d_errorSet.setSelectionRule(options::ErrorSelectionRule::SUM_METRIC);
-
-  if(initialProcessSignals()){
-    d_conflictVariables.purge();
-    Trace("arith::findModel") << "fcFindModel() early conflict" << endl;
-    Assert(d_conflictVariables.empty());
-    return Result::UNSAT;
-  }else if(d_errorSet.errorEmpty()){
-    Trace("arith::findModel") << "fcFindModel() fixed itself" << endl;
-    Assert(d_conflictVariables.empty());
-    return Result::SAT;
-  }
-
-  Trace("arith::findModel") << "fcFindModel() start non-trivial" << endl;
-
-  exactResult |= d_varOrderPivotLimit < 0;
-
-  d_prevWitnessImprovement = HeuristicDegenerate;
-  d_witnessImprovementInARow = 0;
-
-  Result::Status result = Result::UNKNOWN;
-
-  if (result == Result::UNKNOWN)
-  {
-    if(exactResult){
-      d_pivotBudget = -1;
-    }else{
-      d_pivotBudget = d_varOrderPivotLimit;
-    }
-
-    result = dualLike();
-
-    if(result ==  Result::UNSAT){
-      ++(d_statistics.d_fcFoundUnsat);
-    }else if(d_errorSet.errorEmpty()){
-      ++(d_statistics.d_fcFoundSat);
-    }else{
-      ++(d_statistics.d_fcMissed);
-    }
-  }
-
-  Assert(!d_errorSet.moreSignals());
-  if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
-  {
-    result = Result::SAT;
-  }
-
-  // ensure that the conflict variable is still in the queue.
-  d_conflictVariables.purge();
-
-  Trace("arith::findModel") << "end findModel() " << result << endl;
-
-  Assert(d_conflictVariables.empty());
-  return result;
-}
-
-
-void FCSimplexDecisionProcedure::logPivot(WitnessImprovement w){
-  if(d_pivotBudget > 0) {
-    --d_pivotBudget;
-  }
-  Assert(w != AntiProductive);
-
-  if(w == d_prevWitnessImprovement){
-    ++d_witnessImprovementInARow;
-    // ignore overflow : probably never reached
-    if(d_witnessImprovementInARow == 0){
-      --d_witnessImprovementInARow;
-    }
-  }else{
-    if(w != BlandsDegenerate){
-      d_witnessImprovementInARow = 1;
-    }
-    // if w == BlandsDegenerate do not reset the counter
-    d_prevWitnessImprovement = w;
-  }
-  if(strongImprovement(w)){
-    d_leavingCountSinceImprovement.purge();
-  }
-
-  Trace("logPivot") << "logPivot " << d_prevWitnessImprovement << " "  << d_witnessImprovementInARow << endl;
-
-}
-
-uint32_t FCSimplexDecisionProcedure::degeneratePivotsInARow() const {
-  switch(d_prevWitnessImprovement){
-  case ConflictFound:
-  case ErrorDropped:
-  case FocusImproved:
-    return 0;
-  case HeuristicDegenerate:
-  case BlandsDegenerate:
-    return d_witnessImprovementInARow;
-  // Degenerate is unreachable for its own reasons
-  case Degenerate:
-  case FocusShrank:
-  case AntiProductive:
-    Unreachable();
-    return -1;
-  }
-  Unreachable();
-}
-
-void FCSimplexDecisionProcedure::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
-  uint32_t newErrorSize = d_errorSet.errorSize();
-  uint32_t newFocusSize = d_errorSet.focusSize();
-
-  //Assert(!d_conflictVariables.empty() || newFocusSize <= d_focusSize);
-  Assert(!d_conflictVariables.empty() || newErrorSize <= d_errorSize);
-
-  if(newFocusSize == 0 || !d_conflictVariables.empty() ){
-    tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
-    d_focusErrorVar = ARITHVAR_SENTINEL;
-  }else if(2*newFocusSize < d_focusSize ){
-    tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
-    d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
-  }else{
-    adjustInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, focusChanges);
-  }
-
-  d_errorSize = newErrorSize;
-  d_focusSize = newFocusSize;
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::adjustFocusShrank(const ArithVarVec& dropped){
-  Assert(dropped.size() > 0);
-  Assert(d_errorSet.focusSize() == d_focusSize);
-  Assert(d_errorSet.focusSize() > dropped.size());
-
-  uint32_t newFocusSize = d_focusSize - dropped.size();
-  Assert(newFocusSize > 0);
-
-  if(2 * newFocusSize <= d_focusSize){
-    d_errorSet.dropFromFocusAll(dropped);
-    tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
-    d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
-  }else{
-    shrinkInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, dropped);
-    d_errorSet.dropFromFocusAll(dropped);
-  }
-
-  d_focusSize = newFocusSize;
-  Assert(d_errorSet.focusSize() == d_focusSize);
-  return FocusShrank;
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::focusDownToJust(ArithVar v){
-  // uint32_t newErrorSize = d_errorSet.errorSize();
-  // uint32_t newFocusSize = d_errorSet.focusSize();
-  Assert(d_focusSize == d_errorSet.focusSize());
-  Assert(d_focusSize > 1);
-  Assert(d_errorSet.inFocus(v));
-
-  d_errorSet.focusDownToJust(v);
-  Assert(d_errorSet.focusSize() == 1);
-  d_focusSize = 1;
-
-  tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
-  d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
-
-  return FocusShrank;
-}
-
-
-
-UpdateInfo FCSimplexDecisionProcedure::selectPrimalUpdate(ArithVar basic, LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
-  UpdateInfo selected;
-
-  Trace("arith::selectPrimalUpdate")
-      << "selectPrimalUpdate" << endl
-      << basic << " " << d_tableau.basicRowLength(basic) << " "
-      << d_linEq.debugBasicAtBoundCount(basic) << endl;
-
-  static constexpr int s_maxCandidatesAfterImprove = 3;
-  bool isFocus = basic == d_focusErrorVar;
-  Assert(isFocus || d_errorSet.inError(basic));
-  int basicDir =  isFocus? 1 : d_errorSet.getSgn(basic);
-  bool dualLike = !isFocus && d_focusSize > 1;
-
-  if(!isFocus){
-    loadFocusSigns();
-  }
-
-  decreasePenalties();
-
-  typedef std::vector<Cand> CandVector;
-  CandVector candidates;
-
-  for(Tableau::RowIterator ri = d_tableau.basicRowIterator(basic); !ri.atEnd(); ++ri){
-    const Tableau::Entry& e = *ri;
-    ArithVar curr = e.getColVar();
-    if(curr == basic){ continue; }
-
-    int sgn = e.getCoefficient().sgn();
-    int curr_movement = basicDir * sgn;
-
-    bool candidate =
-      (curr_movement > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
-      (curr_movement < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
-
-    Trace("arith::selectPrimalUpdate")
-      << "storing " << basic
-      << " " << curr
-      << " " << candidate
-      << " " << e.getCoefficient()
-      << " " << curr_movement
-      << " " << focusCoefficient(curr) << endl;
-
-    if(!candidate) { continue; }
-
-    if(!isFocus){
-      const Rational& focusC = focusCoefficient(curr);
-      Assert(dualLike || !focusC.isZero());
-      if(dualLike && curr_movement != focusC.sgn()){
-        Trace("arith::selectPrimalUpdate") << "sgn disagreement " << curr << endl;
-        d_sgnDisagreements.push_back(curr);
-        continue;
-      }else{
-        candidates.push_back(Cand(curr, penalty(curr), curr_movement, &focusC));
-      }
-    }else{
-      candidates.push_back(Cand(curr, penalty(curr), curr_movement, &e.getCoefficient()));
-    }
-  }
-
-  CompPenaltyColLength colCmp(&d_linEq, options().arith.havePenalties);
-  CandVector::iterator i = candidates.begin();
-  CandVector::iterator end = candidates.end();
-  std::make_heap(i, end, colCmp);
-
-  bool checkEverything = d_pivots == 0;
-
-  int candidatesAfterFocusImprove = 0;
-  while(i != end && (checkEverything || candidatesAfterFocusImprove <= s_maxCandidatesAfterImprove)){
-    std::pop_heap(i, end, colCmp);
-    --end;
-    Cand& cand = (*end);
-    ArithVar curr = cand.d_nb;
-    const Rational& coeff = *cand.d_coeff;
-
-    LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
-    UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
-
-    Trace("arith::selectPrimalUpdate")
-      << "selected " << selected << endl
-      << "currProp " << currProposal << endl
-      << "coeff " << coeff << endl;
-
-    Assert(!currProposal.uninitialized());
-
-    if(candidatesAfterFocusImprove > 0){
-      candidatesAfterFocusImprove++;
-    }
-
-    if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
-
-      selected = currProposal;
-      WitnessImprovement w = selected.getWitness(false);
-      Trace("arith::selectPrimalUpdate") << "selected " << w << endl;
-      setPenalty(curr, w);
-      if(improvement(w)){
-        bool exitEarly;
-        switch(w){
-        case ConflictFound: exitEarly = true; break;
-        case ErrorDropped:
-          if(checkEverything){
-            exitEarly = d_errorSize + selected.errorsChange() == 0;
-            Trace("arith::selectPrimalUpdate")
-              << "ee " << d_errorSize << " "
-              << selected.errorsChange() << " "
-              << d_errorSize + selected.errorsChange() << endl;
-          }else{
-            exitEarly = true;
-          }
-          break;
-        case FocusImproved:
-          candidatesAfterFocusImprove = 1;
-          exitEarly = false;
-          break;
-        default:
-          exitEarly = false; break;
-        }
-        if(exitEarly){ break; }
-      }
-    }else{
-      Trace("arith::selectPrimalUpdate") << "dropped "<< endl;
-    }
-
-  }
-
-  if(!isFocus){
-    unloadFocusSigns();
-  }
-  return selected;
-}
-
-bool FCSimplexDecisionProcedure::debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
-  if(inf.getWitness(useBlands) == w){
-    switch(w){
-    case ConflictFound: return inf.foundConflict();
-    case ErrorDropped: return inf.errorsChange() < 0;
-    case FocusImproved: return inf.focusDirection() > 0;
-    case FocusShrank: return false; // This is not a valid output
-    case Degenerate: return false; // This is not a valid output
-    case BlandsDegenerate: return useBlands;
-    case HeuristicDegenerate: return !useBlands;
-    case AntiProductive: return false;
-    }
-  }
-  return false;
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::primalImproveError(ArithVar errorVar){
-  bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
-  UpdateInfo selected = selectUpdateForPrimal (errorVar, useBlands);
-  Assert(!selected.uninitialized());
-  WitnessImprovement w = selected.getWitness(useBlands);
-  Assert(debugCheckWitness(selected, w, useBlands));
-
-  updateAndSignal(selected, w);
-  logPivot(w);
-  return w;
-}
-
-
-WitnessImprovement FCSimplexDecisionProcedure::focusUsingSignDisagreements(ArithVar basic){
-  Assert(!d_sgnDisagreements.empty());
-  Assert(d_errorSet.focusSize() >= 2);
-
-  if(TraceIsOn("arith::focus")){
-    d_errorSet.debugPrint(Trace("arith::focus"));
-  }
-
-  ArithVar nb = d_linEq.minBy(d_sgnDisagreements, &LinearEqualityModule::minColLength);
-  const Tableau::Entry& e_evar_nb = d_tableau.basicFindEntry(basic, nb);
-  int oppositeSgn = - (e_evar_nb.getCoefficient().sgn());
-  Trace("arith::focus") << "focusUsingSignDisagreements " << basic << " " << oppositeSgn << endl;
-
-  ArithVarVec dropped;
-
-  Tableau::ColIterator colIter = d_tableau.colIterator(nb);
-  for(; !colIter.atEnd(); ++colIter){
-    const Tableau::Entry& entry = *colIter;
-    Assert(entry.getColVar() == nb);
-
-    int sgn = entry.getCoefficient().sgn();
-    Trace("arith::focus")
-      << "on row "
-      << d_tableau.rowIndexToBasic(entry.getRowIndex())
-      << " "
-      << entry.getCoefficient() << endl;
-    ArithVar currRow = d_tableau.rowIndexToBasic(entry.getRowIndex());
-    if(d_errorSet.inError(currRow) && d_errorSet.inFocus(currRow)){
-      int errSgn = d_errorSet.getSgn(currRow);
-
-      if(errSgn * sgn == oppositeSgn){
-        dropped.push_back(currRow);
-        Trace("arith::focus") << "dropping from focus " << currRow << endl;
-      }
-    }
-  }
-
-  d_sgnDisagreements.clear();
-  return adjustFocusShrank(dropped);
-}
-
-bool debugSelectedErrorDropped(const UpdateInfo& selected, int32_t prevErrorSize, int32_t currErrorSize){
-  int diff = currErrorSize - prevErrorSize;
-  return selected.foundConflict() || diff == selected.errorsChange();
-}
-
-void FCSimplexDecisionProcedure::debugPrintSignal(ArithVar updated) const{
-  Trace("updateAndSignal") << "updated basic " << updated;
-  Trace("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
-  Trace("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
-  int dir = !d_variables.assignmentIsConsistent(updated) ?
-    d_errorSet.getSgn(updated) : 0;
-  Trace("updateAndSignal") << " dir " << dir;
-  Trace("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
-}
-
-bool debugUpdatedBasic(const UpdateInfo& selected, ArithVar updated){
-  if(selected.describesPivot() &&  updated == selected.leaving()){
-    return selected.foundConflict();
-  }else{
-    return true;
-  }
-}
-
-void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
-  ArithVar nonbasic = selected.nonbasic();
-
-  Trace("updateAndSignal") << "updateAndSignal " << selected << endl;
-
-  stringstream ss;
-
-  if(selected.describesPivot()){
-    ConstraintP limiting = selected.limiting();
-    ArithVar basic = limiting->getVariable();
-    Assert(d_linEq.basicIsTracked(basic));
-    d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
-  }else{
-    Assert(!selected.unbounded() || selected.errorsChange() < 0);
-
-    DeltaRational newAssignment =
-      d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
-
-    d_linEq.updateTracked(nonbasic, newAssignment);
-  }
-  d_pivots++;
-
-  increaseLeavingCount(nonbasic);
-
-  vector< pair<ArithVar, int> > focusChanges;
-  while(d_errorSet.moreSignals()){
-    ArithVar updated = d_errorSet.topSignal();
-    int prevFocusSgn = d_errorSet.popSignal();
-
-    if(d_tableau.isBasic(updated)){
-      Assert(!d_variables.assignmentIsConsistent(updated)
-             == d_errorSet.inError(updated));
-      if(TraceIsOn("updateAndSignal")){debugPrintSignal(updated);}
-      if(!d_variables.assignmentIsConsistent(updated)){
-        if(checkBasicForConflict(updated)){
-          reportConflict(updated);
-          Assert(debugUpdatedBasic(selected, updated));
-        }
-      }
-    }else{
-      Trace("updateAndSignal") << "updated nonbasic " << updated << endl;
-    }
-    int currFocusSgn = d_errorSet.focusSgn(updated);
-    if(currFocusSgn != prevFocusSgn){
-      int change = currFocusSgn - prevFocusSgn;
-      focusChanges.push_back(make_pair(updated, change));
-    }
-  }
-
-  if(TraceIsOn("error")){ d_errorSet.debugPrint(Trace("error")); }
-
-  Assert(
-      debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
-
-  adjustFocusAndError(selected, focusChanges);
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::dualLikeImproveError(ArithVar errorVar){
-  Assert(d_sgnDisagreements.empty());
-  Assert(d_focusSize > 1);
-
-  UpdateInfo selected = selectUpdateForDualLike(errorVar);
-
-  if(selected.uninitialized()){
-    // we found no proposals
-    // If this is empty, there must be an error on this variable!
-    // this should not be possible. It Should have been caught as a signal earlier
-    WitnessImprovement dropped = focusUsingSignDisagreements(errorVar);
-    Assert(d_sgnDisagreements.empty());
-
-    return dropped;
-  }else{
-    d_sgnDisagreements.clear();
-  }
-
-  Assert(d_sgnDisagreements.empty());
-  Assert(!selected.uninitialized());
-
-  if(selected.focusDirection() == 0 &&
-     d_prevWitnessImprovement == HeuristicDegenerate &&
-     d_witnessImprovementInARow >= s_focusThreshold){
-
-    Trace("focusDownToJust") << "focusDownToJust " << errorVar << endl;
-
-    return focusDownToJust(errorVar);
-  }else{
-    WitnessImprovement w = selected.getWitness(false);
-    Assert(debugCheckWitness(selected, w, false));
-    updateAndSignal(selected, w);
-    logPivot(w);
-    return w;
-  }
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::focusDownToLastHalf(){
-  Assert(d_focusSize >= 2);
-
-  Trace("focusDownToLastHalf") << "focusDownToLastHalf "
-       << d_errorSet.errorSize()  << " "
-       << d_errorSet.focusSize() << " ";
-
-  uint32_t half = d_focusSize/2;
-  ArithVarVec buf;
-  for(ErrorSet::focus_iterator i = d_errorSet.focusBegin(),
-        i_end = d_errorSet.focusEnd(); i != i_end; ++i){
-    if(half > 0){
-      --half;
-    } else{
-      buf.push_back(*i);
-    }
-  }
-  WitnessImprovement w = adjustFocusShrank(buf);
-  Trace("focusDownToLastHalf") << "-> " << d_errorSet.focusSize() << endl;
-  return w;
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::selectFocusImproving() {
-  Assert(d_focusErrorVar != ARITHVAR_SENTINEL);
-  Assert(d_focusSize >= 2);
-
-  LinearEqualityModule::UpdatePreferenceFunction upf =
-    &LinearEqualityModule::preferWitness<true>;
-
-  LinearEqualityModule::VarPreferenceFunction bpf =
-    &LinearEqualityModule::minRowLength;
-
-  UpdateInfo selected = selectPrimalUpdate(d_focusErrorVar, upf, bpf);
-
-  if(selected.uninitialized()){
-    Trace("selectFocusImproving") << "focus is optimum, but we don't have sat/conflict yet" << endl;
-
-    return focusDownToLastHalf();
-  }
-  Assert(!selected.uninitialized());
-  WitnessImprovement w = selected.getWitness(false);
-  Assert(debugCheckWitness(selected, w, false));
-
-  if(degenerate(w)){
-    Trace("selectFocusImproving") << "only degenerate" << endl;
-    if(d_prevWitnessImprovement == HeuristicDegenerate &&
-       d_witnessImprovementInARow >= s_focusThreshold){
-      Trace("selectFocusImproving") << "focus down been degenerate too long" << endl;
-      return focusDownToLastHalf();
-    }else{
-      Trace("selectFocusImproving") << "taking degenerate" << endl;
-    }
-  }
-  Trace("selectFocusImproving") << "selectFocusImproving did this " << selected << endl;
-
-  updateAndSignal(selected, w);
-  logPivot(w);
-  return w;
-}
-
-bool FCSimplexDecisionProcedure::debugDualLike(WitnessImprovement w,
-                                               ostream& out,
-                                               uint32_t prevFocusSize,
-                                               uint32_t prevErrorSize) const
-{
-  out << "DLV() ";
-  switch(w){
-  case ConflictFound:
-    out << "found conflict" << endl;
-    return !d_conflictVariables.empty();
-  case ErrorDropped:
-    out << "dropped " << prevErrorSize - d_errorSize << endl;
-    return d_errorSize < prevErrorSize;
-  case FocusImproved:
-    out << "focus improved"<< endl;
-    return d_errorSize == prevErrorSize;
-  case FocusShrank:
-    out << "focus shrank"<< endl;
-    return d_errorSize == prevErrorSize && prevFocusSize > d_focusSize;
-  case BlandsDegenerate:
-    out << "bland degenerate"<< endl;
-    return true;
-  case HeuristicDegenerate:
-    out << "heuristic degenerate"<< endl;
-    return true;
-  case AntiProductive:
-    out << "focus blur" << endl;
-    return prevFocusSize == 0;
-  case Degenerate:
-    return false;
-  }
-  return false;
-}
-
-Result::Status FCSimplexDecisionProcedure::dualLike()
-{
-  TimerStat::CodeTimer codeTimer(d_statistics.d_fcTimer);
-
-  Assert(d_sgnDisagreements.empty());
-  Assert(d_pivotBudget != 0);
-  Assert(d_errorSize == d_errorSet.errorSize());
-  Assert(d_errorSize > 0);
-  Assert(d_focusSize == d_errorSet.focusSize());
-  Assert(d_focusSize > 0);
-  Assert(d_conflictVariables.empty());
-  Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
-
-  d_scores.purge();
-  d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
-
-
-  while(d_pivotBudget != 0  && d_errorSize > 0 && d_conflictVariables.empty()){
-    Trace("dualLike") << "dualLike " << endl;
-
-    Assert(d_errorSet.noSignals());
-
-    WitnessImprovement w = AntiProductive;
-    uint32_t prevFocusSize = d_focusSize;
-    uint32_t prevErrorSize = d_errorSize;
-
-    if(d_focusSize == 0){
-      Assert(d_errorSize == d_errorSet.errorSize());
-      Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
-
-      d_errorSet.blur();
-
-      d_focusSize = d_errorSet.focusSize();
-
-      Assert(d_errorSize == d_focusSize);
-      Assert(d_errorSize >= 1);
-
-      d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
-
-      Trace("dualLike") << "blur " << d_focusSize << endl;
-    }else if(d_focusSize == 1){
-      // Possible outcomes:
-      // - errorSet size shrunk
-      // -- fixed v
-      // -- fixed something other than v
-      // - conflict
-      // - budget was exhausted
-
-      ArithVar e = d_errorSet.topFocusVariable();
-      Trace("dualLike") << "primalImproveError " << e << endl;
-      w = primalImproveError(e);
-    }else{
-
-      // Possible outcomes:
-      // - errorSet size shrunk
-      // -- fixed v
-      // -- fixed something other than v
-      // - conflict
-      // - budget was exhausted
-      // - focus went down
-      Assert(d_focusSize > 1);
-      ArithVar e = d_errorSet.topFocusVariable();
-      static constexpr unsigned s_sumMetricThreshold = 1;
-      if(d_errorSet.sumMetric(e) <= s_sumMetricThreshold){
-        Trace("dualLike") << "dualLikeImproveError " << e << endl;
-        w = dualLikeImproveError(e);
-      }else{
-        Trace("dualLike") << "selectFocusImproving " << endl;
-        w = selectFocusImproving();
-      }
-    }
-    Trace("dualLike") << "witnessImprovement: " << w << endl;
-    Assert(d_focusSize == d_errorSet.focusSize());
-    Assert(d_errorSize == d_errorSet.errorSize());
-
-    Assert(debugDualLike(w, Trace("dualLike"), prevFocusSize, prevErrorSize));
-    Trace("dualLike") << "Focus size " << d_focusSize << " (was " << prevFocusSize << ")" << endl;
-    Trace("dualLike") << "Error size " << d_errorSize << " (was " << prevErrorSize << ")" << endl;
-  }
-
-
-  if(d_focusErrorVar != ARITHVAR_SENTINEL){
-    tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
-    d_focusErrorVar = ARITHVAR_SENTINEL;
-  }
-
-  Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
-  if(!d_conflictVariables.empty()){
-    return Result::UNSAT;
-  }else if(d_errorSet.errorEmpty()){
-    Assert(d_errorSet.noSignals());
-    return Result::SAT;
-  }else{
-    Assert(d_pivotBudget == 0);
-    return Result::UNKNOWN;
-  }
-}
-
-
-void FCSimplexDecisionProcedure::loadFocusSigns(){
-  Assert(d_focusCoefficients.empty());
-  Assert(d_focusErrorVar != ARITHVAR_SENTINEL);
-  for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_focusErrorVar); !ri.atEnd(); ++ri){
-    const Tableau::Entry& e = *ri;
-    ArithVar curr = e.getColVar();
-    d_focusCoefficients.set(curr, &e.getCoefficient());
-  }
-}
-
-void FCSimplexDecisionProcedure::unloadFocusSigns(){
-  d_focusCoefficients.purge();
-}
-
-const Rational& FCSimplexDecisionProcedure::focusCoefficient(ArithVar nb) const {
-  if(d_focusCoefficients.isKey(nb)){
-    return *(d_focusCoefficients[nb]);
-  }else{
-    return d_zero;
-  }
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/fc_simplex.h b/src/theory/arith/fc_simplex.h
deleted file mode 100644 (file)
index c946091..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T)decision procedure.
- *
- * This implements the Simplex module for the Simpelx for DPLL(T) decision
- * procedure.
- * See the Simplex for DPLL(T) technical report for more background.(citation?)
- * This shares with the theory a Tableau, and a PartialModel that:
- *  - satisfies the equalities in the Tableau, and
- *  - the assignment for the non-basic variables satisfies their bounds.
- * This is required to either produce a conflict or satisifying PartialModel.
- * Further, we require being told when a basic variable updates its value.
- *
- * During the Simplex search we maintain a queue of variables.
- * The queue is required to contain all of the basic variables that voilate
- * their bounds.
- * As elimination from the queue is more efficient to be done lazily,
- * we do not maintain that the queue of variables needs to be only basic
- * variables or only variables that satisfy their bounds.
- *
- * The simplex procedure roughly follows Alberto's thesis. (citation?)
- * There is one round of selecting using a heuristic pivoting rule.
- * (See PreferenceFunction Documentation for the available options.)
- * The non-basic variable is the one that appears in the fewest pivots.
- * (Bruno says that Leonardo invented this first.)
- * After this, Bland's pivot rule is invoked.
- *
- * During this proccess, we periodically inspect the queue of variables to
- * 1) remove now extraneous extries,
- * 2) detect conflicts that are "waiting" on the queue but may not be detected
- *    by the current queue heuristics, and
- * 3) detect multiple conflicts.
- *
- * Conflicts are greedily slackened to use the weakest bounds that still
- * produce the conflict.
- *
- * Extra things tracked atm: (Subject to change at Tim's whims)
- * - A superset of all of the newly pivoted variables.
- * - A queue of additional conflicts that were discovered by Simplex.
- *   These are theory valid and are currently turned into lemmas
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "theory/arith/error_set.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/simplex.h"
-#include "theory/arith/simplex_update.h"
-#include "util/dense_map.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class FCSimplexDecisionProcedure : public SimplexDecisionProcedure{
-public:
- FCSimplexDecisionProcedure(Env& env,
-                            LinearEqualityModule& linEq,
-                            ErrorSet& errors,
-                            RaiseConflict conflictChannel,
-                            TempVarMalloc tvmalloc);
-
- Result::Status findModel(bool exactResult) override;
-
- // other error variables are dropping
- WitnessImprovement dualLikeImproveError(ArithVar evar);
- WitnessImprovement primalImproveError(ArithVar evar);
-
- // dual like
- // - found conflict
- // - satisfied error set
- Result::Status dualLike();
-
-private:
- static constexpr uint32_t PENALTY = 4;
- DenseMultiset d_scores;
- void decreasePenalties() { d_scores.removeOneOfEverything(); }
- uint32_t penalty(ArithVar x) const { return d_scores.count(x); }
- void setPenalty(ArithVar x, WitnessImprovement w)
- {
-   if (improvement(w))
-   {
-     if (d_scores.count(x) > 0)
-     {
-       d_scores.removeAll(x);
-     }
-   }
-   else
-   {
-     d_scores.setCount(x, PENALTY);
-   }
- }
-
-  /** The size of the focus set. */
-  uint32_t d_focusSize;
-
-  /** The current error focus variable. */
-  ArithVar d_focusErrorVar;
-
-  /**
-   * The signs of the coefficients in the focus set.
-   * This is empty until this has been loaded.
-   */
-  DenseMap<const Rational*> d_focusCoefficients;
-
-  /**
-   * Loads the signs of the coefficients of the variables on the row d_focusErrorVar
-   * into d_focusSgns.
-   */
-  void loadFocusSigns();
-
-  /** Unloads the information from d_focusSgns. */
-  void unloadFocusSigns();
-
-  /**
-   * The signs of a variable in the row of d_focusErrorVar.
-   * d_focusSgns must be loaded.
-   */
-  const Rational& focusCoefficient(ArithVar nb) const;
-
-  int32_t d_pivotBudget;
-
-  WitnessImprovement d_prevWitnessImprovement;
-  uint32_t d_witnessImprovementInARow;
-
-  uint32_t degeneratePivotsInARow() const;
-
-  static constexpr uint32_t s_focusThreshold = 6;
-  static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnLeaving = 100;
-  static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnEntering = 10;
-
-  DenseMap<uint32_t> d_leavingCountSinceImprovement;
-  void increaseLeavingCount(ArithVar x){
-    if(!d_leavingCountSinceImprovement.isKey(x)){
-      d_leavingCountSinceImprovement.set(x,1);
-    }else{
-      (d_leavingCountSinceImprovement.get(x))++;
-    }
-  }
-  LinearEqualityModule::UpdatePreferenceFunction selectLeavingFunction(ArithVar x){
-    bool useBlands = d_leavingCountSinceImprovement.isKey(x) &&
-      d_leavingCountSinceImprovement[x] >= s_maxDegeneratePivotsBeforeBlandsOnEntering;
-    if(useBlands) {
-      return &LinearEqualityModule::preferWitness<false>;
-    } else {
-      return &LinearEqualityModule::preferWitness<true>;
-    }
-  }
-
-  bool debugDualLike(WitnessImprovement w, std::ostream& out,
-                     uint32_t prevFocusSize, uint32_t prevErrorSize) const;
-
-  void debugPrintSignal(ArithVar updated) const;
-
-  ArithVarVec d_sgnDisagreements;
-
-  void logPivot(WitnessImprovement w);
-
-  void updateAndSignal(const UpdateInfo& selected, WitnessImprovement w);
-
-  UpdateInfo selectPrimalUpdate(ArithVar error,
-                                LinearEqualityModule::UpdatePreferenceFunction upf,
-                                LinearEqualityModule::VarPreferenceFunction bpf);
-
-
-  UpdateInfo selectUpdateForDualLike(ArithVar basic){
-    TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForDualLike);
-
-    LinearEqualityModule::UpdatePreferenceFunction upf =
-      &LinearEqualityModule::preferWitness<true>;
-    LinearEqualityModule::VarPreferenceFunction bpf =
-      &LinearEqualityModule::minVarOrder;
-    return selectPrimalUpdate(basic, upf, bpf);
-  }
-
-  UpdateInfo selectUpdateForPrimal(ArithVar basic, bool useBlands){
-    TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForPrimal);
-
-    LinearEqualityModule::UpdatePreferenceFunction upf;
-    if(useBlands) {
-      upf = &LinearEqualityModule::preferWitness<false>;
-    } else {
-      upf = &LinearEqualityModule::preferWitness<true>;
-    }
-
-    LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
-      &LinearEqualityModule::minVarOrder :
-      &LinearEqualityModule::minRowLength;
-
-    return selectPrimalUpdate(basic, upf, bpf);
-  }
-  WitnessImprovement selectFocusImproving() ;
-
-  WitnessImprovement focusUsingSignDisagreements(ArithVar basic);
-  WitnessImprovement focusDownToLastHalf();
-  WitnessImprovement adjustFocusShrank(const ArithVarVec& drop);
-  WitnessImprovement focusDownToJust(ArithVar v);
-
-
-  void adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges);
-
-  /**
-   * This is the main simplex for DPLL(T) loop.
-   * It runs for at most maxIterations.
-   *
-   * Returns true iff it has found a conflict.
-   * d_conflictVariable will be set and the conflict for this row is reported.
-   */
-  bool searchForFeasibleSolution(uint32_t maxIterations);
-
-  bool initialProcessSignals(){
-    TimerStat &timer = d_statistics.d_initialSignalsTime;
-    IntStat& conflictStat  = d_statistics.d_initialConflicts;
-    bool res = standardProcessSignals(timer, conflictStat);
-    d_focusSize = d_errorSet.focusSize();
-    return res;
-  }
-
-  static bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands);
-
-  /** These fields are designed to be accessible to TheoryArith methods. */
-  class Statistics {
-  public:
-    TimerStat d_initialSignalsTime;
-    IntStat d_initialConflicts;
-
-    IntStat d_fcFoundUnsat;
-    IntStat d_fcFoundSat;
-    IntStat d_fcMissed;
-
-    TimerStat d_fcTimer;
-    TimerStat d_fcFocusConstructionTimer;
-
-    TimerStat d_selectUpdateForDualLike;
-    TimerStat d_selectUpdateForPrimal;
-
-    ReferenceStat<uint32_t> d_finalCheckPivotCounter;
-
-    Statistics(const std::string& name, uint32_t& pivots);
-  } d_statistics;
-};/* class FCSimplexDecisionProcedure */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/infer_bounds.cpp b/src/theory/arith/infer_bounds.cpp
deleted file mode 100644 (file)
index 30ae5f9..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Andres Noetzli, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/infer_bounds.h"
-#include "theory/rewriter.h"
-
-using namespace cvc5::internal::kind;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-using namespace inferbounds;
-
-InferBoundAlgorithm::InferBoundAlgorithm()
-  : d_alg(None)
-{}
-
-InferBoundAlgorithm::InferBoundAlgorithm(Algorithms a)
-  : d_alg(a)
-{
-  Assert(a != Simplex);
-}
-
-InferBoundAlgorithm::InferBoundAlgorithm(
-    const std::optional<int>& simplexRounds)
-    : d_alg(Simplex)
-{}
-
-Algorithms InferBoundAlgorithm::getAlgorithm() const{
-  return d_alg;
-}
-
-const std::optional<int>& InferBoundAlgorithm::getSimplexRounds() const
-{
-  Assert(getAlgorithm() == Simplex);
-  return d_simplexRounds;
-}
-
-InferBoundAlgorithm InferBoundAlgorithm::mkLookup(){
-  return InferBoundAlgorithm(Lookup);
-}
-
-InferBoundAlgorithm InferBoundAlgorithm::mkRowSum(){
-  return InferBoundAlgorithm(RowSum);
-}
-
-InferBoundAlgorithm InferBoundAlgorithm::mkSimplex(
-    const std::optional<int>& rounds)
-{
-  return InferBoundAlgorithm(rounds);
-}
-
-ArithEntailmentCheckParameters::ArithEntailmentCheckParameters()
-    : d_algorithms()
-{}
-
-ArithEntailmentCheckParameters::~ArithEntailmentCheckParameters()
-{}
-
-
-void ArithEntailmentCheckParameters::addLookupRowSumAlgorithms(){
-  addAlgorithm(InferBoundAlgorithm::mkLookup());
-  addAlgorithm(InferBoundAlgorithm::mkRowSum());
-}
-
-void ArithEntailmentCheckParameters::addAlgorithm(const inferbounds::InferBoundAlgorithm& alg){
-  d_algorithms.push_back(alg);
-}
-
-ArithEntailmentCheckParameters::const_iterator ArithEntailmentCheckParameters::begin() const{
-  return d_algorithms.begin();
-}
-
-ArithEntailmentCheckParameters::const_iterator ArithEntailmentCheckParameters::end() const{
-  return d_algorithms.end();
-}
-
-InferBoundsResult::InferBoundsResult()
-  : d_foundBound(false)
-  , d_budgetExhausted(false)
-  , d_boundIsProvenOpt(false)
-  , d_inconsistentState(false)
-  , d_reachedThreshold(false)
-  , d_value(false)
-  , d_term(Node::null())
-  , d_upperBound(true)
-  , d_explanation(Node::null())
-{}
-
-InferBoundsResult::InferBoundsResult(Node term, bool ub)
-  : d_foundBound(false)
-  , d_budgetExhausted(false)
-  , d_boundIsProvenOpt(false)
-  , d_inconsistentState(false)
-  , d_reachedThreshold(false)
-  , d_value(false)
-  , d_term(term)
-  , d_upperBound(ub)
-  , d_explanation(Node::null())
-{}
-
-bool InferBoundsResult::foundBound() const {
-  return d_foundBound;
-}
-bool InferBoundsResult::boundIsOptimal() const {
-  return d_boundIsProvenOpt;
-}
-bool InferBoundsResult::inconsistentState() const {
-  return d_inconsistentState;
-}
-
-bool InferBoundsResult::boundIsInteger() const{
-  return foundBound() && d_value.isIntegral();
-}
-
-bool InferBoundsResult::boundIsRational() const {
-  return foundBound() && d_value.infinitesimalIsZero();
-}
-
-Integer InferBoundsResult::valueAsInteger() const{
-  Assert(boundIsInteger());
-  return getValue().floor();
-}
-const Rational& InferBoundsResult::valueAsRational() const{
-  Assert(boundIsRational());
-  return getValue().getNoninfinitesimalPart();
-}
-
-const DeltaRational& InferBoundsResult::getValue() const{
-  return d_value;
-}
-
-Node InferBoundsResult::getTerm() const { return d_term; }
-
-Node InferBoundsResult::getLiteral() const{
-  const Rational& q = getValue().getNoninfinitesimalPart();
-  NodeManager* nm = NodeManager::currentNM();
-  Node qnode = nm->mkConst(CONST_RATIONAL, q);
-
-  Kind k;
-  if(d_upperBound){
-    // x <= q + c*delta
-    Assert(getValue().infinitesimalSgn() <= 0);
-    k = boundIsRational() ? kind::LEQ : kind::LT;
-  }else{
-    // x >= q + c*delta
-    Assert(getValue().infinitesimalSgn() >= 0);
-    k = boundIsRational() ? kind::GEQ : kind::GT;
-  }
-  return nm->mkNode(k, getTerm(), qnode);
-}
-
-/* If there is a bound, this is a node that explains the bound. */
-Node InferBoundsResult::getExplanation() const{
-  return d_explanation;
-}
-
-
-void InferBoundsResult::setBound(const DeltaRational& dr, Node exp){
-  d_foundBound = true;
-  d_value = dr;
-  d_explanation = exp;
-}
-
-void InferBoundsResult::setBudgetExhausted() { d_budgetExhausted = true; }
-void InferBoundsResult::setReachedThreshold() { d_reachedThreshold = true; }
-void InferBoundsResult::setIsOptimal() { d_boundIsProvenOpt = true; }
-void InferBoundsResult::setInconsistent() { d_inconsistentState = true; }
-
-bool InferBoundsResult::thresholdWasReached() const{
-  return d_reachedThreshold;
-}
-bool InferBoundsResult::budgetIsExhausted() const{
-  return d_budgetExhausted;
-}
-
-std::ostream& operator<<(std::ostream& os, const InferBoundsResult& ibr){
-  os << "{InferBoundsResult " << std::endl;
-  os << "on " << ibr.getTerm() << ", ";
-  if(ibr.findUpperBound()){
-    os << "find upper bound, ";
-  }else{
-    os << "find lower bound, ";
-  }
-  if(ibr.foundBound()){
-    os << "found a bound: ";
-    if(ibr.boundIsInteger()){
-      os << ibr.valueAsInteger() << "(int), ";
-    }else if(ibr.boundIsRational()){
-      os << ibr.valueAsRational() << "(rat), ";
-    }else{
-      os << ibr.getValue() << "(extended), ";
-    }
-
-    os << "as term " << ibr.getLiteral() << ", ";
-    os << "explanation " << ibr.getExplanation() << ", ";
-  }else {
-    os << "did not find a bound, ";
-  }
-
-  if(ibr.boundIsOptimal()){
-    os << "(opt), ";
-  }
-
-  if(ibr.inconsistentState()){
-    os << "(inconsistent), ";
-  }
-  if(ibr.budgetIsExhausted()){
-    os << "(budget exhausted), ";
-  }
-  if(ibr.thresholdWasReached()){
-    os << "(reached threshold), ";
-  }
-  os << "}";
-  return os;
-}
-
-ArithEntailmentCheckSideEffects::ArithEntailmentCheckSideEffects()
-    : d_simplexSideEffects(NULL)
-{}
-
-ArithEntailmentCheckSideEffects::~ArithEntailmentCheckSideEffects(){
-  if(d_simplexSideEffects != NULL){
-    delete d_simplexSideEffects;
-    d_simplexSideEffects = NULL;
-  }
-}
-
-InferBoundsResult& ArithEntailmentCheckSideEffects::getSimplexSideEffects(){
-  if(d_simplexSideEffects == NULL){
-    d_simplexSideEffects = new InferBoundsResult;
-  }
-  return *d_simplexSideEffects;
-}
-
-namespace inferbounds { /* namespace arith */
-
-std::ostream& operator<<(std::ostream& os,  const Algorithms a){
-  switch(a){
-  case None:    os << "AlgNone"; break;
-  case Lookup:  os << "AlgLookup"; break;
-  case RowSum:  os << "AlgRowSum"; break;
-  case Simplex: os << "AlgSimplex"; break;
-  default:
-    Unhandled();
-  }
-
-  return os;
-}
-
-} /* namespace inferbounds */
-
-} /* namespace arith */
-} /* namespace theory */
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/infer_bounds.h b/src/theory/arith/infer_bounds.h
deleted file mode 100644 (file)
index cf9d71a..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Andrew Reynolds, Andres Noetzli
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <optional>
-#include <ostream>
-
-#include "expr/node.h"
-#include "theory/arith/delta_rational.h"
-#include "util/integer.h"
-#include "util/rational.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-namespace inferbounds {
-  enum Algorithms {None = 0, Lookup, RowSum, Simplex};
-  enum SimplexParamKind { Unbounded, NumVars, Direct};
-
-class InferBoundAlgorithm {
-private:
-  Algorithms d_alg;
-  std::optional<int> d_simplexRounds;
-  InferBoundAlgorithm(Algorithms a);
-  InferBoundAlgorithm(const std::optional<int>& simplexRounds);
-
- public:
-  InferBoundAlgorithm();
-
-  Algorithms getAlgorithm() const;
-  const std::optional<int>& getSimplexRounds() const;
-
-  static InferBoundAlgorithm mkLookup();
-  static InferBoundAlgorithm mkRowSum();
-  static InferBoundAlgorithm mkSimplex(const std::optional<int>& rounds);
-};
-
-std::ostream& operator<<(std::ostream& os, const Algorithms a);
-} /* namespace inferbounds */
-
-class ArithEntailmentCheckParameters
-{
- private:
-  typedef std::vector<inferbounds::InferBoundAlgorithm> VecInferBoundAlg;
-  VecInferBoundAlg d_algorithms;
-
-public:
-  typedef VecInferBoundAlg::const_iterator const_iterator;
-
-  ArithEntailmentCheckParameters();
-  ~ArithEntailmentCheckParameters();
-
-  void addLookupRowSumAlgorithms();
-  void addAlgorithm(const inferbounds::InferBoundAlgorithm& alg);
-
-  const_iterator begin() const;
-  const_iterator end() const;
-};
-
-
-
-class InferBoundsResult {
-public:
-  InferBoundsResult();
-  InferBoundsResult(Node term, bool ub);
-
-  void setBound(const DeltaRational& dr, Node exp);
-  bool foundBound() const;
-
-  void setIsOptimal();
-  bool boundIsOptimal() const;
-
-  void setInconsistent();
-  bool inconsistentState() const;
-
-  const DeltaRational& getValue() const;
-  bool boundIsRational() const;
-  const Rational& valueAsRational() const;
-  bool boundIsInteger() const;
-  Integer valueAsInteger() const;
-
-  Node getTerm() const;
-  Node getLiteral() const;
-  void setTerm(Node t){ d_term = t; }
-
-  /* If there is a bound, this is a node that explains the bound. */
-  Node getExplanation() const;
-
-  bool budgetIsExhausted() const;
-  void setBudgetExhausted();
-
-  bool thresholdWasReached() const;
-  void setReachedThreshold();
-
-  bool findUpperBound() const { return d_upperBound; }
-
-  void setFindLowerBound() { d_upperBound = false; }
-  void setFindUpperBound() { d_upperBound = true; }
-private:
-  /* was a bound found */
-  bool d_foundBound;
-
-  /* was the budget exhausted */
-  bool d_budgetExhausted;
-
-  /* does the bound have to be optimal*/
-  bool d_boundIsProvenOpt;
-
-  /* was this started on an inconsistent state. */
-  bool d_inconsistentState;
-
-  /* reached the threshold. */
-  bool d_reachedThreshold;
-
-  /* the value of the bound */
-  DeltaRational d_value;
-
-  /* The input term. */
-  Node d_term;
-
-  /* Was the bound found an upper or lower bound.*/
-  bool d_upperBound;
-
-  /* Explanation of the bound. */
-  Node d_explanation;
-};
-
-std::ostream& operator<<(std::ostream& os, const InferBoundsResult& ibr);
-
-class ArithEntailmentCheckSideEffects
-{
- public:
-  ArithEntailmentCheckSideEffects();
-  ~ArithEntailmentCheckSideEffects();
-
-  InferBoundsResult& getSimplexSideEffects();
-
-private:
-  InferBoundsResult* d_simplexSideEffects;
-};
-
-
-} /* namespace arith */
-} /* namespace theory */
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/approx_simplex.cpp b/src/theory/arith/linear/approx_simplex.cpp
new file mode 100644 (file)
index 0000000..81c6183
--- /dev/null
@@ -0,0 +1,3083 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Aina Niemetz
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+#include "theory/arith/linear/approx_simplex.h"
+
+#include <math.h>
+
+#include <cfloat>
+#include <cmath>
+#include <unordered_set>
+
+#include "base/cvc5config.h"
+#include "base/output.h"
+#include "proof/eager_proof_generator.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/cut_log.h"
+#include "theory/arith/linear/matrix.h"
+#include "theory/arith/linear/normal_form.h"
+
+#ifdef CVC5_USE_GLPK
+#include "theory/arith/linear/partial_model.h"
+#endif
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+struct AuxInfo {
+  TreeLog* tl;
+  int pivotLimit;
+  int branchLimit;
+  int branchDepth;
+  MipResult term; /* terminatation */
+};
+
+enum SlackReplace { SlackUndef=0, SlackLB, SlackUB, SlackVLB, SlackVUB };
+
+std::ostream& operator<<(std::ostream& out, MipResult res){
+  switch(res){
+  case MipUnknown:
+    out << "MipUnknown"; break;
+  case MipBingo:
+    out << "MipBingo"; break;
+  case MipClosed:
+    out << "MipClosed"; break;
+  case BranchesExhausted:
+    out << "BranchesExhausted"; break;
+  case PivotsExhauasted:
+    out << "PivotsExhauasted"; break;
+  case ExecExhausted:
+    out << "ExecExhausted"; break;
+  default:
+    out << "Unexpected Mip Value!"; break;
+  }
+  return out;
+}
+struct VirtualBound {
+  // Either x <= d * y or x >= d * y
+  ArithVar x; // variable being bounded
+  Kind k; // either LEQ or GEQ
+  Rational d; // the multiple on y
+  ArithVar y; // the variable that is the upper bound
+  ConstraintP c; // the original constraint relating x and y
+
+  VirtualBound()
+    : x(ARITHVAR_SENTINEL)
+    , k(kind::UNDEFINED_KIND)
+    , d()
+    , y(ARITHVAR_SENTINEL)
+    , c(NullConstraint)
+  {}
+  VirtualBound(ArithVar toBound, Kind rel, const Rational& coeff, ArithVar bounding, ConstraintP orig)
+    : x(toBound)
+    , k(rel)
+    , d(coeff)
+    , y(bounding)
+    , c(orig)
+  {
+    Assert(k == kind::LEQ || k == kind::GEQ);
+  }
+};
+
+struct CutScratchPad {
+  bool d_failure; // if the construction was unsuccessful
+
+  /* GOMORY CUTS Datastructures */
+  ArithVar d_basic; // a variable that is basic in the approximate solver
+  DenseVector d_tabRow;           // a row in the tableau not including d_basic, equal to 0
+  DenseMap<ConstraintP> d_toBound; // each variable in toBound maps each variable in tabRow to either an upper/lower bound
+
+  /* MIR CUTS Datastructures */
+  DenseMap<SlackReplace> d_slacks;// The x'[i] selected for x[i]
+  DenseMap<VirtualBound> d_vub;   // Virtual upper bounds.
+  DenseMap<VirtualBound> d_vlb;   // Virtual lower bounds.
+  DenseMap<Rational> d_compRanges;
+
+  // a sum of rows in the tableau, with possible replacements for fixed
+  // sum aggLhs[i] x[i] = aggRhs;
+  DenseVector d_agg;
+  // Takes agg and replaces x[i] with a slack variable x'[i]
+  // Takes agg and replaces x[i] with a slack variable x'[i]
+  // sum modLhs[i] x'[i] = modRhs;
+  DenseVector d_mod;
+
+  // Takes mod, and performs c-Mir on it
+  // sum alpha[i] x'[i] <= beta
+  DenseVector d_alpha;
+
+  /* The constructed cut */
+  // sum cut[i] x[i] <= cutRhs
+  DenseVector d_cut;
+  Kind d_cutKind;
+
+  /* The constraints used throughout construction. */
+  std::set<ConstraintP> d_explanation; // use pointer equality
+  CutScratchPad(){
+    clear();
+  }
+  void clear(){
+    d_failure = false;
+    d_basic = ARITHVAR_SENTINEL;
+    d_tabRow.purge();
+    d_toBound.purge();
+
+    d_slacks.purge();
+    d_vub.purge();
+    d_vlb.purge();
+    d_compRanges.purge();
+
+    d_agg.purge();
+    d_mod.purge();
+    d_alpha.purge();
+
+    d_cut.purge();
+    d_cutKind = kind::UNDEFINED_KIND;
+    d_explanation.clear();
+  }
+};
+
+ApproximateStatistics::ApproximateStatistics()
+    : d_branchMaxDepth(
+        smtStatisticsRegistry().registerInt("z::approx::branchMaxDepth")),
+      d_branchesMaxOnAVar(
+          smtStatisticsRegistry().registerInt("z::approx::branchesMaxOnAVar")),
+      d_gaussianElimConstructTime(smtStatisticsRegistry().registerTimer(
+          "z::approx::gaussianElimConstruct::time")),
+      d_gaussianElimConstruct(smtStatisticsRegistry().registerInt(
+          "z::approx::gaussianElimConstruct::calls")),
+      d_averageGuesses(
+          smtStatisticsRegistry().registerAverage("z::approx::averageGuesses"))
+{
+}
+
+ApproximateSimplex::ApproximateSimplex(const ArithVariables& v, TreeLog& l,
+                                       ApproximateStatistics& s)
+  : d_vars(v)
+  , d_log(l)
+  , d_stats(s)
+  , d_pivotLimit(std::numeric_limits<int>::max())
+  , d_branchLimit(std::numeric_limits<int>::max())
+  , d_maxDepth(std::numeric_limits<int>::max())
+{}
+
+void ApproximateSimplex::setPivotLimit(int pl){
+  Assert(pl >= 0);
+  d_pivotLimit = pl;
+}
+
+void ApproximateSimplex::setBranchingDepth(int bd){
+  Assert(bd >= 0);
+  d_maxDepth = bd;
+}
+
+void ApproximateSimplex::setBranchOnVariableLimit(int bl){
+  Assert(bl >= 0);
+  d_branchLimit = bl;
+}
+
+bool ApproximateSimplex::roughlyEqual(double a, double b){
+  if (a == 0){
+    return -SMALL_FIXED_DELTA <= b && b <= SMALL_FIXED_DELTA;
+  }else if (b == 0){
+    return -SMALL_FIXED_DELTA <= a && a <= SMALL_FIXED_DELTA;
+  }else{
+    return std::abs(b/a) <= TOLERENCE && std::abs(a/b) <= TOLERENCE;
+  }
+}
+
+Rational ApproximateSimplex::cfeToRational(const vector<Integer>& exp){
+  if(exp.empty()){
+    return Rational(0);
+  }else{
+    Rational result = exp.back();
+    vector<Integer>::const_reverse_iterator exp_iter = exp.rbegin();
+    vector<Integer>::const_reverse_iterator exp_end = exp.rend();
+    ++exp_iter;
+    while(exp_iter != exp_end){
+      result = result.inverse();
+      const Integer& i = *exp_iter;
+      result += i;
+      ++exp_iter;
+    }
+    return result;
+  }
+}
+std::vector<Integer> ApproximateSimplex::rationalToCfe(const Rational& q, int depth){
+  vector<Integer> mods;
+  if(!q.isZero()){
+    Rational carry = q;
+    for(int i = 0; i <= depth; ++i){
+      Assert(!carry.isZero());
+      mods.push_back(Integer());
+      Integer& back = mods.back();
+      back = carry.floor();
+      Trace("rationalToCfe") << "  cfe["<<i<<"]: " << back << endl;
+      carry -= back;
+      if(carry.isZero()){
+        break;
+      }else if(ApproximateSimplex::roughlyEqual(carry.getDouble(), 0.0)){
+        break;
+      }else{
+        carry = carry.inverse();
+      }
+    }
+  }
+  return mods;
+}
+
+
+Rational ApproximateSimplex::estimateWithCFE(const Rational& r, const Integer& K){
+  Trace("estimateWithCFE") << "estimateWithCFE(" << r << ", " << K << ")" <<endl;
+  // references
+  // page 4: http://carlossicoli.free.fr/C/Cassels_J.W.S.-An_introduction_to_diophantine_approximation-University_Press(1965).pdf
+  // http://en.wikipedia.org/wiki/Continued_fraction
+  Assert(K >= Integer(1));
+  if( r.getDenominator() <= K ){
+    return r;
+  }
+
+  // current numerator and denominator that has not been resolved in the cfe
+  Integer num = r.getNumerator(), den = r.getDenominator();
+  Integer quot,rem;
+
+  unsigned t = 0;
+  // For a sequence of candidate solutions q_t/p_t
+  // we keep only 3 time steps: 0[prev], 1[current], 2[next]
+  // timesteps with a fake timestep 0 (p is 0 and q is 1)
+  // at timestep 1
+  Integer p[3]; // h
+  Integer q[3]; // k
+  // load the first 3 time steps manually
+  p[0] =    0; q[0] = 1; // timestep -2
+  p[1] =    1; q[1] = 0; // timestep -1
+
+  Integer::floorQR(quot, rem, num, den);
+  num = den; den = rem;
+
+  q[2] = q[0] + quot*q[1];
+  p[2] = p[0] + quot*p[1];
+  Trace("estimateWithCFE") <<  "  cfe["<<t<<"]: " << p[2] <<"/"<< q[2] << endl;
+  while( q[2] <= K ){
+    p[0] = p[1]; p[1] = p[2];
+    q[0] = q[1]; q[1] = q[2];
+
+
+    Integer::floorQR(quot, rem, num, den);
+    num = den; den = rem;
+
+    p[2] = p[0]+quot*p[1];
+    q[2] = q[0]+quot*q[1];
+    ++t;
+    Trace("estimateWithCFE") << "  cfe["<<t<<"]: " << p[2] <<"/"<< q[2] << endl;
+  }
+
+  Integer k = (K-q[0]).floorDivideQuotient(q[1]);
+  Rational cand_prev(p[0]+k*p[1], q[0]+k*q[1]);
+  Rational cand_curr(p[1], q[1]);
+  Rational dist_prev = (cand_prev - r).abs();
+  Rational dist_curr = (cand_curr - r).abs();
+  if(dist_prev <= dist_curr){
+    Trace("estimateWithCFE") << cand_prev << " is closer than " << cand_curr << endl;
+    return cand_prev;
+  }else{
+    Trace("estimateWithCFE") << cand_curr << " is closer than " << cand_prev << endl;
+    return cand_curr;
+  }
+}
+
+std::optional<Rational> ApproximateSimplex::estimateWithCFE(double d,
+                                                            const Integer& D)
+{
+  if (std::optional<Rational> from_double = Rational::fromDouble(d))
+  {
+    return estimateWithCFE(*from_double, D);
+  }
+  return std::optional<Rational>();
+}
+
+std::optional<Rational> ApproximateSimplex::estimateWithCFE(double d)
+{
+  return estimateWithCFE(d, Integer(s_defaultMaxDenom));
+}
+
+class ApproxNoOp : public ApproximateSimplex {
+public:
+  ApproxNoOp(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s)
+  : ApproximateSimplex(v,l,s)
+  {}
+  ~ApproxNoOp(){}
+
+  LinResult solveRelaxation() override { return LinUnknown; }
+  Solution extractRelaxation() const override { return Solution(); }
+
+  ArithRatPairVec heuristicOptCoeffs() const override
+  {
+    return ArithRatPairVec();
+  }
+
+  MipResult solveMIP(bool al) override { return MipUnknown; }
+  Solution extractMIP() const override { return Solution(); }
+
+  void setOptCoeffs(const ArithRatPairVec& ref) override {}
+
+  void tryCut(int nid, CutInfo& cut) override {}
+
+  std::vector<const CutInfo*> getValidCuts(const NodeLog& node) override
+  {
+    return std::vector<const CutInfo*>();
+  }
+
+  ArithVar getBranchVar(const NodeLog& nl) const override
+  {
+    return ARITHVAR_SENTINEL;
+  }
+
+  double sumInfeasibilities(bool mip) const override { return 0.0; }
+};
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+
+/* Begin the declaration of GLPK specific code. */
+#ifdef CVC5_USE_GLPK
+extern "C" {
+#include <glpk.h>
+}/* extern "C" */
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+Kind glpk_type_to_kind(int glpk_cut_type){
+  switch(glpk_cut_type){
+  case GLP_LO: return kind::GEQ;
+  case GLP_UP: return kind::LEQ;
+  case GLP_FX: return kind::EQUAL;
+  case GLP_DB:
+  case GLP_FR:
+  default:     return kind::UNDEFINED_KIND;
+  }
+}
+
+class GmiInfo;
+class MirInfo;
+class BranchCutInfo;
+
+class ApproxGLPK : public ApproximateSimplex {
+private:
+  glp_prob* d_inputProb; /* a copy of the input prob */
+  glp_prob* d_realProb;  /* a copy of the real relaxation output */
+  glp_prob* d_mipProb;   /* a copy of the integer prob */
+
+  DenseMap<int> d_colIndices;
+  DenseMap<int> d_rowIndices;
+
+  NodeLog::RowIdMap d_rootRowIds;
+  //DenseMap<ArithVar> d_rowToArithVar;
+  DenseMap<ArithVar> d_colToArithVar;
+
+  bool d_solvedRelaxation;
+  bool d_solvedMIP;
+
+  CutScratchPad d_pad;
+
+  std::vector<Integer> d_denomGuesses;
+
+public:
+  ApproxGLPK(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s);
+  ~ApproxGLPK();
+
+  LinResult solveRelaxation() override;
+  Solution extractRelaxation() const override { return extractSolution(false); }
+
+  ArithRatPairVec heuristicOptCoeffs() const override;
+
+  MipResult solveMIP(bool al) override;
+  Solution extractMIP() const override { return extractSolution(true); }
+  void setOptCoeffs(const ArithRatPairVec& ref) override;
+  std::vector<const CutInfo*> getValidCuts(const NodeLog& nodes) override;
+  ArithVar getBranchVar(const NodeLog& con) const override;
+
+  static void printGLPKStatus(int status, std::ostream& out);
+
+
+private:
+ Solution extractSolution(bool mip) const;
+ int guessDir(ArithVar v) const;
+
+ // get this stuff out of here
+ void tryCut(int nid, CutInfo& cut) override;
+
+ ArithVar _getArithVar(int nid, int M, int ind) const;
+ ArithVar getArithVarFromRow(int nid, int ind) const
+ {
+   if (ind >= 0)
+   {
+     const NodeLog& nl = d_log.getNode(nid);
+     return nl.lookupRowId(ind);
+   }
+   return ARITHVAR_SENTINEL;
+  }
+
+  // virtual void mapRowId(int nid, int ind, ArithVar v){
+  //   NodeLog& nl = d_log.getNode(nid);
+  //   nl.mapRowId(ind, v);
+  // }
+  // virtual void applyRowsDeleted(int nid, const RowsDeleted& rd){
+  //   NodeLog& nl = d_log.getNode(nid);
+  //   nl.applyRowsDeleted(rd);
+  // }
+
+  ArithVar getArithVarFromStructural(int ind) const{
+    if(ind >= 0){
+      unsigned u = (unsigned) ind;
+      if(d_colToArithVar.isKey(u)){
+        return d_colToArithVar[u];
+      }
+    }
+    return ARITHVAR_SENTINEL;
+  }
+
+  /**
+   * Attempts to make the row vector vec on the pad.
+   * If this is not in the row span of the original tableau this
+   * raises the failure flag.
+   */
+  bool attemptConstructTableRow(int node, int M, const PrimitiveVec& vec);
+  bool guessCoefficientsConstructTableRow(int node, int M, const PrimitiveVec& vec);
+  bool guessCoefficientsConstructTableRow(int node, int M, const PrimitiveVec& vec, const Integer& D);
+  bool gaussianElimConstructTableRow(int node, int M, const PrimitiveVec& vec);
+
+  /* This is a guess of a vector in the row span of the tableau.
+   * Attempt to cancel out all of the variables.
+   * returns true if this is constructable.
+   */
+  bool guessIsConstructable(const DenseMap<Rational>& guess) const;
+
+  /**
+   * Loads a vector of statuses into a dense map over bounds.
+   * returns true on failure.
+   */
+  bool loadToBound(int node, int M, int len, int* inds, int* statuses,
+                   DenseMap<ConstraintP>& toBound) const;
+
+  /** checks the cut on the pad for whether it is sufficiently similar to cut. */
+  bool checkCutOnPad(int nid, const CutInfo& cut) const;
+
+
+  /** turns the pad into a node and creates an explanation. */
+  //std::pair<Node, Node> makeCutNodes(int nid, const CutInfo& cut) const;
+
+  // true means failure!
+  // BRANCH CUTS
+  bool attemptBranchCut(int nid, const BranchCutInfo& br);
+
+  // GOMORY CUTS
+  bool attemptGmi(int nid, const GmiInfo& gmi);
+  /** tries to turn the information on the pad into a cut. */
+  bool constructGmiCut();
+
+  // MIR CUTS
+  bool attemptMir(int nid, const MirInfo& mir);
+  bool applyCMIRRule(int nid, const MirInfo& mir);
+  bool makeRangeForComplemented(int nid, const MirInfo& mir);
+  bool loadSlacksIntoPad(int nid, const MirInfo& mir);
+  bool loadVirtualBoundsIntoPad(int nid, const MirInfo& mir);
+  bool loadRowSumIntoAgg(int nid, int M, const PrimitiveVec& mir);
+  bool buildModifiedRow(int nid, const MirInfo& mir);
+  bool constructMixedKnapsack();
+  bool replaceSlacksOnCuts();
+  bool loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp);
+
+  double sumInfeasibilities(bool mip) const override
+  {
+    return sumInfeasibilities(mip? d_mipProb : d_realProb);
+  }
+  double sumInfeasibilities(glp_prob* prob, bool mip) const;
+};
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+#endif /*#ifdef CVC5_USE_GLPK */
+/* End the declaration of GLPK specific code. */
+
+/* Begin GPLK/NOGLPK Glue code. */
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+ApproximateSimplex* ApproximateSimplex::mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s){
+#ifdef CVC5_USE_GLPK
+  return new ApproxGLPK(vars, l, s);
+#else
+  return new ApproxNoOp(vars, l, s);
+#endif
+}
+bool ApproximateSimplex::enabled() {
+#ifdef CVC5_USE_GLPK
+  return true;
+#else
+  return false;
+#endif
+}
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+/* End GPLK/NOGLPK Glue code. */
+
+/* Begin GPLK implementation. */
+#ifdef CVC5_USE_GLPK
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+#ifdef CVC5_ASSERTIONS
+static CutInfoKlass fromGlpkClass(int klass){
+  switch(klass){
+  case GLP_RF_GMI: return GmiCutKlass;
+  case GLP_RF_MIR: return MirCutKlass;
+  case GLP_RF_COV:
+  case GLP_RF_CLQ:
+  default:         return UnknownKlass;
+  }
+}
+#endif
+
+ApproxGLPK::ApproxGLPK(const ArithVariables& var,
+                       TreeLog& l,
+                       ApproximateStatistics& s)
+    : ApproximateSimplex(var, l, s),
+      d_inputProb(nullptr),
+      d_realProb(nullptr),
+      d_mipProb(nullptr),
+      d_solvedRelaxation(false),
+      d_solvedMIP(false)
+{
+
+  d_denomGuesses.push_back(Integer(1<<22));
+  d_denomGuesses.push_back(Integer(ApproximateSimplex::s_defaultMaxDenom));
+  d_denomGuesses.push_back(Integer(1ul<<29));
+  d_denomGuesses.push_back(Integer(1ul<<31));
+
+  d_inputProb = glp_create_prob();
+  d_realProb = glp_create_prob();
+  d_mipProb = glp_create_prob();
+  glp_set_obj_dir(d_inputProb, GLP_MAX);
+  glp_set_prob_name(d_inputProb, "ApproximateSimplex::approximateFindModel");
+
+  int numRows = 0;
+  int numCols = 0;
+
+  // Assign each variable to a row and column variable as it appears in the input
+  for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
+    ArithVar v = *vi;
+
+    if(d_vars.isAuxiliary(v)){
+      ++numRows;
+      d_rowIndices.set(v, numRows);
+      //mapRowId(d_log.getRootId(), numRows, v);
+      d_rootRowIds.insert(make_pair(numRows, v));
+      //d_rowToArithVar.set(numRows, v);
+      Trace("approx") << "Row vars: " << v << "<->" << numRows << endl;
+    }else{
+      ++numCols;
+      d_colIndices.set(v, numCols);
+      d_colToArithVar.set(numCols, v);
+      Trace("approx") << "Col vars: " << v << "<->" << numCols << endl;
+    }
+  }
+  Assert(numRows > 0);
+  Assert(numCols > 0);
+
+  glp_add_rows(d_inputProb, numRows);
+  glp_add_cols(d_inputProb, numCols);
+
+  // Assign the upper/lower bounds and types to each variable
+  for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
+    ArithVar v = *vi;
+
+    int type;
+    double lb = 0.0;
+    double ub = 0.0;
+    if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+      if(d_vars.boundsAreEqual(v)){
+        type = GLP_FX;
+      }else{
+        type = GLP_DB;
+      }
+      lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
+      ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
+    }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
+      type = GLP_UP;
+      ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
+    }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+      type = GLP_LO;
+      lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
+    }else{
+      type = GLP_FR;
+    }
+
+    if(d_vars.isAuxiliary(v)){
+      int rowIndex = d_rowIndices[v];
+      glp_set_row_bnds(d_inputProb, rowIndex, type, lb, ub);
+    }else{
+      int colIndex = d_colIndices[v];
+      // is input is correct here
+      int kind = d_vars.isInteger(v) ? GLP_IV : GLP_CV;
+      glp_set_col_kind(d_inputProb, colIndex, kind);
+      glp_set_col_bnds(d_inputProb, colIndex, type, lb, ub);
+    }
+  }
+
+  // Count the number of entries
+  int numEntries = 0;
+  for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
+    ArithVar v = *i;
+    Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
+    for (Polynomial::iterator j = p.begin(), end = p.end(); j != end; ++j)
+    {
+      ++numEntries;
+    }
+  }
+
+  int* ia = new int[numEntries+1];
+  int* ja = new int[numEntries+1];
+  double* ar = new double[numEntries+1];
+
+  int entryCounter = 0;
+  for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
+    ArithVar v = *i;
+    int rowIndex = d_rowIndices[v];
+
+    Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
+
+    for (Polynomial::iterator j = p.begin(), end = p.end(); j != end; ++j)
+    {
+      const Monomial& mono = *j;
+      const Constant& constant = mono.getConstant();
+      const VarList& variable = mono.getVarList();
+
+      Node n = variable.getNode();
+
+      Assert(d_vars.hasArithVar(n));
+      ArithVar av = d_vars.asArithVar(n);
+      int colIndex = d_colIndices[av];
+      double coeff = constant.getValue().getDouble();
+
+      ++entryCounter;
+      ia[entryCounter] = rowIndex;
+      ja[entryCounter] = colIndex;
+      ar[entryCounter] = coeff;
+    }
+  }
+  glp_load_matrix(d_inputProb, numEntries, ia, ja, ar);
+
+  delete[] ia;
+  delete[] ja;
+  delete[] ar;
+}
+int ApproxGLPK::guessDir(ArithVar v) const{
+  if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
+    return -1;
+  }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+    return 1;
+  }else if(!d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
+    return 0;
+  }else{
+    int ubSgn = d_vars.getUpperBound(v).sgn();
+    int lbSgn = d_vars.getLowerBound(v).sgn();
+
+    if(ubSgn != 0 && lbSgn == 0){
+      return -1;
+    }else if(ubSgn == 0 && lbSgn != 0){
+      return 1;
+    }
+
+    return 1;
+  }
+}
+
+ArithRatPairVec ApproxGLPK::heuristicOptCoeffs() const{
+  ArithRatPairVec ret;
+
+  // Strategies are guess:
+  // 1 simple shared "ceiling" variable: danoint, pk1
+  //  x1 >= c, x1 >= tmp1, x1 >= tmp2, ...
+  // 1 large row: fixnet, vpm2, pp08a
+  //  (+ .......... ) <= c
+  // Not yet supported:
+  // 1 complex shared "ceiling" variable: opt1217
+  //  x1 >= c, x1 >= (+ ..... ), x1 >= (+ ..... )
+  //  and all of the ... are the same sign
+
+
+  // Candidates:
+  // 1) Upper and lower bounds are not equal.
+  // 2) The variable is not integer
+  // 3a) For columns look for a ceiling variable
+  // 3B) For rows look for a large row with
+
+  DenseMap<BoundCounts> d_colCandidates;
+  DenseMap<uint32_t> d_rowCandidates;
+
+  double sumRowLength = 0.0;
+  uint32_t maxRowLength = 0;
+  for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
+    ArithVar v = *vi;
+
+    int type;
+    if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+      if(d_vars.boundsAreEqual(v)){
+        type = GLP_FX;
+      }else{
+        type = GLP_DB;
+      }
+    }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
+      type = GLP_UP;
+    }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+      type = GLP_LO;
+    }else{
+      type = GLP_FR;
+    }
+
+    if(type != GLP_FX && type != GLP_FR){
+
+      if(d_vars.isAuxiliary(v)){
+        Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
+        uint32_t len = p.size();
+        d_rowCandidates.set(v, len);
+        sumRowLength += len;
+        maxRowLength = std::max(maxRowLength, len);
+      }else if(!d_vars.isInteger(v)){
+        d_colCandidates.set(v, BoundCounts());
+      }
+    }
+  }
+
+  uint32_t maxCount = 0;
+  for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
+    ArithVar v = *i;
+
+    bool lbCap = d_vars.hasLowerBound(v) && !d_vars.hasUpperBound(v);
+    bool ubCap = !d_vars.hasLowerBound(v) && d_vars.hasUpperBound(v);
+
+    if(lbCap || ubCap){
+      ConstraintP b = lbCap ? d_vars.getLowerBoundConstraint(v)
+        : d_vars.getUpperBoundConstraint(v);
+
+      if(!(b->getValue()).noninfinitesimalIsZero()){ continue; }
+
+      Polynomial poly = Polynomial::parsePolynomial(d_vars.asNode(v));
+      if(poly.size() != 2) { continue; }
+
+      Polynomial::iterator j = poly.begin();
+      Monomial first = *j;
+      ++j;
+      Monomial second = *j;
+
+      bool firstIsPos = first.constantIsPositive();
+      bool secondIsPos = second.constantIsPositive();
+
+      if(firstIsPos == secondIsPos){ continue; }
+
+      Monomial pos = firstIsPos == lbCap ? first : second;
+      Monomial neg = firstIsPos != lbCap ? first : second;
+      // pos >= neg
+      VarList p = pos.getVarList();
+      VarList n = neg.getVarList();
+      if(d_vars.hasArithVar(p.getNode())){
+        ArithVar ap = d_vars.asArithVar(p.getNode());
+        if( d_colCandidates.isKey(ap)){
+          BoundCounts bc = d_colCandidates.get(ap);
+          bc = BoundCounts(bc.lowerBoundCount(), bc.upperBoundCount()+1);
+          maxCount = std::max(maxCount, bc.upperBoundCount());
+          d_colCandidates.set(ap, bc);
+        }
+      }
+      if(d_vars.hasArithVar(n.getNode())){
+        ArithVar an = d_vars.asArithVar(n.getNode());
+        if( d_colCandidates.isKey(an)){
+          BoundCounts bc = d_colCandidates.get(an);
+          bc = BoundCounts(bc.lowerBoundCount()+1, bc.upperBoundCount());
+          maxCount = std::max(maxCount, bc.lowerBoundCount());
+          d_colCandidates.set(an, bc);
+        }
+      }
+    }
+  }
+
+  // Attempt row
+  double avgRowLength = d_rowCandidates.size() >= 1 ?
+    ( sumRowLength / d_rowCandidates.size() ) : 0.0;
+
+  // There is a large row among the candidates
+  bool guessARowCandidate = maxRowLength >= (10.0 * avgRowLength);
+
+  double rowLengthReq = (maxRowLength * .9);
+
+  if (guessARowCandidate)
+  {
+    for (ArithVar r : d_rowCandidates)
+    {
+      uint32_t len = d_rowCandidates[r];
+
+      int dir = guessDir(r);
+      if(len >= rowLengthReq){
+        ret.push_back(ArithRatPair(r, Rational(dir)));
+      }
+    }
+  }
+
+  // Attempt columns
+  bool guessAColCandidate = maxCount >= 4;
+  if (guessAColCandidate)
+  {
+    for (ArithVar c : d_colCandidates)
+    {
+      BoundCounts bc = d_colCandidates[c];
+
+      int dir = guessDir(c);
+      double ubScore = double(bc.upperBoundCount()) / maxCount;
+      double lbScore = double(bc.lowerBoundCount()) / maxCount;
+      if(ubScore  >= .9 || lbScore >= .9){
+        ret.push_back(ArithRatPair(c, Rational(c)));
+      }
+    }
+  }
+
+  return ret;
+}
+
+void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){
+  DenseMap<double> nbCoeffs;
+
+  for(ArithRatPairVec::const_iterator i = ref.begin(), iend = ref.end(); i != iend; ++i){
+    ArithVar v = (*i).first;
+    const Rational& q = (*i).second;
+
+    if(d_vars.isAuxiliary(v)){
+      // replace the variable by its definition and multiply by q
+      Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
+      Polynomial pq = p * q;
+
+      for(Polynomial::iterator j = pq.begin(), jend = pq.end(); j != jend; ++j){
+        const Monomial& mono = *j;
+        const Constant& constant = mono.getConstant();
+        const VarList& variable = mono.getVarList();
+
+        Node n = variable.getNode();
+
+        Assert(d_vars.hasArithVar(n));
+        ArithVar av = d_vars.asArithVar(n);
+        int colIndex = d_colIndices[av];
+        double coeff = constant.getValue().getDouble();
+        if(!nbCoeffs.isKey(colIndex)){
+          nbCoeffs.set(colIndex, 0.0);
+        }
+        nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
+      }
+    }else{
+      int colIndex = d_colIndices[v];
+      double coeff = q.getDouble();
+      if(!nbCoeffs.isKey(colIndex)){
+        nbCoeffs.set(colIndex, 0.0);
+      }
+      nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
+    }
+  }
+  for(DenseMap<double>::const_iterator ci =nbCoeffs.begin(), ciend = nbCoeffs.end(); ci != ciend; ++ci){
+    Index colIndex = *ci;
+    double coeff = nbCoeffs[colIndex];
+    glp_set_obj_coef(d_inputProb, colIndex, coeff);
+  }
+}
+
+
+/*
+ * rough strategy:
+ *  real relaxation
+ *   try approximate real optimization of error function
+ *   pivot in its basis
+ *   update to its assignment
+ *   check with FCSimplex
+ *  check integer solution
+ *   try approximate mixed integer problem
+ *   stop at the first feasible point
+ *   pivot in its basis
+ *   update to its assignment
+ *   check with FCSimplex
+ */
+
+void ApproxGLPK::printGLPKStatus(int status, std::ostream& out){
+  switch(status){
+  case GLP_OPT:
+    out << "GLP_OPT" << endl;
+    break;
+  case GLP_FEAS:
+    out << "GLP_FEAS" << endl;
+    break;
+  case GLP_INFEAS:
+    out << "GLP_INFEAS" << endl;
+    break;
+  case GLP_NOFEAS:
+    out << "GLP_NOFEAS" << endl;
+    break;
+  case GLP_UNBND:
+    out << "GLP_UNBND" << endl;
+    break;
+  case GLP_UNDEF:
+    out << "GLP_UNDEF" << endl;
+    break;
+  default:
+    out << "Status unknown" << endl;
+    break;
+  }
+}
+
+ApproxGLPK::~ApproxGLPK(){
+  glp_delete_prob(d_inputProb);
+  glp_delete_prob(d_realProb);
+  glp_delete_prob(d_mipProb);
+
+}
+
+ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const
+{
+  Assert(d_solvedRelaxation);
+  Assert(!mip || d_solvedMIP);
+
+  ApproximateSimplex::Solution sol;
+  DenseSet& newBasis = sol.newBasis;
+  DenseMap<DeltaRational>& newValues = sol.newValues;
+
+  glp_prob* prob = mip ? d_mipProb : d_realProb;
+
+  for(ArithVariables::var_iterator i = d_vars.var_begin(), i_end = d_vars.var_end(); i != i_end; ++i){
+    ArithVar vi = *i;
+    bool isAux = d_vars.isAuxiliary(vi);
+    int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi];
+
+    int status = isAux ? glp_get_row_stat(prob, glpk_index)
+      : glp_get_col_stat(prob, glpk_index);
+
+    bool useDefaultAssignment = false;
+
+    switch(status){
+    case GLP_BS:
+      newBasis.add(vi);
+      useDefaultAssignment = true;
+      break;
+    case GLP_NL:
+    case GLP_NS:
+      if(!mip){
+        newValues.set(vi, d_vars.getLowerBound(vi));
+      }else{// intentionally fall through otherwise
+        useDefaultAssignment = true;
+      }
+      break;
+    case GLP_NU:
+      if(!mip){
+        newValues.set(vi, d_vars.getUpperBound(vi));
+      }else {// intentionally fall through otherwise
+        useDefaultAssignment = true;
+      }
+      break;
+    default:
+      {
+        useDefaultAssignment = true;
+      }
+      break;
+    }
+
+    if(useDefaultAssignment){
+
+      double newAssign;
+      if(mip){
+        newAssign = (isAux ? glp_mip_row_val(prob, glpk_index)
+                     :  glp_mip_col_val(prob, glpk_index));
+      }else{
+        newAssign = (isAux ? glp_get_row_prim(prob, glpk_index)
+                     :  glp_get_col_prim(prob, glpk_index));
+      }
+      const DeltaRational& oldAssign = d_vars.getAssignment(vi);
+
+      if (d_vars.hasLowerBound(vi)
+          && roughlyEqual(newAssign,
+                          d_vars.getLowerBound(vi).approx(SMALL_FIXED_DELTA)))
+      {
+
+        newValues.set(vi, d_vars.getLowerBound(vi));
+      }
+      else if (d_vars.hasUpperBound(vi)
+               && roughlyEqual(
+                   newAssign,
+                   d_vars.getUpperBound(vi).approx(SMALL_FIXED_DELTA)))
+      {
+        newValues.set(vi, d_vars.getUpperBound(vi));
+      }
+      else
+      {
+        double rounded = round(newAssign);
+        if (roughlyEqual(newAssign, rounded))
+        {
+          newAssign = rounded;
+        }
+
+        DeltaRational proposal;
+        if (std::optional maybe_new = estimateWithCFE(newAssign))
+        {
+          proposal = *maybe_new;
+        }
+        else
+        {
+          // failed to estimate the old value. defaulting to the current.
+          proposal = d_vars.getAssignment(vi);
+        }
+
+        if (roughlyEqual(newAssign, oldAssign.approx(SMALL_FIXED_DELTA)))
+        {
+          proposal = d_vars.getAssignment(vi);
+        }
+
+        if (d_vars.strictlyLessThanLowerBound(vi, proposal))
+        {
+          proposal = d_vars.getLowerBound(vi);
+        }
+        else if (d_vars.strictlyGreaterThanUpperBound(vi, proposal))
+        {
+          proposal = d_vars.getUpperBound(vi);
+        }
+        newValues.set(vi, proposal);
+      }
+    }
+  }
+  return sol;
+}
+
+double ApproxGLPK::sumInfeasibilities(glp_prob* prob, bool mip) const{
+  /* compute the sum of dual infeasibilities */
+  double infeas = 0.0;
+
+  for(ArithVariables::var_iterator i = d_vars.var_begin(), i_end = d_vars.var_end(); i != i_end; ++i){
+    ArithVar vi = *i;
+    bool isAux = d_vars.isAuxiliary(vi);
+    int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi];
+
+    double newAssign;
+    if(mip){
+      newAssign = (isAux ? glp_mip_row_val(prob, glpk_index)
+                   :  glp_mip_col_val(prob, glpk_index));
+    }else{
+      newAssign = (isAux ? glp_get_row_prim(prob, glpk_index)
+                   :  glp_get_col_prim(prob, glpk_index));
+    }
+
+
+    double ub = isAux ?
+      glp_get_row_ub(prob, glpk_index) : glp_get_col_ub(prob, glpk_index);
+
+    double lb = isAux ?
+      glp_get_row_lb(prob, glpk_index) : glp_get_col_lb(prob, glpk_index);
+
+    if(ub != +DBL_MAX){
+      if(newAssign > ub){
+        double ubinf = newAssign - ub;
+        infeas += ubinf;
+        Trace("approx::soi") << "ub inf" << vi << " " << ubinf << " " << infeas << endl;
+      }
+    }
+    if(lb != -DBL_MAX){
+      if(newAssign < lb){
+        double lbinf = lb - newAssign;
+        infeas  += lbinf;
+
+        Trace("approx::soi") << "lb inf" << vi << " " << lbinf << " " << infeas << endl;
+      }
+    }
+  }
+  return infeas;
+}
+
+LinResult ApproxGLPK::solveRelaxation(){
+  Assert(!d_solvedRelaxation);
+
+  glp_smcp parm;
+  glp_init_smcp(&parm);
+  parm.presolve = GLP_OFF;
+  parm.meth = GLP_PRIMAL;
+  parm.pricing = GLP_PT_PSE;
+  parm.it_lim = d_pivotLimit;
+  parm.msg_lev = GLP_MSG_OFF;
+
+  glp_erase_prob(d_realProb);
+  glp_copy_prob(d_realProb, d_inputProb, GLP_OFF);
+
+  int res = glp_simplex(d_realProb, &parm);
+  switch(res){
+  case 0:
+    {
+      int status = glp_get_status(d_realProb);
+      int iterationcount = glp_get_it_cnt(d_realProb);
+      switch(status){
+      case GLP_OPT:
+      case GLP_FEAS:
+      case GLP_UNBND:
+        d_solvedRelaxation = true;
+        return LinFeasible;
+      case GLP_INFEAS:
+      case GLP_NOFEAS:
+        d_solvedRelaxation = true;
+        return LinInfeasible;
+      default:
+        {
+          if(iterationcount >= d_pivotLimit){
+            return LinExhausted;
+          }
+          return LinUnknown;
+        }
+      }
+    }
+  default:
+    return LinUnknown;
+  }
+}
+
+
+struct MirInfo : public CutInfo {
+
+  /** a sum of input rows. */
+  PrimitiveVec row_sum;
+
+  /* the delta used */
+  double delta;
+
+  /* all of these are length vars == N+M*/
+  int nvars;
+  char* cset;
+  char* subst;
+  int*  vlbRows;
+  int*  vubRows;
+  MirInfo(int execOrd, int ord)
+    : CutInfo(MirCutKlass, execOrd, ord)
+    , nvars(0)
+    , cset(NULL)
+    , subst(NULL)
+    , vlbRows(NULL)
+    , vubRows(NULL)
+  {}
+
+  ~MirInfo(){
+    clearSets();
+  }
+  void clearSets(){
+    if(cset != NULL){
+      delete[] cset;
+      delete[] subst;
+      delete[] vlbRows;
+      delete[] vubRows;
+      cset = NULL;
+      nvars = 0;
+    }
+  }
+  void initSet(){
+    Assert(d_N >= 0);
+    Assert(d_mAtCreation >= 0);
+    clearSets();
+
+    int vars = 1 + d_N + d_mAtCreation;
+
+    cset = new char[1+vars];
+    subst = new char[1+vars];
+    vlbRows = new int[1+vars];
+    vubRows = new int[1+vars];
+  }
+};
+
+struct GmiInfo : public CutInfo {
+  int basic;
+  PrimitiveVec tab_row;
+  int* tab_statuses;
+  /* has the length tab_row.length */
+
+  GmiInfo(int execOrd, int ord)
+    : CutInfo(GmiCutKlass, execOrd, ord)
+    , basic(-1)
+    , tab_row()
+    , tab_statuses(NULL)
+  {
+    Assert(!initialized_tab());
+  }
+
+  ~GmiInfo(){
+    if(initialized_tab()){
+      clear_tab();
+    }
+  }
+
+  bool initialized_tab() const {
+    return tab_statuses != NULL;
+  }
+
+  void init_tab(int N){
+    if(initialized_tab()){
+      clear_tab();
+    }
+    tab_row.setup(N);
+    tab_statuses = new int[1+N];
+  }
+
+  void clear_tab() {
+    delete[] tab_statuses;
+    tab_statuses = NULL;
+    tab_row.clear();
+    basic = -1;
+  }
+};
+
+
+
+static void loadCut(glp_tree *tree, CutInfo* cut){
+  int ord, cut_len, cut_klass;
+  int N, M;
+  int* cut_inds;
+  double* cut_coeffs;
+  int glpk_cut_type;
+  double cut_rhs;
+  glp_prob* lp;
+
+  lp = glp_ios_get_prob(tree);
+  ord = cut->poolOrdinal();
+
+  N = glp_get_num_cols(lp);
+  M = glp_get_num_rows(lp);
+
+  cut->setDimensions(N, M);
+
+
+
+  // Get the cut
+  cut_len = glp_ios_get_cut(tree, ord, NULL, NULL, &cut_klass, NULL, NULL);
+  Assert(fromGlpkClass(cut_klass) == cut->getKlass());
+
+  PrimitiveVec& cut_vec = cut->getCutVector();
+  cut_vec.setup(cut_len);
+  cut_inds = cut_vec.inds;
+  cut_coeffs = cut_vec.coeffs;
+
+  cut_vec.len = glp_ios_get_cut(tree, ord, cut_inds, cut_coeffs, &cut_klass, &glpk_cut_type, &cut_rhs);
+  Assert(fromGlpkClass(cut_klass) == cut->getKlass());
+  Assert(cut_vec.len == cut_len);
+
+  cut->setRhs(cut_rhs);
+
+  cut->setKind( glpk_type_to_kind(glpk_cut_type) );
+}
+
+
+static MirInfo* mirCut(glp_tree *tree, int exec_ord, int cut_ord){
+  Trace("approx::mirCut") << "mirCut()" << exec_ord << endl;
+
+  MirInfo* mir;
+  mir = new MirInfo(exec_ord, cut_ord);
+  loadCut(tree, mir);
+  mir->initSet();
+
+
+  int nrows = glp_ios_cut_get_aux_nrows(tree, cut_ord);
+
+  PrimitiveVec& row_sum = mir->row_sum;
+  row_sum.setup(nrows);
+  glp_ios_cut_get_aux_rows(tree, cut_ord, row_sum.inds, row_sum.coeffs);
+
+  glp_ios_cut_get_mir_cset(tree, cut_ord, mir->cset);
+  mir->delta = glp_ios_cut_get_mir_delta(tree, cut_ord);
+  glp_ios_cut_get_mir_subst(tree, cut_ord, mir->subst);
+  glp_ios_cut_get_mir_virtual_rows(tree, cut_ord, mir->vlbRows, mir->vubRows);
+
+  if(TraceIsOn("approx::mirCut")){
+    Trace("approx::mirCut") << "mir_id: " << exec_ord << endl;
+    row_sum.print(Trace("approx::mirCut"));
+  }
+
+  return mir;
+}
+
+static GmiInfo* gmiCut(glp_tree *tree, int exec_ord, int cut_ord){
+  Trace("approx::gmiCut") << "gmiCut()" << exec_ord << endl;
+
+  int gmi_var;
+  int write_pos;
+  int read_pos;
+  int stat;
+  int ind;
+  int i;
+
+  GmiInfo* gmi;
+  glp_prob* lp;
+
+  gmi = new GmiInfo(exec_ord, cut_ord);
+  loadCut(tree, gmi);
+
+  lp = glp_ios_get_prob(tree);
+
+  int N = gmi->getN();
+  int M = gmi->getMAtCreation();
+
+  // Get the tableau row
+  int nrows CVC5_UNUSED = glp_ios_cut_get_aux_nrows(tree, gmi->poolOrdinal());
+  Assert(nrows == 1);
+  int rows[1+1];
+  glp_ios_cut_get_aux_rows(tree, gmi->poolOrdinal(), rows, NULL);
+  gmi_var = rows[1];
+
+  gmi->init_tab(N);
+  gmi->basic = M+gmi_var;
+
+  Trace("approx::gmiCut")
+    << gmi <<" " << gmi->basic << " "
+    << cut_ord<<" "  << M <<" " << gmi_var << endl;
+
+  PrimitiveVec& tab_row = gmi->tab_row;
+  Trace("approx::gmiCut") << "Is N sufficient here?" << endl;
+  tab_row.len = glp_eval_tab_row(lp, gmi->basic, tab_row.inds, tab_row.coeffs);
+
+  Trace("approx::gmiCut") << "gmi_var " << gmi_var << endl;
+
+  Trace("approx::gmiCut") << "tab_pos " << tab_row.len << endl;
+  write_pos = 1;
+  for(read_pos = 1; read_pos <= tab_row.len; ++read_pos){
+    if (fabs(tab_row.coeffs[read_pos]) < 1e-10){
+    }else{
+      tab_row.coeffs[write_pos] = tab_row.coeffs[read_pos];
+      tab_row.inds[write_pos] = tab_row.inds[read_pos];
+      ++write_pos;
+    }
+  }
+  tab_row.len = write_pos-1;
+  Trace("approx::gmiCut") << "write_pos " << write_pos << endl;
+  Assert(tab_row.len > 0);
+
+  for(i = 1; i <= tab_row.len; ++i){
+    ind = tab_row.inds[i];
+    Trace("approx::gmiCut") << "ind " << i << " " << ind << endl;
+    stat = (ind <= M) ?
+      glp_get_row_stat(lp, ind) : glp_get_col_stat(lp, ind - M);
+
+    Trace("approx::gmiCut") << "ind " << i << " " << ind << " stat " << stat << endl;
+    switch (stat){
+    case GLP_NL:
+    case GLP_NU:
+    case GLP_NS:
+      gmi->tab_statuses[i] = stat;
+      break;
+    case GLP_NF:
+    default:
+      Unreachable();
+    }
+  }
+
+  if(TraceIsOn("approx::gmiCut")){
+    gmi->print(Trace("approx::gmiCut"));
+  }
+  return gmi;
+}
+
+static BranchCutInfo* branchCut(glp_tree *tree, int exec_ord, int br_var, double br_val, bool down_bad){
+  //(tree, br_var, br_val, dn < 0);
+  double rhs;
+  Kind k;
+  if(down_bad){
+    // down branch is infeasible
+    // x <= floor(v) is infeasible
+    // - so x >= ceiling(v) is implied
+    k = kind::GEQ;
+    rhs = std::ceil(br_val);
+  }else{
+    // up branch is infeasible
+    // x >= ceiling(v) is infeasible
+    // - so x <= floor(v) is implied
+    k = kind::LEQ;
+    rhs = std::floor(br_val);
+  }
+  BranchCutInfo* br_cut = new BranchCutInfo(exec_ord, br_var, k, rhs);
+  return br_cut;
+}
+
+static void glpkCallback(glp_tree *tree, void *info){
+  AuxInfo* aux = (AuxInfo*)(info);
+  TreeLog& tl = *(aux->tl);
+
+  int exec = tl.getExecutionOrd();
+  int glpk_node_p = -1;
+  int node_ord = -1;
+
+  if(tl.isActivelyLogging()){
+    switch(glp_ios_reason(tree)){
+    case GLP_LI_DELROW:
+      {
+        glpk_node_p = glp_ios_curr_node(tree);
+        node_ord = glp_ios_node_ord(tree, glpk_node_p);
+
+        int nrows = glp_ios_rows_deleted(tree, NULL);
+        int* num = new int[1+nrows];
+        glp_ios_rows_deleted(tree, num);
+
+        NodeLog& node = tl.getNode(node_ord);
+
+        RowsDeleted* rd = new RowsDeleted(exec, nrows, num);
+
+        node.addCut(rd);
+        delete[] num;
+      }
+      break;
+    case GLP_ICUTADDED:
+      {
+        int cut_ord = glp_ios_pool_size(tree);
+        glpk_node_p = glp_ios_curr_node(tree);
+        node_ord = glp_ios_node_ord(tree, glpk_node_p);
+        Assert(cut_ord > 0);
+        Trace("approx") << "curr node " << glpk_node_p
+                        << " cut ordinal " << cut_ord
+                        << " node depth " << glp_ios_node_level(tree, glpk_node_p)
+                        << endl;
+        int klass;
+        glp_ios_get_cut(tree, cut_ord, NULL, NULL, &klass, NULL, NULL);
+
+        NodeLog& node = tl.getNode(node_ord);
+        switch(klass){
+        case GLP_RF_GMI:
+          {
+            GmiInfo* gmi = gmiCut(tree, exec, cut_ord);
+            node.addCut(gmi);
+          }
+          break;
+        case GLP_RF_MIR:
+          {
+            MirInfo* mir = mirCut(tree, exec, cut_ord);
+            node.addCut(mir);
+          }
+          break;
+        case GLP_RF_COV:
+          Trace("approx") << "GLP_RF_COV" << endl;
+          break;
+        case GLP_RF_CLQ:
+          Trace("approx") << "GLP_RF_CLQ" << endl;
+          break;
+        default:
+          break;
+        }
+      }
+      break;
+    case GLP_ICUTSELECT:
+      {
+        glpk_node_p = glp_ios_curr_node(tree);
+        node_ord = glp_ios_node_ord(tree, glpk_node_p);
+        int cuts = glp_ios_pool_size(tree);
+        int* ords = new int[1+cuts];
+        int* rows = new int[1+cuts];
+        int N = glp_ios_selected_cuts(tree, ords, rows);
+
+        NodeLog& nl = tl.getNode(node_ord);
+        Trace("approx") << glpk_node_p << " " << node_ord << " " << cuts << " " << N << std::endl;
+        for(int i = 1; i <= N; ++i){
+          Trace("approx") << "adding to " << node_ord <<" @ i= " << i
+                          << " ords[i] = " << ords[i]
+                          << " rows[i] = " << rows[i] << endl;
+          nl.addSelected(ords[i], rows[i]);
+        }
+        delete[] ords;
+        delete[] rows;
+        nl.applySelected();
+      }
+    break;
+  case GLP_LI_BRANCH:
+    {
+      // a branch was just made
+      int br_var;
+      int p, dn, up;
+      int p_ord, dn_ord, up_ord;
+      double br_val;
+      br_var = glp_ios_branch_log(tree, &br_val, &p, &dn, &up);
+      p_ord = glp_ios_node_ord(tree, p);
+
+      dn_ord = (dn >= 0) ? glp_ios_node_ord(tree, dn) : -1;
+      up_ord = (up >= 0) ? glp_ios_node_ord(tree, up) : -1;
+
+      Trace("approx::") << "branch: "<< br_var << " "  << br_val << " tree " << p << " " << dn << " " << up << endl;
+      Trace("approx::") << "\t " << p_ord << " " << dn_ord << " " << up_ord << endl;
+      if(dn < 0 && up < 0){
+        Trace("approx::") << "branch close " << exec << endl;
+        NodeLog& node = tl.getNode(p_ord);
+        BranchCutInfo* cut_br = branchCut(tree, exec, br_var, br_val, dn < 0);
+        node.addCut(cut_br);
+        tl.close(p_ord);
+      }else if(dn < 0 || up < 0){
+        Trace("approx::") << "branch cut" << exec << endl;
+        NodeLog& node = tl.getNode(p_ord);
+        BranchCutInfo* cut_br = branchCut(tree, exec, br_var, br_val, dn < 0);
+        node.addCut(cut_br);
+      }else{
+        Trace("approx::") << "normal branch" << endl;
+        tl.branch(p_ord, br_var, br_val, dn_ord, up_ord);
+      }
+    }
+    break;
+    case GLP_LI_CLOSE:
+      {
+        glpk_node_p = glp_ios_curr_node(tree);
+        node_ord = glp_ios_node_ord(tree, glpk_node_p);
+        Trace("approx::") << "close " << glpk_node_p << endl;
+        tl.close(node_ord);
+      }
+      break;
+    default:
+      break;
+    }
+  }
+
+  switch(glp_ios_reason(tree)){
+  case GLP_IBINGO:
+    Trace("approx::") << "bingo" << endl;
+    aux->term = MipBingo;
+    glp_ios_terminate(tree);
+    break;
+  case GLP_ICUTADDED:
+    {
+      tl.addCut();
+    }
+    break;
+  case GLP_LI_BRANCH:
+    {
+      int p, dn, up;
+      int br_var = glp_ios_branch_log(tree, NULL, &p, &dn, &up);
+
+      if(br_var >= 0){
+        unsigned v = br_var;
+        tl.logBranch(v);
+        int depth = glp_ios_node_level(tree, p);
+        unsigned ubl =  (aux->branchLimit) >= 0 ? ((unsigned)(aux->branchLimit)) : 0u;
+        if(tl.numBranches(v) >= ubl || depth >= (aux->branchDepth)){
+          aux->term = BranchesExhausted;
+          glp_ios_terminate(tree);
+        }
+      }
+    }
+    break;
+  case GLP_LI_CLOSE:
+    break;
+  default:
+    {
+      glp_prob* prob = glp_ios_get_prob(tree);
+      int iterationcount = glp_get_it_cnt(prob);
+      if(exec > (aux->pivotLimit)){
+        aux->term = ExecExhausted;
+        glp_ios_terminate(tree);
+      }else if(iterationcount > (aux->pivotLimit)){
+        aux->term = PivotsExhauasted;
+        glp_ios_terminate(tree);
+      }
+    }
+    break;
+  }
+}
+
+std::vector<const CutInfo*> ApproxGLPK::getValidCuts(const NodeLog& con)
+{
+  std::vector<const CutInfo*> proven;
+  int nid = con.getNodeId();
+  for(NodeLog::const_iterator j = con.begin(), jend=con.end(); j!=jend; ++j){
+    CutInfo* cut = *j;
+
+    if(cut->getKlass() != RowsDeletedKlass){
+      if(!cut->reconstructed()){
+        Assert(!cut->reconstructed());
+        tryCut(nid, *cut);
+      }
+    }
+
+    if(cut->proven()){
+      proven.push_back(cut);
+    }
+  }
+  return proven;
+}
+
+ArithVar ApproxGLPK::getBranchVar(const NodeLog& con) const{
+  int br_var = con.branchVariable();
+  return getArithVarFromStructural(br_var);
+}
+
+
+MipResult ApproxGLPK::solveMIP(bool activelyLog){
+  Assert(d_solvedRelaxation);
+  // Explicitly disable presolving
+  // We need the basis thus the presolver must be off!
+  // This is default, but this is just being cautious.
+  AuxInfo aux;
+  aux.pivotLimit = d_pivotLimit;
+  aux.branchLimit = d_branchLimit;
+  aux.branchDepth = d_maxDepth;
+  aux.tl = &d_log;
+  aux.term = MipUnknown;
+
+  d_log.reset(d_rootRowIds);
+  if(activelyLog){
+    d_log.makeActive();
+  }else{
+    d_log.makeInactive();
+  }
+
+  glp_iocp parm;
+  glp_init_iocp(&parm);
+  parm.presolve = GLP_OFF;
+  parm.pp_tech = GLP_PP_NONE;
+  parm.fp_heur = GLP_ON;
+  parm.gmi_cuts = GLP_ON;
+  parm.mir_cuts = GLP_ON;
+  parm.cov_cuts = GLP_ON;
+  parm.cb_func = glpkCallback;
+  parm.cb_info = &aux;
+  parm.msg_lev = GLP_MSG_OFF;
+
+  glp_erase_prob(d_mipProb);
+  glp_copy_prob(d_mipProb, d_realProb, GLP_OFF);
+
+  int res = glp_intopt(d_mipProb, &parm);
+
+  Trace("approx::solveMIP") << "res "<<res<<" aux.term "<< aux.term << endl;
+
+  switch(res){
+  case 0:
+  case GLP_ESTOP:
+    {
+      int status = glp_mip_status(d_mipProb);
+      Trace("approx::") << "status " << status << endl;
+      switch(status){
+      case GLP_OPT:
+      case GLP_FEAS:
+        d_solvedMIP = true;
+        Trace("approx::") << "bingo here!" << endl;
+        return MipBingo;
+      case GLP_NOFEAS:
+        d_solvedMIP = true;
+        return MipClosed;
+      default:
+        if(aux.term == MipBingo){
+          d_solvedMIP = true;
+          Trace("approx::") << "bingo here?" << endl;
+        }
+        return aux.term;
+      }
+    }
+  default:
+    return MipUnknown;
+  }
+}
+
+DeltaRational sumConstraints(const DenseMap<Rational>& xs, const DenseMap<ConstraintP>& cs, bool* anyinf){
+  if(anyinf != NULL){
+    *anyinf = false;
+  }
+
+  DeltaRational beta(0);
+  DenseMap<Rational>::const_iterator iter, end;
+  iter = xs.begin();
+  end = xs.end();
+
+  Trace("approx::sumConstraints") << "sumConstraints";
+  for(; iter != end; ++iter){
+    ArithVar x = *iter;
+    const Rational& psi = xs[x];
+    ConstraintP c = cs[x];
+    Assert(c != NullConstraint);
+
+    const DeltaRational& bound = c->getValue();
+    beta += bound * psi;
+    Trace("approx::sumConstraints") << " +("<<bound << "*" << psi <<")";
+    if(anyinf != NULL ){
+      *anyinf = *anyinf || !bound.infinitesimalIsZero();
+    }
+  }
+  Trace("approx::sumConstraints") << "= " << beta << endl;
+
+  return beta;
+}
+
+// remove fixed variables from the vector
+void removeFixed(const ArithVariables& vars, DenseVector& dv, set<ConstraintP>& exp){
+  DenseMap<Rational>& vec = dv.lhs;
+  Rational& removed = dv.rhs;
+  vector<ArithVar> equal;
+  DenseMap<Rational>::const_iterator vec_iter, vec_end;
+  vec_iter = vec.begin(), vec_end = vec.end();
+  for(; vec_iter != vec_end; ++vec_iter){
+    ArithVar x = *vec_iter;
+    if(vars.boundsAreEqual(x)){
+      equal.push_back(x);
+    }
+  }
+  vector<ArithVar>::const_iterator equal_iter, equal_end;
+  equal_iter = equal.begin(), equal_end = equal.end();
+  for(; equal_iter != equal_end; ++equal_iter){
+    ArithVar x = *equal_iter;
+    Assert(vars.boundsAreEqual(x));
+    const DeltaRational& lb = vars.getLowerBound(x);
+    Assert(lb.infinitesimalIsZero());
+    removed -= (vec[x]) * lb.getNoninfinitesimalPart();
+
+    vec.remove(x);
+
+    std::pair<ConstraintP, ConstraintP> p = vars.explainEqualBounds(x);
+    exp.insert(p.first);
+    Trace("removeFixed") << "remove fixed " << p.first << endl;
+    if(p.second != NullConstraint){
+      exp.insert(p.second);
+      Trace("removeFixed") << "remove fixed " << p.second << endl;
+    }
+  }
+}
+void removeZeroes(DenseMap<Rational>& v){
+  // Remove Slack variables
+  vector<ArithVar> zeroes;
+  DenseMap<Rational>::const_iterator i, iend;
+  for(i = v.begin(), iend = v.end(); i != iend; ++i){
+    ArithVar x = *i;
+    if(v[x].isZero()){
+      zeroes.push_back(x);
+    }
+  }
+
+  vector<ArithVar>::const_iterator j, jend;
+  for(j = zeroes.begin(), jend = zeroes.end(); j != jend; ++j){
+    ArithVar x = *j;
+    v.remove(x);
+  }
+}
+void removeZeroes(DenseVector& v){
+  removeZeroes(v.lhs);
+}
+
+void removeAuxillaryVariables(const ArithVariables& vars, DenseMap<Rational>& vec){
+  // Remove auxillary variables
+  vector<ArithVar> aux;
+  DenseMap<Rational>::const_iterator vec_iter, vec_end;
+  vec_iter = vec.begin(), vec_end = vec.end();
+  for(; vec_iter != vec_end; ++vec_iter){
+    ArithVar x = *vec_iter;
+    if(vars.isAuxiliary(x)){
+      aux.push_back(x);
+    }
+  }
+
+  vector<ArithVar>::const_iterator aux_iter, aux_end;
+  aux_iter = aux.begin(), aux_end = aux.end();
+  for(; aux_iter != aux_end; ++aux_iter){
+    ArithVar s = *aux_iter;
+    Rational& s_coeff = vec.get(s);
+    Assert(vars.isAuxiliary(s));
+    Assert(vars.hasNode(s));
+    Node sAsNode = vars.asNode(s);
+    Polynomial p = Polynomial::parsePolynomial(sAsNode);
+    for(Polynomial::iterator j = p.begin(), p_end=p.end(); j != p_end; ++j){
+      Monomial m = *j;
+      const Rational& ns_coeff = m.getConstant().getValue();
+      Node vl = m.getVarList().getNode();
+      ArithVar ns = vars.asArithVar(vl);
+      Rational prod = s_coeff * ns_coeff;
+      if(vec.isKey(ns)){
+        vec.get(ns) += prod;
+      }else{
+        vec.set(ns, prod);
+      }
+    }
+    s_coeff = Rational(0); // subtract s_coeff * s from vec
+  }
+  removeZeroes(vec);
+}
+
+ArithVar ApproxGLPK::_getArithVar(int nid, int M, int ind) const{
+  if(ind <= 0){
+    return ARITHVAR_SENTINEL;
+  }else if(ind <= M){
+    return getArithVarFromRow(nid, ind);
+  }else{
+    return getArithVarFromStructural(ind - M);
+  }
+}
+
+
+bool ApproxGLPK::guessIsConstructable(const DenseMap<Rational>& guess) const {
+  // basic variable
+  // sum g[i] * x_i
+  DenseMap<Rational> g = guess;
+  removeAuxillaryVariables(d_vars, g);
+
+  if(TraceIsOn("guessIsConstructable")){
+    if(!g.empty()){
+      Trace("approx::guessIsConstructable") << "guessIsConstructable failed " << g.size() << endl;
+      DenseVector::print(Trace("approx::guessIsConstructable"), g);
+      Trace("approx::guessIsConstructable") << endl;
+    }
+  }
+  return g.empty();
+}
+
+bool ApproxGLPK::loadToBound(int nid, int M, int len, int* inds, int* statuses, DenseMap<ConstraintP>& toBound) const{
+  for(int i = 1; i <= len; ++i){
+    int status = statuses[i];
+    int ind = inds[i];
+    ArithVar v = _getArithVar(nid, M, ind);
+    ConstraintP c = NullConstraint;
+    if(v == ARITHVAR_SENTINEL){ return true; }
+
+    switch(status){
+    case GLP_NL:
+      c = d_vars.getLowerBoundConstraint(v);
+      break;
+    case GLP_NU:
+    case GLP_NS: // upper bound sufficies for fixed variables
+      c = d_vars.getUpperBoundConstraint(v);
+      break;
+    case GLP_NF:
+    default:
+      return true;
+    }
+    if(c == NullConstraint){
+      Trace("approx::") << "couldn't find " << v << " @ " << nid << endl;
+      return true;
+    }
+    Assert(c != NullConstraint);
+    toBound.set(v, c);
+  }
+  return false;
+}
+
+bool ApproxGLPK::checkCutOnPad(int nid, const CutInfo& cut) const{
+
+  Trace("approx::checkCutOnPad") << "checkCutOnPad(" << nid <<", " << cut.getId() <<")"<<endl;
+
+  const DenseMap<Rational>& constructedLhs = d_pad.d_cut.lhs;
+  const Rational& constructedRhs = d_pad.d_cut.rhs;
+  std::unordered_set<ArithVar> visited;
+
+  if(constructedLhs.empty()){
+    Trace("approx::checkCutOnPad") << "its empty?" <<endl;
+    return true;
+  }
+  if(cut.getKind() != d_pad.d_cutKind) {
+    Trace("approx::checkCutOnPad") << "rel doesn't match" << endl;
+    return true;
+  }
+
+  const PrimitiveVec& cv = cut.getCutVector();
+  for(int i = 1; i <= cv.len; ++i){
+    int ind = cv.inds[i]; // this is always a structural variable
+    double coeff = cv.coeffs[i];
+
+
+
+    if(!d_colToArithVar.isKey(ind)){ return true; }
+    ArithVar x = d_colToArithVar[ind];
+    //if(x == ARITHVAR_SENTINEL){ return true; }
+    visited.insert(x);
+
+
+    if(!constructedLhs.isKey(x)){
+      if(TraceIsOn("approx::checkCutOnPad")){
+        Trace("approx::checkCutOnPad") << " didn't find key for " << x << std::endl;
+        cut.print(Trace("approx::checkCutOnPad"));
+        Trace("approx::checkCutOnPad") << endl;
+        d_pad.d_cut.print(Trace("approx::checkCutOnPad"));
+        Trace("approx::checkCutOnPad") << endl;
+      }
+      return true;
+    }
+
+    const Rational& onConstructed = constructedLhs[x];
+
+    Trace("approx::checkCutOnPad") << ind << " " << coeff  << " " << endl;
+    Trace("approx::checkCutOnPad") << " " << x << " " << onConstructed << endl;
+
+    if(!roughlyEqual(coeff, onConstructed.getDouble())){
+      Trace("approx::checkCutOnPad") << "coeff failure" << endl;
+      return true;
+    }
+  }
+  if(visited.size() != constructedLhs.size()){
+    Trace("approx::checkCutOnPad") << "size mismatch" << endl;
+    return true;
+  }
+
+
+  if(!roughlyEqual(cut.getRhs(), constructedRhs.getDouble())){
+    Trace("approx::checkCutOnPad")
+      << "norm rhs is off " << cut.getRhs() << " " << constructedRhs << endl;
+    return true;
+  }
+  return false;
+}
+
+
+
+bool ApproxGLPK::attemptBranchCut(int nid, const BranchCutInfo& br_cut){
+  d_pad.clear();
+
+  const PrimitiveVec& cut_vec = br_cut.getCutVector();
+  int structural = cut_vec.inds[1];
+  Assert(roughlyEqual(cut_vec.coeffs[1], +1.0));
+
+  ArithVar x = getArithVarFromStructural(structural);
+  d_pad.d_failure = (x == ARITHVAR_SENTINEL);
+  if(d_pad.d_failure){ return true; }
+
+  Kind brKind = br_cut.getKind();
+
+  d_pad.d_failure = (brKind != kind::LEQ && brKind != kind::GEQ);
+  if(d_pad.d_failure){ return true; }
+
+  d_pad.d_cutKind = brKind;
+  d_pad.d_cut.lhs.set(x, Rational(1));
+
+  Rational& rhs = d_pad.d_cut.rhs;
+  std::optional<Rational> br_cut_rhs = Rational::fromDouble(br_cut.getRhs());
+  if (!br_cut_rhs)
+  {
+    return true;
+  }
+
+  rhs = estimateWithCFE(*br_cut_rhs, Integer(1));
+  d_pad.d_failure = !rhs.isIntegral();
+  if(d_pad.d_failure) { return true; }
+
+  d_pad.d_failure = checkCutOnPad(nid, br_cut);
+  if(d_pad.d_failure){ return true; }
+
+  return false;
+}
+
+bool ApproxGLPK::attemptGmi(int nid, const GmiInfo& gmi){
+  d_pad.clear();
+
+  d_pad.d_cutKind = kind::GEQ;
+
+  int M = gmi.getMAtCreation();
+  ArithVar b = _getArithVar(nid, M, gmi.basic);
+  d_pad.d_failure = (b == ARITHVAR_SENTINEL);
+  if(d_pad.d_failure){ return true; }
+
+  d_pad.d_failure = !d_vars.isIntegerInput(b);
+  if(d_pad.d_failure){ return true; }
+
+  d_pad.d_basic = b;
+
+
+  const PrimitiveVec& tab = gmi.tab_row;
+  d_pad.d_failure = attemptConstructTableRow(nid, M, tab);
+  if(d_pad.d_failure){ return true; }
+
+  int* statuses = gmi.tab_statuses;
+  DenseMap<ConstraintP>& toBound = d_pad.d_toBound;
+  d_pad.d_failure = loadToBound(nid, M, tab.len, tab.inds, statuses, toBound);
+  if(d_pad.d_failure){ return true; }
+
+  d_pad.d_failure = constructGmiCut();
+  if(d_pad.d_failure){ return true; }
+
+  d_pad.d_failure = checkCutOnPad(nid, gmi);
+  if(d_pad.d_failure){ return true; }
+
+  return false;
+}
+
+bool ApproxGLPK::applyCMIRRule(int nid, const MirInfo& mir){
+
+  const DenseMap<Rational>& compRanges = d_pad.d_compRanges;
+
+  DenseMap<Rational>& alpha = d_pad.d_alpha.lhs;
+  Rational& b = d_pad.d_alpha.rhs;
+
+  std::optional<Rational> delta = estimateWithCFE(mir.delta);
+  if (!delta)
+  {
+    return true;
+  }
+
+  d_pad.d_failure = (delta->sgn() <= 0);
+  if(d_pad.d_failure){ return true; }
+
+  Trace("approx::mir") << "applyCMIRRule() " << delta << " " << mir.delta << endl;
+
+  DenseMap<Rational>::const_iterator iter, iend;
+  iter = alpha.begin(), iend = alpha.end();
+  for(; iter != iend; ++iter){
+    ArithVar v = *iter;
+    const Rational& curr = alpha[v];
+    Rational next = curr / *delta;
+    if(compRanges.isKey(v)){
+      b -= curr * compRanges[v];
+      alpha.set(v, - next);
+    }else{
+      alpha.set(v, next);
+    }
+  }
+  b = b / *delta;
+
+  Rational roundB = (b + Rational(1,2)).floor();
+  d_pad.d_failure = (b - roundB).abs() < Rational(1,90);
+  // intensionally more generous than glpk here
+  if(d_pad.d_failure){ return true; }
+
+  Rational one(1);
+  Rational fb = b.floor_frac();
+  Rational one_sub_fb = one - fb;
+  Rational gamma = (one / one_sub_fb);
+
+  DenseMap<Rational>& cut = d_pad.d_cut.lhs;
+  Rational& beta = d_pad.d_cut.rhs;
+
+  iter = alpha.begin(), iend = alpha.end();
+  for(; iter != iend; ++iter){
+    ArithVar v = *iter;
+    const Rational& a_j = alpha[v];
+    if(d_vars.isIntegerInput(v)){
+      Rational floor_aj = a_j.floor();
+      Rational frac_aj = a_j.floor_frac();
+      if(frac_aj <= fb){
+        cut.set(v, floor_aj);
+      }else{
+        Rational tmp =  ((frac_aj - fb) / one_sub_fb);
+        cut.set(v, floor_aj + tmp);
+      }
+    }else{
+      cut.set(v, a_j * gamma);
+    }
+  }
+  beta = b.floor();
+
+  iter = cut.begin(), iend = cut.end();
+  for(; iter != iend; ++iter){
+    ArithVar v = *iter;
+    if(compRanges.isKey(v)){
+      Rational neg = - cut[v];
+      beta += neg * compRanges[v];
+      cut.set(v, neg);
+    }
+  }
+
+  return false;
+}
+
+bool ApproxGLPK::attemptMir(int nid, const MirInfo& mir){
+  d_pad.clear();
+
+  d_pad.d_cutKind = kind::LEQ;
+
+  // virtual bounds must be done before slacks
+  d_pad.d_failure = loadVirtualBoundsIntoPad(nid, mir);
+  if(d_pad.d_failure){ return true; }
+
+  d_pad.d_failure = loadSlacksIntoPad(nid, mir);
+  if(d_pad.d_failure){ return true; }
+
+
+  d_pad.d_failure = loadRowSumIntoAgg(nid, mir.getMAtCreation(), mir.row_sum);
+  if(d_pad.d_failure){ return true; }
+
+  removeFixed(d_vars, d_pad.d_agg, d_pad.d_explanation);
+
+  d_pad.d_failure = buildModifiedRow(nid, mir);
+  if(d_pad.d_failure){ return true; }
+
+  d_pad.d_failure =  constructMixedKnapsack();
+  if(d_pad.d_failure){ return true; }
+
+  d_pad.d_failure = makeRangeForComplemented(nid, mir);
+  if(d_pad.d_failure){ return true; }
+
+  d_pad.d_failure = applyCMIRRule(nid, mir);
+  if(d_pad.d_failure){ return true; }
+
+  d_pad.d_failure = replaceSlacksOnCuts();
+  if(d_pad.d_failure){ return true; }
+
+  removeAuxillaryVariables(d_vars, d_pad.d_cut.lhs);
+
+  d_pad.d_failure = checkCutOnPad(nid, mir);
+  if(d_pad.d_failure){ return true; }
+
+  return false;
+  //return makeCutNodes(nid, mir);
+}
+
+/** Returns true on failure. */
+bool ApproxGLPK::loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp){
+  if(ri <= 0) { return true; }
+
+  Trace("glpk::loadVB") << "loadVB()" << endl;
+
+  ArithVar rowVar = _getArithVar(nid, M, ri);
+  ArithVar contVar = _getArithVar(nid, M, j);
+  if(rowVar == ARITHVAR_SENTINEL){
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " rowVar is ARITHVAR_SENTINEL " << rowVar << endl;
+    return true;
+  }
+  if(contVar == ARITHVAR_SENTINEL){
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " contVar is ARITHVAR_SENTINEL " << contVar
+                          << endl;
+    return true; }
+
+  if(!d_vars.isAuxiliary(rowVar)){
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " rowVar is not auxilliary " << rowVar << endl;
+    return true;
+  }
+  // is integer is correct here
+  if(d_vars.isInteger(contVar)){
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " contVar is integer " << contVar << endl;
+    return true;
+  }
+
+  ConstraintP lb = d_vars.getLowerBoundConstraint(rowVar);
+  ConstraintP ub = d_vars.getUpperBoundConstraint(rowVar);
+
+  if(lb != NullConstraint && ub != NullConstraint){
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " lb and ub are both NULL " << lb << " " << ub
+                          << endl;
+    return true;
+  }
+
+  ConstraintP rcon = lb == NullConstraint ? ub : lb;
+  if(rcon == NullConstraint) {
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " rcon is NULL " << rcon << endl;
+    return true;
+  }
+
+  if(!rcon->getValue().isZero()){
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " rcon value is not 0 " << rcon->getValue()
+                          << endl;
+    return true;
+  }
+
+  if(!d_vars.hasNode(rowVar)){
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " does not have node " << rowVar << endl;
+    return true;
+  }
+
+  Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(rowVar));
+  if (p.size() != 2)
+  {
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " polynomial is not binary: " << p.getNode()
+                          << endl;
+    return true;
+  }
+
+  Monomial first = p.getHead(), second = p.getTail().getHead();
+  Rational c1 = first.getConstant().getValue();
+  Rational c2 = second.getConstant().getValue();
+  Node nx1 = first.getVarList().getNode();
+  Node nx2 = second.getVarList().getNode();
+
+  if(!d_vars.hasArithVar(nx1)) {
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " does not have a variable for nx1: " << nx1
+                          << endl;
+    return true;
+  }
+  if(!d_vars.hasArithVar(nx2)) {
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " does not have a variable for nx2 " << nx2
+                          << endl;
+    return true;
+  }
+  ArithVar x1 = d_vars.asArithVar(nx1), x2 = d_vars.asArithVar(nx2);
+
+  Assert(x1 != x2);
+  Assert(!c1.isZero());
+  Assert(!c2.isZero());
+
+  Trace("glpk::loadVB")
+    << " lb " << lb
+    << " ub " << ub
+    << " rcon " << rcon
+    << " x1 " << x1
+    << " x2 " << x2
+    << " c1 " << c1
+    << " c2 " << c2 << endl;
+
+  ArithVar iv = (x1 == contVar) ? x2 : x1;
+  Rational& cc = (x1 == contVar) ? c1 : c2;
+  Rational& ic = (x1 == contVar) ? c2 : c1;
+
+  Trace("glpk::loadVB")
+    << " cv " << contVar
+    << " cc " << cc
+    << " iv " << iv
+    << " c2 " << ic << endl;
+
+  if(!d_vars.isIntegerInput(iv)){
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " iv is not an integer input variable " << iv
+                          << endl;
+    return true;
+  }
+  // cc * cv + ic * iv <= 0 or
+  // cc * cv + ic * iv <= 0
+
+  if(rcon == ub){ // multiply by -1
+    cc = -cc; ic = - ic;
+  }
+  Trace("glpk::loadVB") << " cv " << contVar
+                        << " cc " << cc
+                        << " iv " << iv
+                        << " c2 " << ic << endl;
+
+  // cc * cv + ic * iv >= 0
+  // cc * cv >= -ic * iv
+  // if cc < 0:
+  //   cv <= -ic/cc * iv
+  // elif cc > 0:
+  //   cv >= -ic/cc * iv
+  Assert(!cc.isZero());
+  Rational d = -ic/cc;
+  Trace("glpk::loadVB") << d << " " << cc.sgn() << endl;
+  bool nowUb = cc.sgn() < 0;
+  if(wantUb != nowUb) {
+    Trace("glpk::loadVB") << "loadVB()"
+                          << " wantUb is not nowUb " << wantUb << " " << nowUb
+                          << endl;
+
+    return true;
+  }
+
+  Kind rel = wantUb ? kind::LEQ : kind::GEQ;
+
+  tmp = VirtualBound(contVar, rel, d, iv, rcon);
+  Trace("glpk::loadVB") << "loadVB()"
+                        << " was successful" << endl;
+  return false;
+}
+
+bool ApproxGLPK::loadVirtualBoundsIntoPad(int nid, const MirInfo& mir){
+  Assert(mir.vlbRows != NULL);
+  Assert(mir.vubRows != NULL);
+
+  int N = mir.getN();
+  int M = mir.getMAtCreation();
+
+  // Load the virtual bounds first
+  VirtualBound tmp;
+  for(int j=1; j <= N+M; ++j){
+    if(!loadVB(nid, M, j, mir.vlbRows[j], false, tmp)){
+      if(d_pad.d_vlb.isKey(tmp.x)){ return true; }
+      d_pad.d_vlb.set(tmp.x, tmp);
+    }else if(mir.vlbRows[j] > 0){
+      Trace("approx::mir") << "expected vlb to work" << endl;
+    }
+    if(!loadVB(nid, M, j, mir.vubRows[j], true, tmp)){
+      if(d_pad.d_vub.isKey(tmp.x)){ return true; }
+      d_pad.d_vub.set(tmp.x, tmp);
+    }else if(mir.vubRows[j] > 0){
+      Trace("approx::mir") << "expected vub to work" << endl;
+    }
+  }
+  return false;
+}
+
+bool ApproxGLPK::loadSlacksIntoPad(int nid, const MirInfo& mir){
+  Assert(mir.vlbRows != NULL);
+  Assert(mir.vubRows != NULL);
+
+  int N = mir.getN();
+  int M = mir.getMAtCreation();
+
+  bool useVB;
+  // Load the virtual bounds first
+  SlackReplace rep;
+  bool lb;
+  ConstraintP b;
+  Trace("approx::mir") << "loadSlacksIntoPad(): N="<<N<<", M=" << M << std::endl;
+  for(int j=1; j <= N+M; ++j){
+    ArithVar v = _getArithVar(nid, M, j);
+    if(v == ARITHVAR_SENTINEL){
+      Trace("approx::mir") << " for: " << j << " no variable" << endl;
+      continue;
+    }
+    rep = SlackUndef;
+    char sub = mir.subst[j];
+    switch(sub){
+    case 'L':
+    case 'U':
+      lb = (sub == 'L');
+      useVB = lb ? (mir.vlbRows[j] > 0) : (mir.vubRows[j] > 0);
+      if(useVB){
+        if(lb ? d_pad.d_vlb.isKey(v) : d_pad.d_vub.isKey(v)){
+          rep = lb ? SlackVLB : SlackVUB;
+        }
+      }else{
+        b = lb ? d_vars.getLowerBoundConstraint(v)
+          : d_vars.getUpperBoundConstraint(v);
+        if(b != NullConstraint){
+          if(b->getValue().infinitesimalIsZero()){
+            rep = lb ? SlackLB : SlackUB;
+          }
+        }
+      }
+
+      Trace("approx::mir") << " for: " << j << ", " << v;
+      Trace("approx::mir") << " " << ((rep != SlackUndef) ? "succ" : "fail") << " ";
+      Trace("approx::mir") << sub << " " << rep << " " << mir.vlbRows[j] << " " << mir.vubRows[j]
+                           << endl;
+      if(rep != SlackUndef){
+        d_pad.d_slacks.set(v,rep);
+      }
+      break;
+    case '?':
+      continue;
+    default:
+      Trace("approx::mir") << " for: " << j << " got subst " << (int)sub << endl;
+      continue;
+    }
+  }
+  return false;
+}
+
+bool ApproxGLPK::replaceSlacksOnCuts(){
+  vector<ArithVar> virtualVars;
+
+  DenseMap<Rational>& cut = d_pad.d_cut.lhs;
+  Rational& cutRhs = d_pad.d_cut.rhs;
+
+  DenseMap<Rational>::const_iterator iter, iend;
+  iter = cut.begin(), iend = cut.end();
+  for(; iter != iend; ++iter){
+    ArithVar x = *iter;
+    SlackReplace rep = d_pad.d_slacks[x];
+    if(d_vars.isIntegerInput(x)){
+      Assert(rep == SlackLB || rep == SlackUB);
+      Rational& a = cut.get(x);
+
+      const DeltaRational& bound = (rep == SlackLB) ?
+        d_vars.getLowerBound(x) : d_vars.getUpperBound(x);
+      Assert(bound.infinitesimalIsZero());
+      Rational prod = a * bound.getNoninfinitesimalPart();
+      if(rep == SlackLB){
+        cutRhs += prod;
+      }else{
+        cutRhs -= prod;
+        a = -a;
+      }
+    }else if(rep == SlackVLB){
+      virtualVars.push_back(d_pad.d_vlb[x].y);
+    }else if(rep == SlackVUB){
+      virtualVars.push_back(d_pad.d_vub[x].y);
+    }
+  }
+
+  for(size_t i = 0; i < virtualVars.size(); ++i){
+    ArithVar x = virtualVars[i];
+    if(!cut.isKey(x)){
+      cut.set(x, Rational(0));
+    }
+  }
+
+  iter = cut.begin(), iend = cut.end();
+  for(; iter != iend; ++iter){
+    ArithVar x = *iter;
+    if(!d_vars.isIntegerInput(x)){
+      SlackReplace rep = d_pad.d_slacks[x];
+      Rational& a = cut.get(x);
+      switch(rep){
+      case SlackLB:
+        {
+          const DeltaRational& bound = d_vars.getLowerBound(x);
+          Assert(bound.infinitesimalIsZero());
+          cutRhs += a * bound.getNoninfinitesimalPart();
+        }
+        break;
+      case SlackUB:
+        {
+          const DeltaRational& bound = d_vars.getUpperBound(x);
+          Assert(bound.infinitesimalIsZero());
+          cutRhs -= a * bound.getNoninfinitesimalPart();
+          a = -a;
+        }
+        break;
+      case SlackVLB:
+      case SlackVUB:
+        {
+          bool lb = (rep == SlackVLB);
+          const VirtualBound& vb = lb ?
+            d_pad.d_vlb[x] : d_pad.d_vub[x];
+          ArithVar y = vb.y;
+          Assert(vb.x == x);
+          Assert(cut.isKey(y));
+          Rational& ycoeff = cut.get(y);
+          if(lb){
+            ycoeff -= a * vb.d;
+          }else{
+            ycoeff += a * vb.d;
+            a = -a;
+          }
+        }
+        break;
+      default:
+        return true;
+      }
+    }
+  }
+  removeZeroes(cut);
+  return false;
+}
+
+bool ApproxGLPK::loadRowSumIntoAgg(int nid, int M, const PrimitiveVec& row_sum){
+  DenseMap<Rational>& lhs = d_pad.d_agg.lhs;
+  d_pad.d_agg.rhs = Rational(0);
+
+  int len = row_sum.len;
+  for(int i = 1; i <= len; ++i){
+    int aux_ind = row_sum.inds[i]; // auxillary index
+    double coeff = row_sum.coeffs[i];
+    ArithVar x = _getArithVar(nid, M, aux_ind);
+    if(x == ARITHVAR_SENTINEL){ return true; }
+    std::optional<Rational> c = estimateWithCFE(coeff);
+    if (!c)
+    {
+      return true;
+    }
+
+    if (lhs.isKey(x))
+    {
+      lhs.get(x) -= *c;
+    }
+    else
+    {
+      lhs.set(x, -(*c));
+    }
+  }
+
+  Trace("approx::mir") << "beg loadRowSumIntoAgg() 1" << endl;
+  if(TraceIsOn("approx::mir")) { DenseVector::print(Trace("approx::mir"), lhs); }
+  removeAuxillaryVariables(d_vars, lhs);
+  Trace("approx::mir") << "end loadRowSumIntoAgg() 1" << endl;
+
+  if(TraceIsOn("approx::mir")){
+    Trace("approx::mir") << "loadRowSumIntoAgg() 2" << endl;
+    DenseVector::print(Trace("approx::mir"), lhs);
+    Trace("approx::mir") << "end loadRowSumIntoAgg() 2" << endl;
+  }
+
+  for(int i = 1; i <= len; ++i){
+    int aux_ind = row_sum.inds[i]; // auxillary index
+    double coeff = row_sum.coeffs[i];
+    ArithVar x = _getArithVar(nid, M, aux_ind);
+    Assert(x != ARITHVAR_SENTINEL);
+    std::optional<Rational> c = estimateWithCFE(coeff);
+    if (!c)
+    {
+      return true;
+    }
+    Assert(!lhs.isKey(x));
+    lhs.set(x, *c);
+  }
+
+  if(TraceIsOn("approx::mir")){
+    Trace("approx::mir") << "loadRowSumIntoAgg() 2" << endl;
+    DenseVector::print(Trace("approx::mir"), lhs);
+    Trace("approx::mir") << "end loadRowSumIntoAgg() 3" << endl;
+  }
+  return false;
+}
+
+bool ApproxGLPK::buildModifiedRow(int nid, const MirInfo& mir){
+  const DenseMap<Rational>& agg = d_pad.d_agg.lhs;
+  const Rational& aggRhs = d_pad.d_agg.rhs;
+  DenseMap<Rational>& mod = d_pad.d_mod.lhs;
+  Rational& modRhs = d_pad.d_mod.rhs;
+
+  Trace("approx::mir")
+    << "buildModifiedRow()"
+    << " |agg|=" << d_pad.d_agg.lhs.size()
+    << " |mod|=" << d_pad.d_mod.lhs.size()
+    << " |slacks|=" << d_pad.d_slacks.size()
+    << " |vlb|=" << d_pad.d_vub.size()
+    << " |vub|=" << d_pad.d_vlb.size() << endl;
+
+  mod.addAll(agg);
+  modRhs = aggRhs;
+  DenseMap<Rational>::const_iterator iter, iend;
+  for(iter = agg.begin(), iend = agg.end(); iter != iend; ++iter){
+    ArithVar x = *iter;
+    const Rational& c = mod[x];
+    if(!d_pad.d_slacks.isKey(x)){
+      Trace("approx::mir") << "missed x: " << x << endl;
+      return true;
+    }
+    SlackReplace rep = d_pad.d_slacks[x];
+    switch(rep){
+    case SlackLB: // skip for now
+    case SlackUB:
+      break;
+    case SlackVLB: /* x[k] = lb[k] * x[kk] + x'[k] */
+    case SlackVUB: /* x[k] = ub[k] * x[kk] - x'[k] */
+      {
+        Assert(!d_vars.isIntegerInput(x));
+        bool ub = (rep == SlackVUB);
+        const VirtualBound& vb =
+          ub ? d_pad.d_vub[x] : d_pad.d_vlb[x];
+        Assert(vb.x == x);
+        ArithVar y = vb.y;
+        Rational prod = c * vb.d;
+        if(mod.isKey(y)){
+          mod.get(x) += prod;
+        }else{
+          mod.set(y, prod);
+        }
+        if(ub){
+          mod.set(x, -c);
+        }
+        Assert(vb.c != NullConstraint);
+        d_pad.d_explanation.insert(vb.c);
+      }
+      break;
+    default:
+      return true;
+    }
+  }
+  removeZeroes(mod); /* if something cancelled we don't want it in the explanation */
+  for(iter = mod.begin(), iend = mod.end(); iter != iend; ++iter){
+    ArithVar x = *iter;
+    if(!d_pad.d_slacks.isKey(x)){  return true; }
+
+    SlackReplace rep = d_pad.d_slacks[x];
+    switch(rep){
+    case SlackLB: /* x = lb + x' */
+    case SlackUB: /* x = ub - x' */
+      {
+        bool ub = (rep == SlackUB);
+        ConstraintP b = ub ?  d_vars.getUpperBoundConstraint(x):
+          d_vars.getLowerBoundConstraint(x);
+
+        Assert(b != NullConstraint);
+        Assert(b->getValue().infinitesimalIsZero());
+        const Rational& c = mod.get(x);
+        modRhs -= c * b->getValue().getNoninfinitesimalPart();
+        if(ub){
+          mod.set(x, -c);
+        }
+        d_pad.d_explanation.insert(b);
+      }
+      break;
+    case SlackVLB: /* handled earlier */
+    case SlackVUB:
+      break;
+    default:
+      return true;
+    }
+  }
+  removeZeroes(mod);
+  return false;
+}
+
+bool ApproxGLPK::makeRangeForComplemented(int nid, const MirInfo& mir){
+  DenseMap<Rational>& alpha = d_pad.d_alpha.lhs;
+  int M = mir.getMAtCreation();
+  int N = mir.getN();
+  DenseMap<Rational>& compRanges = d_pad.d_compRanges;
+
+  int complemented = 0;
+
+  for(int j = 1; j <= M + N; ++j){
+    if(mir.cset[j] != 0){
+      complemented++;
+      ArithVar x = _getArithVar(nid, M, j);
+      if(!alpha.isKey(x)){ return true; }
+      if(!d_vars.isIntegerInput(x)){ return true; }
+      Assert(d_pad.d_slacks.isKey(x));
+      Assert(d_pad.d_slacks[x] == SlackLB || d_pad.d_slacks[x] == SlackUB);
+
+      ConstraintP lb = d_vars.getLowerBoundConstraint(x);
+      ConstraintP ub = d_vars.getUpperBoundConstraint(x);
+
+      if(lb == NullConstraint) { return true; }
+      if(ub == NullConstraint) { return true; }
+
+      if(!lb->getValue().infinitesimalIsZero()){
+        return true;
+      }
+      if(!ub->getValue().infinitesimalIsZero()){
+        return true;
+      }
+
+      const Rational& uval = ub->getValue().getNoninfinitesimalPart();
+      const Rational& lval = lb->getValue().getNoninfinitesimalPart();
+
+      d_pad.d_explanation.insert(lb);
+      d_pad.d_explanation.insert(ub);
+
+      Rational u = uval - lval;
+      // u is the same for both rep == LP and rep == UB
+      if(compRanges.isKey(x)) { return true; }
+      compRanges.set(x,u);
+    }
+  }
+
+  Trace("approx::mir") <<  "makeRangeForComplemented()" << complemented << endl;
+  return false;
+}
+
+
+bool ApproxGLPK::constructMixedKnapsack(){
+  const DenseMap<Rational>& mod = d_pad.d_mod.lhs;
+  const Rational& modRhs = d_pad.d_mod.rhs;
+  DenseMap<Rational>& alpha = d_pad.d_alpha.lhs;
+  Rational& beta = d_pad.d_alpha.rhs;
+
+  Assert(alpha.empty());
+  beta = modRhs;
+
+  unsigned intVars = 0;
+  unsigned remain = 0;
+  unsigned dropped = 0;
+  DenseMap<Rational>::const_iterator iter, iend;
+  for(iter = mod.begin(), iend = mod.end(); iter != iend; ++iter){
+    ArithVar v = *iter;
+    const Rational& c = mod[v];
+    Assert(!c.isZero());
+    if(d_vars.isIntegerInput(v)){
+      intVars++;
+      alpha.set(v, c);
+    }else if(c.sgn() < 0){
+      remain++;
+      alpha.set(v, c);
+    }else{
+      dropped++;
+    }
+  }
+
+  Trace("approx::mir")
+    << "constructMixedKnapsack() "
+    <<" dropped " << dropped
+    <<" remain " << remain
+    <<" intVars " << intVars
+    << endl;
+  return intVars == 0; // if this is 0 we have failed
+}
+
+bool ApproxGLPK::attemptConstructTableRow(int nid, int M, const PrimitiveVec& vec){
+  bool failed = guessCoefficientsConstructTableRow(nid, M, vec);
+  if(failed){
+    failed = gaussianElimConstructTableRow(nid, M, vec);
+  }
+
+  return failed;
+}
+
+bool ApproxGLPK::gaussianElimConstructTableRow(int nid, int M, const PrimitiveVec& vec){
+  TimerStat::CodeTimer codeTimer(d_stats.d_gaussianElimConstructTime);
+  ++d_stats.d_gaussianElimConstruct;
+
+  ArithVar basic = d_pad.d_basic;
+  DenseMap<Rational>& tab = d_pad.d_tabRow.lhs;
+  tab.purge();
+  d_pad.d_tabRow.rhs = Rational(0);
+  Assert(basic != ARITHVAR_SENTINEL);
+  Assert(tab.empty());
+  Assert(d_pad.d_tabRow.rhs.isZero());
+
+  if(d_vars.isAuxiliary(basic)) { return true; }
+
+  if(TraceIsOn("gaussianElimConstructTableRow")){
+    Trace("gaussianElimConstructTableRow") << "1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+    vec.print(Trace("gaussianElimConstructTableRow"));
+    Trace("gaussianElimConstructTableRow") << "match " << basic << "("<<d_vars.asNode(basic)<<")"<<endl;
+  }
+
+  set<ArithVar> onrow;
+  for(int i = 1; i <= vec.len; ++i){
+    int ind = vec.inds[i];
+    ArithVar var = _getArithVar(nid, M, ind);
+    if(var == ARITHVAR_SENTINEL){
+      Trace("gaussianElimConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl;
+      return true;
+    }
+    onrow.insert(var);
+  }
+
+
+  Trace("gaussianElimConstructTableRow") << "2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+
+  Matrix<Rational> A;
+  A.increaseSizeTo(d_vars.getNumberOfVariables());
+  std::vector< std::pair<RowIndex, ArithVar> > rows;
+  // load the rows for auxiliary variables into A
+  for (ArithVar v : onrow)
+  {
+    if(d_vars.isAuxiliary(v)){
+      Assert(d_vars.hasNode(v));
+
+      vector<Rational> coeffs;
+      vector<ArithVar> vars;
+
+      coeffs.push_back(Rational(-1));
+      vars.push_back(v);
+
+      Node n = d_vars.asNode(v);
+      Polynomial p = Polynomial::parsePolynomial(n);
+      Polynomial::iterator j = p.begin(), jend=p.end();
+      for(j=p.begin(), jend=p.end(); j!=jend; ++j){
+        Monomial m = *j;
+        if(m.isConstant()) { return true; }
+        VarList vl = m.getVarList();
+        if(!d_vars.hasArithVar(vl.getNode())){ return true; }
+        ArithVar x = d_vars.asArithVar(vl.getNode());
+        const Rational& q = m.getConstant().getValue();
+        coeffs.push_back(q); vars.push_back(x);
+      }
+      RowIndex rid = A.addRow(coeffs, vars);
+      rows.push_back(make_pair(rid, ARITHVAR_SENTINEL));
+    }
+  }
+  Trace("gaussianElimConstructTableRow") << "3 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+
+  for(size_t i=0; i < rows.size(); ++i){
+    RowIndex rid = rows[i].first;
+    Assert(rows[i].second == ARITHVAR_SENTINEL);
+
+    // substitute previous rows
+    for(size_t j=0; j < i; j++){
+      RowIndex prevRow = rows[j].first;
+      ArithVar other = rows[j].second;
+      Assert(other != ARITHVAR_SENTINEL);
+      const Matrix<Rational>::Entry& e = A.findEntry(rid, other);
+      if(!e.blank()){
+        // r_p : 0 = -1 * other + sum a_i x_i
+        // rid : 0 =  e * other + sum b_i x_i
+        // rid += e * r_p
+        //     : 0 = 0 * other + ...
+        Assert(!e.getCoefficient().isZero());
+
+        Rational cp = e.getCoefficient();
+        Trace("gaussianElimConstructTableRow")
+          << "on " << rid << " subst " << cp << "*" << prevRow << " " << other << endl;
+        A.rowPlusRowTimesConstant(rid, prevRow, cp);
+      }
+    }
+    if(TraceIsOn("gaussianElimConstructTableRow")){
+      A.printMatrix(Trace("gaussianElimConstructTableRow"));
+    }
+
+    // solve the row for anything other than non-basics
+    bool solveForBasic = (i + 1 == rows.size());
+    Rational q;
+    ArithVar s = ARITHVAR_SENTINEL;
+    Matrix<Rational>::RowIterator k = A.getRow(rid).begin();
+    Matrix<Rational>::RowIterator k_end = A.getRow(rid).end();
+    for(; k != k_end; ++k){
+      const Matrix<Rational>::Entry& e = *k;
+      ArithVar colVar = e.getColVar();
+      bool selectColVar = false;
+      if(colVar == basic){
+        selectColVar = solveForBasic;
+      }else if(onrow.find(colVar) == onrow.end()) {
+        selectColVar = true;
+      }
+      if(selectColVar){
+        s = colVar;
+        q = e.getCoefficient();
+      }
+    }
+    if(s == ARITHVAR_SENTINEL || q.isZero()){
+      Trace("gaussianElimConstructTableRow") << "3 fail gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+      return true;
+    }else{
+      // 0 = q * s + sum c_i * x_i
+      Rational mult = -(q.inverse());
+      Trace("gaussianElimConstructTableRow") << "selecting " << s << " : " << mult << endl;
+      Trace("gaussianElimConstructTableRow") << "selecting " << rid << " " << s << endl;
+      //cout << "selecting " << s << " : complexity " << mult.complexity() << " " << mult << endl;
+      //cout << "selecting " << rid << " " << s << endl;
+      A.multiplyRowByConstant(rid, mult);
+      rows[i].second = s;
+    }
+  }
+  Trace("gaussianElimConstructTableRow") << "4 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+
+  if(rows.empty()) {
+    Trace("gaussianElimConstructTableRow") << "4 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+    return true;
+  }
+  RowIndex rid_last = rows.back().first;
+  ArithVar rid_var = rows.back().second;
+  if(rid_var != basic){
+    Trace("gaussianElimConstructTableRow") << "4 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+    return true;
+  }
+
+  Assert(tab.empty());
+
+  Matrix<Rational>::RowIterator k = A.getRow(rid_last).begin();
+  Matrix<Rational>::RowIterator k_end = A.getRow(rid_last).end();
+  for(; k != k_end; ++k){
+    const Matrix<Rational>::Entry& e = *k;
+    tab.set(e.getColVar(), e.getCoefficient());
+  }
+  Trace("gaussianElimConstructTableRow") << "5 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+  if(!tab.isKey(basic)){
+    Trace("gaussianElimConstructTableRow") << "5 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+    return true;
+  }
+  if(tab[basic] != Rational(-1)){
+    Trace("gaussianElimConstructTableRow") << "5 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+    return true;
+  }
+
+  tab.remove(basic);
+  Trace("gaussianElimConstructTableRow") << "6 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+
+  if(vec.len < 0 ){
+    Trace("gaussianElimConstructTableRow") << "6 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+    return true;
+  }
+  if(tab.size() != ((unsigned)vec.len) ) {
+    Trace("gaussianElimConstructTableRow") << "6 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<< tab.size() <<  " " << vec.len << endl;
+    return true;
+  }
+
+  Trace("gaussianElimConstructTableRow") << "7 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+
+  for(int i = 1; i <= vec.len; ++i){
+    int ind = vec.inds[i];
+    double coeff = vec.coeffs[i];
+    ArithVar var = _getArithVar(nid, M, ind);
+    Assert(var != ARITHVAR_SENTINEL);
+    if(!tab.isKey(var)){
+      Trace("gaussianElimConstructTableRow") << "7 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+      return true;
+    }
+
+    double est = tab[var].getDouble();
+
+    if(!ApproximateSimplex::roughlyEqual(coeff, est)){
+      Trace("gaussianElimConstructTableRow") << "7 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"
+           << " boink on " << ind << " " << var << " " << est <<endl;
+      return true;
+    }
+    Trace("gaussianElimConstructTableRow") << var << " cfe " << coeff << endl;
+  }
+
+  Trace("gaussianElimConstructTableRow")
+    << "gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"
+    << " superduper" << endl;
+
+  return false;
+}
+bool ApproxGLPK::guessCoefficientsConstructTableRow(int nid, int M, const PrimitiveVec& vec){
+  for(size_t i=0; i < d_denomGuesses.size(); ++i){
+    const Integer& D = d_denomGuesses[i];
+    if(!guessCoefficientsConstructTableRow(nid, M, vec, D)){
+      d_stats.d_averageGuesses << i+1;
+      Trace("approx::gmi") << "guesseditat " << i << " D=" << D << endl;
+      return false;
+    }
+  }
+  return true;
+}
+bool ApproxGLPK::guessCoefficientsConstructTableRow(int nid, int M, const PrimitiveVec& vec, const Integer& D){
+  ArithVar basic = d_pad.d_basic;
+  DenseMap<Rational>& tab = d_pad.d_tabRow.lhs;
+  tab.purge();
+  d_pad.d_tabRow.rhs = Rational(0);
+  Assert(basic != ARITHVAR_SENTINEL);
+  Assert(tab.empty());
+  Assert(d_pad.d_tabRow.rhs.isZero());
+
+  if(TraceIsOn("guessCoefficientsConstructTableRow")){
+    Trace("guessCoefficientsConstructTableRow")  << "attemptConstructTableRow("<<nid <<", "<< basic<<",...," << D<< ")"<<endl;
+    vec.print(Trace("guessCoefficientsConstructTableRow"));
+    Trace("guessCoefficientsConstructTableRow") << "match " << basic << "("<<d_vars.asNode(basic)<<")"<<endl;
+  }
+
+  tab.set(basic, Rational(-1));
+  for(int i = 1; i <= vec.len; ++i){
+    int ind = vec.inds[i];
+    double coeff = vec.coeffs[i];
+    ArithVar var = _getArithVar(nid, M, ind);
+    if(var == ARITHVAR_SENTINEL){
+      Trace("guessCoefficientsConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl;
+      return true;
+    }
+    Trace("guessCoefficientsConstructTableRow") << "match " << ind << "," << var << "("<<d_vars.asNode(var)<<")"<<endl;
+
+    std::optional<Rational> cfe = estimateWithCFE(coeff, D);
+    if (!cfe)
+    {
+      return true;
+    }
+    tab.set(var, *cfe);
+    Trace("guessCoefficientsConstructTableRow") << var << " cfe " << cfe << endl;
+  }
+  if(!guessIsConstructable(tab)){
+    Trace("guessCoefficientsConstructTableRow") << "failed to construct with " << D  << endl;
+    return true;
+  }
+  tab.remove(basic);
+  return false;
+}
+
+/* Maps an ArithVar to either an upper/lower bound */
+bool ApproxGLPK::constructGmiCut(){
+  const DenseMap<Rational>& tabRow = d_pad.d_tabRow.lhs;
+  const DenseMap<ConstraintP>& toBound = d_pad.d_toBound;
+  DenseMap<Rational>& cut = d_pad.d_cut.lhs;
+  std::set<ConstraintP>& explanation = d_pad.d_explanation;
+  Rational& rhs = d_pad.d_cut.rhs;
+
+  DenseMap<Rational>::const_iterator iter, end;
+  Assert(cut.empty());
+
+  // compute beta for a "fake" assignment
+  bool anyInf;
+  DeltaRational dbeta = sumConstraints(tabRow, toBound, &anyInf);
+  const Rational& beta = dbeta.getNoninfinitesimalPart();
+  Trace("approx::gmi") << dbeta << endl;
+  if(anyInf || beta.isIntegral()){ return true; }
+
+  Rational one = Rational(1);
+  Rational fbeta = beta.floor_frac();
+  rhs = fbeta;
+  Assert(fbeta.sgn() > 0);
+  Assert(fbeta < one);
+  Rational one_sub_fbeta = one - fbeta;
+  for(iter = tabRow.begin(), end = tabRow.end(); iter != end; ++iter){
+    ArithVar x = *iter;
+    const Rational& psi = tabRow[x];
+    ConstraintP c = toBound[x];
+    const Rational& bound = c->getValue().getNoninfinitesimalPart();
+    if(d_vars.boundsAreEqual(x)){
+      // do not add a coefficient
+      // implictly substitute the variable w/ its constraint
+      std::pair<ConstraintP, ConstraintP> exp = d_vars.explainEqualBounds(x);
+      explanation.insert(exp.first);
+      if(exp.second != NullConstraint){
+        explanation.insert(exp.second);
+      }
+    }else if(d_vars.isIntegerInput(x) && psi.isIntegral()){
+      // do not add a coefficient
+      // nothing to explain
+      Trace("approx::gmi") << "skipping " << x << endl;
+    }else{
+      explanation.insert(c);
+      Rational phi;
+      Rational alpha = (c->isUpperBound() ? psi : -psi);
+
+      // x - ub <= 0 and lb - x <= 0
+      if(d_vars.isIntegerInput(x)){
+        Assert(!psi.isIntegral());
+        // alpha = slack_sgn * psi
+        Rational falpha = alpha.floor_frac();
+        Assert(falpha.sgn() > 0);
+        Assert(falpha < one);
+        phi = (falpha <= fbeta) ?
+          falpha : ((fbeta / one_sub_fbeta) * (one - falpha));
+      }else{
+        phi = (alpha >= 0) ?
+          alpha : ((fbeta / one_sub_fbeta) * (- alpha));
+      }
+      Assert(phi.sgn() != 0);
+      if(c->isUpperBound()){
+        cut.set(x, -phi);
+        rhs -= phi * bound;
+      }else{
+        cut.set(x, phi);
+        rhs += phi * bound;
+      }
+    }
+  }
+  if(TraceIsOn("approx::gmi")){
+    Trace("approx::gmi") << "pre removeSlackVariables";
+    d_pad.d_cut.print(Trace("approx::gmi"));
+    Trace("approx::gmi") << endl;
+  }
+  removeAuxillaryVariables(d_vars, cut);
+
+  if(TraceIsOn("approx::gmi")){
+    Trace("approx::gmi") << "post removeAuxillaryVariables";
+    d_pad.d_cut.print(Trace("approx::gmi"));
+    Trace("approx::gmi") << endl;
+  }
+  removeFixed(d_vars, d_pad.d_cut, explanation);
+
+  if(TraceIsOn("approx::gmi")){
+    Trace("approx::gmi") << "post removeFixed";
+    d_pad.d_cut.print(Trace("approx::gmi"));
+    Trace("approx::gmi") << endl;
+  }
+  return false;
+}
+
+void ApproxGLPK::tryCut(int nid, CutInfo& cut)
+{
+  Assert(!cut.reconstructed());
+  Assert(cut.getKlass() != RowsDeletedKlass);
+  bool failure = false;
+  switch(cut.getKlass()){
+  case GmiCutKlass:
+    failure = attemptGmi(nid, static_cast<const GmiInfo&>(cut));
+    break;
+  case MirCutKlass:
+    failure = attemptMir(nid, static_cast<const MirInfo&>(cut));
+    break;
+  case BranchCutKlass:
+    failure = attemptBranchCut(nid, dynamic_cast<const BranchCutInfo&>(cut));
+    break;
+  default:
+    break;
+  }
+  Assert(failure == d_pad.d_failure);
+
+  if(!failure){
+    // move the pad to the cut
+    cut.setReconstruction(d_pad.d_cut);
+
+    if(cut.getKlass() != BranchCutKlass){
+      std::set<ConstraintP>& exp = d_pad.d_explanation;
+      ConstraintCPVec asvec(exp.begin(), exp.end());
+      cut.swapExplanation(asvec);
+    }
+  }else{
+    Trace("approx") << "failure " << cut.getKlass() << endl;
+  }
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+#endif /*#ifdef CVC5_USE_GLPK */
+/* End GPLK implementation. */
diff --git a/src/theory/arith/linear/approx_simplex.h b/src/theory/arith/linear/approx_simplex.h
new file mode 100644 (file)
index 0000000..feac815
--- /dev/null
@@ -0,0 +1,168 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <optional>
+#include <vector>
+
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/delta_rational.h"
+#include "util/dense_map.h"
+#include "util/rational.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+enum LinResult {
+  LinUnknown,  /* Unknown error */
+  LinFeasible, /* Relaxation is feasible */
+  LinInfeasible,   /* Relaxation is infeasible/all integer branches closed */
+  LinExhausted
+};
+
+enum MipResult {
+  MipUnknown,  /* Unknown error */
+  MipBingo,    /* Integer feasible */
+  MipClosed,   /* All integer branches closed */
+  BranchesExhausted, /* Exhausted number of branches */
+  PivotsExhauasted,  /* Exhausted number of pivots */
+  ExecExhausted      /* Exhausted total operations */
+};
+std::ostream& operator<<(std::ostream& out, MipResult res);
+
+class ApproximateStatistics {
+ public:
+  ApproximateStatistics();
+
+  IntStat d_branchMaxDepth;
+  IntStat d_branchesMaxOnAVar;
+
+  TimerStat d_gaussianElimConstructTime;
+  IntStat d_gaussianElimConstruct;
+  AverageStat d_averageGuesses;
+};
+
+
+class NodeLog;
+class TreeLog;
+class ArithVariables;
+class CutInfo;
+
+class ApproximateSimplex{
+ public:
+  static bool enabled();
+
+  /**
+   * If glpk is enabled, return a subclass that can do something.
+   * If glpk is disabled, return a subclass that does nothing.
+   */
+  static ApproximateSimplex* mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s);
+  ApproximateSimplex(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s);
+  virtual ~ApproximateSimplex(){}
+
+  /* the maximum pivots allowed in a query. */
+  void setPivotLimit(int pl);
+
+  /* maximum branches allowed on a variable */
+  void setBranchOnVariableLimit(int bl);
+
+  /* maximum branches allowed on a variable */
+  void setBranchingDepth(int bd);
+
+  /** A result is either sat, unsat or unknown.*/
+  struct Solution {
+    DenseSet newBasis;
+    DenseMap<DeltaRational> newValues;
+    Solution() : newBasis(), newValues(){}
+  };
+
+  virtual ArithVar getBranchVar(const NodeLog& nl) const = 0;
+
+  /** Sets a maximization criteria for the approximate solver.*/
+  virtual void setOptCoeffs(const ArithRatPairVec& ref) = 0;
+
+  virtual ArithRatPairVec heuristicOptCoeffs() const = 0;
+
+  virtual LinResult solveRelaxation() = 0;
+  virtual Solution extractRelaxation() const = 0;
+
+  virtual MipResult solveMIP(bool activelyLog) = 0;
+
+  virtual Solution extractMIP() const = 0;
+
+  virtual std::vector<const CutInfo*> getValidCuts(const NodeLog& node) = 0;
+
+  virtual void tryCut(int nid, CutInfo& cut) = 0;
+
+  /** UTILITIES FOR DEALING WITH ESTIMATES */
+
+  static constexpr double SMALL_FIXED_DELTA = .000000001;
+  static constexpr double TOLERENCE = 1 + .000000001;
+
+  /** Returns true if two doubles are roughly equal based on TOLERENCE and SMALL_FIXED_DELTA.*/
+  static bool roughlyEqual(double a, double b);
+
+  /**
+   * Estimates a double as a Rational using continued fraction expansion that
+   * cuts off the estimate once the value is approximately zero.
+   * This is designed for removing rounding artifacts.
+   */
+  static std::optional<Rational> estimateWithCFE(double d);
+  static std::optional<Rational> estimateWithCFE(double d, const Integer& D);
+
+  /**
+   * Converts a rational to a continued fraction expansion representation
+   * using a maximum number of expansions equal to depth as long as the expression
+   * is not roughlyEqual() to 0.
+   */
+  static std::vector<Integer> rationalToCfe(const Rational& q, int depth);
+
+  /** Converts a continued fraction expansion representation to a rational. */
+  static Rational cfeToRational(const std::vector<Integer>& exp);
+
+  /** Estimates a rational as a continued fraction expansion.*/
+  static Rational estimateWithCFE(const Rational& q, const Integer& K);
+
+  virtual double sumInfeasibilities(bool mip) const = 0;
+
+ protected:
+  const ArithVariables& d_vars;
+  TreeLog& d_log;
+  ApproximateStatistics& d_stats;
+
+  /* the maximum pivots allowed in a query. */
+  int d_pivotLimit;
+
+  /* maximum branches allowed on a variable */
+  int d_branchLimit;
+
+  /* maxmimum branching depth allowed.*/
+  int d_maxDepth;
+
+  /* Default denominator for diophatine approximation, 2^{26} .*/
+  static constexpr uint64_t s_defaultMaxDenom = (1 << 26);
+};/* class ApproximateSimplex */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/arith_static_learner.cpp b/src/theory/arith/linear/arith_static_learner.cpp
new file mode 100644 (file)
index 0000000..c07359b
--- /dev/null
@@ -0,0 +1,270 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Dejan Jovanovic, Andres Noetzli
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include <vector>
+
+#include "base/output.h"
+#include "expr/node_algorithm.h"
+#include "options/arith_options.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/arith_static_learner.h"
+#include "theory/arith/arith_utilities.h"
+
+using namespace std;
+using namespace cvc5::internal::kind;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+ArithStaticLearner::ArithStaticLearner(context::Context* userContext)
+    : d_minMap(userContext), d_maxMap(userContext), d_statistics()
+{
+}
+
+ArithStaticLearner::~ArithStaticLearner(){
+}
+
+ArithStaticLearner::Statistics::Statistics()
+    : d_iteMinMaxApplications(smtStatisticsRegistry().registerInt(
+        "theory::arith::iteMinMaxApplications")),
+      d_iteConstantApplications(smtStatisticsRegistry().registerInt(
+          "theory::arith::iteConstantApplications"))
+{
+}
+
+void ArithStaticLearner::staticLearning(TNode n, NodeBuilder& learned)
+{
+  vector<TNode> workList;
+  workList.push_back(n);
+  TNodeSet processed;
+
+  //Contains an underapproximation of nodes that must hold.
+  TNodeSet defTrue;
+
+  defTrue.insert(n);
+
+  while(!workList.empty()) {
+    n = workList.back();
+
+    bool unprocessedChildren = false;
+    for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
+      if(processed.find(*i) == processed.end()) {
+        // unprocessed child
+        workList.push_back(*i);
+        unprocessedChildren = true;
+      }
+    }
+    if(n.getKind() == AND && defTrue.find(n) != defTrue.end() ){
+      for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
+        defTrue.insert(*i);
+      }
+    }
+
+    if(unprocessedChildren) {
+      continue;
+    }
+
+    workList.pop_back();
+    // has node n been processed in the meantime ?
+    if(processed.find(n) != processed.end()) {
+      continue;
+    }
+    processed.insert(n);
+
+    process(n,learned, defTrue);
+
+  }
+}
+
+void ArithStaticLearner::process(TNode n,
+                                 NodeBuilder& learned,
+                                 const TNodeSet& defTrue)
+{
+  Trace("arith::static") << "===================== looking at " << n << endl;
+
+  switch(n.getKind()){
+  case ITE:
+    if (expr::hasBoundVar(n))
+    {
+      // Unsafe with non-ground ITEs; do nothing
+      Trace("arith::static")
+          << "(potentially) non-ground ITE, ignoring..." << endl;
+      break;
+    }
+
+    if(n[0].getKind() != EQUAL &&
+       isRelationOperator(n[0].getKind())  ){
+      iteMinMax(n, learned);
+    }
+
+    if((d_minMap.find(n[1]) != d_minMap.end() && d_minMap.find(n[2]) != d_minMap.end()) ||
+       (d_maxMap.find(n[1]) != d_maxMap.end() && d_maxMap.find(n[2]) != d_maxMap.end())) {
+      iteConstant(n, learned);
+    }
+    break;
+
+  case CONST_RATIONAL:
+  case CONST_INTEGER:
+    // Mark constants as minmax
+    d_minMap.insert(n, n.getConst<Rational>());
+    d_maxMap.insert(n, n.getConst<Rational>());
+    break;
+  default: // Do nothing
+    break;
+  }
+}
+
+void ArithStaticLearner::iteMinMax(TNode n, NodeBuilder& learned)
+{
+  Assert(n.getKind() == kind::ITE);
+  Assert(n[0].getKind() != EQUAL);
+  Assert(isRelationOperator(n[0].getKind()));
+
+  TNode c = n[0];
+  Kind k = oldSimplifiedKind(c);
+  TNode t = n[1];
+  TNode e = n[2];
+  TNode cleft = (c.getKind() == NOT) ? c[0][0] : c[0];
+  TNode cright = (c.getKind() == NOT) ? c[0][1] : c[1];
+
+  if((t == cright) && (e == cleft)){
+    TNode tmp = t;
+    t = e;
+    e = tmp;
+    k = reverseRelationKind(k);
+  }
+  //(ite (< x y) x y)
+  //(ite (x < y) x y)
+  //(ite (x - y < 0) x y)
+  // ----------------
+  // (ite (x - y < -c) )
+
+  if(t == cleft && e == cright){
+    // t == cleft && e == cright
+    Assert(t == cleft);
+    Assert(e == cright);
+    switch(k){
+    case LT:   // (ite (< x y) x y)
+    case LEQ: { // (ite (<= x y) x y)
+      Node nLeqX = NodeBuilder(LEQ) << n << t;
+      Node nLeqY = NodeBuilder(LEQ) << n << e;
+      Trace("arith::static") << n << "is a min =>"  << nLeqX << nLeqY << endl;
+      learned << nLeqX << nLeqY;
+      ++(d_statistics.d_iteMinMaxApplications);
+      break;
+    }
+    case GT: // (ite (> x y) x y)
+    case GEQ: { // (ite (>= x y) x y)
+      Node nGeqX = NodeBuilder(GEQ) << n << t;
+      Node nGeqY = NodeBuilder(GEQ) << n << e;
+      Trace("arith::static") << n << "is a max =>"  << nGeqX << nGeqY << endl;
+      learned << nGeqX << nGeqY;
+      ++(d_statistics.d_iteMinMaxApplications);
+      break;
+    }
+    default: Unreachable();
+    }
+  }
+}
+
+void ArithStaticLearner::iteConstant(TNode n, NodeBuilder& learned)
+{
+  Assert(n.getKind() == ITE);
+
+  Trace("arith::static") << "iteConstant(" << n << ")" << endl;
+
+  if (d_minMap.find(n[1]) != d_minMap.end() && d_minMap.find(n[2]) != d_minMap.end()) {
+    const DeltaRational& first = d_minMap[n[1]];
+    const DeltaRational& second = d_minMap[n[2]];
+    DeltaRational min = std::min(first, second);
+    CDNodeToMinMaxMap::const_iterator minFind = d_minMap.find(n);
+    if (minFind == d_minMap.end() || (*minFind).second < min) {
+      d_minMap.insert(n, min);
+      NodeManager* nm = NodeManager::currentNM();
+      Node nGeqMin = nm->mkNode(
+          min.getInfinitesimalPart() == 0 ? kind::GEQ : kind::GT,
+          n,
+          nm->mkConstRealOrInt(n.getType(), min.getNoninfinitesimalPart()));
+      learned << nGeqMin;
+      Trace("arith::static") << n << " iteConstant"  << nGeqMin << endl;
+      ++(d_statistics.d_iteConstantApplications);
+    }
+  }
+
+  if (d_maxMap.find(n[1]) != d_maxMap.end() && d_maxMap.find(n[2]) != d_maxMap.end()) {
+    const DeltaRational& first = d_maxMap[n[1]];
+    const DeltaRational& second = d_maxMap[n[2]];
+    DeltaRational max = std::max(first, second);
+    CDNodeToMinMaxMap::const_iterator maxFind = d_maxMap.find(n);
+    if (maxFind == d_maxMap.end() || (*maxFind).second > max) {
+      d_maxMap.insert(n, max);
+      NodeManager* nm = NodeManager::currentNM();
+      Node nLeqMax = nm->mkNode(
+          max.getInfinitesimalPart() == 0 ? kind::LEQ : kind::LT,
+          n,
+          nm->mkConstRealOrInt(n.getType(), max.getNoninfinitesimalPart()));
+      learned << nLeqMax;
+      Trace("arith::static") << n << " iteConstant"  << nLeqMax << endl;
+      ++(d_statistics.d_iteConstantApplications);
+    }
+  }
+}
+
+std::set<Node> listToSet(TNode l){
+  std::set<Node> ret;
+  while(l.getKind() == OR){
+    Assert(l.getNumChildren() == 2);
+    ret.insert(l[0]);
+    l = l[1];
+  }
+  return ret;
+}
+
+void ArithStaticLearner::addBound(TNode n) {
+
+  CDNodeToMinMaxMap::const_iterator minFind = d_minMap.find(n[0]);
+  CDNodeToMinMaxMap::const_iterator maxFind = d_maxMap.find(n[0]);
+
+  Rational constant = n[1].getConst<Rational>();
+  DeltaRational bound = constant;
+
+  switch(Kind k = n.getKind()) {
+    case kind::LT: bound = DeltaRational(constant, -1); CVC5_FALLTHROUGH;
+    case kind::LEQ:
+      if (maxFind == d_maxMap.end() || (*maxFind).second > bound)
+      {
+        d_maxMap.insert(n[0], bound);
+        Trace("arith::static") << "adding bound " << n << endl;
+      }
+      break;
+    case kind::GT: bound = DeltaRational(constant, 1); CVC5_FALLTHROUGH;
+    case kind::GEQ:
+      if (minFind == d_minMap.end() || (*minFind).second < bound)
+      {
+        d_minMap.insert(n[0], bound);
+        Trace("arith::static") << "adding bound " << n << endl;
+      }
+      break;
+    default: Unhandled() << k; break;
+  }
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/arith_static_learner.h b/src/theory/arith/linear/arith_static_learner.h
new file mode 100644 (file)
index 0000000..c07681b
--- /dev/null
@@ -0,0 +1,80 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Andres Noetzli, Dejan Jovanovic
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H
+#define CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H
+
+#include "context/cdhashmap.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/delta_rational.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::context {
+class Context;
+}
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class ArithStaticLearner {
+private:
+
+  /**
+   * Map from a node to it's minimum and maximum.
+   */
+ typedef context::CDHashMap<Node, DeltaRational> CDNodeToMinMaxMap;
+ CDNodeToMinMaxMap d_minMap;
+ CDNodeToMinMaxMap d_maxMap;
+
+public:
+ ArithStaticLearner(context::Context* userContext);
+ ~ArithStaticLearner();
+ void staticLearning(TNode n, NodeBuilder& learned);
+
+ void addBound(TNode n);
+
+private:
+ void process(TNode n, NodeBuilder& learned, const TNodeSet& defTrue);
+
+ void iteMinMax(TNode n, NodeBuilder& learned);
+ void iteConstant(TNode n, NodeBuilder& learned);
+
+ /**
+  * These fields are designed to be accessible to ArithStaticLearner methods.
+  */
+ class Statistics
+ {
+  public:
+   IntStat d_iteMinMaxApplications;
+   IntStat d_iteConstantApplications;
+
+   Statistics();
+ };
+
+  Statistics d_statistics;
+
+};/* class ArithStaticLearner */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H */
diff --git a/src/theory/arith/linear/arithvar.cpp b/src/theory/arith/linear/arithvar.cpp
new file mode 100644 (file)
index 0000000..2cebf54
--- /dev/null
@@ -0,0 +1,36 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/arithvar.h"
+#include <limits>
+#include <set>
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+const ArithVar ARITHVAR_SENTINEL = std::numeric_limits<ArithVar>::max();
+
+bool debugIsASet(const std::vector<ArithVar>& variables){
+  std::set<ArithVar> asSet(variables.begin(), variables.end());
+  return asSet.size() == variables.size();
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/arithvar.h b/src/theory/arith/linear/arithvar.h
new file mode 100644 (file)
index 0000000..f767941
--- /dev/null
@@ -0,0 +1,45 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Defines ArithVar which is the internal representation of variables in
+ * arithmetic
+ *
+ * This defines ArithVar which is the internal representation of variables in
+ * arithmetic. This is a typedef from Index to ArithVar.
+ * This file also provides utilities for ArithVars.
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <vector>
+
+#include "util/index.h"
+#include "util/rational.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+typedef Index ArithVar;
+extern const ArithVar ARITHVAR_SENTINEL;
+
+typedef std::vector<ArithVar> ArithVarVec;
+typedef std::pair<ArithVar, Rational> ArithRatPair;
+typedef std::vector< ArithRatPair > ArithRatPairVec;
+
+extern bool debugIsASet(const ArithVarVec& variables);
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/arithvar_node_map.h b/src/theory/arith/linear/arithvar_node_map.h
new file mode 100644 (file)
index 0000000..067d990
--- /dev/null
@@ -0,0 +1,99 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Aina Niemetz
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H
+#define CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H
+
+#include "theory/arith/linear/arithvar.h"
+#include "context/context.h"
+#include "context/cdlist.h"
+#include "context/cdhashmap.h"
+#include "context/cdo.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+//Maps from Nodes -> ArithVars, and vice versa
+typedef std::unordered_map<Node, ArithVar> NodeToArithVarMap;
+typedef DenseMap<Node> ArithVarToNodeMap;
+
+class ArithVarNodeMap {
+private:
+  /**
+   * Bidirectional map between Nodes and ArithVars.
+   */
+  NodeToArithVarMap d_nodeToArithVarMap;
+  ArithVarToNodeMap d_arithVarToNodeMap;
+
+public:
+
+  typedef ArithVarToNodeMap::const_iterator var_iterator;
+
+  ArithVarNodeMap() {}
+
+  inline bool hasArithVar(TNode x) const {
+    return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end();
+  }
+
+  inline bool hasNode(ArithVar a) const {
+    return d_arithVarToNodeMap.isKey(a);
+  }
+
+  inline ArithVar asArithVar(TNode x) const{
+    Assert(hasArithVar(x));
+    Assert((d_nodeToArithVarMap.find(x))->second <= ARITHVAR_SENTINEL);
+    return (d_nodeToArithVarMap.find(x))->second;
+  }
+
+  inline Node asNode(ArithVar a) const{
+    Assert(hasNode(a));
+    return d_arithVarToNodeMap[a];
+  }
+
+  inline void setArithVar(TNode x, ArithVar a){
+    Assert(!hasArithVar(x));
+    Assert(!d_arithVarToNodeMap.isKey(a));
+    d_arithVarToNodeMap.set(a, x);
+    d_nodeToArithVarMap[x] = a;
+  }
+
+  inline void remove(ArithVar x){
+    Assert(hasNode(x));
+    Node node = asNode(x);
+
+    d_nodeToArithVarMap.erase(d_nodeToArithVarMap.find(node));
+    d_arithVarToNodeMap.remove(x);
+  }
+
+  var_iterator var_begin() const {
+    return d_arithVarToNodeMap.begin();
+  }
+  var_iterator var_end() const {
+    return d_arithVarToNodeMap.end();
+  }
+
+};/* class ArithVarNodeMap */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H */
diff --git a/src/theory/arith/linear/attempt_solution_simplex.cpp b/src/theory/arith/linear/attempt_solution_simplex.cpp
new file mode 100644 (file)
index 0000000..7ded877
--- /dev/null
@@ -0,0 +1,152 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+#include "theory/arith/linear/attempt_solution_simplex.h"
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/tableau.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+AttemptSolutionSDP::AttemptSolutionSDP(Env& env,
+                                       LinearEqualityModule& linEq,
+                                       ErrorSet& errors,
+                                       RaiseConflict conflictChannel,
+                                       TempVarMalloc tvmalloc)
+    : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
+      d_statistics()
+{ }
+
+AttemptSolutionSDP::Statistics::Statistics()
+    : d_searchTime(smtStatisticsRegistry().registerTimer(
+        "theory::arith::attempt::searchTime")),
+      d_queueTime(smtStatisticsRegistry().registerTimer(
+          "theory::arith::attempt::queueTime")),
+      d_conflicts(smtStatisticsRegistry().registerInt(
+          "theory::arith::attempt::conflicts"))
+{
+}
+
+bool AttemptSolutionSDP::matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const{
+  return nv[v] == d_variables.getAssignment(v);
+}
+
+Result::Status AttemptSolutionSDP::attempt(
+    const ApproximateSimplex::Solution& sol)
+{
+  const DenseSet& newBasis = sol.newBasis;
+  const DenseMap<DeltaRational>& newValues = sol.newValues;
+
+  DenseSet needsToBeAdded;
+  for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){
+    ArithVar b = *i;
+    if(!d_tableau.isBasic(b)){
+      needsToBeAdded.add(b);
+    }
+  }
+  DenseMap<DeltaRational>::const_iterator nvi = newValues.begin(), nvi_end = newValues.end();
+  for(; nvi != nvi_end; ++nvi){
+    ArithVar currentlyNb = *nvi;
+    if(!d_tableau.isBasic(currentlyNb)){
+      if(!matchesNewValue(newValues, currentlyNb)){
+        const DeltaRational& newValue = newValues[currentlyNb];
+        Trace("arith::updateMany")
+          << "updateMany:" << currentlyNb << " "
+          << d_variables.getAssignment(currentlyNb) << " to "<< newValue << endl;
+        d_linEq.update(currentlyNb, newValue);
+        Assert(d_variables.assignmentIsConsistent(currentlyNb));
+      }
+    }
+  }
+  d_errorSet.reduceToSignals();
+  d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
+
+  if(processSignals()){
+    Trace("arith::findModel") << "attemptSolution() early conflict" << endl;
+    d_conflictVariables.purge();
+    return Result::UNSAT;
+  }else if(d_errorSet.errorEmpty()){
+    Trace("arith::findModel") << "attemptSolution() fixed itself" << endl;
+    return Result::SAT;
+  }
+
+  while(!needsToBeAdded.empty() && !d_errorSet.errorEmpty()){
+    ArithVar toRemove = ARITHVAR_SENTINEL;
+    ArithVar toAdd = ARITHVAR_SENTINEL;
+    DenseSet::const_iterator i = needsToBeAdded.begin(), i_end = needsToBeAdded.end();
+    for(; toAdd == ARITHVAR_SENTINEL && i != i_end; ++i){
+      ArithVar v = *i;
+
+      Tableau::ColIterator colIter = d_tableau.colIterator(v);
+      for(; !colIter.atEnd(); ++colIter){
+        const Tableau::Entry& entry = *colIter;
+        Assert(entry.getColVar() == v);
+        ArithVar b = d_tableau.rowIndexToBasic(entry.getRowIndex());
+        if(!newBasis.isMember(b)){
+          toAdd = v;
+
+          bool favorBOverToRemove =
+            (toRemove == ARITHVAR_SENTINEL) ||
+            (matchesNewValue(newValues, toRemove) && !matchesNewValue(newValues, b)) ||
+            (d_tableau.basicRowLength(toRemove) > d_tableau.basicRowLength(b));
+
+          if(favorBOverToRemove){
+            toRemove = b;
+          }
+        }
+      }
+    }
+    Assert(toRemove != ARITHVAR_SENTINEL);
+    Assert(toAdd != ARITHVAR_SENTINEL);
+
+    Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl;
+
+    d_linEq.pivotAndUpdate(toRemove, toAdd, newValues[toRemove]);
+
+    Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
+    needsToBeAdded.remove(toAdd);
+
+    bool conflict = processSignals();
+    if(conflict){
+      d_errorSet.reduceToSignals();
+      d_conflictVariables.purge();
+
+      return Result::UNSAT;
+    }
+  }
+  Assert(d_conflictVariables.empty());
+
+  if(d_errorSet.errorEmpty()){
+    return Result::SAT;
+  }else{
+    d_errorSet.reduceToSignals();
+    return Result::UNKNOWN;
+  }
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/attempt_solution_simplex.h b/src/theory/arith/linear/attempt_solution_simplex.h
new file mode 100644 (file)
index 0000000..1e3682b
--- /dev/null
@@ -0,0 +1,100 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ *
+ * This implements the Simplex module for the Simpelx for DPLL(T) decision
+ * procedure.
+ * See the Simplex for DPLL(T) technical report for more background.(citation?)
+ * This shares with the theory a Tableau, and a PartialModel that:
+ *  - satisfies the equalities in the Tableau, and
+ *  - the assignment for the non-basic variables satisfies their bounds.
+ * This is required to either produce a conflict or satisifying PartialModel.
+ * Further, we require being told when a basic variable updates its value.
+ *
+ * During the Simplex search we maintain a queue of variables.
+ * The queue is required to contain all of the basic variables that voilate
+ * their bounds.
+ * As elimination from the queue is more efficient to be done lazily,
+ * we do not maintain that the queue of variables needs to be only basic
+ * variables or only variables that satisfy their bounds.
+ *
+ * The simplex procedure roughly follows Alberto's thesis. (citation?)
+ * There is one round of selecting using a heuristic pivoting rule.
+ * (See PreferenceFunction Documentation for the available options.)
+ * The non-basic variable is the one that appears in the fewest pivots.
+ * (Bruno says that Leonardo invented this first.)
+ * After this, Bland's pivot rule is invoked.
+ *
+ * During this proccess, we periodically inspect the queue of variables to
+ * 1) remove now extraneous extries,
+ * 2) detect conflicts that are "waiting" on the queue but may not be detected
+ *    by the current queue heuristics, and
+ * 3) detect multiple conflicts.
+ *
+ * Conflicts are greedily slackened to use the weakest bounds that still
+ * produce the conflict.
+ *
+ * Extra things tracked atm: (Subject to change at Tim's whims)
+ * - A superset of all of the newly pivoted variables.
+ * - A queue of additional conflicts that were discovered by Simplex.
+ *   These are theory valid and are currently turned into lemmas
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "theory/arith/linear/approx_simplex.h"
+#include "theory/arith/linear/simplex.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class AttemptSolutionSDP : public SimplexDecisionProcedure {
+public:
+ AttemptSolutionSDP(Env& env,
+                    LinearEqualityModule& linEq,
+                    ErrorSet& errors,
+                    RaiseConflict conflictChannel,
+                    TempVarMalloc tvmalloc);
+
+ Result::Status attempt(const ApproximateSimplex::Solution& sol);
+
+ Result::Status findModel(bool exactResult) override { Unreachable(); }
+
+private:
+ bool matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const;
+
+ bool processSignals()
+ {
+   TimerStat& timer = d_statistics.d_queueTime;
+   IntStat& conflictStat = d_statistics.d_conflicts;
+   return standardProcessSignals(timer, conflictStat);
+  }
+  /** These fields are designed to be accessible to TheoryArith methods. */
+  class Statistics {
+  public:
+    TimerStat d_searchTime;
+    TimerStat d_queueTime;
+    IntStat d_conflicts;
+
+    Statistics();
+  } d_statistics;
+};/* class AttemptSolutionSDP */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/bound_counts.h b/src/theory/arith/linear/bound_counts.h
new file mode 100644 (file)
index 0000000..3524e1c
--- /dev/null
@@ -0,0 +1,235 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Clark Barrett
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+#pragma once
+
+#include "base/check.h"
+#include "theory/arith/linear/arithvar.h"
+#include "util/dense_map.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class BoundCounts {
+private:
+  uint32_t d_lowerBoundCount;
+  uint32_t d_upperBoundCount;
+
+public:
+  BoundCounts() : d_lowerBoundCount(0), d_upperBoundCount(0) {}
+  BoundCounts(uint32_t lbs, uint32_t ubs)
+  : d_lowerBoundCount(lbs), d_upperBoundCount(ubs) {}
+
+  bool operator==(BoundCounts bc) const {
+    return d_lowerBoundCount == bc.d_lowerBoundCount
+      && d_upperBoundCount == bc.d_upperBoundCount;
+  }
+  bool operator!=(BoundCounts bc) const {
+    return  d_lowerBoundCount != bc.d_lowerBoundCount
+      || d_upperBoundCount != bc.d_upperBoundCount;
+  }
+  /** This is not a total order! */
+  bool operator>=(BoundCounts bc) const {
+    return d_lowerBoundCount >= bc.d_lowerBoundCount &&
+      d_upperBoundCount >= bc.d_upperBoundCount;
+  }
+
+  inline bool isZero() const{ return d_lowerBoundCount == 0 && d_upperBoundCount == 0; }
+  inline uint32_t lowerBoundCount() const{
+    return d_lowerBoundCount;
+  }
+  inline uint32_t upperBoundCount() const{
+    return d_upperBoundCount;
+  }
+
+  inline BoundCounts operator+(BoundCounts bc) const{
+    return BoundCounts(d_lowerBoundCount + bc.d_lowerBoundCount,
+                       d_upperBoundCount + bc.d_upperBoundCount);
+  }
+
+  inline BoundCounts operator-(BoundCounts bc) const {
+    Assert(*this >= bc);
+    return BoundCounts(d_lowerBoundCount - bc.d_lowerBoundCount,
+                       d_upperBoundCount - bc.d_upperBoundCount);
+  }
+
+
+  inline BoundCounts& operator+=(BoundCounts bc) {
+    d_upperBoundCount += bc.d_upperBoundCount;
+    d_lowerBoundCount += bc.d_lowerBoundCount;
+    return *this;
+  }
+
+  inline BoundCounts& operator-=(BoundCounts bc) {
+    Assert(d_lowerBoundCount >= bc.d_lowerBoundCount);
+    Assert(d_upperBoundCount >= bc.d_upperBoundCount);
+    d_upperBoundCount -= bc.d_upperBoundCount;
+    d_lowerBoundCount -= bc.d_lowerBoundCount;
+
+    return *this;
+  }
+
+  /** Based on the sign coefficient a variable is multiplied by,
+   * the effects the bound counts either has no effect (sgn == 0),
+   * the lower bounds and upper bounds flip (sgn < 0), or nothing (sgn >0).
+   */
+  inline BoundCounts multiplyBySgn(int sgn) const{
+    if(sgn > 0){
+      return *this;
+    }else if(sgn == 0){
+      return BoundCounts(0,0);
+    }else{
+      return BoundCounts(d_upperBoundCount, d_lowerBoundCount);
+    }
+  }
+
+  inline void addInChange(int sgn, BoundCounts before, BoundCounts after){
+    if(before == after){
+      return;
+    }else if(sgn < 0){
+      Assert(d_upperBoundCount >= before.d_lowerBoundCount);
+      Assert(d_lowerBoundCount >= before.d_upperBoundCount);
+      d_upperBoundCount += after.d_lowerBoundCount - before.d_lowerBoundCount;
+      d_lowerBoundCount += after.d_upperBoundCount - before.d_upperBoundCount;
+    }else if(sgn > 0){
+      Assert(d_upperBoundCount >= before.d_upperBoundCount);
+      Assert(d_lowerBoundCount >= before.d_lowerBoundCount);
+      d_upperBoundCount += after.d_upperBoundCount - before.d_upperBoundCount;
+      d_lowerBoundCount += after.d_lowerBoundCount - before.d_lowerBoundCount;
+    }
+  }
+
+  inline void addInSgn(BoundCounts bc, int before, int after){
+    Assert(before != after);
+    Assert(!bc.isZero());
+
+    if(before < 0){
+      d_upperBoundCount -= bc.d_lowerBoundCount;
+      d_lowerBoundCount -= bc.d_upperBoundCount;
+    }else if(before > 0){
+      d_upperBoundCount -= bc.d_upperBoundCount;
+      d_lowerBoundCount -= bc.d_lowerBoundCount;
+    }
+    if(after < 0){
+      d_upperBoundCount += bc.d_lowerBoundCount;
+      d_lowerBoundCount += bc.d_upperBoundCount;
+    }else if(after > 0){
+      d_upperBoundCount += bc.d_upperBoundCount;
+      d_lowerBoundCount += bc.d_lowerBoundCount;
+    }
+  }
+};
+
+class BoundsInfo {
+private:
+
+  /**
+   * x = \sum_{a < 0} a_i i + \sum_{b > 0} b_j j
+   *
+   * AtUpperBound = {assignment(i) = lb(i)} \cup {assignment(j) = ub(j)}
+   * AtLowerBound = {assignment(i) = ub(i)} \cup {assignment(j) = lb(j)}
+   */
+  BoundCounts d_atBounds;
+
+  /** This is for counting how many upper and lower bounds a row has. */
+  BoundCounts d_hasBounds;
+
+public:
+  BoundsInfo() : d_atBounds(), d_hasBounds() {}
+  BoundsInfo(BoundCounts atBounds, BoundCounts hasBounds)
+    : d_atBounds(atBounds), d_hasBounds(hasBounds) {}
+
+  BoundCounts atBounds() const { return d_atBounds; }
+  BoundCounts hasBounds() const { return d_hasBounds; }
+
+  /** This corresponds to adding in another variable to the row. */
+  inline BoundsInfo operator+(const BoundsInfo& bc) const{
+    return BoundsInfo(d_atBounds + bc.d_atBounds,
+                      d_hasBounds + bc.d_hasBounds);
+  }
+  /** This corresponds to removing a variable from the row. */
+  inline BoundsInfo operator-(const BoundsInfo& bc) const {
+    Assert(*this >= bc);
+    return BoundsInfo(d_atBounds - bc.d_atBounds,
+                      d_hasBounds - bc.d_hasBounds);
+  }
+
+  inline BoundsInfo& operator+=(const BoundsInfo& bc) {
+    d_atBounds += bc.d_atBounds;
+    d_hasBounds += bc.d_hasBounds;
+    return (*this);
+  }
+
+  /** Based on the sign coefficient a variable is multiplied by,
+   * the effects the bound counts either has no effect (sgn == 0),
+   * the lower bounds and upper bounds flip (sgn < 0), or nothing (sgn >0).
+   */
+  inline BoundsInfo multiplyBySgn(int sgn) const{
+    return BoundsInfo(d_atBounds.multiplyBySgn(sgn), d_hasBounds.multiplyBySgn(sgn));
+  }
+
+  bool operator==(const BoundsInfo& other) const{
+    return d_atBounds == other.d_atBounds && d_hasBounds == other.d_hasBounds;
+  }
+  bool operator!=(const BoundsInfo& other) const{
+    return !(*this == other);
+  }
+  /** This is not a total order! */
+  bool operator>=(const BoundsInfo& other) const{
+    return d_atBounds >= other.d_atBounds && d_hasBounds >= other.d_hasBounds;
+  }
+  void addInChange(int sgn, const BoundsInfo& before, const BoundsInfo& after){
+    addInAtBoundChange(sgn, before.d_atBounds, after.d_atBounds);
+    addInHasBoundChange(sgn, before.d_hasBounds, after.d_hasBounds);
+  }
+  void addInAtBoundChange(int sgn, BoundCounts before, BoundCounts after){
+    d_atBounds.addInChange(sgn, before, after);
+  }
+  void addInHasBoundChange(int sgn, BoundCounts before, BoundCounts after){
+    d_hasBounds.addInChange(sgn, before, after);
+  }
+
+  inline void addInSgn(const BoundsInfo& bc, int before, int after){
+    if(!bc.d_atBounds.isZero()){ d_atBounds.addInSgn(bc.d_atBounds, before, after);}
+    if(!bc.d_hasBounds.isZero()){ d_hasBounds.addInSgn(bc.d_hasBounds, before, after);}
+  }
+};
+
+/** This is intended to map each row to its relevant bound information. */
+typedef DenseMap<BoundsInfo> BoundInfoMap;
+
+inline std::ostream& operator<<(std::ostream& os, const BoundCounts& bc){
+  os << "[bc " << bc.lowerBoundCount() << ", " << bc.upperBoundCount() << "]";
+  return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os, const BoundsInfo& inf){
+  os << "[bi : @ " << inf.atBounds() << ", " << inf.hasBounds() << "]";
+  return os;
+}
+class BoundUpdateCallback {
+public:
+  virtual ~BoundUpdateCallback() {}
+  virtual void operator()(ArithVar v, const BoundsInfo&  up) = 0;
+};
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/callbacks.cpp b/src/theory/arith/linear/callbacks.cpp
new file mode 100644 (file)
index 0000000..87b022c
--- /dev/null
@@ -0,0 +1,212 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/callbacks.h"
+
+#include "expr/skolem_manager.h"
+#include "proof/proof_node.h"
+#include "theory/arith/linear/theory_arith_private.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+SetupLiteralCallBack::SetupLiteralCallBack(TheoryArithPrivate& ta)
+  : d_arith(ta)
+{}
+void SetupLiteralCallBack::operator()(TNode lit){
+  TNode atom = (lit.getKind() == kind::NOT) ? lit[0] : lit;
+  if(!d_arith.isSetup(atom)){
+    d_arith.setupAtom(atom);
+  }
+}
+
+DeltaComputeCallback::DeltaComputeCallback(const TheoryArithPrivate& ta)
+  : d_ta(ta)
+{}
+Rational DeltaComputeCallback::operator()() const{
+  return d_ta.deltaValueForTotalOrder();
+}
+
+TempVarMalloc::TempVarMalloc(TheoryArithPrivate& ta)
+: d_ta(ta)
+{}
+ArithVar TempVarMalloc::request(){
+  NodeManager* nm = NodeManager::currentNM();
+  SkolemManager* sm = nm->getSkolemManager();
+  Node skolem = sm->mkDummySkolem("tmpVar", nm->realType());
+  return d_ta.requestArithVar(skolem, false, true);
+}
+void TempVarMalloc::release(ArithVar v){
+  d_ta.releaseArithVar(v);
+}
+
+BasicVarModelUpdateCallBack::BasicVarModelUpdateCallBack(TheoryArithPrivate& ta)
+  : d_ta(ta)
+{}
+void BasicVarModelUpdateCallBack::operator()(ArithVar x){
+  d_ta.signal(x);
+}
+
+RaiseConflict::RaiseConflict(TheoryArithPrivate& ta)
+  : d_ta(ta)
+{}
+
+void RaiseConflict::raiseConflict(ConstraintCP c, InferenceId id) const{
+  Assert(c->inConflict());
+  d_ta.raiseConflict(c, id);
+}
+
+FarkasConflictBuilder::FarkasConflictBuilder(bool produceProofs)
+    : d_farkas(),
+      d_constraints(),
+      d_consequent(NullConstraint),
+      d_consequentSet(false),
+      d_produceProofs(produceProofs)
+{
+  reset();
+}
+
+bool FarkasConflictBuilder::underConstruction() const{
+  return d_consequent != NullConstraint;
+}
+
+bool FarkasConflictBuilder::consequentIsSet() const{
+  return d_consequentSet;
+}
+
+void FarkasConflictBuilder::reset(){
+  d_consequent = NullConstraint;
+  d_constraints.clear();
+  d_consequentSet = false;
+  if (d_produceProofs)
+  {
+    d_farkas.clear();
+  }
+  Assert(!underConstruction());
+}
+
+/* Adds a constraint to the constraint under construction. */
+void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc){
+  Assert(
+      !d_produceProofs
+      || (!underConstruction() && d_constraints.empty() && d_farkas.empty())
+      || (underConstruction() && d_constraints.size() + 1 == d_farkas.size()));
+  Assert(d_produceProofs || d_farkas.empty());
+  Assert(c->isTrue());
+
+  if(d_consequent == NullConstraint){
+    d_consequent = c;
+  } else {
+    d_constraints.push_back(c);
+  }
+  if (d_produceProofs)
+  {
+    d_farkas.push_back(fc);
+  }
+  Assert(!d_produceProofs || d_constraints.size() + 1 == d_farkas.size());
+  Assert(d_produceProofs || d_farkas.empty());
+}
+
+void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc, const Rational& mult){
+  Assert(!mult.isZero());
+  if (d_produceProofs && !mult.isOne())
+  {
+    Rational prod = fc * mult;
+    addConstraint(c, prod);
+  }
+  else
+  {
+    addConstraint(c, fc);
+  }
+}
+
+void FarkasConflictBuilder::makeLastConsequent(){
+  Assert(!d_consequentSet);
+  Assert(underConstruction());
+
+  if(d_constraints.empty()){
+    // no-op
+    d_consequentSet = true;
+  } else {
+    Assert(d_consequent != NullConstraint);
+    ConstraintCP last = d_constraints.back();
+    d_constraints.back() = d_consequent;
+    d_consequent = last;
+    if (d_produceProofs)
+    {
+      std::swap(d_farkas.front(), d_farkas.back());
+    }
+    d_consequentSet = true;
+  }
+
+  Assert(!d_consequent->negationHasProof());
+  Assert(d_consequentSet);
+}
+
+/* Turns the vector under construction into a conflict */
+ConstraintCP FarkasConflictBuilder::commitConflict(){
+  Assert(underConstruction());
+  Assert(!d_constraints.empty());
+  Assert(
+      !d_produceProofs
+      || (!underConstruction() && d_constraints.empty() && d_farkas.empty())
+      || (underConstruction() && d_constraints.size() + 1 == d_farkas.size()));
+  Assert(d_produceProofs || d_farkas.empty());
+  Assert(d_consequentSet);
+
+  ConstraintP not_c = d_consequent->getNegation();
+  RationalVectorCP coeffs = d_produceProofs ? &d_farkas : nullptr;
+  not_c->impliedByFarkas(d_constraints, coeffs, true );
+
+  reset();
+  Assert(!underConstruction());
+  Assert(not_c->inConflict());
+  Assert(!d_consequentSet);
+  return not_c;
+}
+
+RaiseEqualityEngineConflict::RaiseEqualityEngineConflict(TheoryArithPrivate& ta)
+  : d_ta(ta)
+{}
+
+/* If you are not an equality engine, don't use this! */
+void RaiseEqualityEngineConflict::raiseEEConflict(
+    Node n, std::shared_ptr<ProofNode> pf) const
+{
+  d_ta.raiseBlackBoxConflict(n, pf);
+}
+
+BoundCountingLookup::BoundCountingLookup(TheoryArithPrivate& ta)
+: d_ta(ta)
+{}
+
+const BoundsInfo& BoundCountingLookup::boundsInfo(ArithVar basic) const{
+  return d_ta.boundsInfo(basic);
+}
+
+BoundCounts BoundCountingLookup::atBounds(ArithVar basic) const{
+  return boundsInfo(basic).atBounds();
+}
+BoundCounts BoundCountingLookup::hasBounds(ArithVar basic) const {
+  return boundsInfo(basic).hasBounds();
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/callbacks.h b/src/theory/arith/linear/callbacks.h
new file mode 100644 (file)
index 0000000..c93db91
--- /dev/null
@@ -0,0 +1,205 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Mathias Preiner, Clark Barrett
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#pragma once
+
+#include "expr/node.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/bound_counts.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/inference_id.h"
+#include "util/rational.h"
+
+namespace cvc5::internal {
+
+class ProofNode;
+
+namespace theory {
+namespace arith::linear {
+
+class TheoryArithPrivate;
+
+/**
+ * ArithVarCallBack provides a mechanism for agreeing on callbacks while
+ * breaking mutual recursion inclusion order problems.
+ */
+class ArithVarCallBack {
+public:
+  virtual ~ArithVarCallBack() {}
+  virtual void operator()(ArithVar x) = 0;
+};
+
+/**
+ * Requests arithmetic variables for internal use,
+ * and releases arithmetic variables that are no longer being used.
+ */
+class ArithVarMalloc {
+public:
+  virtual ~ArithVarMalloc() {}
+  virtual ArithVar request() = 0;
+  virtual void release(ArithVar v) = 0;
+};
+
+class TNodeCallBack {
+public:
+  virtual ~TNodeCallBack() {}
+  virtual void operator()(TNode n) = 0;
+};
+
+class NodeCallBack {
+public:
+  virtual ~NodeCallBack() {}
+  virtual void operator()(Node n) = 0;
+};
+
+class RationalCallBack {
+public:
+  virtual ~RationalCallBack() {}
+  virtual Rational operator()() const = 0;
+};
+
+class SetupLiteralCallBack : public TNodeCallBack {
+private:
+  TheoryArithPrivate& d_arith;
+public:
+  SetupLiteralCallBack(TheoryArithPrivate& ta);
+  void operator()(TNode lit) override;
+};
+
+class DeltaComputeCallback : public RationalCallBack {
+private:
+  const TheoryArithPrivate& d_ta;
+public:
+  DeltaComputeCallback(const TheoryArithPrivate& ta);
+  Rational operator()() const override;
+};
+
+class BasicVarModelUpdateCallBack : public ArithVarCallBack{
+private:
+  TheoryArithPrivate& d_ta;
+public:
+  BasicVarModelUpdateCallBack(TheoryArithPrivate& ta);
+  void operator()(ArithVar x) override;
+};
+
+class TempVarMalloc : public ArithVarMalloc {
+private:
+  TheoryArithPrivate& d_ta;
+public:
+  TempVarMalloc(TheoryArithPrivate& ta);
+  ArithVar request() override;
+  void release(ArithVar v) override;
+};
+
+class RaiseConflict {
+private:
+  TheoryArithPrivate& d_ta;
+public:
+  RaiseConflict(TheoryArithPrivate& ta);
+
+  /** Calls d_ta.raiseConflict(c) */
+  void raiseConflict(ConstraintCP c, InferenceId id) const;
+};
+
+class FarkasConflictBuilder {
+private:
+  RationalVector d_farkas;
+  ConstraintCPVec d_constraints;
+  ConstraintCP d_consequent;
+  bool d_consequentSet;
+  bool d_produceProofs;
+
+ public:
+
+  /**
+   * Constructs a new FarkasConflictBuilder.
+   */
+  FarkasConflictBuilder(bool produceProofs);
+
+  /**
+   * Adds an antecedent constraint to the conflict under construction
+   * with the farkas coefficient fc * mult.
+   *
+   * The value mult is either 1 or -1.
+   */
+  void addConstraint(ConstraintCP c, const Rational& fc, const Rational& mult);
+
+  /**
+   * Adds an antecedent constraint to the conflict under construction
+   * with the farkas coefficient fc.
+   */
+  void addConstraint(ConstraintCP c, const Rational& fc);
+  
+  /**
+   * Makes the last constraint added the consequent.
+   * Can be done exactly once per reset().
+   */
+  void makeLastConsequent();
+  
+  /**
+   * Turns the antecendents into a proof of the negation of one of the
+   * antecedents.
+   *
+   * The buffer is no longer underConstruction afterwards.
+   *
+   * precondition:
+   * - At least two constraints have been asserted.
+   * - makeLastConsequent() has been called.
+   *
+   * postcondition: The returned constraint is in conflict.
+   */
+  ConstraintCP commitConflict();
+
+  /** Returns true if a conflict has been pushed back since the last reset. */
+  bool underConstruction() const;
+  
+  /** Returns true if the consequent has been set since the last reset. */
+  bool consequentIsSet() const;
+
+  /** Resets the state of the buffer. */
+  void reset();
+};
+
+
+class RaiseEqualityEngineConflict {
+private:
+  TheoryArithPrivate& d_ta;
+  
+public:
+  RaiseEqualityEngineConflict(TheoryArithPrivate& ta);
+
+  /* If you are not an equality engine, don't use this!
+   *
+   * The proof should prove that `n` is a conflict.
+   * */
+  void raiseEEConflict(Node n, std::shared_ptr<ProofNode> pf) const;
+};
+
+class BoundCountingLookup {
+private:
+  TheoryArithPrivate& d_ta;
+public:
+  BoundCountingLookup(TheoryArithPrivate& ta);
+  const BoundsInfo& boundsInfo(ArithVar basic) const;
+  BoundCounts atBounds(ArithVar basic) const;
+  BoundCounts hasBounds(ArithVar basic) const;
+};
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/congruence_manager.cpp b/src/theory/arith/linear/congruence_manager.cpp
new file mode 100644 (file)
index 0000000..da4d81a
--- /dev/null
@@ -0,0 +1,715 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Alex Ozdemir, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/congruence_manager.h"
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "proof/proof_node.h"
+#include "proof/proof_node_manager.h"
+#include "smt/env.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/ee_setup_info.h"
+#include "theory/rewriter.h"
+#include "theory/uf/equality_engine.h"
+#include "theory/uf/proof_equality_engine.h"
+
+using namespace cvc5::internal::kind;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+ArithCongruenceManager::ArithCongruenceManager(
+    Env& env,
+    ConstraintDatabase& cd,
+    SetupLiteralCallBack setup,
+    const ArithVariables& avars,
+    RaiseEqualityEngineConflict raiseConflict)
+    : EnvObj(env),
+      d_inConflict(context()),
+      d_raiseConflict(raiseConflict),
+      d_notify(*this),
+      d_keepAlive(context()),
+      d_propagatations(context()),
+      d_explanationMap(context()),
+      d_constraintDatabase(cd),
+      d_setupLiteral(setup),
+      d_avariables(avars),
+      d_ee(nullptr),
+      d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
+                                           : nullptr),
+      // Construct d_pfGenEe with the SAT context, since its proof include
+      // unclosed assumptions of theory literals.
+      d_pfGenEe(new EagerProofGenerator(
+          d_pnm, context(), "ArithCongruenceManager::pfGenEe")),
+      // Construct d_pfGenEe with the USER context, since its proofs are closed.
+      d_pfGenExplain(new EagerProofGenerator(
+          d_pnm, userContext(), "ArithCongruenceManager::pfGenExplain")),
+      d_pfee(nullptr)
+{
+}
+
+ArithCongruenceManager::~ArithCongruenceManager() {}
+
+bool ArithCongruenceManager::needsEqualityEngine(EeSetupInfo& esi)
+{
+  Assert(!options().arith.arithEqSolver);
+  esi.d_notify = &d_notify;
+  esi.d_name = "arithCong::ee";
+  return true;
+}
+
+void ArithCongruenceManager::finishInit(eq::EqualityEngine* ee)
+{
+  if (options().arith.arithEqSolver)
+  {
+    // use our own copy
+    d_allocEe = std::make_unique<eq::EqualityEngine>(
+        d_env, context(), d_notify, "arithCong::ee", true);
+    d_ee = d_allocEe.get();
+    if (d_pnm != nullptr)
+    {
+      // allocate an internal proof equality engine
+      d_allocPfee = std::make_unique<eq::ProofEqEngine>(d_env, *d_ee);
+      d_ee->setProofEqualityEngine(d_allocPfee.get());
+    }
+  }
+  else
+  {
+    Assert(ee != nullptr);
+    // otherwise, we use the official one
+    d_ee = ee;
+  }
+  // set the congruence kinds on the separate equality engine
+  d_ee->addFunctionKind(kind::NONLINEAR_MULT);
+  d_ee->addFunctionKind(kind::EXPONENTIAL);
+  d_ee->addFunctionKind(kind::SINE);
+  d_ee->addFunctionKind(kind::IAND);
+  d_ee->addFunctionKind(kind::POW2);
+  // the proof equality engine is the one from the equality engine
+  d_pfee = d_ee->getProofEqualityEngine();
+  // have proof equality engine only if proofs are enabled
+  Assert(isProofEnabled() == (d_pfee != nullptr));
+}
+
+ArithCongruenceManager::Statistics::Statistics()
+    : d_watchedVariables(smtStatisticsRegistry().registerInt(
+        "theory::arith::congruence::watchedVariables")),
+      d_watchedVariableIsZero(smtStatisticsRegistry().registerInt(
+          "theory::arith::congruence::watchedVariableIsZero")),
+      d_watchedVariableIsNotZero(smtStatisticsRegistry().registerInt(
+          "theory::arith::congruence::watchedVariableIsNotZero")),
+      d_equalsConstantCalls(smtStatisticsRegistry().registerInt(
+          "theory::arith::congruence::equalsConstantCalls")),
+      d_propagations(smtStatisticsRegistry().registerInt(
+          "theory::arith::congruence::propagations")),
+      d_propagateConstraints(smtStatisticsRegistry().registerInt(
+          "theory::arith::congruence::propagateConstraints")),
+      d_conflicts(smtStatisticsRegistry().registerInt(
+          "theory::arith::congruence::conflicts"))
+{
+}
+
+ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager& acm)
+  : d_acm(acm)
+{}
+
+bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(
+    TNode predicate, bool value)
+{
+  Assert(predicate.getKind() == kind::EQUAL);
+  Trace("arith::congruences")
+      << "ArithCongruenceNotify::eqNotifyTriggerPredicate(" << predicate << ", "
+      << (value ? "true" : "false") << ")" << std::endl;
+  if (value) {
+    return d_acm.propagate(predicate);
+  }
+  return d_acm.propagate(predicate.notNode());
+}
+
+bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) {
+  Trace("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl;
+  if (value) {
+    return d_acm.propagate(t1.eqNode(t2));
+  } else {
+    return d_acm.propagate(t1.eqNode(t2).notNode());
+  }
+}
+void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1, TNode t2) {
+  Trace("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl;
+  d_acm.propagate(t1.eqNode(t2));
+}
+void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t) {
+}
+void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyMerge(TNode t1,
+                                                                  TNode t2)
+{
+}
+void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) {
+}
+
+void ArithCongruenceManager::raiseConflict(Node conflict,
+                                           std::shared_ptr<ProofNode> pf)
+{
+  Assert(!inConflict());
+  Trace("arith::conflict") << "difference manager conflict   " << conflict << std::endl;
+  d_inConflict.raise();
+  d_raiseConflict.raiseEEConflict(conflict, pf);
+}
+bool ArithCongruenceManager::inConflict() const{
+  return d_inConflict.isRaised();
+}
+
+bool ArithCongruenceManager::hasMorePropagations() const {
+  return !d_propagatations.empty();
+}
+const Node ArithCongruenceManager::getNextPropagation() {
+  Assert(hasMorePropagations());
+  Node prop = d_propagatations.front();
+  d_propagatations.dequeue();
+  return prop;
+}
+
+bool ArithCongruenceManager::canExplain(TNode n) const {
+  return d_explanationMap.find(n) != d_explanationMap.end();
+}
+
+Node ArithCongruenceManager::externalToInternal(TNode n) const{
+  Assert(canExplain(n));
+  ExplainMap::const_iterator iter = d_explanationMap.find(n);
+  size_t pos = (*iter).second;
+  return d_propagatations[pos];
+}
+
+void ArithCongruenceManager::pushBack(TNode n){
+  d_explanationMap.insert(n, d_propagatations.size());
+  d_propagatations.enqueue(n);
+
+  ++(d_statistics.d_propagations);
+}
+void ArithCongruenceManager::pushBack(TNode n, TNode r){
+  d_explanationMap.insert(r, d_propagatations.size());
+  d_explanationMap.insert(n, d_propagatations.size());
+  d_propagatations.enqueue(n);
+
+  ++(d_statistics.d_propagations);
+}
+void ArithCongruenceManager::pushBack(TNode n, TNode r, TNode w){
+  d_explanationMap.insert(w, d_propagatations.size());
+  d_explanationMap.insert(r, d_propagatations.size());
+  d_explanationMap.insert(n, d_propagatations.size());
+  d_propagatations.enqueue(n);
+
+  ++(d_statistics.d_propagations);
+}
+
+void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub){
+  Assert(lb->isLowerBound());
+  Assert(ub->isUpperBound());
+  Assert(lb->getVariable() == ub->getVariable());
+  Assert(lb->getValue().sgn() == 0);
+  Assert(ub->getValue().sgn() == 0);
+
+  ++(d_statistics.d_watchedVariableIsZero);
+
+  ArithVar s = lb->getVariable();
+  TNode eq = d_watchedEqualities[s];
+  ConstraintCP eqC = d_constraintDatabase.getConstraint(
+      s, ConstraintType::Equality, lb->getValue());
+  NodeBuilder reasonBuilder(Kind::AND);
+  auto pfLb = lb->externalExplainByAssertions(reasonBuilder);
+  auto pfUb = ub->externalExplainByAssertions(reasonBuilder);
+  Node reason = mkAndFromBuilder(reasonBuilder);
+  std::shared_ptr<ProofNode> pf{};
+  if (isProofEnabled())
+  {
+    pf = d_pnm->mkNode(
+        PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eqC->getProofLiteral()});
+    pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {eq});
+  }
+
+  d_keepAlive.push_back(reason);
+  Trace("arith-ee") << "Asserting an equality on " << s << ", on trichotomy"
+                    << std::endl;
+  Trace("arith-ee") << "  based on " << lb << std::endl;
+  Trace("arith-ee") << "  based on " << ub << std::endl;
+  assertionToEqualityEngine(true, s, reason, pf);
+}
+
+void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq){
+  Trace("arith::cong") << "Cong::watchedVariableIsZero: " << *eq << std::endl;
+
+  Assert(eq->isEquality());
+  Assert(eq->getValue().sgn() == 0);
+
+  ++(d_statistics.d_watchedVariableIsZero);
+
+  ArithVar s = eq->getVariable();
+
+  //Explain for conflict is correct as these proofs are generated
+  //and stored eagerly
+  //These will be safe for propagation later as well
+  NodeBuilder nb(Kind::AND);
+  // An open proof of eq from literals now in reason.
+  if (TraceIsOn("arith::cong"))
+  {
+    eq->printProofTree(Trace("arith::cong"));
+  }
+  auto pf = eq->externalExplainByAssertions(nb);
+  if (isProofEnabled())
+  {
+    pf = d_pnm->mkNode(
+        PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {d_watchedEqualities[s]});
+  }
+  Node reason = mkAndFromBuilder(nb);
+
+  d_keepAlive.push_back(reason);
+  assertionToEqualityEngine(true, s, reason, pf);
+}
+
+void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c){
+  Trace("arith::cong::notzero")
+      << "Cong::watchedVariableCannotBeZero " << *c << std::endl;
+  ++(d_statistics.d_watchedVariableIsNotZero);
+
+  ArithVar s = c->getVariable();
+  Node disEq = d_watchedEqualities[s].negate();
+
+  //Explain for conflict is correct as these proofs are generated and stored eagerly
+  //These will be safe for propagation later as well
+  NodeBuilder nb(Kind::AND);
+  // An open proof of eq from literals now in reason.
+  auto pf = c->externalExplainByAssertions(nb);
+  if (TraceIsOn("arith::cong::notzero"))
+  {
+    Trace("arith::cong::notzero") << "  original proof ";
+    pf->printDebug(Trace("arith::cong::notzero"));
+    Trace("arith::cong::notzero") << std::endl;
+  }
+  Node reason = mkAndFromBuilder(nb);
+  if (isProofEnabled())
+  {
+    if (c->getType() == ConstraintType::Disequality)
+    {
+      Assert(c->getLiteral() == d_watchedEqualities[s].negate());
+      // We have to prove equivalence to the watched disequality.
+      pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {disEq});
+    }
+    else
+    {
+      Trace("arith::cong::notzero")
+          << "  proof modification needed" << std::endl;
+
+      // Four cases:
+      //   c has form x_i = d, d > 0     => multiply c by -1 in Farkas proof
+      //   c has form x_i = d, d > 0     => multiply c by 1 in Farkas proof
+      //   c has form x_i <= d, d < 0     => multiply c by 1 in Farkas proof
+      //   c has form x_i >= d, d > 0     => multiply c by -1 in Farkas proof
+      const bool scaleCNegatively = c->getType() == ConstraintType::LowerBound
+                                    || (c->getType() == ConstraintType::Equality
+                                        && c->getValue().sgn() > 0);
+      const int cSign = scaleCNegatively ? -1 : 1;
+      TNode isZero = d_watchedEqualities[s];
+      TypeNode type = isZero[0].getType();
+      const auto isZeroPf = d_pnm->mkAssume(isZero);
+      const auto nm = NodeManager::currentNM();
+      const auto sumPf =
+          d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
+                        {isZeroPf, pf},
+                        // Trick for getting correct, opposing signs.
+                        {nm->mkConstRealOrInt(type, Rational(-1 * cSign)),
+                         nm->mkConstRealOrInt(type, Rational(cSign))});
+      const auto botPf = d_pnm->mkNode(
+          PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
+      std::vector<Node> assumption = {isZero};
+      pf = d_pnm->mkScope(botPf, assumption, false);
+      Trace("arith::cong::notzero") << "  new proof ";
+      pf->printDebug(Trace("arith::cong::notzero"));
+      Trace("arith::cong::notzero") << std::endl;
+    }
+    Assert(pf->getResult() == disEq);
+  }
+  d_keepAlive.push_back(reason);
+  assertionToEqualityEngine(false, s, reason, pf);
+}
+
+
+bool ArithCongruenceManager::propagate(TNode x){
+  Trace("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x<<")"<<std::endl;
+  if(inConflict()){
+    return true;
+  }
+
+  Node rewritten = rewrite(x);
+
+  //Need to still propagate this!
+  if(rewritten.getKind() == kind::CONST_BOOLEAN){
+    pushBack(x);
+
+    if(rewritten.getConst<bool>()){
+      return true;
+    }else{
+      // x rewrites to false.
+      ++(d_statistics.d_conflicts);
+      TrustNode trn = explainInternal(x);
+      Node conf = flattenAnd(trn.getNode());
+      Trace("arith::congruenceManager") << "rewritten to false "<<x<<" with explanation "<< conf << std::endl;
+      if (isProofEnabled())
+      {
+        auto pf = trn.getGenerator()->getProofFor(trn.getProven());
+        auto confPf = d_pnm->mkNode(
+            PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {conf.negate()});
+        raiseConflict(conf, confPf);
+      }
+      else
+      {
+        raiseConflict(conf);
+      }
+      return false;
+    }
+  }
+
+  Assert(rewritten.getKind() != kind::CONST_BOOLEAN);
+
+  ConstraintP c = d_constraintDatabase.lookup(rewritten);
+  if(c == NullConstraint){
+    //using setup as there may not be a corresponding congruence literal yet
+    d_setupLiteral(rewritten);
+    c = d_constraintDatabase.lookup(rewritten);
+    Assert(c != NullConstraint);
+  }
+
+  Trace("arith::congruenceManager")<< "x is "
+                                   <<  c->hasProof() << " "
+                                   << (x == rewritten) << " "
+                                   << c->canBePropagated() << " "
+                                   << c->negationHasProof() << std::endl;
+
+  if(c->negationHasProof()){
+    TrustNode texpC = explainInternal(x);
+    Node expC = texpC.getNode();
+    ConstraintCP negC = c->getNegation();
+    Node neg = Constraint::externalExplainByAssertions({negC});
+    Node conf = expC.andNode(neg);
+    Node final = flattenAnd(conf);
+
+    ++(d_statistics.d_conflicts);
+    raiseConflict(final);
+    Trace("arith::congruenceManager") << "congruenceManager found a conflict " << final << std::endl;
+    return false;
+  }
+
+  // Cases for propagation
+  // C : c has a proof
+  // S : x == rewritten
+  // P : c can be propagated
+  //
+  // CSP
+  // 000 : propagate x, and mark C it as being explained
+  // 001 : propagate x, and propagate c after marking it as being explained
+  // 01* : propagate x, mark c but do not propagate c
+  // 10* : propagate x, do not mark c and do not propagate c
+  // 11* : drop the constraint, do not propagate x or c
+
+  if(!c->hasProof() && x != rewritten){
+    if(c->assertedToTheTheory()){
+      pushBack(x, rewritten, c->getWitness());
+    }else{
+      pushBack(x, rewritten);
+    }
+
+    c->setEqualityEngineProof();
+    if(c->canBePropagated() && !c->assertedToTheTheory()){
+
+      ++(d_statistics.d_propagateConstraints);
+      c->propagate();
+    }
+  }else if(!c->hasProof() && x == rewritten){
+    if(c->assertedToTheTheory()){
+      pushBack(x, c->getWitness());
+    }else{
+      pushBack(x);
+    }
+    c->setEqualityEngineProof();
+  }else if(c->hasProof() && x != rewritten){
+    if(c->assertedToTheTheory()){
+      pushBack(x);
+    }else{
+      pushBack(x);
+    }
+  }else{
+    Assert(c->hasProof() && x == rewritten);
+  }
+  return true;
+}
+
+void ArithCongruenceManager::explain(TNode literal, std::vector<TNode>& assumptions) {
+  if (literal.getKind() != kind::NOT) {
+    d_ee->explainEquality(literal[0], literal[1], true, assumptions);
+  } else {
+    d_ee->explainEquality(literal[0][0], literal[0][1], false, assumptions);
+  }
+}
+
+void ArithCongruenceManager::enqueueIntoNB(const std::set<TNode> s,
+                                           NodeBuilder& nb)
+{
+  std::set<TNode>::const_iterator it = s.begin();
+  std::set<TNode>::const_iterator it_end = s.end();
+  for(; it != it_end; ++it) {
+    nb << *it;
+  }
+}
+
+TrustNode ArithCongruenceManager::explainInternal(TNode internal)
+{
+  if (isProofEnabled())
+  {
+    return d_pfee->explain(internal);
+  }
+  // otherwise, explain without proof generator
+  Node exp = d_ee->mkExplainLit(internal);
+  return TrustNode::mkTrustPropExp(internal, exp, nullptr);
+}
+
+TrustNode ArithCongruenceManager::explain(TNode external)
+{
+  Trace("arith-ee") << "Ask for explanation of " << external << std::endl;
+  Node internal = externalToInternal(external);
+  Trace("arith-ee") << "...internal = " << internal << std::endl;
+  TrustNode trn = explainInternal(internal);
+  if (isProofEnabled() && trn.getProven()[1] != external)
+  {
+    Assert(trn.getKind() == TrustNodeKind::PROP_EXP);
+    Assert(trn.getProven().getKind() == Kind::IMPLIES);
+    Assert(trn.getGenerator() != nullptr);
+    Trace("arith-ee") << "tweaking proof to prove " << external << " not "
+                      << trn.getProven()[1] << std::endl;
+    std::vector<std::shared_ptr<ProofNode>> assumptionPfs;
+    std::vector<Node> assumptions = andComponents(trn.getNode());
+    assumptionPfs.push_back(trn.toProofNode());
+    for (const auto& a : assumptions)
+    {
+      assumptionPfs.push_back(
+          d_pnm->mkNode(PfRule::TRUE_INTRO, {d_pnm->mkAssume(a)}, {}));
+    }
+    auto litPf = d_pnm->mkNode(
+        PfRule::MACRO_SR_PRED_TRANSFORM, {assumptionPfs}, {external});
+    auto extPf = d_pnm->mkScope(litPf, assumptions);
+    return d_pfGenExplain->mkTrustedPropagation(external, trn.getNode(), extPf);
+  }
+  return trn;
+}
+
+void ArithCongruenceManager::explain(TNode external, NodeBuilder& out)
+{
+  Node internal = externalToInternal(external);
+
+  std::vector<TNode> assumptions;
+  explain(internal, assumptions);
+  std::set<TNode> assumptionSet;
+  assumptionSet.insert(assumptions.begin(), assumptions.end());
+
+  enqueueIntoNB(assumptionSet, out);
+}
+
+void ArithCongruenceManager::addWatchedPair(ArithVar s, TNode x, TNode y){
+  Assert(!isWatchedVariable(s));
+
+  Trace("arith::congruenceManager")
+    << "addWatchedPair(" << s << ", " << x << ", " << y << ")" << std::endl;
+
+
+  ++(d_statistics.d_watchedVariables);
+
+  d_watchedVariables.add(s);
+
+  Node eq = x.eqNode(y);
+  d_watchedEqualities.set(s, eq);
+}
+
+void ArithCongruenceManager::assertLitToEqualityEngine(
+    Node lit, TNode reason, std::shared_ptr<ProofNode> pf)
+{
+  bool isEquality = lit.getKind() != Kind::NOT;
+  Node eq = isEquality ? lit : lit[0];
+  Assert(eq.getKind() == Kind::EQUAL);
+
+  Trace("arith-ee") << "Assert to Eq " << lit << ", reason " << reason
+                    << std::endl;
+  if (isProofEnabled())
+  {
+    if (CDProof::isSame(lit, reason))
+    {
+      Trace("arith-pfee") << "Asserting only, b/c implied by symm" << std::endl;
+      // The equality engine doesn't ref-count for us...
+      d_keepAlive.push_back(eq);
+      d_keepAlive.push_back(reason);
+      d_ee->assertEquality(eq, isEquality, reason);
+    }
+    else if (hasProofFor(lit))
+    {
+      Trace("arith-pfee") << "Skipping b/c already done" << std::endl;
+    }
+    else
+    {
+      setProofFor(lit, pf);
+      Trace("arith-pfee") << "Actually asserting" << std::endl;
+      if (TraceIsOn("arith-pfee"))
+      {
+        Trace("arith-pfee") << "Proof: ";
+        pf->printDebug(Trace("arith-pfee"));
+        Trace("arith-pfee") << std::endl;
+      }
+      // The proof equality engine *does* ref-count for us...
+      d_pfee->assertFact(lit, reason, d_pfGenEe.get());
+    }
+  }
+  else
+  {
+    // The equality engine doesn't ref-count for us...
+    d_keepAlive.push_back(eq);
+    d_keepAlive.push_back(reason);
+    d_ee->assertEquality(eq, isEquality, reason);
+  }
+}
+
+void ArithCongruenceManager::assertionToEqualityEngine(
+    bool isEquality, ArithVar s, TNode reason, std::shared_ptr<ProofNode> pf)
+{
+  Assert(isWatchedVariable(s));
+
+  TNode eq = d_watchedEqualities[s];
+  Assert(eq.getKind() == kind::EQUAL);
+
+  Node lit = isEquality ? Node(eq) : eq.notNode();
+  Trace("arith-ee") << "Assert to Eq " << eq << ", pol " << isEquality
+                    << ", reason " << reason << std::endl;
+  assertLitToEqualityEngine(lit, reason, pf);
+}
+
+bool ArithCongruenceManager::hasProofFor(TNode f) const
+{
+  Assert(isProofEnabled());
+  if (d_pfGenEe->hasProofFor(f))
+  {
+    return true;
+  }
+  Node sym = CDProof::getSymmFact(f);
+  Assert(!sym.isNull());
+  return d_pfGenEe->hasProofFor(sym);
+}
+
+void ArithCongruenceManager::setProofFor(TNode f,
+                                         std::shared_ptr<ProofNode> pf) const
+{
+  Assert(!hasProofFor(f));
+  d_pfGenEe->mkTrustNode(f, pf);
+  Node symF = CDProof::getSymmFact(f);
+  auto symPf = d_pnm->mkNode(PfRule::SYMM, {pf}, {});
+  d_pfGenEe->mkTrustNode(symF, symPf);
+}
+
+void ArithCongruenceManager::equalsConstant(ConstraintCP c){
+  Assert(c->isEquality());
+
+  ++(d_statistics.d_equalsConstantCalls);
+  Trace("equalsConstant") << "equals constant " << c << std::endl;
+
+  ArithVar x = c->getVariable();
+  Node xAsNode = d_avariables.asNode(x);
+  NodeManager* nm = NodeManager::currentNM();
+  Node asRational = nm->mkConstRealOrInt(
+      xAsNode.getType(), c->getValue().getNoninfinitesimalPart());
+
+  // No guarentee this is in normal form!
+  // Note though, that it happens to be in proof normal form!
+  Node eq = xAsNode.eqNode(asRational);
+  d_keepAlive.push_back(eq);
+
+  NodeBuilder nb(Kind::AND);
+  auto pf = c->externalExplainByAssertions(nb);
+  Node reason = mkAndFromBuilder(nb);
+  d_keepAlive.push_back(reason);
+
+  Trace("arith-ee") << "Assert equalsConstant " << eq << ", reason " << reason << std::endl;
+  assertLitToEqualityEngine(eq, reason, pf);
+}
+
+void ArithCongruenceManager::equalsConstant(ConstraintCP lb, ConstraintCP ub){
+  Assert(lb->isLowerBound());
+  Assert(ub->isUpperBound());
+  Assert(lb->getVariable() == ub->getVariable());
+
+  ++(d_statistics.d_equalsConstantCalls);
+  Trace("equalsConstant") << "equals constant " << lb << std::endl
+                          << ub << std::endl;
+
+  ArithVar x = lb->getVariable();
+  NodeBuilder nb(Kind::AND);
+  auto pfLb = lb->externalExplainByAssertions(nb);
+  auto pfUb = ub->externalExplainByAssertions(nb);
+  Node reason = mkAndFromBuilder(nb);
+
+  Node xAsNode = d_avariables.asNode(x);
+  NodeManager* nm = NodeManager::currentNM();
+  Node asRational = nm->mkConstRealOrInt(
+      xAsNode.getType(), lb->getValue().getNoninfinitesimalPart());
+
+  // No guarentee this is in normal form!
+  // Note though, that it happens to be in proof normal form!
+  Node eq = xAsNode.eqNode(asRational);
+  std::shared_ptr<ProofNode> pf;
+  if (isProofEnabled())
+  {
+    pf = d_pnm->mkNode(PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eq});
+  }
+  d_keepAlive.push_back(eq);
+  d_keepAlive.push_back(reason);
+
+  Trace("arith-ee") << "Assert equalsConstant2 " << eq << ", reason " << reason << std::endl;
+
+  assertLitToEqualityEngine(eq, reason, pf);
+}
+
+bool ArithCongruenceManager::isProofEnabled() const { return d_pnm != nullptr; }
+
+std::vector<Node> andComponents(TNode an)
+{
+  auto nm = NodeManager::currentNM();
+  if (an == nm->mkConst(true))
+  {
+    return {};
+  }
+  else if (an.getKind() != Kind::AND)
+  {
+    return {an};
+  }
+  std::vector<Node> a{};
+  a.reserve(an.getNumChildren());
+  a.insert(a.end(), an.begin(), an.end());
+  return a;
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/congruence_manager.h b/src/theory/arith/linear/congruence_manager.h
new file mode 100644 (file)
index 0000000..c9dfb15
--- /dev/null
@@ -0,0 +1,301 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Alex Ozdemir, Tim King, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "context/cdhashmap.h"
+#include "context/cdlist.h"
+#include "context/cdmaybe.h"
+#include "context/cdtrail_queue.h"
+#include "proof/trust_node.h"
+#include "smt/env_obj.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/arithvar_node_map.h"
+#include "theory/arith/linear/callbacks.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/uf/equality_engine_notify.h"
+#include "util/dense_map.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::context {
+class Context;
+class UserContext;
+}  // namespace cvc5::context
+
+namespace cvc5::internal {
+
+class ProofNodeManager;
+class EagerProofGenerator;
+
+namespace theory {
+struct EeSetupInfo;
+
+namespace eq {
+class ProofEqEngine;
+class EqualityEngine;
+}
+
+namespace arith::linear {
+
+class ArithVariables;
+
+class ArithCongruenceManager : protected EnvObj
+{
+ private:
+  context::CDRaised d_inConflict;
+  RaiseEqualityEngineConflict d_raiseConflict;
+
+  /**
+   * The set of ArithVars equivalent to a pair of terms.
+   * If this is 0 or cannot be 0, this can be signalled.
+   * The pair of terms for the congruence is stored in watched equalities.
+   */
+  DenseSet d_watchedVariables;
+  /** d_watchedVariables |-> (= x y) */
+  ArithVarToNodeMap d_watchedEqualities;
+
+
+  class ArithCongruenceNotify : public eq::EqualityEngineNotify {
+  private:
+    ArithCongruenceManager& d_acm;
+  public:
+    ArithCongruenceNotify(ArithCongruenceManager& acm);
+
+    bool eqNotifyTriggerPredicate(TNode predicate, bool value) override;
+
+    bool eqNotifyTriggerTermEquality(TheoryId tag,
+                                     TNode t1,
+                                     TNode t2,
+                                     bool value) override;
+
+    void eqNotifyConstantTermMerge(TNode t1, TNode t2) override;
+    void eqNotifyNewClass(TNode t) override;
+    void eqNotifyMerge(TNode t1, TNode t2) override;
+    void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) override;
+  };
+  ArithCongruenceNotify d_notify;
+
+  context::CDList<Node> d_keepAlive;
+
+  /** Store the propagations. */
+  context::CDTrailQueue<Node> d_propagatations;
+
+  /* This maps the node a theory engine will request on an explain call to
+   * to its corresponding PropUnit.
+   * This is node is potentially both the propagation or
+   * rewrite(propagation).
+   */
+  typedef context::CDHashMap<Node, size_t> ExplainMap;
+  ExplainMap d_explanationMap;
+
+  ConstraintDatabase& d_constraintDatabase;
+  SetupLiteralCallBack d_setupLiteral;
+
+  const ArithVariables& d_avariables;
+
+  /** The equality engine being used by this class */
+  eq::EqualityEngine* d_ee;
+  /** The equality engine we allocated */
+  std::unique_ptr<eq::EqualityEngine> d_allocEe;
+  /** proof manager */
+  ProofNodeManager* d_pnm;
+  /** A proof generator for storing proofs of facts that are asserted to the EQ
+   * engine. Note that these proofs **are not closed**; they may contain
+   * literals from the explanation of their fact as unclosed assumptions.
+   * This makes these proofs SAT-context depdent.
+   *
+   * This is why this generator is separate from the TheoryArithPrivate
+   * generator, which stores closed proofs.
+   */
+  std::unique_ptr<EagerProofGenerator> d_pfGenEe;
+  /** A proof generator for TrustNodes sent to TheoryArithPrivate.
+   *
+   * When TheoryArithPrivate requests an explanation from
+   * ArithCongruenceManager, it can request a TrustNode for that explanation.
+   * This proof generator is the one used in that TrustNode: it stores the
+   * (closed) proofs of implications proved by the
+   * ArithCongruenceManager/EqualityEngine.
+   *
+   * It is insufficient to just use the ProofGenerator from the ProofEqEngine,
+   * since sometimes the ArithCongruenceManager needs to add some
+   * arith-specific reasoning to extend the proof (e.g. rewriting the result
+   * into a normal form).
+   * */
+  std::unique_ptr<EagerProofGenerator> d_pfGenExplain;
+
+  /** Pointer to the proof equality engine of TheoryArith */
+  theory::eq::ProofEqEngine* d_pfee;
+  /** The proof equality engine we allocated */
+  std::unique_ptr<eq::ProofEqEngine> d_allocPfee;
+
+  /** Raise a conflict node `conflict` to the theory of arithmetic.
+   *
+   * When proofs are enabled, a (closed) proof of the conflict should be
+   * provided.
+   */
+  void raiseConflict(Node conflict, std::shared_ptr<ProofNode> pf = nullptr);
+  /**
+   * Are proofs enabled? This is true if a non-null proof manager was provided
+   * to the constructor of this class.
+   */
+  bool isProofEnabled() const;
+
+ public:
+  bool inConflict() const;
+
+  bool hasMorePropagations() const;
+
+  const Node getNextPropagation();
+
+  bool canExplain(TNode n) const;
+
+private:
+  Node externalToInternal(TNode n) const;
+
+  void pushBack(TNode n);
+
+  void pushBack(TNode n, TNode r);
+
+  void pushBack(TNode n, TNode r, TNode w);
+
+  bool propagate(TNode x);
+  void explain(TNode literal, std::vector<TNode>& assumptions);
+
+  /** Assert this literal to the eq engine. Common functionality for
+   *   * assertionToEqualityEngine(..)
+   *   * equalsConstant(c)
+   *   * equalsConstant(lb, ub)
+   * If proof is off, then just asserts.
+   */
+  void assertLitToEqualityEngine(Node lit,
+                                 TNode reason,
+                                 std::shared_ptr<ProofNode> pf);
+  /** This sends a shared term to the uninterpreted equality engine. */
+  void assertionToEqualityEngine(bool eq,
+                                 ArithVar s,
+                                 TNode reason,
+                                 std::shared_ptr<ProofNode> pf);
+
+  /** Check for proof for this or a symmetric fact
+   *
+   * The proof submitted to this method are stored in `d_pfGenEe`, and should
+   * have closure properties consistent with the documentation for that member.
+   *
+   * @returns whether this or a symmetric fact has a proof.
+   */
+  bool hasProofFor(TNode f) const;
+  /**
+   * Sets the proof for this fact and the symmetric one.
+   *
+   * The proof submitted to this method are stored in `d_pfGenEe`, and should
+   * have closure properties consistent with the documentation for that member.
+   * */
+  void setProofFor(TNode f, std::shared_ptr<ProofNode> pf) const;
+
+  /** Dequeues the delay queue and asserts these equalities.*/
+  void enableSharedTerms();
+  void dequeueLiterals();
+
+  void enqueueIntoNB(const std::set<TNode> all, NodeBuilder& nb);
+
+  /**
+   * Determine an explaination for `internal`. That is a conjunction of theory
+   * literals which imply `internal`.
+   *
+   * The TrustNode here is a trusted propagation.
+   */
+  TrustNode explainInternal(TNode internal);
+
+ public:
+  ArithCongruenceManager(Env& env,
+                         ConstraintDatabase&,
+                         SetupLiteralCallBack,
+                         const ArithVariables&,
+                         RaiseEqualityEngineConflict raiseConflict);
+  ~ArithCongruenceManager();
+
+  //--------------------------------- initialization
+  /**
+   * Returns true if we need an equality engine, see
+   * Theory::needsEqualityEngine.
+   */
+  bool needsEqualityEngine(EeSetupInfo& esi);
+  /**
+   * Finish initialize. This class is instructed by TheoryArithPrivate to use
+   * the equality engine ee.
+   */
+  void finishInit(eq::EqualityEngine* ee);
+  //--------------------------------- end initialization
+
+  /**
+   * Return the trust node for the explanation of literal. The returned
+   * trust node is generated by the proof equality engine of this class.
+   */
+  TrustNode explain(TNode literal);
+
+  void explain(TNode lit, NodeBuilder& out);
+
+  void addWatchedPair(ArithVar s, TNode x, TNode y);
+
+  inline bool isWatchedVariable(ArithVar s) const {
+    return d_watchedVariables.isMember(s);
+  }
+
+  /** Assert an equality. */
+  void watchedVariableIsZero(ConstraintCP eq);
+
+  /** Assert a conjunction from lb and ub. */
+  void watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub);
+
+  /** Assert that the value cannot be zero. */
+  void watchedVariableCannotBeZero(ConstraintCP c);
+
+  /** Assert that the value cannot be zero. */
+  void watchedVariableCannotBeZero(ConstraintCP c, ConstraintCP d);
+
+
+  /** Assert that the value is congruent to a constant. */
+  void equalsConstant(ConstraintCP eq);
+  void equalsConstant(ConstraintCP lb, ConstraintCP ub);
+
+ private:
+  class Statistics {
+  public:
+    IntStat d_watchedVariables;
+    IntStat d_watchedVariableIsZero;
+    IntStat d_watchedVariableIsNotZero;
+
+    IntStat d_equalsConstantCalls;
+
+    IntStat d_propagations;
+    IntStat d_propagateConstraints;
+    IntStat d_conflicts;
+
+    Statistics();
+  } d_statistics;
+
+}; /* class ArithCongruenceManager */
+
+std::vector<Node> andComponents(TNode an);
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/constraint.cpp b/src/theory/arith/linear/constraint.cpp
new file mode 100644 (file)
index 0000000..9ed758f
--- /dev/null
@@ -0,0 +1,2463 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Alex Ozdemir, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+#include "theory/arith/linear/constraint.h"
+
+#include <algorithm>
+#include <ostream>
+#include <unordered_set>
+
+#include "base/output.h"
+#include "options/smt_options.h"
+#include "proof/eager_proof_generator.h"
+#include "proof/proof_node_manager.h"
+#include "smt/env.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/congruence_manager.h"
+#include "theory/arith/linear/normal_form.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/builtin/proof_checker.h"
+#include "theory/rewriter.h"
+
+using namespace std;
+using namespace cvc5::internal::kind;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+ConstraintRule::ConstraintRule()
+    : d_constraint(NullConstraint),
+      d_proofType(NoAP),
+      d_antecedentEnd(AntecedentIdSentinel)
+{
+  d_farkasCoefficients = RationalVectorCPSentinel;
+}
+
+ConstraintRule::ConstraintRule(ConstraintP con, ArithProofType pt)
+    : d_constraint(con), d_proofType(pt), d_antecedentEnd(AntecedentIdSentinel)
+{
+  d_farkasCoefficients = RationalVectorCPSentinel;
+}
+ConstraintRule::ConstraintRule(ConstraintP con,
+                               ArithProofType pt,
+                               AntecedentId antecedentEnd)
+    : d_constraint(con), d_proofType(pt), d_antecedentEnd(antecedentEnd)
+{
+  d_farkasCoefficients = RationalVectorCPSentinel;
+}
+
+ConstraintRule::ConstraintRule(ConstraintP con,
+                               ArithProofType pt,
+                               AntecedentId antecedentEnd,
+                               RationalVectorCP coeffs)
+    : d_constraint(con), d_proofType(pt), d_antecedentEnd(antecedentEnd)
+{
+  Assert(con->isProofProducing() || coeffs == RationalVectorCPSentinel);
+  d_farkasCoefficients = coeffs;
+}
+
+/** Given a simplifiedKind this returns the corresponding ConstraintType. */
+//ConstraintType constraintTypeOfLiteral(Kind k);
+ConstraintType Constraint::constraintTypeOfComparison(const Comparison& cmp){
+  Kind k = cmp.comparisonKind();
+  switch(k){
+  case LT:
+  case LEQ:
+    {
+      Polynomial l = cmp.getLeft();
+      if(l.leadingCoefficientIsPositive()){ // (< x c)
+        return UpperBound;
+      }else{
+        return LowerBound; // (< (-x) c)
+      }
+    }
+  case GT:
+  case GEQ:
+    {
+      Polynomial l = cmp.getLeft();
+      if(l.leadingCoefficientIsPositive()){
+        return LowerBound; // (> x c)
+      }else{
+        return UpperBound; // (> (-x) c)
+      }
+    }
+  case EQUAL:
+    return Equality;
+  case DISTINCT:
+    return Disequality;
+  default: Unhandled() << k;
+  }
+}
+
+Constraint::Constraint(ArithVar x,
+                       ConstraintType t,
+                       const DeltaRational& v,
+                       bool produceProofs)
+    : d_variable(x),
+      d_type(t),
+      d_value(v),
+      d_database(NULL),
+      d_literal(Node::null()),
+      d_negation(NullConstraint),
+      d_canBePropagated(false),
+      d_assertionOrder(AssertionOrderSentinel),
+      d_witness(TNode::null()),
+      d_crid(ConstraintRuleIdSentinel),
+      d_split(false),
+      d_variablePosition(),
+      d_produceProofs(produceProofs)
+{
+  Assert(!initialized());
+}
+
+
+std::ostream& operator<<(std::ostream& o, const ArithProofType apt){
+  switch(apt){
+  case NoAP:  o << "NoAP"; break;
+  case AssumeAP:  o << "AssumeAP"; break;
+  case InternalAssumeAP:  o << "InternalAssumeAP"; break;
+  case FarkasAP:  o << "FarkasAP"; break;
+  case TrichotomyAP:  o << "TrichotomyAP"; break;
+  case EqualityEngineAP:  o << "EqualityEngineAP"; break;
+  case IntTightenAP: o << "IntTightenAP"; break;
+  case IntHoleAP: o << "IntHoleAP"; break;
+  default: break;
+  }
+  return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const ConstraintCP c){
+  if(c == NullConstraint){
+    return o << "NullConstraint";
+  }else{
+    return o << *c;
+  }
+}
+
+std::ostream& operator<<(std::ostream& o, const ConstraintP c){
+  if(c == NullConstraint){
+    return o << "NullConstraint";
+  }else{
+    return o << *c;
+  }
+}
+
+std::ostream& operator<<(std::ostream& o, const ConstraintType t){
+  switch(t){
+  case LowerBound:
+    return o << ">=";
+  case UpperBound:
+    return o << "<=";
+  case Equality:
+    return o << "=";
+  case Disequality:
+    return o << "!=";
+  default:
+    Unreachable();
+  }
+}
+
+std::ostream& operator<<(std::ostream& o, const Constraint& c){
+  o << c.getVariable() << ' ' << c.getType() << ' ' << c.getValue();
+  if(c.hasLiteral()){
+    o << "(node " << c.getLiteral() << ')';
+  }
+  return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const ValueCollection& vc){
+  o << "{";
+  bool pending = false;
+  if(vc.hasEquality()){
+    o << "eq: " << vc.getEquality();
+    pending = true;
+  }
+  if(vc.hasLowerBound()){
+    if(pending){
+      o << ", ";
+    }
+    o << "lb: " << vc.getLowerBound();
+    pending = true;
+  }
+  if(vc.hasUpperBound()){
+    if(pending){
+      o << ", ";
+    }
+    o << "ub: " << vc.getUpperBound();
+    pending = true;
+  }
+  if(vc.hasDisequality()){
+    if(pending){
+      o << ", ";
+    }
+    o << "de: " << vc.getDisequality();
+  }
+  return o << "}";
+}
+
+std::ostream& operator<<(std::ostream& o, const ConstraintCPVec& v){
+  o << "[" << v.size() << "x";
+  ConstraintCPVec::const_iterator i, end;
+  for(i=v.begin(), end=v.end(); i != end; ++i){
+    ConstraintCP c = *i;
+    o << ", " << (*c);
+  }
+  o << "]";
+  return o;
+}
+
+ValueCollection::ValueCollection()
+  : d_lowerBound(NullConstraint),
+    d_upperBound(NullConstraint),
+    d_equality(NullConstraint),
+    d_disequality(NullConstraint)
+{}
+
+bool ValueCollection::hasLowerBound() const{
+  return d_lowerBound != NullConstraint;
+}
+
+bool ValueCollection::hasUpperBound() const{
+  return d_upperBound != NullConstraint;
+}
+
+bool ValueCollection::hasEquality() const{
+  return d_equality != NullConstraint;
+}
+
+bool ValueCollection::hasDisequality() const {
+  return d_disequality != NullConstraint;
+}
+
+ConstraintP ValueCollection::getLowerBound() const {
+  Assert(hasLowerBound());
+  return d_lowerBound;
+}
+
+ConstraintP ValueCollection::getUpperBound() const {
+  Assert(hasUpperBound());
+  return d_upperBound;
+}
+
+ConstraintP ValueCollection::getEquality() const {
+  Assert(hasEquality());
+  return d_equality;
+}
+
+ConstraintP ValueCollection::getDisequality() const {
+  Assert(hasDisequality());
+  return d_disequality;
+}
+
+
+void ValueCollection::push_into(std::vector<ConstraintP>& vec) const {
+  Trace("arith::constraint") << "push_into " << *this << endl;
+  if(hasEquality()){
+    vec.push_back(d_equality);
+  }
+  if(hasLowerBound()){
+    vec.push_back(d_lowerBound);
+  }
+  if(hasUpperBound()){
+    vec.push_back(d_upperBound);
+  }
+  if(hasDisequality()){
+    vec.push_back(d_disequality);
+  }
+}
+
+ValueCollection ValueCollection::mkFromConstraint(ConstraintP c){
+  ValueCollection ret;
+  Assert(ret.empty());
+  switch(c->getType()){
+  case LowerBound:
+    ret.d_lowerBound = c;
+    break;
+  case UpperBound:
+    ret.d_upperBound = c;
+    break;
+  case Equality:
+    ret.d_equality = c;
+    break;
+  case Disequality:
+    ret.d_disequality = c;
+    break;
+  default:
+    Unreachable();
+  }
+  return ret;
+}
+
+bool ValueCollection::hasConstraintOfType(ConstraintType t) const{
+  switch(t){
+  case LowerBound:
+    return hasLowerBound();
+  case UpperBound:
+    return hasUpperBound();
+  case Equality:
+    return hasEquality();
+  case Disequality:
+    return hasDisequality();
+  default:
+    Unreachable();
+  }
+}
+
+ArithVar ValueCollection::getVariable() const{
+  Assert(!empty());
+  return nonNull()->getVariable();
+}
+
+const DeltaRational& ValueCollection::getValue() const{
+  Assert(!empty());
+  return nonNull()->getValue();
+}
+
+void ValueCollection::add(ConstraintP c){
+  Assert(c != NullConstraint);
+
+  Assert(empty() || getVariable() == c->getVariable());
+  Assert(empty() || getValue() == c->getValue());
+
+  switch(c->getType()){
+  case LowerBound:
+    Assert(!hasLowerBound());
+    d_lowerBound = c;
+    break;
+  case Equality:
+    Assert(!hasEquality());
+    d_equality = c;
+    break;
+  case UpperBound:
+    Assert(!hasUpperBound());
+    d_upperBound = c;
+    break;
+  case Disequality:
+    Assert(!hasDisequality());
+    d_disequality = c;
+    break;
+  default:
+    Unreachable();
+  }
+}
+
+ConstraintP ValueCollection::getConstraintOfType(ConstraintType t) const{
+  switch(t){
+    case LowerBound: Assert(hasLowerBound()); return d_lowerBound;
+    case Equality: Assert(hasEquality()); return d_equality;
+    case UpperBound: Assert(hasUpperBound()); return d_upperBound;
+    case Disequality: Assert(hasDisequality()); return d_disequality;
+    default: Unreachable();
+  }
+}
+
+void ValueCollection::remove(ConstraintType t){
+  switch(t){
+  case LowerBound:
+    Assert(hasLowerBound());
+    d_lowerBound = NullConstraint;
+    break;
+  case Equality:
+    Assert(hasEquality());
+    d_equality = NullConstraint;
+    break;
+  case UpperBound:
+    Assert(hasUpperBound());
+    d_upperBound = NullConstraint;
+    break;
+  case Disequality:
+    Assert(hasDisequality());
+    d_disequality = NullConstraint;
+    break;
+  default:
+    Unreachable();
+  }
+}
+
+bool ValueCollection::empty() const{
+  return
+    !(hasLowerBound() ||
+      hasUpperBound() ||
+      hasEquality() ||
+      hasDisequality());
+}
+
+ConstraintP ValueCollection::nonNull() const{
+  //This can be optimized by caching, but this is not necessary yet!
+  /* "Premature optimization is the root of all evil." */
+  if(hasLowerBound()){
+    return d_lowerBound;
+  }else if(hasUpperBound()){
+    return d_upperBound;
+  }else if(hasEquality()){
+    return d_equality;
+  }else if(hasDisequality()){
+    return d_disequality;
+  }else{
+    return NullConstraint;
+  }
+}
+
+bool Constraint::initialized() const {
+  return d_database != NULL;
+}
+
+const ConstraintDatabase& Constraint::getDatabase() const{
+  Assert(initialized());
+  return *d_database;
+}
+
+void Constraint::initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, ConstraintP negation){
+  Assert(!initialized());
+  d_database = db;
+  d_variablePosition = v;
+  d_negation = negation;
+}
+
+Constraint::~Constraint() {
+  // Call this instead of safeToGarbageCollect()
+  Assert(!contextDependentDataIsSet());
+
+  if(initialized()){
+    ValueCollection& vc =  d_variablePosition->second;
+    Trace("arith::constraint") << "removing" << vc << endl;
+
+    vc.remove(getType());
+
+    if(vc.empty()){
+      Trace("arith::constraint") << "erasing" << vc << endl;
+      SortedConstraintMap& perVariable = d_database->getVariableSCM(getVariable());
+      perVariable.erase(d_variablePosition);
+    }
+
+    if(hasLiteral()){
+      d_database->d_nodetoConstraintMap.erase(getLiteral());
+    }
+  }
+}
+
+const ConstraintRule& Constraint::getConstraintRule() const {
+  Assert(hasProof());
+  return d_database->d_watches->d_constraintProofs[d_crid];
+}
+
+const ValueCollection& Constraint::getValueCollection() const{
+  return d_variablePosition->second;
+}
+
+
+ConstraintP Constraint::getCeiling() {
+  Trace("getCeiling") << "Constraint_::getCeiling on " << *this << endl;
+  Assert(getValue().getInfinitesimalPart().sgn() > 0);
+
+  const DeltaRational ceiling(getValue().ceiling());
+  return d_database->getConstraint(getVariable(), getType(), ceiling);
+}
+
+ConstraintP Constraint::getFloor() {
+  Assert(getValue().getInfinitesimalPart().sgn() < 0);
+
+  const DeltaRational floor(Rational(getValue().floor()));
+  return d_database->getConstraint(getVariable(), getType(), floor);
+}
+
+void Constraint::setCanBePropagated() {
+  Assert(!canBePropagated());
+  d_database->pushCanBePropagatedWatch(this);
+}
+
+void Constraint::setAssertedToTheTheory(TNode witness, bool nowInConflict) {
+  Assert(hasLiteral());
+  Assert(!assertedToTheTheory());
+  Assert(negationHasProof() == nowInConflict);
+  d_database->pushAssertionOrderWatch(this, witness);
+
+  if(TraceIsOn("constraint::conflictCommit") && nowInConflict ){
+    Trace("constraint::conflictCommit") << "inConflict@setAssertedToTheTheory";
+    Trace("constraint::conflictCommit") << "\t" << this << std::endl;
+    Trace("constraint::conflictCommit") << "\t" << getNegation() << std::endl;
+    Trace("constraint::conflictCommit") << "\t" << getNegation()->externalExplainByAssertions() << std::endl;
+
+  }
+}
+
+bool Constraint::satisfiedBy(const DeltaRational& dr) const {
+  switch(getType()){
+  case LowerBound:
+    return getValue() <= dr;
+  case Equality:
+    return getValue() == dr;
+  case UpperBound:
+    return getValue() >= dr;
+  case Disequality:
+    return getValue() != dr;
+  }
+  Unreachable();
+}
+
+bool Constraint::isInternalAssumption() const {
+  return getProofType() == InternalAssumeAP;
+}
+
+TrustNode Constraint::externalExplainByAssertions() const
+{
+  NodeBuilder nb(kind::AND);
+  auto pfFromAssumptions = externalExplain(nb, AssertionOrderSentinel);
+  Node exp = mkAndFromBuilder(nb);
+  if (d_database->isProofEnabled())
+  {
+    std::vector<Node> assumptions;
+    if (exp.getKind() == Kind::AND)
+    {
+      assumptions.insert(assumptions.end(), exp.begin(), exp.end());
+    }
+    else
+    {
+      assumptions.push_back(exp);
+    }
+    auto pf = d_database->d_pnm->mkScope(pfFromAssumptions, assumptions);
+    return d_database->d_pfGen->mkTrustedPropagation(
+        getLiteral(), NodeManager::currentNM()->mkAnd(assumptions), pf);
+  }
+  return TrustNode::mkTrustPropExp(getLiteral(), exp);
+}
+
+bool Constraint::isAssumption() const {
+  return getProofType() == AssumeAP;
+}
+
+bool Constraint::hasEqualityEngineProof() const {
+  return getProofType() == EqualityEngineAP;
+}
+
+bool Constraint::hasFarkasProof() const {
+  return getProofType() == FarkasAP;
+}
+
+bool Constraint::hasSimpleFarkasProof() const
+{
+  Trace("constraints::hsfp") << "hasSimpleFarkasProof " << this << std::endl;
+  if (!hasFarkasProof())
+  {
+    Trace("constraints::hsfp") << "There is no simple Farkas proof because "
+                                  "there is no farkas proof."
+                               << std::endl;
+    return false;
+  }
+
+  // For each antecdent ...
+  AntecedentId i = getConstraintRule().d_antecedentEnd;
+  for (ConstraintCP a = d_database->getAntecedent(i); a != NullConstraint;
+       a = d_database->getAntecedent(--i))
+  {
+    // ... that antecdent must be an assumption OR a tightened assumption ...
+    if (a->isPossiblyTightenedAssumption())
+    {
+      continue;
+    }
+
+    // ... otherwise, we do not have a simple Farkas proof.
+    if (TraceIsOn("constraints::hsfp"))
+    {
+      Trace("constraints::hsfp") << "There is no simple Farkas proof b/c there "
+                                    "is an antecdent w/ rule ";
+      a->getConstraintRule().print(Trace("constraints::hsfp"), d_produceProofs);
+      Trace("constraints::hsfp") << std::endl;
+    }
+
+    return false;
+  }
+  return true;
+}
+
+bool Constraint::isPossiblyTightenedAssumption() const
+{
+  // ... that antecdent must be an assumption ...
+
+  if (isAssumption()) return true;
+  if (!hasIntTightenProof()) return false;
+  if (getConstraintRule().d_antecedentEnd == AntecedentIdSentinel) return false;
+  return d_database->getAntecedent(getConstraintRule().d_antecedentEnd)
+      ->isAssumption();
+}
+
+bool Constraint::hasIntTightenProof() const {
+  return getProofType() == IntTightenAP;
+}
+
+bool Constraint::hasIntHoleProof() const {
+  return getProofType() == IntHoleAP;
+}
+
+bool Constraint::hasTrichotomyProof() const {
+  return getProofType() == TrichotomyAP;
+}
+
+void Constraint::printProofTree(std::ostream& out, size_t depth) const
+{
+  if (d_produceProofs)
+  {
+    const ConstraintRule& rule = getConstraintRule();
+    out << std::string(2 * depth, ' ') << "* " << getVariable() << " [";
+    out << getProofLiteral();
+    if (assertedToTheTheory())
+    {
+      out << " | wit: " << getWitness();
+    }
+    out << "]" << ' ' << getType() << ' ' << getValue() << " ("
+        << getProofType() << ")";
+    if (getProofType() == FarkasAP)
+    {
+      out << " [";
+      bool first = true;
+      for (const auto& coeff : *rule.d_farkasCoefficients)
+      {
+        if (not first)
+        {
+          out << ", ";
+        }
+        first = false;
+        out << coeff;
+      }
+      out << "]";
+    }
+    out << endl;
+
+    for (AntecedentId i = rule.d_antecedentEnd; i != AntecedentIdSentinel; --i)
+    {
+      ConstraintCP antecdent = d_database->getAntecedent(i);
+      if (antecdent == NullConstraint)
+      {
+        break;
+      }
+      antecdent->printProofTree(out, depth + 1);
+    }
+    return;
+  }
+  out << "Cannot print proof. This is not a proof build." << endl;
+}
+
+bool Constraint::sanityChecking(Node n) const {
+  Comparison cmp = Comparison::parseNormalForm(n);
+  Kind k = cmp.comparisonKind();
+  Polynomial pleft = cmp.normalizedVariablePart();
+  Assert(k == EQUAL || k == DISTINCT || pleft.leadingCoefficientIsPositive());
+  Assert(k != EQUAL || Monomial::isMember(n[0]));
+  Assert(k != DISTINCT || Monomial::isMember(n[0][0]));
+
+  TNode left = pleft.getNode();
+  DeltaRational right = cmp.normalizedDeltaRational();
+
+  const ArithVariables& avariables = d_database->getArithVariables();
+
+  Trace("Constraint::sanityChecking") << cmp.getNode() << endl;
+  Trace("Constraint::sanityChecking") << k << endl;
+  Trace("Constraint::sanityChecking") << pleft.getNode() << endl;
+  Trace("Constraint::sanityChecking") << left << endl;
+  Trace("Constraint::sanityChecking") << right << endl;
+  Trace("Constraint::sanityChecking") << getValue() << endl;
+  Trace("Constraint::sanityChecking") << avariables.hasArithVar(left) << endl;
+  Trace("Constraint::sanityChecking") << avariables.asArithVar(left) << endl;
+  Trace("Constraint::sanityChecking") << getVariable() << endl;
+
+
+  if(avariables.hasArithVar(left) &&
+     avariables.asArithVar(left) == getVariable() &&
+     getValue() == right){
+    switch(getType()){
+    case LowerBound:
+    case UpperBound:
+      //Be overapproximate
+      return k == GT || k == GEQ ||k == LT || k == LEQ;
+    case Equality:
+      return k == EQUAL;
+    case Disequality:
+      return k == DISTINCT;
+    default:
+      Unreachable();
+    }
+  }else{
+    return false;
+  }
+}
+
+ConstraintCP ConstraintDatabase::getAntecedent (AntecedentId p) const {
+  Assert(p < d_antecedents.size());
+  return d_antecedents[p];
+}
+
+void ConstraintRule::print(std::ostream& out, bool produceProofs) const
+{
+  RationalVectorCP coeffs = produceProofs ? d_farkasCoefficients : nullptr;
+  out << "{ConstraintRule, ";
+  out << d_constraint << std::endl;
+  out << "d_proofType= " << d_proofType << ", " << std::endl;
+  out << "d_antecedentEnd= "<< d_antecedentEnd << std::endl;
+
+  if (d_constraint != NullConstraint && d_antecedentEnd != AntecedentIdSentinel)
+  {
+    const ConstraintDatabase& database = d_constraint->getDatabase();
+
+    size_t coeffIterator = (coeffs != RationalVectorCPSentinel) ? coeffs->size()-1 : 0;
+    AntecedentId p = d_antecedentEnd;
+    // must have at least one antecedent
+    ConstraintCP antecedent = database.getAntecedent(p);
+    while(antecedent != NullConstraint){
+      if(coeffs != RationalVectorCPSentinel){
+        out << coeffs->at(coeffIterator);
+      } else {
+        out << "_";
+      }
+      out << " * (" << *antecedent << ")" << std::endl;
+
+      Assert((coeffs == RationalVectorCPSentinel) || coeffIterator > 0);
+      --p;
+      coeffIterator = (coeffs != RationalVectorCPSentinel) ? coeffIterator-1 : 0;
+      antecedent = database.getAntecedent(p);
+    }
+    if(coeffs != RationalVectorCPSentinel){
+      out << coeffs->front();
+    } else {
+      out << "_";
+    }
+    out << " * (" << *(d_constraint->getNegation()) << ")";
+    out << " [not d_constraint] " << endl;
+  }
+  out << "}";
+}
+
+bool Constraint::wellFormedFarkasProof() const {
+  Assert(hasProof());
+
+  const ConstraintRule& cr = getConstraintRule();
+  if(cr.d_constraint != this){ return false; }
+  if(cr.d_proofType != FarkasAP){ return false; }
+
+  AntecedentId p = cr.d_antecedentEnd;
+
+  // must have at least one antecedent
+  ConstraintCP antecedent = d_database->d_antecedents[p];
+  if(antecedent  == NullConstraint) { return false; }
+
+  if (!d_produceProofs)
+  {
+    return cr.d_farkasCoefficients == RationalVectorCPSentinel;
+  }
+  Assert(d_produceProofs);
+
+  if(cr.d_farkasCoefficients == RationalVectorCPSentinel){ return false; }
+  if(cr.d_farkasCoefficients->size() < 2){ return false; }
+
+  const ArithVariables& vars = d_database->getArithVariables();
+
+  DeltaRational rhs(0);
+  Node lhs = Polynomial::mkZero().getNode();
+
+  RationalVector::const_iterator coeffIterator = cr.d_farkasCoefficients->end()-1;
+  RationalVector::const_iterator coeffBegin = cr.d_farkasCoefficients->begin();
+
+  while(antecedent != NullConstraint){
+    Assert(lhs.isNull() || Polynomial::isMember(lhs));
+
+    const Rational& coeff = *coeffIterator;
+    int coeffSgn = coeff.sgn();
+
+    rhs += antecedent->getValue() * coeff;
+
+    ArithVar antVar = antecedent->getVariable();
+    if(!lhs.isNull() && vars.hasNode(antVar)){
+      Node antAsNode = vars.asNode(antVar);
+      if(Polynomial::isMember(antAsNode)){
+        Polynomial lhsPoly = Polynomial::parsePolynomial(lhs);
+        Polynomial antPoly = Polynomial::parsePolynomial(antAsNode);
+        Polynomial sum = lhsPoly + (antPoly * coeff);
+        lhs = sum.getNode();
+      }else{
+        lhs = Node::null();
+      }
+    } else {
+      lhs = Node::null();
+    }
+    Trace("constraints::wffp") << "running sum: " << lhs << " <= " << rhs << endl;
+
+    switch( antecedent->getType() ){
+    case LowerBound:
+      // fc[l] < 0, therefore return false if coeffSgn >= 0
+      if(coeffSgn >= 0){ return false; }
+      break;
+    case UpperBound:
+      // fc[u] > 0, therefore return false if coeffSgn <= 0
+      if(coeffSgn <= 0){ return false; }
+      break;
+    case Equality:
+      if(coeffSgn == 0) { return false; }
+      break;
+    case Disequality:
+    default:
+      return false;
+    }
+
+    if(coeffIterator == coeffBegin){ return false; }
+    --coeffIterator;
+    --p;
+    antecedent = d_database->d_antecedents[p];
+  }
+  if(coeffIterator != coeffBegin){ return false; }
+
+  const Rational& firstCoeff = (*coeffBegin);
+  int firstCoeffSgn = firstCoeff.sgn();
+  rhs += (getNegation()->getValue()) * firstCoeff;
+  if(!lhs.isNull() && vars.hasNode(getVariable())){
+    Node firstAsNode = vars.asNode(getVariable());
+    if(Polynomial::isMember(firstAsNode)){
+      Polynomial lhsPoly = Polynomial::parsePolynomial(lhs);
+      Polynomial firstPoly = Polynomial::parsePolynomial(firstAsNode);
+      Polynomial sum = lhsPoly + (firstPoly * firstCoeff);
+      lhs = sum.getNode();
+    }else{
+      lhs = Node::null();
+    }
+  }else{
+    lhs = Node::null();
+  }
+
+  switch( getNegation()->getType() ){
+  case LowerBound:
+    // fc[l] < 0, therefore return false if coeffSgn >= 0
+    if(firstCoeffSgn >= 0){ return false; }
+    break;
+  case UpperBound:
+    // fc[u] > 0, therefore return false if coeffSgn <= 0
+    if(firstCoeffSgn <= 0){ return false; }
+    break;
+  case Equality:
+    if(firstCoeffSgn == 0) { return false; }
+    break;
+  case Disequality:
+  default:
+    return false;
+  }
+  Trace("constraints::wffp") << "final sum: " << lhs << " <= " << rhs << endl;
+  // 0 = lhs <= rhs < 0
+  return (lhs.isNull() || (Constant::isMember(lhs) && Constant(lhs).isZero()))
+         && rhs.sgn() < 0;
+}
+
+ConstraintP Constraint::makeNegation(ArithVar v,
+                                     ConstraintType t,
+                                     const DeltaRational& r,
+                                     bool produceProofs)
+{
+  switch(t){
+  case LowerBound:
+    {
+      Assert(r.infinitesimalSgn() >= 0);
+      if(r.infinitesimalSgn() > 0){
+        Assert(r.getInfinitesimalPart() == 1);
+        // make (not (v > r)), which is (v <= r)
+        DeltaRational dropInf(r.getNoninfinitesimalPart(), 0);
+        return new Constraint(v, UpperBound, dropInf, produceProofs);
+      }else{
+        Assert(r.infinitesimalSgn() == 0);
+        // make (not (v >= r)), which is (v < r)
+        DeltaRational addInf(r.getNoninfinitesimalPart(), -1);
+        return new Constraint(v, UpperBound, addInf, produceProofs);
+      }
+    }
+  case UpperBound:
+    {
+      Assert(r.infinitesimalSgn() <= 0);
+      if(r.infinitesimalSgn() < 0){
+        Assert(r.getInfinitesimalPart() == -1);
+        // make (not (v < r)), which is (v >= r)
+        DeltaRational dropInf(r.getNoninfinitesimalPart(), 0);
+        return new Constraint(v, LowerBound, dropInf, produceProofs);
+      }else{
+        Assert(r.infinitesimalSgn() == 0);
+        // make (not (v <= r)), which is (v > r)
+        DeltaRational addInf(r.getNoninfinitesimalPart(), 1);
+        return new Constraint(v, LowerBound, addInf, produceProofs);
+      }
+    }
+    case Equality: return new Constraint(v, Disequality, r, produceProofs);
+    case Disequality: return new Constraint(v, Equality, r, produceProofs);
+    default: Unreachable(); return NullConstraint;
+  }
+}
+
+ConstraintDatabase::ConstraintDatabase(Env& env,
+                                       const ArithVariables& avars,
+                                       ArithCongruenceManager& cm,
+                                       RaiseConflict raiseConflict,
+                                       EagerProofGenerator* pfGen)
+    : EnvObj(env),
+      d_varDatabases(),
+      d_toPropagate(context()),
+      d_antecedents(context(), false),
+      d_watches(new Watches(context(), userContext())),
+      d_avariables(avars),
+      d_congruenceManager(cm),
+      d_pfGen(pfGen),
+      d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
+                                           : nullptr),
+      d_raiseConflict(raiseConflict),
+      d_one(1),
+      d_negOne(-1)
+{
+}
+
+SortedConstraintMap& ConstraintDatabase::getVariableSCM(ArithVar v) const{
+  Assert(variableDatabaseIsSetup(v));
+  return d_varDatabases[v]->d_constraints;
+}
+
+void ConstraintDatabase::pushSplitWatch(ConstraintP c){
+  Assert(!c->d_split);
+  c->d_split = true;
+  d_watches->d_splitWatches.push_back(c);
+}
+
+
+void ConstraintDatabase::pushCanBePropagatedWatch(ConstraintP c){
+  Assert(!c->d_canBePropagated);
+  c->d_canBePropagated = true;
+  d_watches->d_canBePropagatedWatches.push_back(c);
+}
+
+void ConstraintDatabase::pushAssertionOrderWatch(ConstraintP c, TNode witness){
+  Assert(!c->assertedToTheTheory());
+  c->d_assertionOrder = d_watches->d_assertionOrderWatches.size();
+  c->d_witness = witness;
+  d_watches->d_assertionOrderWatches.push_back(c);
+}
+
+
+void ConstraintDatabase::pushConstraintRule(const ConstraintRule& crp){
+  ConstraintP c = crp.d_constraint;
+  Assert(c->d_crid == ConstraintRuleIdSentinel);
+  Assert(!c->hasProof());
+  c->d_crid = d_watches->d_constraintProofs.size();
+  d_watches->d_constraintProofs.push_back(crp);
+}
+
+ConstraintP ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r){
+  //This must always return a constraint.
+
+  SortedConstraintMap& scm = getVariableSCM(v);
+  pair<SortedConstraintMapIterator, bool> insertAttempt;
+  insertAttempt = scm.insert(make_pair(r, ValueCollection()));
+
+  SortedConstraintMapIterator pos = insertAttempt.first;
+  ValueCollection& vc = pos->second;
+  if(vc.hasConstraintOfType(t)){
+    return vc.getConstraintOfType(t);
+  }else{
+    ConstraintP c = new Constraint(v, t, r, options().smt.produceProofs);
+    ConstraintP negC =
+        Constraint::makeNegation(v, t, r, options().smt.produceProofs);
+
+    SortedConstraintMapIterator negPos;
+    if(t == Equality || t == Disequality){
+      negPos = pos;
+    }else{
+      pair<SortedConstraintMapIterator, bool> negInsertAttempt;
+      negInsertAttempt = scm.insert(make_pair(negC->getValue(), ValueCollection()));
+      Assert(negInsertAttempt.second
+             || !negInsertAttempt.first->second.hasConstraintOfType(
+                 negC->getType()));
+      negPos = negInsertAttempt.first;
+    }
+
+    c->initialize(this, pos, negC);
+    negC->initialize(this, negPos, c);
+
+    vc.add(c);
+    negPos->second.add(negC);
+
+    return c;
+  }
+}
+
+ConstraintP ConstraintDatabase::ensureConstraint(ValueCollection& vc, ConstraintType t){
+  if(vc.hasConstraintOfType(t)){
+    return vc.getConstraintOfType(t);
+  }else{
+    return getConstraint(vc.getVariable(), t, vc.getValue());
+  }
+}
+
+bool ConstraintDatabase::emptyDatabase(const std::vector<PerVariableDatabase>& vec){
+  std::vector<PerVariableDatabase>::const_iterator first = vec.begin();
+  std::vector<PerVariableDatabase>::const_iterator last = vec.end();
+  return std::find_if(first, last, PerVariableDatabase::IsEmpty) == last;
+}
+
+ConstraintDatabase::~ConstraintDatabase(){
+  delete d_watches;
+
+  std::vector<ConstraintP> constraintList;
+
+  while(!d_varDatabases.empty()){
+    PerVariableDatabase* back = d_varDatabases.back();
+
+    SortedConstraintMap& scm = back->d_constraints;
+    SortedConstraintMapIterator i = scm.begin(), i_end = scm.end();
+    for(; i != i_end; ++i){
+      (i->second).push_into(constraintList);
+    }
+    while(!constraintList.empty()){
+      ConstraintP c = constraintList.back();
+      constraintList.pop_back();
+      delete c;
+    }
+    Assert(scm.empty());
+    d_varDatabases.pop_back();
+    delete back;
+  }
+
+  Assert(d_nodetoConstraintMap.empty());
+}
+
+ConstraintDatabase::Statistics::Statistics()
+    : d_unatePropagateCalls(smtStatisticsRegistry().registerInt(
+        "theory::arith::cd::unatePropagateCalls")),
+      d_unatePropagateImplications(smtStatisticsRegistry().registerInt(
+          "theory::arith::cd::unatePropagateImplications"))
+{
+}
+
+void ConstraintDatabase::deleteConstraintAndNegation(ConstraintP c){
+  Assert(c->safeToGarbageCollect());
+  ConstraintP neg = c->getNegation();
+  Assert(neg->safeToGarbageCollect());
+  delete c;
+  delete neg;
+}
+
+void ConstraintDatabase::addVariable(ArithVar v){
+  if(d_reclaimable.isMember(v)){
+    SortedConstraintMap& scm = getVariableSCM(v);
+
+    std::vector<ConstraintP> constraintList;
+
+    for(SortedConstraintMapIterator i = scm.begin(), end = scm.end(); i != end; ++i){
+      (i->second).push_into(constraintList);
+    }
+    while(!constraintList.empty()){
+      ConstraintP c = constraintList.back();
+      constraintList.pop_back();
+      Assert(c->safeToGarbageCollect());
+      delete c;
+    }
+    Assert(scm.empty());
+
+    d_reclaimable.remove(v);
+  }else{
+    Trace("arith::constraint") << "about to fail" << v << " " << d_varDatabases.size() << endl;
+    Assert(v == d_varDatabases.size());
+    d_varDatabases.push_back(new PerVariableDatabase(v));
+  }
+}
+
+void ConstraintDatabase::removeVariable(ArithVar v){
+  Assert(!d_reclaimable.isMember(v));
+  d_reclaimable.add(v);
+}
+
+bool Constraint::safeToGarbageCollect() const{
+  // Do not call during destructor as getNegation() may be Null by this point
+  Assert(getNegation() != NullConstraint);
+  return !contextDependentDataIsSet() && ! getNegation()->contextDependentDataIsSet();
+}
+
+bool Constraint::contextDependentDataIsSet() const{
+  return hasProof() || isSplit() || canBePropagated() || assertedToTheTheory();
+}
+
+TrustNode Constraint::split()
+{
+  Assert(isEquality() || isDisequality());
+
+  bool isEq = isEquality();
+
+  ConstraintP eq = isEq ? this : d_negation;
+  ConstraintP diseq = isEq ? d_negation : this;
+
+  TNode eqNode = eq->getLiteral();
+  Assert(eqNode.getKind() == kind::EQUAL);
+  TNode lhs = eqNode[0];
+  TNode rhs = eqNode[1];
+
+  Node leqNode = NodeBuilder(kind::LEQ) << lhs << rhs;
+  Node ltNode = NodeBuilder(kind::LT) << lhs << rhs;
+  Node gtNode = NodeBuilder(kind::GT) << lhs << rhs;
+  Node geqNode = NodeBuilder(kind::GEQ) << lhs << rhs;
+
+  Node lemma = NodeBuilder(OR) << leqNode << geqNode;
+
+  TrustNode trustedLemma;
+  if (d_database->isProofEnabled())
+  {
+    TypeNode type = lhs.getType();
+    // Farkas proof that this works.
+    auto nm = NodeManager::currentNM();
+    auto nLeqPf = d_database->d_pnm->mkAssume(leqNode.negate());
+    auto gtPf = d_database->d_pnm->mkNode(
+        PfRule::MACRO_SR_PRED_TRANSFORM, {nLeqPf}, {gtNode});
+    auto nGeqPf = d_database->d_pnm->mkAssume(geqNode.negate());
+    auto ltPf = d_database->d_pnm->mkNode(
+        PfRule::MACRO_SR_PRED_TRANSFORM, {nGeqPf}, {ltNode});
+    auto sumPf =
+        d_database->d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
+                                  {gtPf, ltPf},
+                                  {nm->mkConstRealOrInt(type, Rational(-1)),
+                                   nm->mkConstRealOrInt(type, Rational(1))});
+    auto botPf = d_database->d_pnm->mkNode(
+        PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
+    std::vector<Node> a = {leqNode.negate(), geqNode.negate()};
+    auto notAndNotPf = d_database->d_pnm->mkScope(botPf, a);
+    // No need to ensure that the expected node aggrees with `a` because we are
+    // not providing an expected node.
+    auto orNotNotPf =
+        d_database->d_pnm->mkNode(PfRule::NOT_AND, {notAndNotPf}, {});
+    auto orPf = d_database->d_pnm->mkNode(
+        PfRule::MACRO_SR_PRED_TRANSFORM, {orNotNotPf}, {lemma});
+    trustedLemma = d_database->d_pfGen->mkTrustNode(lemma, orPf);
+  }
+  else
+  {
+    trustedLemma = TrustNode::mkTrustLemma(lemma);
+  }
+
+  eq->d_database->pushSplitWatch(eq);
+  diseq->d_database->pushSplitWatch(diseq);
+
+  return trustedLemma;
+}
+
+bool ConstraintDatabase::hasLiteral(TNode literal) const {
+  return lookup(literal) != NullConstraint;
+}
+
+ConstraintP ConstraintDatabase::addLiteral(TNode literal){
+  Assert(!hasLiteral(literal));
+  bool isNot = (literal.getKind() == NOT);
+  Node atomNode = (isNot ? literal[0] : literal);
+  Node negationNode  = atomNode.notNode();
+
+  Assert(!hasLiteral(atomNode));
+  Assert(!hasLiteral(negationNode));
+  Comparison posCmp = Comparison::parseNormalForm(atomNode);
+
+  ConstraintType posType = Constraint::constraintTypeOfComparison(posCmp);
+
+  Polynomial nvp = posCmp.normalizedVariablePart();
+  ArithVar v = d_avariables.asArithVar(nvp.getNode());
+
+  DeltaRational posDR = posCmp.normalizedDeltaRational();
+
+  ConstraintP posC =
+      new Constraint(v, posType, posDR, options().smt.produceProofs);
+
+  Trace("arith::constraint") << "addliteral( literal ->" << literal << ")" << endl;
+  Trace("arith::constraint") << "addliteral( posC ->" << posC << ")" << endl;
+
+  SortedConstraintMap& scm = getVariableSCM(posC->getVariable());
+  pair<SortedConstraintMapIterator, bool> insertAttempt;
+  insertAttempt = scm.insert(make_pair(posC->getValue(), ValueCollection()));
+
+  SortedConstraintMapIterator posI = insertAttempt.first;
+  // If the attempt succeeds, i points to a new empty ValueCollection
+  // If the attempt fails, i points to a pre-existing ValueCollection
+
+  if(posI->second.hasConstraintOfType(posC->getType())){
+    //This is the situation where the ConstraintP exists, but
+    //the literal has not been  associated with it.
+    ConstraintP hit = posI->second.getConstraintOfType(posC->getType());
+    Trace("arith::constraint") << "hit " << hit << endl;
+    Trace("arith::constraint") << "posC " << posC << endl;
+
+    delete posC;
+
+    hit->setLiteral(atomNode);
+    hit->getNegation()->setLiteral(negationNode);
+    return isNot ? hit->getNegation(): hit;
+  }else{
+    Comparison negCmp = Comparison::parseNormalForm(negationNode);
+
+    ConstraintType negType = Constraint::constraintTypeOfComparison(negCmp);
+    DeltaRational negDR = negCmp.normalizedDeltaRational();
+
+    ConstraintP negC =
+        new Constraint(v, negType, negDR, options().smt.produceProofs);
+
+    SortedConstraintMapIterator negI;
+
+    if(posC->isEquality()){
+      negI = posI;
+    }else{
+      Assert(posC->isLowerBound() || posC->isUpperBound());
+
+      pair<SortedConstraintMapIterator, bool> negInsertAttempt;
+      negInsertAttempt = scm.insert(make_pair(negC->getValue(), ValueCollection()));
+
+      Trace("nf::tmp") << "sdhjfgdhjkldfgljkhdfg" << endl;
+      Trace("nf::tmp") << negC << endl;
+      Trace("nf::tmp") << negC->getValue() << endl;
+
+      //This should always succeed as the DeltaRational for the negation is unique!
+      Assert(negInsertAttempt.second);
+
+      negI = negInsertAttempt.first;
+    }
+
+    (posI->second).add(posC);
+    (negI->second).add(negC);
+
+    posC->initialize(this, posI, negC);
+    negC->initialize(this, negI, posC);
+
+    posC->setLiteral(atomNode);
+    negC->setLiteral(negationNode);
+
+    return isNot ? negC : posC;
+  }
+}
+
+
+ConstraintP ConstraintDatabase::lookup(TNode literal) const{
+  NodetoConstraintMap::const_iterator iter = d_nodetoConstraintMap.find(literal);
+  if(iter == d_nodetoConstraintMap.end()){
+    return NullConstraint;
+  }else{
+    return iter->second;
+  }
+}
+
+void Constraint::setAssumption(bool nowInConflict){
+  Trace("constraints::pf") << "setAssumption(" << this << ")" << std::endl;
+  Assert(!hasProof());
+  Assert(negationHasProof() == nowInConflict);
+  Assert(hasLiteral());
+  Assert(assertedToTheTheory());
+
+  d_database->pushConstraintRule(ConstraintRule(this, AssumeAP));
+
+  Assert(inConflict() == nowInConflict);
+  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+    Trace("constraint::conflictCommit") << "inConflict@setAssumption " << this << std::endl;
+  }
+}
+
+void Constraint::tryToPropagate(){
+  Assert(hasProof());
+  Assert(!isAssumption());
+  Assert(!isInternalAssumption());
+
+  if(canBePropagated() && !assertedToTheTheory() && !isAssumption() && !isInternalAssumption()){
+    propagate();
+  }
+}
+
+void Constraint::propagate(){
+  Assert(hasProof());
+  Assert(canBePropagated());
+  Assert(!assertedToTheTheory());
+  Assert(!isAssumption());
+  Assert(!isInternalAssumption());
+
+  d_database->d_toPropagate.push(this);
+}
+
+
+/*
+ * Example:
+ *    x <= a and a < b
+ * |= x <= b
+ * ---
+ *  1*(x <= a) + (-1)*(x > b) => (0 <= a-b)
+ */
+void Constraint::impliedByUnate(ConstraintCP imp, bool nowInConflict){
+  Trace("constraints::pf") << "impliedByUnate(" << this << ", " << *imp << ")" << std::endl;
+  Assert(!hasProof());
+  Assert(imp->hasProof());
+  Assert(negationHasProof() == nowInConflict);
+
+  d_database->d_antecedents.push_back(NullConstraint);
+  d_database->d_antecedents.push_back(imp);
+
+  AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
+
+  RationalVectorP coeffs;
+  if (d_produceProofs)
+  {
+    std::pair<int, int> sgns = unateFarkasSigns(getNegation(), imp);
+
+    Rational first(sgns.first);
+    Rational second(sgns.second);
+
+    coeffs = new RationalVector();
+    coeffs->push_back(first);
+    coeffs->push_back(second);
+  }
+  else
+  {
+    coeffs = RationalVectorPSentinel;
+  }
+  // no need to delete coeffs the memory is owned by ConstraintRule
+  d_database->pushConstraintRule(ConstraintRule(this, FarkasAP, antecedentEnd, coeffs));
+
+  Assert(inConflict() == nowInConflict);
+  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+    Trace("constraint::conflictCommit") << "inConflict@impliedByUnate " << this << std::endl;
+  }
+
+  if(TraceIsOn("constraints::wffp") && !wellFormedFarkasProof()){
+    getConstraintRule().print(Trace("constraints::wffp"), d_produceProofs);
+  }
+  Assert(wellFormedFarkasProof());
+}
+
+void Constraint::impliedByTrichotomy(ConstraintCP a, ConstraintCP b, bool nowInConflict){
+  Trace("constraints::pf") << "impliedByTrichotomy(" << this << ", " << *a << ", ";
+  Trace("constraints::pf") << *b << ")" << std::endl;
+  Assert(!hasProof());
+  Assert(negationHasProof() == nowInConflict);
+  Assert(a->hasProof());
+  Assert(b->hasProof());
+
+  d_database->d_antecedents.push_back(NullConstraint);
+  d_database->d_antecedents.push_back(a);
+  d_database->d_antecedents.push_back(b);
+
+  AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
+  d_database->pushConstraintRule(ConstraintRule(this, TrichotomyAP, antecedentEnd));
+
+  Assert(inConflict() == nowInConflict);
+  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+    Trace("constraint::conflictCommit") << "inConflict@impliedByTrichotomy " << this << std::endl;
+  }
+}
+
+
+bool Constraint::allHaveProof(const ConstraintCPVec& b){
+  for(ConstraintCPVec::const_iterator i=b.begin(), i_end=b.end(); i != i_end; ++i){
+    ConstraintCP cp = *i;
+    if(! (cp->hasProof())){ return false; }
+  }
+  return true;
+}
+
+void Constraint::impliedByIntTighten(ConstraintCP a, bool nowInConflict){
+  Trace("constraints::pf") << "impliedByIntTighten(" << this << ", " << *a << ")" << std::endl;
+  Assert(!hasProof());
+  Assert(negationHasProof() == nowInConflict);
+  Assert(a->hasProof());
+  Trace("pf::arith") << "impliedByIntTighten(" << this << ", " << a << ")"
+                     << std::endl;
+
+  d_database->d_antecedents.push_back(NullConstraint);
+  d_database->d_antecedents.push_back(a);
+  AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
+  d_database->pushConstraintRule(ConstraintRule(this, IntTightenAP, antecedentEnd));
+
+  Assert(inConflict() == nowInConflict);
+  if(inConflict()){
+    Trace("constraint::conflictCommit") << "inConflict impliedByIntTighten" << this << std::endl;
+  }
+}
+
+void Constraint::impliedByIntHole(ConstraintCP a, bool nowInConflict){
+  Trace("constraints::pf") << "impliedByIntHole(" << this << ", " << *a << ")" << std::endl;
+  Assert(!hasProof());
+  Assert(negationHasProof() == nowInConflict);
+  Assert(a->hasProof());
+  Trace("pf::arith") << "impliedByIntHole(" << this << ", " << a << ")"
+                     << std::endl;
+
+  d_database->d_antecedents.push_back(NullConstraint);
+  d_database->d_antecedents.push_back(a);
+  AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
+  d_database->pushConstraintRule(ConstraintRule(this, IntHoleAP, antecedentEnd));
+
+  Assert(inConflict() == nowInConflict);
+  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+    Trace("constraint::conflictCommit") << "inConflict impliedByIntHole" << this << std::endl;
+  }
+}
+
+void Constraint::impliedByIntHole(const ConstraintCPVec& b, bool nowInConflict){
+  Trace("constraints::pf") << "impliedByIntHole(" << this;
+  if (TraceIsOn("constraints::pf")) {
+    for (const ConstraintCP& p : b)
+    {
+      Trace("constraints::pf") << ", " << p;
+    }
+  }
+  Trace("constraints::pf") << ")" << std::endl;
+
+  Assert(!hasProof());
+  Assert(negationHasProof() == nowInConflict);
+  Assert(allHaveProof(b));
+
+  CDConstraintList& antecedents = d_database->d_antecedents;
+  antecedents.push_back(NullConstraint);
+  for(ConstraintCPVec::const_iterator i=b.begin(), i_end=b.end(); i != i_end; ++i){
+    antecedents.push_back(*i);
+  }
+  AntecedentId antecedentEnd = antecedents.size() - 1;
+
+  d_database->pushConstraintRule(ConstraintRule(this, IntHoleAP, antecedentEnd));
+
+  Assert(inConflict() == nowInConflict);
+  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+    Trace("constraint::conflictCommit") << "inConflict@impliedByIntHole[vec] " << this << std::endl;
+  }
+}
+
+/*
+ * If proofs are off, coeffs == RationalVectorSentinal.
+ * If proofs are on,
+ *   coeffs != RationalVectorSentinal,
+ *   coeffs->size() = a.size() + 1,
+ *   for i in [0,a.size) : coeff[i] corresponds to a[i], and
+ *   coeff.back() corresponds to the current constraint.
+ */
+void Constraint::impliedByFarkas(const ConstraintCPVec& a, RationalVectorCP coeffs, bool nowInConflict){
+  Trace("constraints::pf") << "impliedByFarkas(" << this;
+  if (TraceIsOn("constraints::pf")) {
+    for (const ConstraintCP& p : a)
+    {
+      Trace("constraints::pf") << ", " << p;
+    }
+  }
+  Trace("constraints::pf") << ", <coeffs>";
+  Trace("constraints::pf") << ")" << std::endl;
+  Assert(!hasProof());
+  Assert(negationHasProof() == nowInConflict);
+  Assert(allHaveProof(a));
+
+  Assert(d_produceProofs == (coeffs != RationalVectorCPSentinel));
+  Assert(!d_produceProofs || coeffs->size() == a.size() + 1);
+
+  Assert(a.size() >= 1);
+
+  d_database->d_antecedents.push_back(NullConstraint);
+  for(ConstraintCPVec::const_iterator i = a.begin(), end = a.end(); i != end; ++i){
+    ConstraintCP c_i = *i;
+    Assert(c_i->hasProof());
+    d_database->d_antecedents.push_back(c_i);
+  }
+  AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
+
+  RationalVectorCP coeffsCopy;
+  if (d_produceProofs)
+  {
+    Assert(coeffs != RationalVectorCPSentinel);
+    coeffsCopy = new RationalVector(*coeffs);
+  }
+  else
+  {
+    coeffsCopy = RationalVectorCPSentinel;
+  }
+  d_database->pushConstraintRule(ConstraintRule(this, FarkasAP, antecedentEnd, coeffsCopy));
+
+  Assert(inConflict() == nowInConflict);
+  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+    Trace("constraint::conflictCommit") << "inConflict@impliedByFarkas " << this << std::endl;
+  }
+  if(TraceIsOn("constraints::wffp") && !wellFormedFarkasProof()){
+    getConstraintRule().print(Trace("constraints::wffp"), d_produceProofs);
+  }
+  Assert(wellFormedFarkasProof());
+}
+
+
+void Constraint::setInternalAssumption(bool nowInConflict){
+  Trace("constraints::pf") << "setInternalAssumption(" << this;
+  Trace("constraints::pf") << ")" << std::endl;
+  Assert(!hasProof());
+  Assert(negationHasProof() == nowInConflict);
+  Assert(!assertedToTheTheory());
+
+  d_database->pushConstraintRule(ConstraintRule(this, InternalAssumeAP));
+
+  Assert(inConflict() == nowInConflict);
+  if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+    Trace("constraint::conflictCommit") << "inConflict@setInternalAssumption " << this << std::endl;
+  }
+}
+
+
+void Constraint::setEqualityEngineProof(){
+  Trace("constraints::pf") << "setEqualityEngineProof(" << this;
+  Trace("constraints::pf") << ")" << std::endl;
+  Assert(truthIsUnknown());
+  Assert(hasLiteral());
+  d_database->pushConstraintRule(ConstraintRule(this, EqualityEngineAP));
+}
+
+
+SortedConstraintMap& Constraint::constraintSet() const{
+  Assert(d_database->variableDatabaseIsSetup(d_variable));
+  return (d_database->d_varDatabases[d_variable])->d_constraints;
+}
+
+bool Constraint::antecentListIsEmpty() const{
+  Assert(hasProof());
+  return d_database->d_antecedents[getEndAntecedent()] == NullConstraint;
+}
+
+bool Constraint::antecedentListLengthIsOne() const {
+  Assert(hasProof());
+  return !antecentListIsEmpty() &&
+    d_database->d_antecedents[getEndAntecedent()-1] == NullConstraint;
+}
+
+Node Constraint::externalImplication(const ConstraintCPVec& b) const{
+  Assert(hasLiteral());
+  Node antecedent = externalExplainByAssertions(b);
+  Node implied = getLiteral();
+  return antecedent.impNode(implied);
+}
+
+
+Node Constraint::externalExplainByAssertions(const ConstraintCPVec& b){
+  return externalExplain(b, AssertionOrderSentinel);
+}
+
+TrustNode Constraint::externalExplainForPropagation(TNode lit) const
+{
+  Assert(hasProof());
+  Assert(!isAssumption());
+  Assert(!isInternalAssumption());
+  NodeBuilder nb(Kind::AND);
+  auto pfFromAssumptions = externalExplain(nb, d_assertionOrder);
+  Node n = mkAndFromBuilder(nb);
+  if (d_database->isProofEnabled())
+  {
+    std::vector<Node> assumptions;
+    if (n.getKind() == Kind::AND)
+    {
+      assumptions.insert(assumptions.end(), n.begin(), n.end());
+    }
+    else
+    {
+      assumptions.push_back(n);
+    }
+    if (getProofLiteral() != lit)
+    {
+      pfFromAssumptions = d_database->d_pnm->mkNode(
+          PfRule::MACRO_SR_PRED_TRANSFORM, {pfFromAssumptions}, {lit});
+    }
+    auto pf = d_database->d_pnm->mkScope(pfFromAssumptions, assumptions);
+    return d_database->d_pfGen->mkTrustedPropagation(
+        lit, NodeManager::currentNM()->mkAnd(assumptions), pf);
+  }
+  else
+  {
+    return TrustNode::mkTrustPropExp(lit, n);
+  }
+}
+
+TrustNode Constraint::externalExplainConflict() const
+{
+  Trace("pf::arith::explain") << this << std::endl;
+  Assert(inConflict());
+  NodeBuilder nb(kind::AND);
+  auto pf1 = externalExplainByAssertions(nb);
+  auto not2 = getNegation()->getProofLiteral().negate();
+  auto pf2 = getNegation()->externalExplainByAssertions(nb);
+  Node n = mkAndFromBuilder(nb);
+  if (d_database->isProofEnabled())
+  {
+    auto pfNot2 = d_database->d_pnm->mkNode(
+        PfRule::MACRO_SR_PRED_TRANSFORM, {pf1}, {not2});
+    std::vector<Node> lits;
+    if (n.getKind() == Kind::AND)
+    {
+      lits.insert(lits.end(), n.begin(), n.end());
+    }
+    else
+    {
+      lits.push_back(n);
+    }
+    if (TraceIsOn("arith::pf::externalExplainConflict"))
+    {
+      Trace("arith::pf::externalExplainConflict") << "Lits:" << std::endl;
+      for (const auto& l : lits)
+      {
+        Trace("arith::pf::externalExplainConflict") << "  : " << l << std::endl;
+      }
+    }
+    std::vector<Node> contraLits = {getProofLiteral(),
+                                    getNegation()->getProofLiteral()};
+    auto bot =
+        not2.getKind() == Kind::NOT
+            ? d_database->d_pnm->mkNode(PfRule::CONTRA, {pf2, pfNot2}, {})
+            : d_database->d_pnm->mkNode(PfRule::CONTRA, {pfNot2, pf2}, {});
+    if (TraceIsOn("arith::pf::tree"))
+    {
+      Trace("arith::pf::tree") << *this << std::endl;
+      Trace("arith::pf::tree") << *getNegation() << std::endl;
+      Trace("arith::pf::tree") << "\n\nTree:\n";
+      printProofTree(Trace("arith::pf::tree"));
+      getNegation()->printProofTree(Trace("arith::pf::tree"));
+    }
+    auto confPf = d_database->d_pnm->mkScope(bot, lits);
+    return d_database->d_pfGen->mkTrustNode(
+        NodeManager::currentNM()->mkAnd(lits), confPf, true);
+  }
+  else
+  {
+    return TrustNode::mkTrustConflict(n);
+  }
+}
+
+struct ConstraintCPHash {
+  /* Todo replace with an id */
+  size_t operator()(ConstraintCP c) const{
+    Assert(sizeof(ConstraintCP) > 0);
+    return ((size_t)c)/sizeof(ConstraintCP);
+  }
+};
+
+void Constraint::assertionFringe(ConstraintCPVec& v){
+  unordered_set<ConstraintCP, ConstraintCPHash> visited;
+  size_t writePos = 0;
+
+  if(!v.empty()){
+    const ConstraintDatabase* db = v.back()->d_database;
+    const CDConstraintList& antecedents = db->d_antecedents;
+    for(size_t i = 0; i < v.size(); ++i){
+      ConstraintCP vi = v[i];
+      if(visited.find(vi) == visited.end()){
+        Assert(vi->hasProof());
+        visited.insert(vi);
+        if(vi->onFringe()){
+          v[writePos] = vi;
+          writePos++;
+        }else{
+          Assert(vi->hasTrichotomyProof() || vi->hasFarkasProof()
+                 || vi->hasIntHoleProof() || vi->hasIntTightenProof());
+          AntecedentId p = vi->getEndAntecedent();
+
+          ConstraintCP antecedent = antecedents[p];
+          while(antecedent != NullConstraint){
+            v.push_back(antecedent);
+            --p;
+            antecedent = antecedents[p];
+          }
+        }
+      }
+    }
+    v.resize(writePos);
+  }
+}
+
+void Constraint::assertionFringe(ConstraintCPVec& o, const ConstraintCPVec& i){
+  o.insert(o.end(), i.begin(), i.end());
+  assertionFringe(o);
+}
+
+Node Constraint::externalExplain(const ConstraintCPVec& v, AssertionOrder order){
+  NodeBuilder nb(kind::AND);
+  ConstraintCPVec::const_iterator i, end;
+  for(i = v.begin(), end = v.end(); i != end; ++i){
+    ConstraintCP v_i = *i;
+    v_i->externalExplain(nb, order);
+  }
+  return mkAndFromBuilder(nb);
+}
+
+std::shared_ptr<ProofNode> Constraint::externalExplain(
+    NodeBuilder& nb, AssertionOrder order) const
+{
+  if (TraceIsOn("pf::arith::explain"))
+  {
+    this->printProofTree(Trace("arith::pf::tree"));
+    Trace("pf::arith::explain") << "Explaining: " << this << " with rule ";
+    getConstraintRule().print(Trace("pf::arith::explain"), d_produceProofs);
+    Trace("pf::arith::explain") << std::endl;
+  }
+  Assert(hasProof());
+  Assert(!isAssumption() || assertedToTheTheory());
+  Assert(!isInternalAssumption());
+  std::shared_ptr<ProofNode> pf{};
+
+  ProofNodeManager* pnm = d_database->d_pnm;
+
+  if (assertedBefore(order))
+  {
+    Trace("pf::arith::explain") << "  already asserted" << std::endl;
+    nb << getWitness();
+    if (d_database->isProofEnabled())
+    {
+      pf = pnm->mkAssume(getWitness());
+      // If the witness and literal differ, prove the difference through a
+      // rewrite.
+      if (getWitness() != getProofLiteral())
+      {
+        pf = pnm->mkNode(
+            PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {getProofLiteral()});
+      }
+    }
+  }
+  else if (hasEqualityEngineProof())
+  {
+    Trace("pf::arith::explain") << "  going to ee:" << std::endl;
+    TrustNode exp = d_database->eeExplain(this);
+    if (d_database->isProofEnabled())
+    {
+      Assert(exp.getProven().getKind() == Kind::IMPLIES);
+      std::vector<std::shared_ptr<ProofNode>> hypotheses;
+      hypotheses.push_back(exp.getGenerator()->getProofFor(exp.getProven()));
+      if (exp.getNode().getKind() == Kind::AND)
+      {
+        for (const auto& h : exp.getNode())
+        {
+          hypotheses.push_back(
+              pnm->mkNode(PfRule::TRUE_INTRO, {pnm->mkAssume(h)}, {}));
+        }
+      }
+      else
+      {
+        hypotheses.push_back(pnm->mkNode(
+            PfRule::TRUE_INTRO, {pnm->mkAssume(exp.getNode())}, {}));
+      }
+      pf = pnm->mkNode(
+          PfRule::MACRO_SR_PRED_TRANSFORM, {hypotheses}, {getProofLiteral()});
+    }
+    Trace("pf::arith::explain")
+        << "    explanation: " << exp.getNode() << std::endl;
+    if (exp.getNode().getKind() == Kind::AND)
+    {
+      nb.append(exp.getNode().begin(), exp.getNode().end());
+    }
+    else
+    {
+      nb << exp.getNode();
+    }
+  }
+  else
+  {
+    Trace("pf::arith::explain") << "  recursion!" << std::endl;
+    Assert(!isAssumption());
+    AntecedentId p = getEndAntecedent();
+    ConstraintCP antecedent = d_database->d_antecedents[p];
+    std::vector<std::shared_ptr<ProofNode>> children;
+
+    while (antecedent != NullConstraint)
+    {
+      Trace("pf::arith::explain") << "Explain " << antecedent << std::endl;
+      auto pn = antecedent->externalExplain(nb, order);
+      if (d_database->isProofEnabled())
+      {
+        children.push_back(pn);
+      }
+      --p;
+      antecedent = d_database->d_antecedents[p];
+    }
+
+    if (d_database->isProofEnabled())
+    {
+      switch (getProofType())
+      {
+        case ArithProofType::AssumeAP:
+        case ArithProofType::EqualityEngineAP:
+        {
+          Unreachable() << "These should be handled above";
+          break;
+        }
+        case ArithProofType::FarkasAP:
+        {
+          // Per docs in constraint.h,
+          // the 0th farkas coefficient is for the negation of the deduced
+          // constraint the 1st corresponds to the last antecedent the nth
+          // corresponds to the first antecedent Then, the farkas coefficients
+          // and the antecedents are in the same order.
+
+          // Enumerate child proofs (negation included) in d_farkasCoefficients
+          // order
+          Node plit = getNegation()->getProofLiteral();
+          std::vector<std::shared_ptr<ProofNode>> farkasChildren;
+          farkasChildren.push_back(pnm->mkAssume(plit));
+          farkasChildren.insert(
+              farkasChildren.end(), children.rbegin(), children.rend());
+
+          NodeManager* nm = NodeManager::currentNM();
+
+          // Enumerate d_farkasCoefficients as nodes.
+          std::vector<Node> farkasCoeffs;
+          TypeNode type = plit[0].getType();
+          for (Rational r : *getFarkasCoefficients())
+          {
+            farkasCoeffs.push_back(nm->mkConstRealOrInt(type, Rational(r)));
+          }
+
+          // Apply the scaled-sum rule.
+          std::shared_ptr<ProofNode> sumPf = pnm->mkNode(
+              PfRule::MACRO_ARITH_SCALE_SUM_UB, farkasChildren, farkasCoeffs);
+
+          // Provable rewrite the result
+          auto botPf = pnm->mkNode(
+              PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
+
+          // Scope out the negated constraint, yielding a proof of the
+          // constraint.
+          std::vector<Node> assump{plit};
+          auto maybeDoubleNotPf = pnm->mkScope(botPf, assump, false);
+
+          // No need to ensure that the expected node aggrees with `assump`
+          // because we are not providing an expected node.
+          //
+          // Prove that this is the literal (may need to clean a double-not)
+          pf = pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
+                           {maybeDoubleNotPf},
+                           {getProofLiteral()});
+
+          break;
+        }
+        case ArithProofType::IntTightenAP:
+        {
+          if (isUpperBound())
+          {
+            pf = pnm->mkNode(
+                PfRule::INT_TIGHT_UB, children, {}, getProofLiteral());
+          }
+          else if (isLowerBound())
+          {
+            pf = pnm->mkNode(
+                PfRule::INT_TIGHT_LB, children, {}, getProofLiteral());
+          }
+          else
+          {
+            Unreachable();
+          }
+          break;
+        }
+        case ArithProofType::IntHoleAP:
+        {
+          Node t =
+              builtin::BuiltinProofRuleChecker::mkTheoryIdNode(THEORY_ARITH);
+          pf = pnm->mkNode(PfRule::THEORY_INFERENCE,
+                           children,
+                           {getProofLiteral(), t},
+                           getProofLiteral());
+          break;
+        }
+        case ArithProofType::TrichotomyAP:
+        {
+          pf = pnm->mkNode(PfRule::ARITH_TRICHOTOMY,
+                           children,
+                           {getProofLiteral()},
+                           getProofLiteral());
+          break;
+        }
+        case ArithProofType::InternalAssumeAP:
+        case ArithProofType::NoAP:
+        default:
+        {
+          Unreachable() << getProofType()
+                        << " should not be visible in explanation";
+          break;
+        }
+      }
+    }
+  }
+  return pf;
+}
+
+Node Constraint::externalExplainByAssertions(ConstraintCP a, ConstraintCP b){
+  NodeBuilder nb(kind::AND);
+  a->externalExplainByAssertions(nb);
+  b->externalExplainByAssertions(nb);
+  return nb;
+}
+
+Node Constraint::externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c){
+  NodeBuilder nb(kind::AND);
+  a->externalExplainByAssertions(nb);
+  b->externalExplainByAssertions(nb);
+  c->externalExplainByAssertions(nb);
+  return nb;
+}
+
+ConstraintP Constraint::getStrictlyWeakerLowerBound(bool hasLiteral, bool asserted) const {
+  Assert(initialized());
+  Assert(!asserted || hasLiteral);
+
+  SortedConstraintMapConstIterator i = d_variablePosition;
+  const SortedConstraintMap& scm = constraintSet();
+  SortedConstraintMapConstIterator i_begin = scm.begin();
+  while(i != i_begin){
+    --i;
+    const ValueCollection& vc = i->second;
+    if(vc.hasLowerBound()){
+      ConstraintP weaker = vc.getLowerBound();
+
+      // asserted -> hasLiteral
+      // hasLiteral -> weaker->hasLiteral()
+      // asserted -> weaker->assertedToTheTheory()
+      if((!hasLiteral || (weaker->hasLiteral())) &&
+         (!asserted || ( weaker->assertedToTheTheory()))){
+        return weaker;
+      }
+    }
+  }
+  return NullConstraint;
+}
+
+ConstraintP Constraint::getStrictlyWeakerUpperBound(bool hasLiteral, bool asserted) const {
+  SortedConstraintMapConstIterator i = d_variablePosition;
+  const SortedConstraintMap& scm = constraintSet();
+  SortedConstraintMapConstIterator i_end = scm.end();
+
+  ++i;
+  for(; i != i_end; ++i){
+    const ValueCollection& vc = i->second;
+    if(vc.hasUpperBound()){
+      ConstraintP weaker = vc.getUpperBound();
+      if((!hasLiteral || (weaker->hasLiteral())) &&
+         (!asserted || ( weaker->assertedToTheTheory()))){
+        return weaker;
+      }
+    }
+  }
+
+  return NullConstraint;
+}
+
+ConstraintP ConstraintDatabase::getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const {
+  Assert(variableDatabaseIsSetup(v));
+  Assert(t == UpperBound || t == LowerBound);
+
+  SortedConstraintMap& scm = getVariableSCM(v);
+  if(t == UpperBound){
+    SortedConstraintMapConstIterator i = scm.lower_bound(r);
+    SortedConstraintMapConstIterator i_end = scm.end();
+    Assert(i == i_end || r <= i->first);
+    for(; i != i_end; i++){
+      Assert(r <= i->first);
+      const ValueCollection& vc = i->second;
+      if(vc.hasUpperBound()){
+        return vc.getUpperBound();
+      }
+    }
+    return NullConstraint;
+  }else{
+    Assert(t == LowerBound);
+    if(scm.empty()){
+      return NullConstraint;
+    }else{
+      SortedConstraintMapConstIterator i = scm.lower_bound(r);
+      SortedConstraintMapConstIterator i_begin = scm.begin();
+      SortedConstraintMapConstIterator i_end = scm.end();
+      Assert(i == i_end || r <= i->first);
+
+      int fdj = 0;
+
+      if(i == i_end){
+        --i;
+        Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
+      }else if( (i->first) > r){
+        if(i == i_begin){
+          return NullConstraint;
+        }else{
+          --i;
+          Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
+        }
+      }
+
+      do{
+        Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
+        Assert(r >= i->first);
+        const ValueCollection& vc = i->second;
+
+        if(vc.hasLowerBound()){
+          return vc.getLowerBound();
+        }
+
+        if(i == i_begin){
+          break;
+        }else{
+          --i;
+        }
+      }while(true);
+      return NullConstraint;
+    }
+  }
+}
+TrustNode ConstraintDatabase::eeExplain(const Constraint* const c) const
+{
+  Assert(c->hasLiteral());
+  return d_congruenceManager.explain(c->getLiteral());
+}
+
+void ConstraintDatabase::eeExplain(ConstraintCP c, NodeBuilder& nb) const
+{
+  Assert(c->hasLiteral());
+  // NOTE: this is not a recommended method since it ignores proofs
+  d_congruenceManager.explain(c->getLiteral(), nb);
+}
+
+bool ConstraintDatabase::variableDatabaseIsSetup(ArithVar v) const {
+  return v < d_varDatabases.size();
+}
+
+ConstraintDatabase::Watches::Watches(context::Context* satContext,
+                                     context::Context* userContext)
+    : d_constraintProofs(satContext),
+      d_canBePropagatedWatches(satContext),
+      d_assertionOrderWatches(satContext),
+      d_splitWatches(userContext)
+{}
+
+
+void Constraint::setLiteral(Node n) {
+  Trace("arith::constraint") << "Mapping " << *this << " to " << n << std::endl;
+  Assert(Comparison::isNormalAtom(n));
+  Assert(!hasLiteral());
+  Assert(sanityChecking(n));
+  d_literal = n;
+  NodetoConstraintMap& map = d_database->d_nodetoConstraintMap;
+  Assert(map.find(n) == map.end());
+  map.insert(make_pair(d_literal, this));
+}
+
+Node Constraint::getProofLiteral() const
+{
+  Assert(d_database != nullptr);
+  Assert(d_database->d_avariables.hasNode(d_variable));
+  Node varPart = d_database->d_avariables.asNode(d_variable);
+  Kind cmp;
+  bool neg = false;
+  switch (d_type)
+  {
+    case ConstraintType::UpperBound:
+    {
+      if (d_value.infinitesimalIsZero())
+      {
+        cmp = Kind::LEQ;
+      }
+      else
+      {
+        cmp = Kind::LT;
+      }
+      break;
+    }
+    case ConstraintType::LowerBound:
+    {
+      if (d_value.infinitesimalIsZero())
+      {
+        cmp = Kind::GEQ;
+      }
+      else
+      {
+        cmp = Kind::GT;
+      }
+      break;
+    }
+    case ConstraintType::Equality:
+    {
+      cmp = Kind::EQUAL;
+      break;
+    }
+    case ConstraintType::Disequality:
+    {
+      cmp = Kind::EQUAL;
+      neg = true;
+      break;
+    }
+    default: Unreachable() << d_type;
+  }
+  NodeManager* nm = NodeManager::currentNM();
+  Node constPart = nm->mkConstRealOrInt(
+      varPart.getType(), Rational(d_value.getNoninfinitesimalPart()));
+  Node posLit = nm->mkNode(cmp, varPart, constPart);
+  return neg ? posLit.negate() : posLit;
+}
+
+void ConstraintDatabase::proveOr(std::vector<TrustNode>& out,
+                                 ConstraintP a,
+                                 ConstraintP b,
+                                 bool negateSecond) const
+{
+  Node la = a->getLiteral();
+  Node lb = b->getLiteral();
+  Node orN = (la < lb) ? la.orNode(lb) : lb.orNode(la);
+  if (isProofEnabled())
+  {
+    Assert(b->getNegation()->getType() != ConstraintType::Disequality);
+    auto nm = NodeManager::currentNM();
+    Node alit = a->getNegation()->getProofLiteral();
+    TypeNode type = alit[0].getType();
+    auto pf_neg_la = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
+                                   {d_pnm->mkAssume(la.negate())},
+                                   {alit});
+    Node blit = b->getNegation()->getProofLiteral();
+    auto pf_neg_lb = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
+                                   {d_pnm->mkAssume(lb.negate())},
+                                   {blit});
+    int sndSign = negateSecond ? -1 : 1;
+    auto bot_pf = d_pnm->mkNode(
+        PfRule::MACRO_SR_PRED_TRANSFORM,
+        {d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
+                       {pf_neg_la, pf_neg_lb},
+                       {nm->mkConstRealOrInt(type, Rational(-1 * sndSign)),
+                        nm->mkConstRealOrInt(type, Rational(sndSign))})},
+        {nm->mkConst(false)});
+    std::vector<Node> as;
+    std::transform(orN.begin(), orN.end(), std::back_inserter(as), [](Node n) {
+      return n.negate();
+    });
+    // No need to ensure that the expected node aggrees with `as` because we
+    // are not providing an expected node.
+    auto pf = d_pnm->mkNode(
+        PfRule::MACRO_SR_PRED_TRANSFORM,
+        {d_pnm->mkNode(PfRule::NOT_AND, {d_pnm->mkScope(bot_pf, as)}, {})},
+        {orN});
+    out.push_back(d_pfGen->mkTrustNode(orN, pf));
+  }
+  else
+  {
+    out.push_back(TrustNode::mkTrustLemma(orN));
+  }
+}
+
+void ConstraintDatabase::implies(std::vector<TrustNode>& out,
+                                 ConstraintP a,
+                                 ConstraintP b) const
+{
+  Node la = a->getLiteral();
+  Node lb = b->getLiteral();
+
+  Node neg_la = (la.getKind() == kind::NOT)? la[0] : la.notNode();
+
+  Assert(lb != neg_la);
+  Assert(b->getNegation()->getType() == ConstraintType::LowerBound
+         || b->getNegation()->getType() == ConstraintType::UpperBound);
+  proveOr(out,
+          a->getNegation(),
+          b,
+          b->getNegation()->getType() == ConstraintType::LowerBound);
+}
+
+void ConstraintDatabase::mutuallyExclusive(std::vector<TrustNode>& out,
+                                           ConstraintP a,
+                                           ConstraintP b) const
+{
+  Node la = a->getLiteral();
+  Node lb = b->getLiteral();
+
+  Node neg_la = la.negate();
+  Node neg_lb = lb.negate();
+  proveOr(out, a->getNegation(), b->getNegation(), true);
+}
+
+void ConstraintDatabase::outputUnateInequalityLemmas(
+    std::vector<TrustNode>& out, ArithVar v) const
+{
+  SortedConstraintMap& scm = getVariableSCM(v);
+  SortedConstraintMapConstIterator scm_iter = scm.begin();
+  SortedConstraintMapConstIterator scm_end = scm.end();
+  ConstraintP prev = NullConstraint;
+  //get transitive unates
+  //Only lower bounds or upperbounds should be done.
+  for(; scm_iter != scm_end; ++scm_iter){
+    const ValueCollection& vc = scm_iter->second;
+    if(vc.hasUpperBound()){
+      ConstraintP ub = vc.getUpperBound();
+      if(ub->hasLiteral()){
+        if(prev != NullConstraint){
+          implies(out, prev, ub);
+        }
+        prev = ub;
+      }
+    }
+  }
+}
+
+void ConstraintDatabase::outputUnateEqualityLemmas(std::vector<TrustNode>& out,
+                                                   ArithVar v) const
+{
+  vector<ConstraintP> equalities;
+
+  SortedConstraintMap& scm = getVariableSCM(v);
+  SortedConstraintMapConstIterator scm_iter = scm.begin();
+  SortedConstraintMapConstIterator scm_end = scm.end();
+
+  for(; scm_iter != scm_end; ++scm_iter){
+    const ValueCollection& vc = scm_iter->second;
+    if(vc.hasEquality()){
+      ConstraintP eq = vc.getEquality();
+      if(eq->hasLiteral()){
+        equalities.push_back(eq);
+      }
+    }
+  }
+
+  vector<ConstraintP>::const_iterator i, j, eq_end = equalities.end();
+  for(i = equalities.begin(); i != eq_end; ++i){
+    ConstraintP at_i = *i;
+    for(j= i + 1; j != eq_end; ++j){
+      ConstraintP at_j = *j;
+
+      mutuallyExclusive(out, at_i, at_j);
+    }
+  }
+
+  for(i = equalities.begin(); i != eq_end; ++i){
+    ConstraintP eq = *i;
+    const ValueCollection& vc = eq->getValueCollection();
+    Assert(vc.hasEquality() && vc.getEquality()->hasLiteral());
+
+    bool hasLB = vc.hasLowerBound() && vc.getLowerBound()->hasLiteral();
+    bool hasUB = vc.hasUpperBound() && vc.getUpperBound()->hasLiteral();
+
+    ConstraintP lb = hasLB ?
+      vc.getLowerBound() : eq->getStrictlyWeakerLowerBound(true, false);
+    ConstraintP ub = hasUB ?
+      vc.getUpperBound() : eq->getStrictlyWeakerUpperBound(true, false);
+
+    if(hasUB && hasLB && !eq->isSplit()){
+      out.push_back(eq->split());
+    }
+    if(lb != NullConstraint){
+      implies(out, eq, lb);
+    }
+    if(ub != NullConstraint){
+      implies(out, eq, ub);
+    }
+  }
+}
+
+void ConstraintDatabase::outputUnateEqualityLemmas(
+    std::vector<TrustNode>& lemmas) const
+{
+  for(ArithVar v = 0, N = d_varDatabases.size(); v < N; ++v){
+    outputUnateEqualityLemmas(lemmas, v);
+  }
+}
+
+void ConstraintDatabase::outputUnateInequalityLemmas(
+    std::vector<TrustNode>& lemmas) const
+{
+  for(ArithVar v = 0, N = d_varDatabases.size(); v < N; ++v){
+    outputUnateInequalityLemmas(lemmas, v);
+  }
+}
+
+bool ConstraintDatabase::handleUnateProp(ConstraintP ant, ConstraintP cons){
+  if(cons->negationHasProof()){
+    Trace("arith::unate") << "handleUnate: " << ant << " implies " << cons << endl;
+    cons->impliedByUnate(ant, true);
+    d_raiseConflict.raiseConflict(cons, InferenceId::ARITH_CONF_UNATE_PROP);
+    return true;
+  }else if(!cons->isTrue()){
+    ++d_statistics.d_unatePropagateImplications;
+    Trace("arith::unate") << "handleUnate: " << ant << " implies " << cons << endl;
+    cons->impliedByUnate(ant, false);
+    cons->tryToPropagate();
+    return false;
+  } else {
+    return false;
+  }
+}
+
+void ConstraintDatabase::unatePropLowerBound(ConstraintP curr, ConstraintP prev){
+  Trace("arith::unate") << "unatePropLowerBound " << curr << " " << prev << endl;
+  Assert(curr != prev);
+  Assert(curr != NullConstraint);
+  bool hasPrev = ! (prev == NullConstraint);
+  Assert(!hasPrev || curr->getValue() > prev->getValue());
+
+  ++d_statistics.d_unatePropagateCalls;
+
+  const SortedConstraintMap& scm = curr->constraintSet();
+  const SortedConstraintMapConstIterator scm_begin = scm.begin();
+  SortedConstraintMapConstIterator scm_i = curr->d_variablePosition;
+
+  //Ignore the first ValueCollection
+  // NOPE: (>= p c) then (= p c) NOPE
+  // NOPE: (>= p c) then (not (= p c)) NOPE
+
+  while(scm_i != scm_begin){
+    --scm_i; // move the iterator back
+
+    const ValueCollection& vc = scm_i->second;
+
+    //If it has the previous element, do nothing and stop!
+    if(hasPrev &&
+       vc.hasConstraintOfType(prev->getType())
+       && vc.getConstraintOfType(prev->getType()) == prev){
+      break;
+    }
+
+    //Don't worry about implying the negation of upperbound.
+    //These should all be handled by propagating the LowerBounds!
+    if(vc.hasLowerBound()){
+      ConstraintP lb = vc.getLowerBound();
+      if(handleUnateProp(curr, lb)){ return; }
+    }
+    if(vc.hasDisequality()){
+      ConstraintP dis = vc.getDisequality();
+      if(handleUnateProp(curr, dis)){ return; }
+    }
+  }
+}
+
+void ConstraintDatabase::unatePropUpperBound(ConstraintP curr, ConstraintP prev){
+  Trace("arith::unate") << "unatePropUpperBound " << curr << " " << prev << endl;
+  Assert(curr != prev);
+  Assert(curr != NullConstraint);
+  bool hasPrev = ! (prev == NullConstraint);
+  Assert(!hasPrev || curr->getValue() < prev->getValue());
+
+  ++d_statistics.d_unatePropagateCalls;
+
+  const SortedConstraintMap& scm = curr->constraintSet();
+  const SortedConstraintMapConstIterator scm_end = scm.end();
+  SortedConstraintMapConstIterator scm_i = curr->d_variablePosition;
+  ++scm_i;
+  for(; scm_i != scm_end; ++scm_i){
+    const ValueCollection& vc = scm_i->second;
+
+    //If it has the previous element, do nothing and stop!
+    if(hasPrev &&
+       vc.hasConstraintOfType(prev->getType()) &&
+       vc.getConstraintOfType(prev->getType()) == prev){
+      break;
+    }
+    //Don't worry about implying the negation of upperbound.
+    //These should all be handled by propagating the UpperBounds!
+    if(vc.hasUpperBound()){
+      ConstraintP ub = vc.getUpperBound();
+      if(handleUnateProp(curr, ub)){ return; }
+    }
+    if(vc.hasDisequality()){
+      ConstraintP dis = vc.getDisequality();
+      if(handleUnateProp(curr, dis)){ return; }
+    }
+  }
+}
+
+void ConstraintDatabase::unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB){
+  Trace("arith::unate") << "unatePropEquality " << curr << " " << prevLB << " " << prevUB << endl;
+  Assert(curr != prevLB);
+  Assert(curr != prevUB);
+  Assert(curr != NullConstraint);
+  bool hasPrevLB = ! (prevLB == NullConstraint);
+  bool hasPrevUB = ! (prevUB == NullConstraint);
+  Assert(!hasPrevLB || curr->getValue() >= prevLB->getValue());
+  Assert(!hasPrevUB || curr->getValue() <= prevUB->getValue());
+
+  ++d_statistics.d_unatePropagateCalls;
+
+  const SortedConstraintMap& scm = curr->constraintSet();
+  SortedConstraintMapConstIterator scm_curr = curr->d_variablePosition;
+  SortedConstraintMapConstIterator scm_last = hasPrevUB ? prevUB->d_variablePosition : scm.end();
+  SortedConstraintMapConstIterator scm_i;
+  if(hasPrevLB){
+    scm_i = prevLB->d_variablePosition;
+    if(scm_i != scm_curr){ // If this does not move this past scm_curr, move it one forward
+      ++scm_i;
+    }
+  }else{
+    scm_i = scm.begin();
+  }
+
+  for(; scm_i != scm_curr; ++scm_i){
+    // between the previous LB and the curr
+    const ValueCollection& vc = scm_i->second;
+
+    //Don't worry about implying the negation of upperbound.
+    //These should all be handled by propagating the LowerBounds!
+    if(vc.hasLowerBound()){
+      ConstraintP lb = vc.getLowerBound();
+      if(handleUnateProp(curr, lb)){ return; }
+    }
+    if(vc.hasDisequality()){
+      ConstraintP dis = vc.getDisequality();
+      if(handleUnateProp(curr, dis)){ return; }
+    }
+  }
+  Assert(scm_i == scm_curr);
+  if(!hasPrevUB || scm_i != scm_last){
+    ++scm_i;
+  } // hasPrevUB implies scm_i != scm_last
+
+  for(; scm_i != scm_last; ++scm_i){
+    // between the curr and the previous UB imply the upperbounds and disequalities.
+    const ValueCollection& vc = scm_i->second;
+
+    //Don't worry about implying the negation of upperbound.
+    //These should all be handled by propagating the UpperBounds!
+    if(vc.hasUpperBound()){
+      ConstraintP ub = vc.getUpperBound();
+      if(handleUnateProp(curr, ub)){ return; }
+    }
+    if(vc.hasDisequality()){
+      ConstraintP dis = vc.getDisequality();
+      if(handleUnateProp(curr, dis)){ return; }
+    }
+  }
+}
+
+std::pair<int, int> Constraint::unateFarkasSigns(ConstraintCP ca, ConstraintCP cb){
+  ConstraintType a = ca->getType();
+  ConstraintType b = cb->getType();
+
+  Assert(a != Disequality);
+  Assert(b != Disequality);
+
+  int a_sgn = (a == LowerBound) ? -1 : ((a == UpperBound) ? 1 : 0);
+  int b_sgn = (b == LowerBound) ? -1 : ((b == UpperBound) ? 1 : 0);
+
+  if(a_sgn == 0 && b_sgn == 0){
+    Assert(a == Equality);
+    Assert(b == Equality);
+    Assert(ca->getValue() != cb->getValue());
+    if(ca->getValue() < cb->getValue()){
+      a_sgn = 1;
+      b_sgn = -1;
+    }else{
+      a_sgn = -1;
+      b_sgn = 1;
+    }
+  }else if(a_sgn == 0){
+    Assert(b_sgn != 0);
+    Assert(a == Equality);
+    a_sgn = -b_sgn;
+  }else if(b_sgn == 0){
+    Assert(a_sgn != 0);
+    Assert(b == Equality);
+    b_sgn = -a_sgn;
+  }
+  Assert(a_sgn != 0);
+  Assert(b_sgn != 0);
+
+  Trace("arith::unateFarkasSigns") << "Constraint::unateFarkasSigns("<<a <<", " << b << ") -> "
+                                   << "("<<a_sgn<<", "<< b_sgn <<")"<< endl;
+  return make_pair(a_sgn, b_sgn);
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/constraint.h b/src/theory/arith/linear/constraint.h
new file mode 100644 (file)
index 0000000..745462e
--- /dev/null
@@ -0,0 +1,1267 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Alex Ozdemir, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Defines Constraint and ConstraintDatabase which is the internal
+ * representation of variables in arithmetic
+ *
+ * This file defines Constraint and ConstraintDatabase.
+ * A Constraint is the internal representation of literals in TheoryArithmetic.
+ * Constraints are fundamentally a triple:
+ *  - ArithVar associated with the constraint,
+ *  - a DeltaRational value,
+ *  - and a ConstraintType.
+ *
+ * Literals:
+ *   The constraint may also keep track of a node corresponding to the
+ *   Constraint.
+ *   This can be accessed by getLiteral() in O(1) if it has been set.
+ *   This node must be in normal form and may be used for communication with
+ *   the TheoryEngine.
+ *
+ * In addition, Constraints keep track of the following:
+ *  - A Constraint that is the negation of the Constraint.
+ *  - An iterator into a set of Constraints for the ArithVar sorted by
+ *    DeltaRational value.
+ *  - A context dependent internal proof of the node that can be used for
+ *    explanations.
+ *  - Whether an equality/disequality has been split in the user context via a
+ *    lemma.
+ *  - Whether a constraint, be be used in explanations sent to the context
+ *
+ * Looking up constraints:
+ *  - All of the Constraints with associated nodes in the ConstraintDatabase
+ *    can be accessed via a single hashtable lookup until the Constraint is
+ *    removed.
+ *  - Nodes that have not been associated to a constraints can be
+ *    inserted/associated to existing nodes in O(log n) time.
+ *
+ * Implications:
+ *  - A Constraint can be used to find unate implications.
+ *  - A unate implication is an implication based purely on the ArithVar
+ *    matching  and the DeltaRational value.
+ *    (implies (<= x c) (<= x d)) given c <= d
+ *  - This is done using the iterator into the sorted set of constraints.
+ *  - Given a tight constraint and previous tightest constraint, this will
+ *    efficiently propagate internally.
+ *
+ * Additing and Removing Constraints
+ *  - Adding Constraints takes O(log n) time where n is the number of
+ *    constraints associated with the ArithVar.
+ *  - Removing Constraints takes O(1) time.
+ *
+ * Internals:
+ *  - Constraints are pointers to ConstraintValues.
+ *  - Undefined Constraints are NullConstraint.
+ *
+ * Assumption vs. Assertion:
+ * - An assertion is anything on the theory d_fact queue.
+ *   This includes any thing propagated and returned to the fact queue.
+ *   These can be used in external conflicts and propagations of earlier
+ *   proofs.
+ * - An assumption is anything on the theory d_fact queue that has no further
+ *   explanation i.e. this theory did not propagate it.
+ * - To set something an assumption, first set it as being as assertion.
+ * - Internal assumptions have no explanations and must be regressed out of the
+ *   proof.
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__CONSTRAINT_H
+#define CVC5__THEORY__ARITH__CONSTRAINT_H
+
+#include <unordered_map>
+#include <vector>
+
+#include "base/configuration_private.h"
+#include "context/cdlist.h"
+#include "context/cdqueue.h"
+#include "expr/node.h"
+#include "proof/trust_node.h"
+#include "smt/env_obj.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/callbacks.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/arith/delta_rational.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::context {
+class Context;
+}
+namespace cvc5::internal {
+
+class ProofNodeManager;
+class EagerProofGenerator;
+
+namespace theory {
+
+namespace arith::linear {
+
+class Comparison;
+class ArithCongruenceManager;
+class ArithVariables;
+
+/**
+ * Logs the types of different proofs.
+ * Current, proof types:
+ * - NoAP             : This constraint is not known to be true.
+ * - AssumeAP         : This is an input assertion. There is no proof.
+ *                    : Something can be both asserted and have a proof.
+ * - InternalAssumeAP : An internal assumption. This has no guarantee of having an external proof.
+ *                    : This must be removed by regression.
+ * - FarkasAP         : A proof with Farka's coefficients, i.e.
+ *                    :  \sum lambda_i ( asNode(x_i) <= c_i  ) |= 0 < 0
+ *                    : If proofs are on, coefficients will be logged.
+ *                    : If proofs are off, coefficients will not be logged.
+ *                    : A unate implication is a FarkasAP.
+ * - TrichotomyAP     : This is any entailment using (x<= a and x >=a) => x = a
+ *                    : Equivalently, (x > a or x < a or x = a)
+ *                    : There are 3 candidate ways this can propagate:
+ *                    :   !(x > a) and !(x = a) => x < a
+ *                    :   !(x < a) and !(x = a) => x > a
+ *                    :   !(x > a) and !(x < a) => x = a
+ * - EqualityEngineAP : This is propagated by the equality engine.
+ *                    : Consult this for the proof.
+ * - IntTightenAP     : This is indicates that a bound involving integers was tightened.
+ *                    : e.g. i < 5.5 became i <= 5, when i is an integer.
+ * - IntHoleAP        : This is currently a catch-all for all integer specific reason.
+ */
+enum ArithProofType
+  { NoAP,
+    AssumeAP,
+    InternalAssumeAP,
+    FarkasAP,
+    TrichotomyAP,
+    EqualityEngineAP,
+    IntTightenAP,
+    IntHoleAP};
+
+/**
+ * The types of constraints.
+ * The convex constraints are the constraints are LowerBound, Equality,
+ * and UpperBound.
+ */
+enum ConstraintType {LowerBound, Equality, UpperBound, Disequality};
+
+typedef context::CDList<ConstraintCP> CDConstraintList;
+
+typedef std::unordered_map<Node, ConstraintP> NodetoConstraintMap;
+
+typedef size_t ConstraintRuleID;
+static constexpr ConstraintRuleID ConstraintRuleIdSentinel =
+    std::numeric_limits<ConstraintRuleID>::max();
+
+typedef size_t AntecedentId;
+static constexpr AntecedentId AntecedentIdSentinel =
+    std::numeric_limits<AntecedentId>::max();
+
+typedef size_t AssertionOrder;
+static constexpr AssertionOrder AssertionOrderSentinel =
+    std::numeric_limits<AssertionOrder>::max();
+
+/**
+ * A ValueCollection binds together convex constraints that have the same
+ * DeltaRational value.
+ */
+class ValueCollection {
+private:
+
+  ConstraintP d_lowerBound;
+  ConstraintP d_upperBound;
+  ConstraintP d_equality;
+  ConstraintP d_disequality;
+
+public:
+  ValueCollection();
+
+  static ValueCollection mkFromConstraint(ConstraintP c);
+
+  bool hasLowerBound() const;
+  bool hasUpperBound() const;
+  bool hasEquality() const;
+  bool hasDisequality() const;
+
+  bool hasConstraintOfType(ConstraintType t) const;
+
+  ConstraintP getLowerBound() const;
+  ConstraintP getUpperBound() const;
+  ConstraintP getEquality() const;
+  ConstraintP getDisequality() const;
+
+  ConstraintP getConstraintOfType(ConstraintType t) const;
+
+  /** Returns true if any of the constraints are non-null. */
+  bool empty() const;
+
+  /**
+   * Remove the constraint of the type t from the collection.
+   * Returns true if the ValueCollection is now empty.
+   * If true is returned, d_value is now NULL.
+   */
+  void remove(ConstraintType t);
+
+  /**
+   * Adds a constraint to the set.
+   * The collection must not have a constraint of that type already.
+   */
+  void add(ConstraintP c);
+
+  void push_into(std::vector<ConstraintP>& vec) const;
+
+  ConstraintP nonNull() const;
+
+  ArithVar getVariable() const;
+  const DeltaRational& getValue() const;
+};
+
+/**
+ * A Map of ValueCollections sorted by the associated DeltaRational values.
+ *
+ * Discussion:
+ * While it is more natural to consider this a set, this cannot be a set as in
+ * sets the type of both iterator and const_iterator in sets are
+ * "constant iterators".  We require iterators that dereference to
+ * ValueCollection&.
+ *
+ * See:
+ * http://gcc.gnu.org/onlinedocs/libstdc++/ext/lwg-defects.html#103
+ */
+typedef std::map<DeltaRational, ValueCollection> SortedConstraintMap;
+typedef SortedConstraintMap::iterator SortedConstraintMapIterator;
+typedef SortedConstraintMap::const_iterator SortedConstraintMapConstIterator;
+
+/** A Pair associating a variables and a Sorted ConstraintSet. */
+struct PerVariableDatabase{
+  ArithVar d_var;
+  SortedConstraintMap d_constraints;
+
+  // x ? c_1, x ? c_2, x ? c_3, ...
+  // where ? is a non-empty subset of {lb, ub, eq}
+  // c_1 < c_2 < c_3 < ...
+
+  PerVariableDatabase(ArithVar v) : d_var(v), d_constraints() {}
+
+  bool empty() const {
+    return d_constraints.empty();
+  }
+
+  static bool IsEmpty(const PerVariableDatabase& p){
+    return p.empty();
+  }
+};
+
+/**
+ * If proofs are on, there is a vector of rationals for farkas coefficients.
+ * This is the owner of the memory for the vector, and calls delete upon
+ * cleanup.
+ *
+ */
+struct ConstraintRule {
+  ConstraintP d_constraint;
+  ArithProofType d_proofType;
+  AntecedentId d_antecedentEnd;
+
+  /**
+   * In this comment, we abbreviate ConstraintDatabase::d_antecedents
+   * and d_farkasCoefficients as ans and fc.
+   *
+   * This list is always empty if proofs are not enabled.
+   *
+   * If proofs are enabled, the proof of constraint c at p in ans[p] of length n
+   * is (NullConstraint, ans[p-(n-1)], ... , ans[p-1], ans[p])
+   *
+   * Farkas' proofs show a contradiction with the negation of c, c_not =
+   * c->getNegation().
+   *
+   * We treat the position for NullConstraint (p-n) as the position for the
+   * farkas coefficient for so we pretend c_not is ans[p-n]. So this correlation
+   * for the constraints we are going to use: (c_not, ans[p-n+(1)], ... ,
+   * ans[p-n+(n-1)], ans[p-n+(n)]) With the coefficients at positions: (fc[0],
+   * fc[1)], ... fc[n])
+   *
+   * The index of the constraints in the proof are {i | i <= 0 <= n] } (with
+   * c_not being p-n). Partition the indices into L, U, and E, the lower bounds,
+   * the upper bounds and equalities.
+   *
+   * We standardize the proofs to be upper bound oriented following the
+   * convention: A x <= b with the proof witness of the form (lambda) Ax <=
+   * (lambda) b and lambda >= 0.
+   *
+   * To accomplish this cleanly, the fc coefficients must be negative for lower
+   * bounds. The signs of equalities can be either positive or negative.
+   *
+   * Thus the proof corresponds to (with multiplication over inequalities):
+   *    \sum_{u in U} fc[u] ans[p-n+u] + \sum_{e in E} fc[e] ans[p-n+e]
+   *  + \sum_{l in L} fc[l] ans[p-n+l]
+   * |= 0 < 0
+   * where fc[u] > 0, fc[l] < 0, and fc[e] != 0 (i.e. it can be either +/-).
+   *
+   * There is no requirement that the proof is minimal.
+   * We do however use all of the constraints by requiring non-zero
+   * coefficients.
+   */
+  RationalVectorCP d_farkasCoefficients;
+
+  ConstraintRule();
+  ConstraintRule(ConstraintP con, ArithProofType pt);
+  ConstraintRule(ConstraintP con,
+                 ArithProofType pt,
+                 AntecedentId antecedentEnd);
+  ConstraintRule(ConstraintP con,
+                 ArithProofType pt,
+                 AntecedentId antecedentEnd,
+                 RationalVectorCP coeffs);
+
+  void print(std::ostream& out, bool produceProofs) const;
+}; /* class ConstraintRule */
+
+class Constraint {
+
+  friend class ConstraintDatabase;
+
+ public:
+  /**
+   * This begins construction of a minimal constraint.
+   *
+   * This should only be called by ConstraintDatabase.
+   *
+   * Because of circular dependencies a Constraint is not fully valid until
+   * initialize has been called on it.
+   */
+  Constraint(ArithVar x,
+             ConstraintType t,
+             const DeltaRational& v,
+             bool produceProofs);
+
+  /**
+   * Destructor for a constraint.
+   * This should only be called if safeToGarbageCollect() is true.
+   */
+  ~Constraint();
+
+  static ConstraintType constraintTypeOfComparison(const Comparison& cmp);
+
+  inline ConstraintType getType() const {
+    return d_type;
+  }
+
+  inline ArithVar getVariable() const {
+    return d_variable;
+  }
+
+  const DeltaRational& getValue() const {
+    return d_value;
+  }
+
+  inline ConstraintP getNegation() const {
+    return d_negation;
+  }
+
+  bool isEquality() const{
+    return d_type == Equality;
+  }
+  bool isDisequality() const{
+    return d_type == Disequality;
+  }
+  bool isLowerBound() const{
+    return d_type == LowerBound;
+  }
+  bool isUpperBound() const{
+    return d_type == UpperBound;
+  }
+  bool isStrictUpperBound() const{
+    Assert(isUpperBound());
+    return getValue().infinitesimalSgn() < 0;
+  }
+
+  bool isStrictLowerBound() const{
+    Assert(isLowerBound());
+    return getValue().infinitesimalSgn() > 0;
+  }
+
+  bool isSplit() const {
+    return d_split;
+  }
+
+  /**
+   * Splits the node in the user context.
+   * Returns a lemma that is assumed to be true for the rest of the user context.
+   * Constraint must be an equality or disequality.
+   */
+  TrustNode split();
+
+  bool canBePropagated() const {
+    return d_canBePropagated;
+  }
+  void setCanBePropagated();
+
+  /**
+   * Light wrapper for calling setCanBePropagated(),
+   * on this and this->d_negation.
+   */
+  void setPreregistered(){
+    setCanBePropagated();
+    d_negation->setCanBePropagated();
+  }
+
+  bool assertedToTheTheory() const {
+    Assert((d_assertionOrder < AssertionOrderSentinel) != d_witness.isNull());
+    return d_assertionOrder < AssertionOrderSentinel;
+  }
+  TNode getWitness() const {
+    Assert(assertedToTheTheory());
+    return d_witness;
+  }
+
+  bool assertedBefore(AssertionOrder time) const {
+    return d_assertionOrder < time;
+  }
+
+  /**
+   * Sets the witness literal for a node being on the assertion stack.
+   *
+   * If the negation of the node is true, inConflict must be true.
+   * If the negation of the node is false, inConflict must be false.
+   * Hence, negationHasProof() == inConflict.
+   *
+   * This replaces:
+   *   void setAssertedToTheTheory(TNode witness);
+   *   void setAssertedToTheTheoryWithNegationTrue(TNode witness);
+   */
+  void setAssertedToTheTheory(TNode witness, bool inConflict);
+
+  bool hasLiteral() const {
+    return !d_literal.isNull();
+  }
+
+  void setLiteral(Node n);
+
+  Node getLiteral() const {
+    Assert(hasLiteral());
+    return d_literal;
+  }
+
+  /** Gets a literal in the normal form suitable for proofs.
+   * That is, (sum of non-const monomials) >< const.
+   *
+   * This is a sister method to `getLiteral`, which returns a normal form
+   * literal, suitable for external solving use.
+   */
+  Node getProofLiteral() const;
+
+  /**
+   * Set the node as having a proof and being an assumption.
+   * The node must be assertedToTheTheory().
+   *
+   * Precondition: negationHasProof() == inConflict.
+   *
+   * Replaces:
+   *  selfExplaining().
+   *  selfExplainingWithNegationTrue().
+   */
+  void setAssumption(bool inConflict);
+
+  /** Returns true if the node is an assumption.*/
+  bool isAssumption() const;
+
+  /** Whether we produce proofs */
+  bool isProofProducing() const { return d_produceProofs; }
+
+  /** Set the constraint to have an EqualityEngine proof. */
+  void setEqualityEngineProof();
+  bool hasEqualityEngineProof() const;
+
+  /** Returns true if the node has a Farkas' proof. */
+  bool hasFarkasProof() const;
+
+  /**
+   * @brief Returns whether this constraint is provable using a Farkas
+   * proof applied to (possibly tightened) input assertions.
+   *
+   * An example of a constraint that has a simple Farkas proof:
+   *    x <= 0 proven from x + y <= 0 and x - y <= 0.
+   *
+   * An example of another constraint that has a simple Farkas proof:
+   *    x <= 0 proven from x + y <= 0 and x - y <= 0.5 for integers x, y
+   *       (integer bound-tightening is applied first!).
+   *
+   * An example of a constraint that might be proven **without** a simple
+   * Farkas proof:
+   *    x < 0 proven from not(x == 0) and not(x > 0).
+   *
+   * This could be proven internally by the arithmetic theory using
+   * `TrichotomyAP` as the proof type.
+   *
+   */
+  bool hasSimpleFarkasProof() const;
+  /**
+   * Returns whether this constraint is an assumption or a tightened
+   * assumption.
+   */
+  bool isPossiblyTightenedAssumption() const;
+
+  /** Returns true if the node has a int bound tightening proof. */
+  bool hasIntTightenProof() const;
+
+  /** Returns true if the node has a int hole proof. */
+  bool hasIntHoleProof() const;
+
+  /** Returns true if the node has a trichotomy proof. */
+  bool hasTrichotomyProof() const;
+
+  void printProofTree(std::ostream & out, size_t depth = 0) const;
+
+  /**
+   * A sets the constraint to be an internal assumption.
+   *
+   * This does not need to have a witness or an associated literal.
+   * This is always itself in the explanation fringe for both conflicts
+   * and propagation.
+   * This cannot be converted back into a Node conflict or explanation.
+   *
+   * This cannot have a proof or be asserted to the theory!
+   *
+   */
+  void setInternalAssumption(bool inConflict);
+  bool isInternalAssumption() const;
+
+  /**
+   * Returns a explanation of the constraint that is appropriate for conflicts.
+   *
+   * This is not appropriate for propagation!
+   *
+   * This is the minimum fringe of the implication tree s.t.
+   * every constraint is assertedToTheTheory() or hasEqualityEngineProof().
+   */
+  TrustNode externalExplainByAssertions() const;
+
+  /**
+   * Writes an explanation of a constraint into the node builder.
+   * Pushes back an explanation that is acceptable to send to the sat solver.
+   * nb is assumed to be an AND.
+   *
+   * This is the minimum fringe of the implication tree s.t.
+   * every constraint is assertedToTheTheory() or hasEqualityEngineProof().
+   *
+   * This is not appropriate for propagation!
+   * Use explainForPropagation() instead.
+   */
+  std::shared_ptr<ProofNode> externalExplainByAssertions(NodeBuilder& nb) const
+  {
+    return externalExplain(nb, AssertionOrderSentinel);
+  }
+
+  /* Equivalent to calling externalExplainByAssertions on all constraints in b */
+  static Node externalExplainByAssertions(const ConstraintCPVec& b);
+  static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b);
+  static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c);
+
+  /**
+   * This is the minimum fringe of the implication tree s.t. every constraint is
+   * - assertedToTheTheory(),
+   * - isInternalDecision() or
+   * - hasEqualityEngineProof().
+   */
+  static void assertionFringe(ConstraintCPVec& v);
+  static void assertionFringe(ConstraintCPVec& out, const ConstraintCPVec& in);
+
+  /** The fringe of a farkas' proof. */
+  bool onFringe() const {
+    return assertedToTheTheory() || isInternalAssumption() || hasEqualityEngineProof();
+  }
+
+  /**
+   * Returns an explanation of a propagation by the ConstraintDatabase.
+   * The constraint must have a proof.
+   * The constraint cannot be an assumption.
+   *
+   * This is the minimum fringe of the implication tree (excluding the
+   * constraint itself) s.t. every constraint is assertedToTheTheory() or
+   * hasEqualityEngineProof().
+   *
+   * All return conjuncts were asserted before this constraint.
+   *
+   * Requires the given node to rewrite to the canonical literal for this
+   * constraint.
+   *
+   * @params n the literal to prove
+   *           n must rewrite to the constraint's canonical literal
+   *
+   * @returns a trust node of the form:
+   *         (=> explanation n)
+   */
+  TrustNode externalExplainForPropagation(TNode n) const;
+
+  /**
+   * Explain the constraint and its negation in terms of assertions.
+   * The constraint must be in conflict.
+   */
+  TrustNode externalExplainConflict() const;
+
+  /** The constraint is known to be true. */
+  inline bool hasProof() const {
+    return d_crid != ConstraintRuleIdSentinel;
+  }
+
+  /** The negation of the constraint is known to hold. */
+  inline bool negationHasProof() const {
+    return d_negation->hasProof();
+  }
+
+  /** Neither the contraint has a proof nor the negation has a proof.*/
+  bool truthIsUnknown() const {
+    return !hasProof() && !negationHasProof();
+  }
+
+  /** This is a synonym for hasProof(). */
+  inline bool isTrue() const {
+    return hasProof();
+  }
+
+  /** Both the constraint and its negation are true. */
+  inline bool inConflict() const {
+    return hasProof() && negationHasProof();
+  }
+
+  /**
+   * Returns the constraint that corresponds to taking
+   *    x r ceiling(getValue()) where r is the node's getType().
+   * Esstentially this is an up branch.
+   */
+  ConstraintP getCeiling();
+
+  /**
+   * Returns the constraint that corresponds to taking
+   *    x r floor(getValue()) where r is the node's getType().
+   * Esstentially this is a down branch.
+   */
+  ConstraintP getFloor();
+
+  static ConstraintP makeNegation(ArithVar v,
+                                  ConstraintType t,
+                                  const DeltaRational& r,
+                                  bool produceProofs);
+
+  const ValueCollection& getValueCollection() const;
+
+
+  ConstraintP getStrictlyWeakerUpperBound(bool hasLiteral, bool mustBeAsserted) const;
+  ConstraintP getStrictlyWeakerLowerBound(bool hasLiteral, bool mustBeAsserted) const;
+
+  /**
+   * Marks a the constraint c as being entailed by a.
+   * The Farkas proof 1*(a) + -1 (c) |= 0<0
+   *
+   * After calling impliedByUnate(), the caller should either raise a conflict
+   * or try call tryToPropagate().
+   */
+  void impliedByUnate(ConstraintCP a, bool inConflict);
+
+  /**
+   * Marks a the constraint c as being entailed by a.
+   * The reason has to do with integer bound tightening.
+   *
+   * After calling impliedByIntTighten(), the caller should either raise a conflict
+   * or try call tryToPropagate().
+   */
+  void impliedByIntTighten(ConstraintCP a, bool inConflict);
+
+  /**
+   * Marks a the constraint c as being entailed by a.
+   * The reason has to do with integer reasoning.
+   *
+   * After calling impliedByIntHole(), the caller should either raise a conflict
+   * or try call tryToPropagate().
+   */
+  void impliedByIntHole(ConstraintCP a, bool inConflict);
+
+  /**
+   * Marks a the constraint c as being entailed by a.
+   * The reason has to do with integer reasoning.
+   *
+   * After calling impliedByIntHole(), the caller should either raise a conflict
+   * or try call tryToPropagate().
+   */
+  void impliedByIntHole(const ConstraintCPVec& b, bool inConflict);
+
+  /**
+   * This is a lemma of the form:
+   *   x < d or x = d or x > d
+   * The current constraint c is one of the above constraints and {a,b}
+   * are the negation of the other two constraints.
+   *
+   * Preconditions:
+   * - negationHasProof() == inConflict.
+   *
+   * After calling impliedByTrichotomy(), the caller should either raise a conflict
+   * or try call tryToPropagate().
+   */
+  void impliedByTrichotomy(ConstraintCP a, ConstraintCP b, bool inConflict);
+
+  /**
+   * Marks the node as having a Farkas proof.
+   *
+   * Preconditions:
+   * - coeffs == NULL if proofs are off.
+   * - See the comments for ConstraintRule for the form of coeffs when
+   *   proofs are on.
+   * - negationHasProof() == inConflict.
+   *
+   * After calling impliedByFarkas(), the caller should either raise a conflict
+   * or try call tryToPropagate().
+   */
+  void impliedByFarkas(const ConstraintCPVec& b, RationalVectorCP coeffs, bool inConflict);
+
+  /**
+   * Generates an implication node, B => getLiteral(),
+   * where B is the result of externalExplainByAssertions(b).
+   * Does not guarantee b is the explanation of the constraint.
+   */
+  Node externalImplication(const ConstraintCPVec& b) const;
+
+  /**
+   * Returns true if the variable is assigned the value dr,
+   * the constraint would be satisfied.
+   */
+  bool satisfiedBy(const DeltaRational& dr) const;
+
+  /**
+   * The node must have a proof already and be eligible for propagation!
+   * You probably want to call tryToPropagate() instead.
+   *
+   * Preconditions:
+   * - hasProof()
+   * - canBePropagated()
+   * - !assertedToTheTheory()
+   */
+  void propagate();
+
+  /**
+   * If the constraint
+   *   canBePropagated() and
+   *   !assertedToTheTheory(),
+   * the constraint is added to the database's propagation queue.
+   *
+   * Precondition:
+   * - hasProof()
+   */
+  void tryToPropagate();
+
+  /**
+   * Returns a reference to the containing database.
+   * Precondition: the constraint must be initialized.
+   */
+  const ConstraintDatabase& getDatabase() const;
+
+  /** Returns the constraint rule at the position. */
+  const ConstraintRule& getConstraintRule() const;
+
+ private:
+  /**  Returns true if the constraint has been initialized. */
+  bool initialized() const;
+
+  /**
+   * This initializes the fields that cannot be set in the constructor due to
+   * circular dependencies.
+   */
+  void initialize(ConstraintDatabase* db,
+                  SortedConstraintMapIterator v,
+                  ConstraintP negation);
+
+  class ConstraintRuleCleanup
+  {
+   public:
+    inline void operator()(ConstraintRule* crp)
+    {
+      Assert(crp != NULL);
+      ConstraintP constraint = crp->d_constraint;
+      Assert(constraint->d_crid != ConstraintRuleIdSentinel);
+      constraint->d_crid = ConstraintRuleIdSentinel;
+      if (constraint->isProofProducing())
+      {
+        if (crp->d_farkasCoefficients != RationalVectorCPSentinel)
+        {
+          delete crp->d_farkasCoefficients;
+        }
+      }
+    }
+  };
+
+  class CanBePropagatedCleanup
+  {
+   public:
+    inline void operator()(ConstraintP* p)
+    {
+      ConstraintP constraint = *p;
+      Assert(constraint->d_canBePropagated);
+      constraint->d_canBePropagated = false;
+    }
+  };
+
+  class AssertionOrderCleanup
+  {
+   public:
+    inline void operator()(ConstraintP* p)
+    {
+      ConstraintP constraint = *p;
+      Assert(constraint->assertedToTheTheory());
+      constraint->d_assertionOrder = AssertionOrderSentinel;
+      constraint->d_witness = TNode::null();
+      Assert(!constraint->assertedToTheTheory());
+    }
+  };
+
+  class SplitCleanup
+  {
+   public:
+    inline void operator()(ConstraintP* p)
+    {
+      ConstraintP constraint = *p;
+      Assert(constraint->d_split);
+      constraint->d_split = false;
+    }
+  };
+
+  /**
+   * Returns true if the node is safe to garbage collect.
+   * Both it and its negation must have no context dependent data set.
+   */
+  bool safeToGarbageCollect() const;
+
+  /**
+   * Returns true if the constraint has no context dependent data set.
+   */
+  bool contextDependentDataIsSet() const;
+
+  /**
+   * Returns true if the node correctly corresponds to the constraint that is
+   * being set.
+   */
+  bool sanityChecking(Node n) const;
+
+  /** Returns a reference to the map for d_variable. */
+  SortedConstraintMap& constraintSet() const;
+
+  /** Returns coefficients for the proofs for farkas cancellation. */
+  static std::pair<int, int> unateFarkasSigns(ConstraintCP a, ConstraintCP b);
+
+  Node externalExplain(AssertionOrder order) const;
+  /**
+   * Returns an explanation of that was assertedBefore(order).
+   * The constraint must have a proof.
+   * The constraint cannot be selfExplaining().
+   *
+   * This is the minimum fringe of the implication tree
+   * s.t. every constraint is assertedBefore(order) or hasEqualityEngineProof().
+   */
+  std::shared_ptr<ProofNode> externalExplain(NodeBuilder& nb,
+                                             AssertionOrder order) const;
+
+  static Node externalExplain(const ConstraintCPVec& b, AssertionOrder order);
+
+  inline ArithProofType getProofType() const {
+    return getConstraintRule().d_proofType;
+  }
+
+  inline AntecedentId getEndAntecedent() const {
+    return getConstraintRule().d_antecedentEnd;
+  }
+
+  inline RationalVectorCP getFarkasCoefficients() const
+  {
+    return d_produceProofs ? getConstraintRule().d_farkasCoefficients : nullptr;
+  }
+
+  /**
+   * The proof of the node is empty.
+   * The proof must be a special proof. Either
+   *   isSelfExplaining() or
+   *    hasEqualityEngineProof()
+   */
+  bool antecentListIsEmpty() const;
+
+  bool antecedentListLengthIsOne() const;
+
+  /** Return true if every element in b has a proof. */
+  static bool allHaveProof(const ConstraintCPVec& b);
+
+  /** Precondition: hasFarkasProof()
+   * Computes the combination implied by the farkas coefficients. Sees if it is
+   * a contradiction.
+   */
+
+  bool wellFormedFarkasProof() const;
+
+  /** The ArithVar associated with the constraint. */
+  const ArithVar d_variable;
+
+  /** The type of the Constraint. */
+  const ConstraintType d_type;
+
+  /** The DeltaRational value with the constraint. */
+  const DeltaRational d_value;
+
+  /** A pointer to the associated database for the Constraint. */
+  ConstraintDatabase* d_database;
+
+  /**
+   * The node to be communicated with the TheoryEngine.
+   *
+   * This is not context dependent, but may be set once.
+   *
+   * This must be set if the constraint canBePropagated().
+   * This must be set if the constraint assertedToTheTheory().
+   * Otherwise, this may be null().
+   */
+  Node d_literal;
+
+  /** Pointer to the negation of the Constraint. */
+  ConstraintP d_negation;
+
+  /**
+   * This is true if the associated node can be propagated.
+   *
+   * This should be enabled if the node has been preregistered.
+   *
+   * Sat Context Dependent.
+   * This is initially false.
+   */
+  bool d_canBePropagated;
+
+  /**
+   * This is the order the constraint was asserted to the theory.
+   * If this has been set, the node can be used in conflicts.
+   * If this is c.d_assertedOrder < d.d_assertedOrder, then c can be used in the
+   * explanation of d.
+   *
+   * This should be set after the literal is dequeued by Theory::get().
+   *
+   * Sat Context Dependent.
+   * This is initially AssertionOrderSentinel.
+   */
+  AssertionOrder d_assertionOrder;
+
+  /**
+   * This is guaranteed to be on the fact queue.
+   * For example if x + y = x + 1 is on the fact queue, then use this
+   */
+  TNode d_witness;
+
+  /**
+   * The position of the constraint in the constraint rule id.
+   *
+   * Sat Context Dependent.
+   * This is initially
+   */
+  ConstraintRuleID d_crid;
+
+  /**
+   * True if the equality has been split.
+   * Only meaningful if ConstraintType == Equality.
+   *
+   * User Context Dependent.
+   * This is initially false.
+   */
+  bool d_split;
+
+  /**
+   * Position in sorted constraint set for the variable.
+   * Unset if d_type is Disequality.
+   */
+  SortedConstraintMapIterator d_variablePosition;
+
+  /** Whether to produce proofs, */
+  bool d_produceProofs;
+
+}; /* class ConstraintValue */
+
+std::ostream& operator<<(std::ostream& o, const Constraint& c);
+std::ostream& operator<<(std::ostream& o, const ConstraintP c);
+std::ostream& operator<<(std::ostream& o, const ConstraintCP c);
+std::ostream& operator<<(std::ostream& o, const ConstraintType t);
+std::ostream& operator<<(std::ostream& o, const ValueCollection& c);
+std::ostream& operator<<(std::ostream& o, const ConstraintCPVec& v);
+std::ostream& operator<<(std::ostream& o, const ArithProofType);
+
+class ConstraintDatabase : protected EnvObj
+{
+ private:
+  /**
+   * The map from ArithVars to their unique databases.
+   * When the vector changes size, we cannot allow the maps to move so this
+   * is a vector of pointers.
+   */
+  std::vector<PerVariableDatabase*> d_varDatabases;
+
+  SortedConstraintMap& getVariableSCM(ArithVar v) const;
+
+  /** Maps literals to constraints.*/
+  NodetoConstraintMap d_nodetoConstraintMap;
+
+  /**
+   * A queue of propagated constraints.
+   * ConstraintCP are pointers.
+   * The elements of the queue do not require destruction.
+   */
+  context::CDQueue<ConstraintCP> d_toPropagate;
+
+  /**
+   * Proofs are lists of valid constraints terminated by the first null
+   * sentinel value in the proof list.
+   * We abbreviate d_antecedents as ans in the comment.
+   *
+   * The proof at p in ans[p] of length n is
+   *  (NullConstraint, ans[p-(n-1)], ... , ans[p-1], ans[p])
+   *
+   * The proof at p corresponds to the conjunction:
+   *  (and x_i)
+   *
+   * So the proof of a Constraint c corresponds to the horn clause:
+   *  (implies (and x_i) c)
+   * where (and x_i) is the proof at c.d_crid d_antecedentEnd.
+   *
+   * Constraints are pointers so this list is designed not to require any destruction.
+   */
+  CDConstraintList d_antecedents;
+
+  typedef context::CDList<ConstraintRule, Constraint::ConstraintRuleCleanup>
+      ConstraintRuleList;
+  typedef context::CDList<ConstraintP, Constraint::CanBePropagatedCleanup>
+      CBPList;
+  typedef context::CDList<ConstraintP, Constraint::AssertionOrderCleanup>
+      AOList;
+  typedef context::CDList<ConstraintP, Constraint::SplitCleanup> SplitList;
+
+  /**
+   * The watch lists are collected together as they need to be garbage collected
+   * carefully.
+   */
+  struct Watches{
+
+    /**
+     * Contains the exact list of constraints that have a proof.
+     * Upon pop, this unsets d_crid to NoAP.
+     *
+     * The index in this list is the proper ordering of the proofs.
+     */
+    ConstraintRuleList d_constraintProofs;
+
+    /**
+     * Contains the exact list of constraints that can be used for propagation.
+     */
+    CBPList d_canBePropagatedWatches;
+
+    /**
+     * Contains the exact list of constraints that have been asserted to the theory.
+     */
+    AOList d_assertionOrderWatches;
+
+
+    /**
+     * Contains the exact list of atoms that have been preregistered.
+     * This is a pointer as it must be destroyed before the elements of
+     * d_varDatabases.
+     */
+    SplitList d_splitWatches;
+    Watches(context::Context* satContext, context::Context* userContext);
+  };
+  Watches* d_watches;
+
+  void pushSplitWatch(ConstraintP c);
+  void pushCanBePropagatedWatch(ConstraintP c);
+  void pushAssertionOrderWatch(ConstraintP c, TNode witness);
+
+  /** Assumes that antecedents have already been pushed. */
+  void pushConstraintRule(const ConstraintRule& crp);
+
+  /** Returns true if all of the entries of the vector are empty. */
+  static bool emptyDatabase(const std::vector<PerVariableDatabase>& vec);
+
+  /** Map from nodes to arithvars. */
+  const ArithVariables& d_avariables;
+
+  const ArithVariables& getArithVariables() const{
+    return d_avariables;
+  }
+
+  ArithCongruenceManager& d_congruenceManager;
+
+  /** Owned by the TheoryArithPrivate, used here. */
+  EagerProofGenerator* d_pfGen;
+  /** Owned by the TheoryArithPrivate, used here. */
+  ProofNodeManager* d_pnm;
+
+  RaiseConflict d_raiseConflict;
+
+
+  const Rational d_one;
+  const Rational d_negOne;
+
+  friend class Constraint;
+
+ public:
+  ConstraintDatabase(Env& env,
+                     const ArithVariables& variables,
+                     ArithCongruenceManager& dm,
+                     RaiseConflict conflictCallBack,
+                     EagerProofGenerator* pfGen);
+
+  ~ConstraintDatabase();
+
+  /** Adds a literal to the database. */
+  ConstraintP addLiteral(TNode lit);
+
+  /**
+   * If hasLiteral() is true, returns the constraint.
+   * Otherwise, returns NullConstraint.
+   */
+  ConstraintP lookup(TNode literal) const;
+
+  /**
+   * Returns true if the literal has been added to the database.
+   * This is a hash table lookup.
+   * It does not look in the database for an equivalent corresponding constraint.
+   */
+  bool hasLiteral(TNode literal) const;
+
+  bool hasMorePropagations() const{
+    return !d_toPropagate.empty();
+  }
+
+  ConstraintCP nextPropagation(){
+    Assert(hasMorePropagations());
+
+    ConstraintCP p = d_toPropagate.front();
+    d_toPropagate.pop();
+
+    return p;
+  }
+
+  void addVariable(ArithVar v);
+  bool variableDatabaseIsSetup(ArithVar v) const;
+  void removeVariable(ArithVar v);
+
+  /** Get an explanation and proof for this constraint from the equality engine
+   */
+  TrustNode eeExplain(ConstraintCP c) const;
+  /** Get an explanation for this constraint from the equality engine */
+  void eeExplain(ConstraintCP c, NodeBuilder& nb) const;
+
+  /**
+   * Returns a constraint with the variable v, the constraint type t, and a value
+   * dominated by r (explained below) if such a constraint exists in the database.
+   * If no such constraint exists, NullConstraint is returned.
+   *
+   * t must be either UpperBound or LowerBound.
+   * The returned value v is dominated:
+   *  If t is UpperBound, r <= v
+   *  If t is LowerBound, r >= v
+   *
+   * variableDatabaseIsSetup(v) must be true.
+   */
+  ConstraintP getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const;
+
+  /** Returns the constraint, if it exists */
+  ConstraintP lookupConstraint(ArithVar v, ConstraintType t, const DeltaRational& r) const;
+
+  /**
+   * Returns a constraint with the variable v, the constraint type t and the value r.
+   * If there is such a constraint in the database already, it is returned.
+   * If there is no such constraint, this constraint is added to the database.
+   *
+   */
+  ConstraintP getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r);
+
+  /**
+   * Returns a constraint of the given type for the value and variable
+   * for the given ValueCollection, vc.
+   * This is made if there is no such constraint.
+   */
+  ConstraintP ensureConstraint(ValueCollection& vc, ConstraintType t);
+
+
+  void deleteConstraintAndNegation(ConstraintP c);
+
+  /** Given constraints `a` and `b` such that `a OR b` by unate reasoning,
+   *  adds a TrustNode to `out` which proves `a OR b` as a lemma.
+   *
+   *  Example: `x <= 5` OR `5 <= x`.
+   */
+  void proveOr(std::vector<TrustNode>& out,
+               ConstraintP a,
+               ConstraintP b,
+               bool negateSecond) const;
+  /** Given constraints `a` and `b` such that `a` implies `b` by unate
+   * reasoning, adds a TrustNode to `out` which proves `-a OR b` as a lemma.
+   *
+   *  Example: `x >= 5` -> `x >= 4`.
+   */
+  void implies(std::vector<TrustNode>& out, ConstraintP a, ConstraintP b) const;
+  /** Given constraints `a` and `b` such that `not(a AND b)` by unate reasoning,
+   *  adds a TrustNode to `out` which proves `-a OR -b` as a lemma.
+   *
+   *  Example: `x >= 4` -> `x <= 3`.
+   */
+  void mutuallyExclusive(std::vector<TrustNode>& out,
+                         ConstraintP a,
+                         ConstraintP b) const;
+
+  /**
+   * Outputs a minimal set of unate implications onto the vector for the variable.
+   * This outputs lemmas of the general forms
+   *     (= p c) implies (<= p d) for c < d, or
+   *     (= p c) implies (not (= p d)) for c != d.
+   */
+  void outputUnateEqualityLemmas(std::vector<TrustNode>& lemmas) const;
+  void outputUnateEqualityLemmas(std::vector<TrustNode>& lemmas,
+                                 ArithVar v) const;
+
+  /**
+   * Outputs a minimal set of unate implications onto the vector for the variable.
+   *
+   * If ineqs is true, this outputs lemmas of the general form
+   *     (<= p c) implies (<= p d) for c < d.
+   */
+  void outputUnateInequalityLemmas(std::vector<TrustNode>& lemmas) const;
+  void outputUnateInequalityLemmas(std::vector<TrustNode>& lemmas,
+                                   ArithVar v) const;
+
+  void unatePropLowerBound(ConstraintP curr, ConstraintP prev);
+  void unatePropUpperBound(ConstraintP curr, ConstraintP prev);
+  void unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB);
+
+  /** AntecendentID must be in range. */
+  ConstraintCP getAntecedent(AntecedentId p) const;
+
+  bool isProofEnabled() const { return d_pnm != nullptr; }
+
+ private:
+  /** returns true if cons is now in conflict. */
+  bool handleUnateProp(ConstraintP ant, ConstraintP cons);
+
+  DenseSet d_reclaimable;
+
+  class Statistics {
+  public:
+    IntStat d_unatePropagateCalls;
+    IntStat d_unatePropagateImplications;
+
+    Statistics();
+  } d_statistics;
+
+}; /* ConstraintDatabase */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__CONSTRAINT_H */
diff --git a/src/theory/arith/linear/constraint_forward.h b/src/theory/arith/linear/constraint_forward.h
new file mode 100644 (file)
index 0000000..fa7f16c
--- /dev/null
@@ -0,0 +1,53 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Aina Niemetz
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Forward declarations of the ConstraintValue and ConstraintDatabase
+ * classes.
+ *
+ * This is the forward declarations of the ConstraintValue and
+ * ConstraintDatabase and the typedef for Constraint.
+ * This is used to break circular dependencies and minimize interaction
+ * between header files.
+ */
+
+#ifndef CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H
+#define CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H
+
+#include <vector>
+
+#include "cvc5_private.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class Constraint;
+typedef Constraint* ConstraintP;
+typedef const Constraint* ConstraintCP;
+
+static constexpr ConstraintP NullConstraint = nullptr;
+
+class ConstraintDatabase;
+
+typedef std::vector<ConstraintCP> ConstraintCPVec;
+
+typedef std::vector<Rational> RationalVector;
+typedef RationalVector* RationalVectorP;
+typedef const RationalVector* RationalVectorCP;
+static constexpr RationalVectorCP RationalVectorCPSentinel = nullptr;
+static constexpr RationalVectorP RationalVectorPSentinel = nullptr;
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H */
diff --git a/src/theory/arith/linear/cut_log.cpp b/src/theory/arith/linear/cut_log.cpp
new file mode 100644 (file)
index 0000000..9bf3cb1
--- /dev/null
@@ -0,0 +1,711 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Andrew Reynolds, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/cut_log.h"
+
+#include <limits.h>
+#include <math.h>
+
+#include <cmath>
+#include <iomanip>
+#include <map>
+
+#include "base/cvc5config.h"
+#include "base/output.h"
+#include "theory/arith/linear/approx_simplex.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/normal_form.h"
+#include "util/ostream_util.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+NodeLog::const_iterator NodeLog::begin() const { return d_cuts.begin(); }
+NodeLog::const_iterator NodeLog::end() const { return d_cuts.end(); }
+
+NodeLog& TreeLog::getNode(int nid) {
+  ToNodeMap::iterator i = d_toNode.find(nid);
+  Assert(i != d_toNode.end());
+  return (*i).second;
+}
+
+TreeLog::const_iterator TreeLog::begin() const { return d_toNode.begin(); }
+TreeLog::const_iterator TreeLog::end() const { return d_toNode.end(); }
+
+int TreeLog::getExecutionOrd(){
+  int res = next_exec_ord;
+  ++next_exec_ord;
+  return res;
+}
+void TreeLog::makeInactive(){  d_active = false; }
+void TreeLog::makeActive(){  d_active = true; }
+bool TreeLog::isActivelyLogging() const { return d_active; }
+
+
+PrimitiveVec::PrimitiveVec()
+  : len(0)
+  , inds(NULL)
+  , coeffs(NULL)
+{}
+
+PrimitiveVec::~PrimitiveVec(){
+  clear();
+}
+bool PrimitiveVec::initialized() const {
+  return inds != NULL;
+}
+void PrimitiveVec::clear() {
+  if(initialized()){
+    delete[] inds;
+    delete[] coeffs;
+    len = 0;
+    inds = NULL;
+    coeffs = NULL;
+  }
+}
+void PrimitiveVec::setup(int l){
+  Assert(!initialized());
+  len = l;
+  inds = new int[1+len];
+  coeffs = new double[1+len];
+}
+void PrimitiveVec::print(std::ostream& out) const{
+  Assert(initialized());
+  StreamFormatScope scope(out);
+
+  out << len << " " << std::setprecision(15);
+  for(int i = 1; i <= len; ++i){
+    out << "["<< inds[i] <<", " << coeffs[i]<<"]";
+  }
+}
+std::ostream& operator<<(std::ostream& os, const PrimitiveVec& pv){
+  pv.print(os);
+  return os;
+}
+
+CutInfo::CutInfo(CutInfoKlass kl, int eid, int o)
+    : d_klass(kl),
+      d_execOrd(eid),
+      d_poolOrd(o),
+      d_cutType(kind::UNDEFINED_KIND),
+      d_cutRhs(),
+      d_cutVec(),
+      d_mAtCreation(-1),
+      d_N(-1),
+      d_rowId(-1),
+      d_exactPrecision(nullptr),
+      d_explanation(nullptr)
+{}
+
+CutInfo::~CutInfo(){
+}
+
+int CutInfo::getId() const {
+  return d_execOrd;
+}
+
+int CutInfo::getRowId() const{
+  return d_rowId;
+}
+
+void CutInfo::setRowId(int rid){
+  d_rowId = rid;
+}
+
+void CutInfo::print(ostream& out) const{
+  out << "[CutInfo " << d_execOrd << " " << d_poolOrd
+      << " " << d_klass << " " << d_cutType << " " << d_cutRhs
+      << " ";
+  d_cutVec.print(out);
+  out << "]" << endl;
+}
+
+PrimitiveVec& CutInfo::getCutVector(){
+  return d_cutVec;
+}
+
+const PrimitiveVec& CutInfo::getCutVector() const{
+  return d_cutVec;
+}
+
+// void CutInfo::init_cut(int l){
+//   cut_vec.setup(l);
+// }
+
+Kind CutInfo::getKind() const{
+  return d_cutType;
+}
+
+void CutInfo::setKind(Kind k){
+  Assert(k == kind::LEQ || k == kind::GEQ);
+  d_cutType = k;
+}
+
+double CutInfo::getRhs() const{
+  return d_cutRhs;
+}
+
+void CutInfo::setRhs(double r){
+  d_cutRhs = r;
+}
+
+bool CutInfo::reconstructed() const { return d_exactPrecision != nullptr; }
+
+CutInfoKlass CutInfo::getKlass() const{
+  return d_klass;
+}
+
+int CutInfo::poolOrdinal() const{
+  return d_poolOrd;
+}
+
+void CutInfo::setDimensions(int N, int M){
+  d_mAtCreation = M;
+  d_N = N;
+}
+
+int CutInfo::getN() const{
+  return d_N;
+}
+
+int CutInfo::getMAtCreation() const{
+  return d_mAtCreation;
+}
+
+/* Returns true if the cut has an explanation. */
+bool CutInfo::proven() const { return d_explanation != nullptr; }
+
+bool CutInfo::operator<(const CutInfo& o) const{
+  return d_execOrd < o.d_execOrd;
+}
+
+
+void CutInfo::setReconstruction(const DenseVector& ep){
+  Assert(!reconstructed());
+  d_exactPrecision.reset(new DenseVector(ep));
+}
+
+void CutInfo::setExplanation(const ConstraintCPVec& ex){
+  Assert(reconstructed());
+  if (d_explanation == nullptr)
+  {
+    d_explanation.reset(new ConstraintCPVec(ex));
+  }
+  else
+  {
+    *d_explanation = ex;
+  }
+}
+
+void CutInfo::swapExplanation(ConstraintCPVec& ex){
+  Assert(reconstructed());
+  Assert(!proven());
+  if (d_explanation == nullptr)
+  {
+    d_explanation.reset(new ConstraintCPVec());
+  }
+  d_explanation->swap(ex);
+}
+
+const DenseVector& CutInfo::getReconstruction() const {
+  Assert(reconstructed());
+  return *d_exactPrecision;
+}
+
+void CutInfo::clearReconstruction(){
+  if(proven()){
+    d_explanation = nullptr;
+  }
+
+  if(reconstructed()){
+    d_exactPrecision = nullptr;
+  }
+
+  Assert(!reconstructed());
+  Assert(!proven());
+}
+
+const ConstraintCPVec& CutInfo::getExplanation() const {
+  Assert(proven());
+  return *d_explanation;
+}
+
+std::ostream& operator<<(std::ostream& os, const CutInfo& ci){
+  ci.print(os);
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& out, CutInfoKlass kl){
+  switch(kl){
+  case MirCutKlass:
+    out << "MirCutKlass"; break;
+  case GmiCutKlass:
+    out << "GmiCutKlass"; break;
+  case BranchCutKlass:
+    out << "BranchCutKlass"; break;
+  case RowsDeletedKlass:
+    out << "RowDeletedKlass"; break;
+  case UnknownKlass:
+    out << "UnknownKlass"; break;
+  default:
+    out << "unexpected CutInfoKlass"; break;
+  }
+  return out;
+}
+bool NodeLog::isBranch() const{
+  return d_brVar >= 0;
+}
+
+NodeLog::NodeLog()
+  : d_nid(-1)
+  , d_parent(NULL)
+  , d_tl(NULL)
+  , d_cuts()
+  , d_rowIdsSelected()
+  , d_stat(Open)
+  , d_brVar(-1)
+  , d_brVal(0.0)
+  , d_downId(-1)
+  , d_upId(-1)
+  , d_rowId2ArithVar()
+{}
+
+NodeLog::NodeLog(TreeLog* tl, int node, const RowIdMap& m)
+  : d_nid(node)
+  , d_parent(NULL)
+  , d_tl(tl)
+  , d_cuts()
+  , d_rowIdsSelected()
+  , d_stat(Open)
+  , d_brVar(-1)
+  , d_brVal(0.0)
+  , d_downId(-1)
+  , d_upId(-1)
+  , d_rowId2ArithVar(m)
+{}
+
+NodeLog::NodeLog(TreeLog* tl, NodeLog* parent, int node)
+  : d_nid(node)
+  , d_parent(parent)
+  , d_tl(tl)
+  , d_cuts()
+  , d_rowIdsSelected()
+  , d_stat(Open)
+  , d_brVar(-1)
+  , d_brVal(0.0)
+  , d_downId(-1)
+  , d_upId(-1)
+  , d_rowId2ArithVar()
+{}
+
+NodeLog::~NodeLog(){
+  CutSet::iterator i = d_cuts.begin(), iend = d_cuts.end();
+  for(; i != iend; ++i){
+    CutInfo* c = *i;
+    delete c;
+  }
+  d_cuts.clear();
+  Assert(d_cuts.empty());
+}
+
+std::ostream& operator<<(std::ostream& os, const NodeLog& nl){
+  nl.print(os);
+  return os;
+}
+
+void NodeLog::copyParentRowIds() {
+  Assert(d_parent != NULL);
+  d_rowId2ArithVar = d_parent->d_rowId2ArithVar;
+}
+
+int NodeLog::branchVariable() const {
+  return d_brVar;
+}
+double NodeLog::branchValue() const{
+  return d_brVal;
+}
+int NodeLog::getNodeId() const {
+  return d_nid;
+}
+int NodeLog::getDownId() const{
+  return d_downId;
+}
+int NodeLog::getUpId() const{
+  return d_upId;
+}
+void NodeLog::addSelected(int ord, int sel){
+  Assert(d_rowIdsSelected.find(ord) == d_rowIdsSelected.end());
+  d_rowIdsSelected[ord] = sel;
+  Trace("approx::nodelog") << "addSelected("<< ord << ", "<< sel << ")" << endl;
+}
+void NodeLog::applySelected() {
+  CutSet::iterator iter = d_cuts.begin(), iend = d_cuts.end(), todelete;
+  while(iter != iend){
+    CutInfo* curr = *iter;
+    int poolOrd = curr->poolOrdinal();
+    if(curr->getRowId() >= 0 ){
+      // selected previously, kip
+      ++iter;
+    }else if(curr->getKlass() == RowsDeletedKlass){
+      // skip
+      ++iter;
+    }else if(curr->getKlass() == BranchCutKlass){
+      // skip
+      ++iter;
+    }else if(d_rowIdsSelected.find(poolOrd) == d_rowIdsSelected.end()){
+      todelete = iter;
+      ++iter;
+      d_cuts.erase(todelete);
+      delete curr;
+    }else{
+      Trace("approx::nodelog") << "applySelected " << curr->getId() << " " << poolOrd << "->" << d_rowIdsSelected[poolOrd] << endl;
+      curr->setRowId( d_rowIdsSelected[poolOrd] );
+      ++iter;
+    }
+  }
+  d_rowIdsSelected.clear();
+}
+
+void NodeLog::applyRowsDeleted(const RowsDeleted& rd) {
+  std::map<int, CutInfo*> currInOrd; //sorted
+
+  const PrimitiveVec& cv = rd.getCutVector();
+  std::vector<int> sortedRemoved (cv.inds+1, cv.inds+cv.len+1);
+  sortedRemoved.push_back(INT_MAX);
+  std::sort(sortedRemoved.begin(), sortedRemoved.end());
+
+  if(TraceIsOn("approx::nodelog")){
+    Trace("approx::nodelog") << "Removing #" << sortedRemoved.size()<< "...";
+    for(unsigned k = 0; k<sortedRemoved.size(); k++){
+      Trace("approx::nodelog") << ", " << sortedRemoved[k];
+    }
+    Trace("approx::nodelog") << endl;
+    Trace("approx::nodelog") << "cv.len" << cv.len  << endl;
+  }
+
+  int min = sortedRemoved.front();
+
+  CutSet::iterator iter = d_cuts.begin(), iend = d_cuts.end();
+  while(iter != iend){
+    CutInfo* curr= *iter;
+    if(curr->getId() < rd.getId()){
+      if(d_rowId2ArithVar.find(curr->getRowId()) != d_rowId2ArithVar.end()){
+        if(curr->getRowId() >= min){
+          currInOrd.insert(make_pair(curr->getRowId(), curr));
+        }
+      }
+    }
+    ++iter;
+  }
+
+  RowIdMap::const_iterator i, end;
+  i=d_rowId2ArithVar.begin(), end = d_rowId2ArithVar.end();
+  for(; i != end; ++i){
+    int key = (*i).first;
+    if(key >= min){
+      if(currInOrd.find(key) == currInOrd.end()){
+        CutInfo* null = NULL;
+        currInOrd.insert(make_pair(key, null));
+      }
+    }
+  }
+
+
+
+  std::map<int, CutInfo*>::iterator j, jend;
+
+  int posInSorted = 0;
+  for(j = currInOrd.begin(), jend=currInOrd.end(); j!=jend; ++j){
+    int origOrd = (*j).first;
+    ArithVar v = d_rowId2ArithVar[origOrd];
+    int headRemovedOrd = sortedRemoved[posInSorted];
+    while(headRemovedOrd < origOrd){
+      ++posInSorted;
+      headRemovedOrd  = sortedRemoved[posInSorted];
+    }
+    // headRemoveOrd >= origOrd
+    Assert(headRemovedOrd >= origOrd);
+
+    CutInfo* ci = (*j).second;
+    if(headRemovedOrd == origOrd){
+
+      if(ci == NULL){
+        Trace("approx::nodelog") << "deleting from above because of " << rd << endl;
+        Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
+        d_rowId2ArithVar.erase(origOrd);
+      }else{
+        Trace("approx::nodelog") << "deleting " << ci << " because of " << rd << endl;
+        Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
+        d_rowId2ArithVar.erase(origOrd);
+        ci->setRowId(-1);
+      }
+    }else{
+      Assert(headRemovedOrd > origOrd);
+      // headRemoveOrd > origOrd
+      int newOrd = origOrd - posInSorted;
+      Assert(newOrd > 0);
+      if(ci == NULL){
+        Trace("approx::nodelog") << "shifting above down due to " << rd << endl;
+        Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
+        Trace("approx::nodelog") << "now have " << newOrd << " <-> " << v << endl;
+        d_rowId2ArithVar.erase(origOrd);
+        mapRowId(newOrd, v);
+      }else{
+        Trace("approx::nodelog") << "shifting " << ci << " down due to " << rd << endl;
+        Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
+        Trace("approx::nodelog") << "now have " << newOrd << " <-> " << v << endl;
+        ci->setRowId(newOrd);
+        d_rowId2ArithVar.erase(origOrd);
+        mapRowId(newOrd, v);
+      }
+    }
+  }
+
+}
+
+// void NodeLog::adjustRowId(CutInfo& ci, const RowsDeleted& rd) {
+//   int origRowId = ci.getRowId();
+//   int newRowId = ci.getRowId();
+//   ArithVar v = d_rowId2ArithVar[origRowId];
+
+//   const PrimitiveVec& cv = rd.getCutVector();
+
+//   for(int j = 1, N = cv.len; j <= N; j++){
+//     int ind = cv.inds[j];
+//     if(ind == origRowId){
+//       newRowId = -1;
+//       break;
+//     }else if(ind < origRowId){
+//       newRowId--;
+//     }
+//   }
+
+//   if(newRowId < 0){
+//     cout << "deleting " << ci << " because of " << rd << endl;
+//     cout << "had " << origRowId << " <-> " << v << endl;
+//     d_rowId2ArithVar.erase(origRowId);
+//     ci.setRowId(-1);
+//   }else if(newRowId != origRowId){
+//     cout << "adjusting " << ci << " because of " << rd << endl;
+//     cout << "had " << origRowId << " <-> " << v << endl;
+//     cout << "now have " << newRowId << " <-> " << v << endl;
+//     d_rowId2ArithVar.erase(origRowId);
+//     ci.setRowId(newRowId);
+//     mapRowId(newRowId, v);
+//   }else{
+//     cout << "row id unchanged " << ci << " because of " << rd << endl;
+//   }
+// }
+
+
+ArithVar NodeLog::lookupRowId(int rowId) const{
+  RowIdMap::const_iterator i = d_rowId2ArithVar.find(rowId);
+  if(i == d_rowId2ArithVar.end()){
+    return ARITHVAR_SENTINEL;
+  }else{
+    return (*i).second;
+  }
+}
+
+void NodeLog::mapRowId(int rowId, ArithVar v){
+  Assert(lookupRowId(rowId) == ARITHVAR_SENTINEL);
+  Trace("approx::nodelog")
+    << "On " << getNodeId()
+    << " adding row id " << rowId << " <-> " << v << endl;
+  d_rowId2ArithVar[rowId] = v;
+}
+
+
+
+void NodeLog::addCut(CutInfo* ci){
+  Assert(ci != NULL);
+  d_cuts.insert(ci);
+}
+
+void NodeLog::print(ostream& o) const{
+  o << "[n" << getNodeId();
+  for(const_iterator iter = begin(), iend = end(); iter != iend; ++iter ){
+    CutInfo* cut = *iter;
+    o << ", " << cut->poolOrdinal();
+    if(cut->getRowId() >= 0){
+      o << " " << cut->getRowId();
+    }
+  }
+  o << "]" << std::endl;
+}
+
+void NodeLog::closeNode(){
+  Assert(d_stat == Open);
+  d_stat = Closed;
+}
+
+void NodeLog::setBranch(int br, double val, int d, int u){
+  Assert(d_stat == Open);
+  d_brVar = br;
+  d_brVal = val;
+  d_downId = d;
+  d_upId = u;
+  d_stat = Branched;
+}
+
+TreeLog::TreeLog()
+  : next_exec_ord(0)
+  , d_toNode()
+  , d_branches()
+  , d_numCuts(0)
+  , d_active(false)
+{
+  NodeLog::RowIdMap empty;
+  reset(empty);
+}
+
+int TreeLog::getRootId() const{
+  return 1;
+}
+
+NodeLog& TreeLog::getRootNode(){
+  return getNode(getRootId());
+}
+
+void TreeLog::clear(){
+  next_exec_ord = 0;
+  d_toNode.clear();
+  d_branches.purge();
+
+  d_numCuts = 0;
+
+  // add root
+}
+
+void TreeLog::reset(const NodeLog::RowIdMap& m){
+  clear();
+  d_toNode.insert(make_pair(getRootId(), NodeLog(this, getRootId(), m)));
+}
+
+void TreeLog::addCut(){ d_numCuts++; }
+uint32_t TreeLog::cutCount() const { return d_numCuts; }
+void TreeLog::logBranch(uint32_t x){
+  d_branches.add(x);
+}
+uint32_t TreeLog::numBranches(uint32_t x){
+  return d_branches.count(x);
+}
+
+void TreeLog::branch(int nid, int br, double val, int dn, int up){
+  NodeLog& nl = getNode(nid);
+  nl.setBranch(br, val, dn, up);
+
+  d_toNode.insert(make_pair(dn, NodeLog(this, &nl, dn)));
+  d_toNode.insert(make_pair(up, NodeLog(this, &nl, up)));
+}
+
+void TreeLog::close(int nid){
+  NodeLog& nl = getNode(nid);
+  nl.closeNode();
+}
+
+
+
+// void TreeLog::applySelected() {
+//   std::map<int, NodeLog>::iterator iter, end;
+//   for(iter = d_toNode.begin(), end = d_toNode.end(); iter != end; ++iter){
+//     NodeLog& onNode = (*iter).second;
+//     //onNode.applySelected();
+//   }
+// }
+
+void TreeLog::print(ostream& o) const{
+  o << "TreeLog: " << d_toNode.size() << std::endl;
+  for(const_iterator iter = begin(), iend = end(); iter != iend; ++iter){
+    const NodeLog& onNode = (*iter).second;
+    onNode.print(o);
+  }
+}
+
+void TreeLog::applyRowsDeleted(int nid, const RowsDeleted& rd){
+  NodeLog& nl = getNode(nid);
+  nl.applyRowsDeleted(rd);
+}
+
+void TreeLog::mapRowId(int nid, int ind, ArithVar v){
+  NodeLog& nl = getNode(nid);
+  nl.mapRowId(ind, v);
+}
+
+void DenseVector::purge() {
+  lhs.purge();
+  rhs = Rational(0);
+}
+
+RowsDeleted::RowsDeleted(int execOrd, int nrows, const int num[])
+  : CutInfo(RowsDeletedKlass, execOrd, 0)
+{
+  d_cutVec.setup(nrows);
+  for(int j=1; j <= nrows; j++){
+    d_cutVec.coeffs[j] = 0;
+    d_cutVec.inds[j] = num[j];
+  }
+}
+
+BranchCutInfo::BranchCutInfo(int execOrd, int br, Kind dir, double val)
+  : CutInfo(BranchCutKlass, execOrd, 0)
+{
+  d_cutVec.setup(1);
+  d_cutVec.inds[1] = br;
+  d_cutVec.coeffs[1] = +1.0;
+  d_cutRhs = val;
+  d_cutType = dir;
+}
+
+void TreeLog::printBranchInfo(ostream& os) const{
+  uint32_t total = 0;
+  DenseMultiset::const_iterator iter = d_branches.begin(),  iend = d_branches.end();
+  for(; iter != iend; ++iter){
+    uint32_t el = *iter;
+    total += el;
+  }
+  os << "printBranchInfo() : " << total << endl;
+  iter = d_branches.begin(),  iend = d_branches.end();
+  for(; iter != iend; ++iter){
+    uint32_t el = *iter;
+    os << "["<<el <<", " << d_branches.count(el) << "]";
+  }
+  os << endl;
+}
+
+
+void DenseVector::print(std::ostream& os) const {
+  os << rhs << " + ";
+  print(os, lhs);
+}
+void DenseVector::print(ostream& out, const DenseMap<Rational>& v){
+  out << "[DenseVec len " <<  v.size();
+  DenseMap<Rational>::const_iterator iter, end;
+  for(iter = v.begin(), end = v.end(); iter != end; ++iter){
+    ArithVar x = *iter;
+    out << ", "<< x << " " << v[x];
+  }
+  out << "]";
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/cut_log.h b/src/theory/arith/linear/cut_log.h
new file mode 100644 (file)
index 0000000..bfcabc3
--- /dev/null
@@ -0,0 +1,295 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Morgan Deters, Kshitij Bansal
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <set>
+#include <unordered_map>
+
+#include "expr/kind.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "util/dense_map.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/** A low level vector of indexed doubles. */
+struct PrimitiveVec {
+  int len;
+  int* inds;
+  double* coeffs;
+  PrimitiveVec();
+  ~PrimitiveVec();
+  bool initialized() const;
+  void clear();
+  void setup(int l);
+  void print(std::ostream& out) const;
+};
+std::ostream& operator<<(std::ostream& os, const PrimitiveVec& pv);
+
+struct DenseVector {
+  DenseMap<Rational> lhs;
+  Rational rhs;
+  void purge();
+  void print(std::ostream& os) const;
+
+  static void print(std::ostream& os, const DenseMap<Rational>& lhs);
+};
+
+/** The different kinds of cuts. */
+enum CutInfoKlass{ MirCutKlass, GmiCutKlass, BranchCutKlass,
+                   RowsDeletedKlass,
+                   UnknownKlass};
+std::ostream& operator<<(std::ostream& os, CutInfoKlass kl);
+
+/** A general class for describing a cut. */
+class CutInfo {
+protected:
+  CutInfoKlass d_klass;
+  int d_execOrd;
+
+  int d_poolOrd;    /* cut's ordinal in the current node pool */
+  Kind d_cutType;   /* Lowerbound, upperbound or undefined. */
+  double d_cutRhs; /* right hand side of the cut */
+  PrimitiveVec d_cutVec; /* vector of the cut */
+
+  /**
+   * The number of rows at the time the cut was made.
+   * This is required to descramble indices after the fact!
+   */
+  int d_mAtCreation;
+
+  /** This is the number of structural variables. */
+  int d_N;
+
+  /** if selected, make this non-zero */
+  int d_rowId;
+
+  /* If the cut has been successfully created,
+   * the cut is stored in exact precision in d_exactPrecision.
+   * If the cut has not yet been proven, this is null.
+   */
+  std::unique_ptr<DenseVector> d_exactPrecision;
+
+  std::unique_ptr<ConstraintCPVec> d_explanation;
+
+ public:
+  CutInfo(CutInfoKlass kl, int cutid, int ordinal);
+
+  virtual ~CutInfo();
+
+  int getId() const;
+
+  int getRowId() const;
+  void setRowId(int rid);
+
+  void print(std::ostream& out) const;
+  //void init_cut(int l);
+  PrimitiveVec& getCutVector();
+  const PrimitiveVec& getCutVector() const;
+
+  Kind getKind() const;
+  void setKind(Kind k);
+
+
+  void setRhs(double r);
+  double getRhs() const;
+
+  CutInfoKlass getKlass() const;
+  int poolOrdinal() const;
+
+  void setDimensions(int N, int M);
+  int getN() const;
+  int getMAtCreation() const;
+
+  bool operator<(const CutInfo& o) const;
+
+  /* Returns true if the cut was successfully made in exact precision.*/
+  bool reconstructed() const;
+
+  /* Returns true if the cut has an explanation. */
+  bool proven() const;
+
+  void setReconstruction(const DenseVector& ep);
+  void setExplanation(const ConstraintCPVec& ex);
+  void swapExplanation(ConstraintCPVec& ex);
+
+  const DenseVector& getReconstruction() const;
+  const ConstraintCPVec& getExplanation() const;
+
+  void clearReconstruction();
+};
+std::ostream& operator<<(std::ostream& os, const CutInfo& ci);
+
+class BranchCutInfo : public CutInfo {
+public:
+  BranchCutInfo(int execOrd, int br,  Kind dir, double val);
+};
+
+class RowsDeleted : public CutInfo {
+public:
+  RowsDeleted(int execOrd, int nrows, const int num[]);
+};
+
+class TreeLog;
+
+class NodeLog {
+private:
+  int d_nid;
+  NodeLog* d_parent; /* If null this is the root */
+  TreeLog* d_tl;     /* TreeLog containing the node. */
+
+  struct CmpCutPointer{
+    int operator()(const CutInfo* a, const CutInfo* b) const{
+      return *a < *b;
+    }
+  };
+  typedef std::set<CutInfo*, CmpCutPointer> CutSet;
+  CutSet d_cuts;
+  std::map<int, int> d_rowIdsSelected;
+
+  enum Status {Open, Closed, Branched};
+  Status d_stat;
+
+  int d_brVar; // branching variable
+  double d_brVal;
+  int d_downId;
+  int d_upId;
+
+public:
+  typedef std::unordered_map<int, ArithVar> RowIdMap;
+private:
+  RowIdMap d_rowId2ArithVar;
+
+public:
+  NodeLog(); /* default constructor. */
+  NodeLog(TreeLog* tl, int node, const RowIdMap& m); /* makes a root node. */
+  NodeLog(TreeLog* tl, NodeLog* parent, int node);/* makes a non-root node. */
+
+  ~NodeLog();
+
+  int getNodeId() const;
+  void addSelected(int ord, int sel);
+  void applySelected();
+  void addCut(CutInfo* ci);
+  void print(std::ostream& o) const;
+
+  bool isRoot() const;
+  const NodeLog& getParent() const;
+
+  void copyParentRowIds();
+
+  bool isBranch() const;
+  int branchVariable() const;
+  double branchValue() const;
+
+  typedef CutSet::const_iterator const_iterator;
+  const_iterator begin() const;
+  const_iterator end() const;
+
+  void setBranch(int br, double val, int dn, int up);
+  void closeNode();
+
+  int getDownId() const;
+  int getUpId() const;
+
+  /**
+   * Looks up a row id to the appropriate arith variable.
+   * Be careful these are deleted in context during replay!
+   * failure returns ARITHVAR_SENTINEL */
+  ArithVar lookupRowId(int rowId) const;
+
+  /**
+   * Maps a row id to an arithvar.
+   * Be careful these are deleted in context during replay!
+   */
+  void mapRowId(int rowid, ArithVar v);
+  void applyRowsDeleted(const RowsDeleted& rd);
+
+};
+std::ostream& operator<<(std::ostream& os, const NodeLog& nl);
+
+class TreeLog {
+private:
+  int next_exec_ord;
+  typedef std::map<int, NodeLog> ToNodeMap;
+  ToNodeMap d_toNode;
+  DenseMultiset d_branches;
+
+  uint32_t d_numCuts;
+
+  bool d_active;
+
+public:
+  TreeLog();
+
+  NodeLog& getNode(int nid);
+  void branch(int nid, int br, double val, int dn, int up);
+  void close(int nid);
+
+  //void applySelected();
+  void print(std::ostream& o) const;
+
+  typedef ToNodeMap::const_iterator const_iterator;
+  const_iterator begin() const;
+  const_iterator end() const;
+
+  int getExecutionOrd();
+
+  void reset(const NodeLog::RowIdMap& m);
+
+  // Applies rd tp to the node with id nid
+  void applyRowsDeleted(int nid, const RowsDeleted& rd);
+
+  // Synonym for getNode(nid).mapRowId(ind, v)
+  void mapRowId(int nid, int ind, ArithVar v);
+
+private:
+  void clear();
+
+public:
+  void makeInactive();
+  void makeActive();
+
+  bool isActivelyLogging() const;
+
+  void addCut();
+  uint32_t cutCount() const;
+
+  void logBranch(uint32_t x);
+  uint32_t numBranches(uint32_t x);
+
+  int getRootId() const;
+
+  uint32_t numNodes() const{
+    return d_toNode.size();
+  }
+
+  NodeLog& getRootNode();
+  void printBranchInfo(std::ostream& os) const;
+};
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/dio_solver.cpp b/src/theory/arith/linear/dio_solver.cpp
new file mode 100644 (file)
index 0000000..b3eede0
--- /dev/null
@@ -0,0 +1,832 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Diophantine equation solver
+ *
+ * A Diophantine equation solver for the theory of arithmetic.
+ */
+#include "theory/arith/linear/dio_solver.h"
+
+#include <iostream>
+
+#include "base/output.h"
+#include "expr/skolem_manager.h"
+#include "options/arith_options.h"
+#include "smt/env.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/partial_model.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+inline Node makeIntegerVariable(){
+  NodeManager* nm = NodeManager::currentNM();
+  SkolemManager* sm = nm->getSkolemManager();
+  return sm->mkDummySkolem("intvar",
+                           nm->integerType(),
+                           "is an integer variable created by the dio solver");
+}
+
+DioSolver::DioSolver(Env& env)
+    : EnvObj(env),
+      d_lastUsedProofVariable(context(), 0),
+      d_inputConstraints(context()),
+      d_nextInputConstraintToEnqueue(context(), 0),
+      d_trail(context()),
+      d_subs(context()),
+      d_currentF(),
+      d_savedQueue(context()),
+      d_savedQueueIndex(context(), 0),
+      d_conflictIndex(context()),
+      d_maxInputCoefficientLength(context(), 0),
+      d_usedDecomposeIndex(context(), false),
+      d_lastPureSubstitution(context(), 0),
+      d_pureSubstitionIter(context(), 0),
+      d_decompositionLemmaQueue(context())
+{
+}
+
+DioSolver::Statistics::Statistics()
+    : d_conflictCalls(smtStatisticsRegistry().registerInt(
+        "theory::arith::dio::conflictCalls")),
+      d_cutCalls(
+          smtStatisticsRegistry().registerInt("theory::arith::dio::cutCalls")),
+      d_cuts(smtStatisticsRegistry().registerInt("theory::arith::dio::cuts")),
+      d_conflicts(
+          smtStatisticsRegistry().registerInt("theory::arith::dio::conflicts")),
+      d_conflictTimer(smtStatisticsRegistry().registerTimer(
+          "theory::arith::dio::conflictTimer")),
+      d_cutTimer(
+          smtStatisticsRegistry().registerTimer("theory::arith::dio::cutTimer"))
+{
+}
+
+bool DioSolver::queueConditions(TrailIndex t){
+  Trace("queueConditions") << !inConflict() << std::endl;
+  Trace("queueConditions") << gcdIsOne(t) << std::endl;
+  Trace("queueConditions") << !debugAnySubstitionApplies(t) << std::endl;
+  Trace("queueConditions") << !triviallySat(t) << std::endl;
+  Trace("queueConditions") << !triviallyUnsat(t) << std::endl;
+
+  return
+    !inConflict() &&
+    gcdIsOne(t) &&
+    !debugAnySubstitionApplies(t) &&
+    !triviallySat(t) &&
+    !triviallyUnsat(t);
+}
+
+size_t DioSolver::allocateProofVariable() {
+  Assert(d_lastUsedProofVariable <= d_proofVariablePool.size());
+  if(d_lastUsedProofVariable == d_proofVariablePool.size()){
+    Assert(d_lastUsedProofVariable == d_proofVariablePool.size());
+    Node intVar = makeIntegerVariable();
+    d_proofVariablePool.push_back(Variable(intVar));
+  }
+  size_t res = d_lastUsedProofVariable;
+  d_lastUsedProofVariable = d_lastUsedProofVariable + 1;
+  return res;
+}
+
+
+Node DioSolver::nextPureSubstitution(){
+  Assert(hasMorePureSubstitutions());
+  SubIndex curr = d_pureSubstitionIter;
+  d_pureSubstitionIter = d_pureSubstitionIter + 1;
+
+  Assert(d_subs[curr].d_fresh.isNull());
+  Variable v = d_subs[curr].d_eliminated;
+
+  SumPair sp = d_trail[d_subs[curr].d_constraint].d_eq;
+  Polynomial p = sp.getPolynomial();
+  Constant c = -sp.getConstant();
+  Polynomial cancelV = p + Polynomial::mkPolynomial(v);
+  Node eq = NodeManager::currentNM()->mkNode(kind::EQUAL, v.getNode(), cancelV.getNode());
+  return eq;
+}
+
+
+bool DioSolver::debugEqualityInInputEquations(Node eq){
+  typedef context::CDList<InputConstraint>::const_iterator const_iterator;
+  const_iterator i=d_inputConstraints.begin(), end = d_inputConstraints.end();
+  for(; i != end; ++i){
+    Node reason_i = (*i).d_reason;
+    if(eq == reason_i){
+      return true;
+    }
+  }
+  return false;
+}
+
+
+void DioSolver::pushInputConstraint(const Comparison& eq, Node reason){
+  Assert(!debugEqualityInInputEquations(reason));
+  Assert(eq.debugIsIntegral());
+  Assert(eq.getNode().getKind() == kind::EQUAL);
+
+  SumPair sp = eq.toSumPair();
+  if(sp.isNonlinear()){
+    return;
+  }
+
+
+
+  uint32_t length = sp.maxLength();
+  if(length > d_maxInputCoefficientLength){
+    d_maxInputCoefficientLength = length;
+  }
+
+  size_t varIndex = allocateProofVariable();
+  Variable proofVariable(d_proofVariablePool[varIndex]);
+  //Variable proofVariable(makeIntegerVariable());
+
+  TrailIndex posInTrail = d_trail.size();
+  Trace("dio::pushInputConstraint") << "pushInputConstraint @ " << posInTrail
+                                    << " " << eq.getNode()
+                                    << " " << reason << endl;
+  d_trail.push_back(Constraint(sp,Polynomial::mkPolynomial(proofVariable)));
+
+  size_t posInConstraintList = d_inputConstraints.size();
+  d_inputConstraints.push_back(InputConstraint(reason, posInTrail));
+
+  d_varToInputConstraintMap[proofVariable.getNode()] = posInConstraintList;
+}
+
+
+DioSolver::TrailIndex DioSolver::scaleEqAtIndex(DioSolver::TrailIndex i, const Integer& g){
+  Assert(g != 0);
+  Constant invg = Constant::mkConstant(Rational(Integer(1),g));
+  const SumPair& sp = d_trail[i].d_eq;
+  const Polynomial& proof = d_trail[i].d_proof;
+
+  SumPair newSP = sp * invg;
+  Polynomial newProof = proof * invg;
+
+  Assert(newSP.isIntegral());
+  Assert(newSP.gcd() == 1);
+
+  TrailIndex j = d_trail.size();
+
+  d_trail.push_back(Constraint(newSP, newProof));
+
+  Trace("arith::dio") << "scaleEqAtIndex(" << i <<","<<g<<")"<<endl;
+  Trace("arith::dio") << "derived "<< newSP.getNode()
+                      <<" with proof " << newProof.getNode() << endl;
+  return j;
+}
+
+Node DioSolver::proveIndex(TrailIndex i){
+  Assert(inRange(i));
+  const Polynomial& proof = d_trail[i].d_proof;
+  Assert(!proof.isConstant());
+
+  NodeBuilder nb(kind::AND);
+  for(Polynomial::iterator iter = proof.begin(), end = proof.end(); iter!= end; ++iter){
+    Monomial m = (*iter);
+    Assert(!m.isConstant());
+    VarList vl = m.getVarList();
+    Assert(vl.singleton());
+    Variable v = vl.getHead();
+
+    Node input = proofVariableToReason(v);
+    if(input.getKind() == kind::AND){
+      for(Node::iterator input_iter = input.begin(), input_end = input.end(); input_iter != input_end; ++input_iter){
+       Node inputChild = *input_iter;
+       nb << inputChild;
+      }
+    }else{
+      nb << input;
+    }
+  }
+
+  Node result = (nb.getNumChildren() == 1) ? nb[0] : (Node)nb;
+  Trace("arith::dio") << "Proof at " << i << " is "
+                      << d_trail[i].d_eq.getNode() << endl
+                      << d_trail[i].d_proof.getNode() << endl
+                      << " which becomes " << result << endl;
+  return result;
+}
+
+bool DioSolver::anyCoefficientExceedsMaximum(TrailIndex j) const{
+  uint32_t length = d_trail[j].d_eq.maxLength();
+  uint32_t nmonos = d_trail[j].d_eq.getPolynomial().numMonomials();
+
+  bool result =
+    nmonos >= 2 &&
+    length > d_maxInputCoefficientLength + MAX_GROWTH_RATE;
+  if(TraceIsOn("arith::dio::max") && result){
+
+    const SumPair& eq = d_trail[j].d_eq;
+    const Polynomial& proof = d_trail[j].d_proof;
+
+    Trace("arith::dio::max") << "about to drop:" << std::endl;
+    Trace("arith::dio::max") << "d_trail[" << j << "].d_eq = " << eq.getNode() << std::endl;
+    Trace("arith::dio::max") << "d_trail[" << j << "].d_proof = " << proof.getNode() << std::endl;
+  }
+  return result;
+}
+
+void DioSolver::enqueueInputConstraints(){
+  Assert(d_currentF.empty());
+  while(d_savedQueueIndex < d_savedQueue.size()){
+    d_currentF.push_back(d_savedQueue[d_savedQueueIndex]);
+    d_savedQueueIndex = d_savedQueueIndex + 1;
+  }
+
+  while(d_nextInputConstraintToEnqueue < d_inputConstraints.size()  && !inConflict()){
+    size_t curr = d_nextInputConstraintToEnqueue;
+    d_nextInputConstraintToEnqueue = d_nextInputConstraintToEnqueue + 1;
+
+    TrailIndex i = d_inputConstraints[curr].d_trailPos;
+    TrailIndex j = applyAllSubstitutionsToIndex(i);
+
+    if(!triviallySat(j)){
+      if(triviallyUnsat(j)){
+        raiseConflict(j);
+      }else{
+        TrailIndex k = reduceByGCD(j);
+
+        if(!inConflict()){
+          if(triviallyUnsat(k)){
+            raiseConflict(k);
+          }else if(!(triviallySat(k) || anyCoefficientExceedsMaximum(k))){
+            pushToQueueBack(k);
+          }
+        }
+      }
+    }
+  }
+}
+
+
+/*TODO Currently linear in the size of the Queue
+ *It is not clear if am O(log n) strategy would be better.
+ *Right before this in the algorithm is a substitution which could potentially
+ *effect the key values of everything in the queue.
+ */
+void DioSolver::moveMinimumByAbsToQueueFront(){
+  Assert(!queueEmpty());
+
+  //Select the minimum element.
+  size_t indexInQueue = 0;
+  Monomial minMonomial = d_trail[d_currentF[indexInQueue]].d_minimalMonomial;
+
+  size_t N = d_currentF.size();
+  for(size_t i=1; i < N; ++i){
+    Monomial curr = d_trail[d_currentF[i]].d_minimalMonomial;
+    if(curr.absCmp(minMonomial) < 0){
+      indexInQueue = i;
+      minMonomial = curr;
+    }
+  }
+
+  TrailIndex tmp = d_currentF[indexInQueue];
+  d_currentF[indexInQueue] = d_currentF.front();
+  d_currentF.front() = tmp;
+}
+
+bool DioSolver::queueEmpty() const{
+  return d_currentF.empty();
+}
+
+Node DioSolver::columnGcdIsOne() const{
+  std::unordered_map<Node, Integer> gcdMap;
+
+  std::deque<TrailIndex>::const_iterator iter, end;
+  for(iter = d_currentF.begin(), end = d_currentF.end(); iter != end; ++iter){
+    TrailIndex curr = *iter;
+    Polynomial p = d_trail[curr].d_eq.getPolynomial();
+    Polynomial::iterator monoIter = p.begin(), monoEnd = p.end();
+    for(; monoIter != monoEnd; ++monoIter){
+      Monomial m = *monoIter;
+      VarList vl = m.getVarList();
+      Node vlNode = vl.getNode();
+
+      Constant c = m.getConstant();
+      Integer zc = c.getValue().getNumerator();
+      if(gcdMap.find(vlNode) == gcdMap.end()){
+        // have not seen vl yet.
+        // gcd is c
+        Assert(c.isIntegral());
+        Assert(!m.absCoefficientIsOne());
+        gcdMap.insert(make_pair(vlNode, zc.abs()));
+      }else{
+        const Integer& currentGcd = gcdMap[vlNode];
+        Integer newGcd = currentGcd.gcd(zc);
+        if(newGcd == 1){
+          return vlNode;
+        }else{
+          gcdMap[vlNode] = newGcd;
+        }
+      }
+    }
+  }
+  return Node::null();
+}
+
+void DioSolver::saveQueue(){
+  std::deque<TrailIndex>::const_iterator iter, end;
+  for(iter = d_currentF.begin(), end = d_currentF.end(); iter != end; ++iter){
+    d_savedQueue.push_back(*iter);
+  }
+}
+
+DioSolver::TrailIndex DioSolver::impliedGcdOfOne(){
+  Node canReduce = columnGcdIsOne();
+  if(canReduce.isNull()){
+    return 0;
+  }else{
+    VarList vl = VarList::parseVarList(canReduce);
+
+    TrailIndex current;
+    Integer currentCoeff, currentGcd;
+
+    //step 1 find the first equation containing vl
+    //Set current and currentCoefficient
+    std::deque<TrailIndex>::const_iterator iter, end;
+    for(iter = d_currentF.begin(), end = d_currentF.end(); true; ++iter){
+      Assert(iter != end);
+      current = *iter;
+      Constant coeff = d_trail[current].d_eq.getPolynomial().getCoefficient(vl);
+      if(!coeff.isZero()){
+        currentCoeff = coeff.getValue().getNumerator();
+        currentGcd = currentCoeff.abs();
+
+        ++iter;
+        break;
+      }
+    }
+
+    //For the rest of the equations keep reducing until the coefficient is one
+    for(; iter != end; ++iter){
+      Trace("arith::dio") << "next round : " << currentCoeff << " " << currentGcd << endl;
+      TrailIndex inQueue = *iter;
+      Constant iqc = d_trail[inQueue].d_eq.getPolynomial().getCoefficient(vl);
+      if(!iqc.isZero()){
+        Integer inQueueCoeff = iqc.getValue().getNumerator();
+
+        //mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, mpz_t a, mpz_t b);
+        Integer g, s, t;
+        // g = a*s + b*t
+        Integer::extendedGcd(g, s, t, currentCoeff, inQueueCoeff);
+
+        Trace("arith::dio") << "extendedReduction : " << endl;
+        Trace("arith::dio") << g << " = " << s <<"*"<< currentCoeff << " + " << t <<"*"<< inQueueCoeff << endl;
+
+        Assert(g <= currentGcd);
+        if(g < currentGcd){
+          if(s.sgn() == 0){
+            Trace("arith::dio") << "extendedReduction drop" << endl;
+            Assert(inQueueCoeff.divides(currentGcd));
+            current = *iter;
+            currentCoeff = inQueueCoeff;
+            currentGcd = inQueueCoeff.abs();
+          }else{
+
+            Trace("arith::dio") << "extendedReduction combine" << endl;
+            TrailIndex next = combineEqAtIndexes(current, s, inQueue, t);
+
+            Assert(d_trail[next]
+                       .d_eq.getPolynomial()
+                       .getCoefficient(vl)
+                       .getValue()
+                       .getNumerator()
+                   == g);
+
+            current = next;
+            currentCoeff = g;
+            currentGcd = g;
+            if(currentGcd == 1){
+              return current;
+            }
+          }
+        }
+      }
+    }
+    //This is not reachble as it is assured that the gcd of the column is 1
+    Unreachable();
+  }
+}
+
+bool DioSolver::processEquations(bool allowDecomposition){
+  Assert(!inConflict());
+
+  enqueueInputConstraints();
+  while(! queueEmpty() && !inConflict()){
+    moveMinimumByAbsToQueueFront();
+
+    TrailIndex minimum = d_currentF.front();
+    TrailIndex reduceIndex;
+
+    Assert(inRange(minimum));
+    Assert(!inConflict());
+
+    Trace("arith::dio") << "processEquations " << minimum << " : " << d_trail[minimum].d_eq.getNode() << endl;
+
+    Assert(queueConditions(minimum));
+
+    bool canDirectlySolve = d_trail[minimum].d_minimalMonomial.absCoefficientIsOne();
+
+    std::pair<SubIndex, TrailIndex> p;
+    if(canDirectlySolve){
+      d_currentF.pop_front();
+      p = solveIndex(minimum);
+      reduceIndex = minimum;
+    }else{
+      TrailIndex implied = impliedGcdOfOne();
+
+      if(implied != 0){
+        p = solveIndex(implied);
+        reduceIndex = implied;
+      }else if(allowDecomposition){
+        d_currentF.pop_front();
+        p = decomposeIndex(minimum);
+        reduceIndex = minimum;
+      }else {
+        // Cannot make progress without decomposeIndex
+        saveQueue();
+        break;
+      }
+    }
+
+    SubIndex subIndex = p.first;
+    TrailIndex next = p.second;
+    subAndReduceCurrentFByIndex(subIndex);
+
+    if(next != reduceIndex){
+      if(triviallyUnsat(next)){
+        raiseConflict(next);
+      }else if(! triviallySat(next) ){
+        pushToQueueBack(next);
+      }
+    }
+  }
+
+  d_currentF.clear();
+  return inConflict();
+}
+
+Node DioSolver::processEquationsForConflict(){
+  TimerStat::CodeTimer codeTimer(d_statistics.d_conflictTimer);
+  ++(d_statistics.d_conflictCalls);
+
+  Assert(!inConflict());
+  if(processEquations(true)){
+    ++(d_statistics.d_conflicts);
+    return proveIndex(getConflictIndex());
+  }else{
+    return Node::null();
+  }
+}
+
+SumPair DioSolver::processEquationsForCut(){
+  TimerStat::CodeTimer codeTimer(d_statistics.d_cutTimer);
+  ++(d_statistics.d_cutCalls);
+
+  Assert(!inConflict());
+  if(processEquations(true)){
+    ++(d_statistics.d_cuts);
+    return purifyIndex(getConflictIndex());
+  }else{
+    return SumPair::mkZero();
+  }
+}
+
+
+SumPair DioSolver::purifyIndex(TrailIndex i){
+  // TODO: "This uses the substitution trail to reverse the substitutions from the sum term. Using the proof term should be more efficient."
+
+  SumPair curr = d_trail[i].d_eq;
+
+  Constant negOne = Constant::mkConstant(-1);
+
+  for(uint32_t revIter = d_subs.size(); revIter > 0; --revIter){
+    uint32_t i2 = revIter - 1;
+    Node freshNode = d_subs[i2].d_fresh;
+    if(freshNode.isNull()){
+      continue;
+    }else{
+      Variable var(freshNode);
+      Polynomial vsum = curr.getPolynomial();
+
+      Constant a = vsum.getCoefficient(VarList(var));
+      if(!a.isZero()){
+        const SumPair& sj = d_trail[d_subs[i2].d_constraint].d_eq;
+        Assert(sj.getPolynomial().getCoefficient(VarList(var)).isOne());
+        SumPair newSi = (curr * negOne) + (sj * a);
+        Assert(newSi.getPolynomial().getCoefficient(VarList(var)).isZero());
+        curr = newSi;
+      }
+    }
+  }
+  return curr;
+}
+
+DioSolver::TrailIndex DioSolver::combineEqAtIndexes(DioSolver::TrailIndex i, const Integer& q, DioSolver::TrailIndex j, const Integer& r){
+  Constant cq = Constant::mkConstant(q);
+  Constant cr = Constant::mkConstant(r);
+
+  const SumPair& si = d_trail[i].d_eq;
+  const SumPair& sj = d_trail[j].d_eq;
+
+  Trace("arith::dio") << "combineEqAtIndexes(" << i <<","<<q<<","<<j<<","<<r<<")"<<endl;
+  Trace("arith::dio") << "d_facts[i] = " << si.getNode() << endl
+                      << "d_facts[j] = " << sj.getNode() << endl;
+
+
+  SumPair newSi = (si * cq) + (sj * cr);
+
+
+  const Polynomial& pi = d_trail[i].d_proof;
+  const Polynomial& pj = d_trail[j].d_proof;
+  Polynomial newPi = (pi * cq) + (pj * cr);
+
+  TrailIndex k = d_trail.size();
+  d_trail.push_back(Constraint(newSi, newPi));
+
+
+  Trace("arith::dio") << "derived "<< newSi.getNode()
+                      <<" with proof " << newPi.getNode() << endl;
+
+  return k;
+
+}
+
+void DioSolver::printQueue(){
+  Trace("arith::dio") << "DioSolver::printQueue()" << endl;
+  for(TrailIndex i = 0, last = d_trail.size(); i < last; ++i){
+    Trace("arith::dio") << "d_trail[i].d_eq = " << d_trail[i].d_eq.getNode() << endl;
+    Trace("arith::dio") << "d_trail[i].d_proof = " << d_trail[i].d_proof.getNode() << endl;
+  }
+
+  Trace("arith::dio") << "DioSolver::printSubs()" << endl;
+  for(SubIndex si=0, sN=d_subs.size(); si < sN; ++si){
+    Trace("arith::dio") << "d_subs[i] = {"
+                        << "d_fresh="<< d_subs[si].d_fresh <<","
+                        << "d_eliminated="<< d_subs[si].d_eliminated.getNode() <<","
+                        << "d_constraint="<< d_subs[si].d_constraint <<"}" << endl;
+    Trace("arith::dio") << "d_trail[d_subs[i].d_constraint].d_eq="
+                        << d_trail[d_subs[si].d_constraint].d_eq.getNode() << endl;
+  }
+}
+
+DioSolver::TrailIndex DioSolver::applyAllSubstitutionsToIndex(DioSolver::TrailIndex trailIndex){
+  TrailIndex currentIndex = trailIndex;
+  for(SubIndex subIter = 0, siEnd = d_subs.size(); subIter < siEnd; ++subIter){
+    currentIndex = applySubstitution(subIter, currentIndex);
+  }
+  return currentIndex;
+}
+
+bool DioSolver::debugSubstitutionApplies(DioSolver::SubIndex si, DioSolver::TrailIndex ti){
+  Variable var = d_subs[si].d_eliminated;
+
+  const SumPair& curr = d_trail[ti].d_eq;
+  Polynomial vsum = curr.getPolynomial();
+
+  Constant a = vsum.getCoefficient(VarList(var));
+  return !a.isZero();
+}
+
+bool DioSolver::debugAnySubstitionApplies(DioSolver::TrailIndex i){
+  for(SubIndex subIter = 0, siEnd = d_subs.size(); subIter < siEnd; ++subIter){
+    if(debugSubstitutionApplies(subIter, i)){
+      return true;
+    }
+  }
+  return false;
+}
+
+std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::solveIndex(DioSolver::TrailIndex i){
+  const SumPair& si = d_trail[i].d_eq;
+
+  Trace("arith::dio") << "before solveIndex("<<i<<":"<<si.getNode()<< ")" << endl;
+
+#ifdef CVC5_ASSERTIONS
+  const Polynomial& p = si.getPolynomial();
+#endif
+
+  Assert(p.isIntegral());
+
+  Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
+  const Monomial av = d_trail[i].d_minimalMonomial;
+
+  VarList vl = av.getVarList();
+  Assert(vl.singleton());
+  Variable var = vl.getHead();
+  Constant a = av.getConstant();
+  Integer a_abs = a.getValue().getNumerator().abs();
+
+  Assert(a_abs == 1);
+
+  TrailIndex ci = !a.isNegative() ? scaleEqAtIndex(i, Integer(-1)) : i;
+
+  SubIndex subBy = d_subs.size();
+  d_subs.push_back(Substitution(Node::null(), var, ci));
+
+  Trace("arith::dio") << "after solveIndex " <<  d_trail[ci].d_eq.getNode() << " for " << av.getNode() << endl;
+  Assert(d_trail[ci].d_eq.getPolynomial().getCoefficient(vl)
+         == Constant::mkConstant(-1));
+
+  return make_pair(subBy, i);
+}
+
+std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::decomposeIndex(DioSolver::TrailIndex i){
+  const SumPair& si = d_trail[i].d_eq;
+
+  d_usedDecomposeIndex = true;
+
+  Trace("arith::dio") << "before decomposeIndex("<<i<<":"<<si.getNode()<< ")" << endl;
+
+#ifdef CVC5_ASSERTIONS
+  const Polynomial& p = si.getPolynomial();
+#endif
+
+  Assert(p.isIntegral());
+
+  Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
+  const Monomial& av = d_trail[i].d_minimalMonomial;
+
+  VarList vl = av.getVarList();
+  Assert(vl.singleton());
+  Variable var = vl.getHead();
+  Constant a = av.getConstant();
+  Integer a_abs = a.getValue().getNumerator().abs();
+
+  Assert(a_abs > 1);
+
+  //It is not sufficient to reduce the case where abs(a) == 1 to abs(a) > 1.
+  //We need to handle both cases seperately to ensure termination.
+  Node qr = SumPair::computeQR(si, a.getValue().getNumerator());
+
+  Assert(qr.getKind() == kind::ADD);
+  Assert(qr.getNumChildren() == 2);
+  SumPair q = SumPair::parseSumPair(qr[0]);
+  SumPair r = SumPair::parseSumPair(qr[1]);
+
+  Assert(q.getPolynomial().getCoefficient(vl) == Constant::mkConstant(1));
+
+  Assert(!r.isZero());
+  Node freshNode = makeIntegerVariable();
+  Variable fresh(freshNode);
+  SumPair fresh_one=SumPair::mkSumPair(fresh);
+  SumPair fresh_a = fresh_one * a;
+
+  SumPair newSI = SumPair(fresh_one) - q;
+  // this normalizes the coefficient of var to -1
+
+
+  TrailIndex ci = d_trail.size();
+  d_trail.push_back(Constraint(newSI, Polynomial::mkZero()));
+  // no longer reference av safely!
+  addTrailElementAsLemma(ci);
+
+  Trace("arith::dio") << "Decompose ci(" << ci <<":" <<  d_trail[ci].d_eq.getNode()
+                      << ") for " << d_trail[i].d_minimalMonomial.getNode() << endl;
+  Assert(d_trail[ci].d_eq.getPolynomial().getCoefficient(vl)
+         == Constant::mkConstant(-1));
+
+  SumPair newFact = r + fresh_a;
+
+  TrailIndex nextIndex = d_trail.size();
+  d_trail.push_back(Constraint(newFact, d_trail[i].d_proof));
+
+  SubIndex subBy = d_subs.size();
+  d_subs.push_back(Substitution(freshNode, var, ci));
+
+  Trace("arith::dio") << "Decompose nextIndex " <<  d_trail[nextIndex].d_eq.getNode() << endl;
+  return make_pair(subBy, nextIndex);
+}
+
+
+DioSolver::TrailIndex DioSolver::applySubstitution(DioSolver::SubIndex si, DioSolver::TrailIndex ti){
+  Variable var = d_subs[si].d_eliminated;
+  TrailIndex subIndex = d_subs[si].d_constraint;
+
+  const SumPair& curr = d_trail[ti].d_eq;
+  Polynomial vsum = curr.getPolynomial();
+
+  Constant a = vsum.getCoefficient(VarList(var));
+  Assert(a.isIntegral());
+  if(!a.isZero()){
+    Integer one(1);
+    TrailIndex afterSub = combineEqAtIndexes(ti, one, subIndex, a.getValue().getNumerator());
+    Assert(d_trail[afterSub]
+               .d_eq.getPolynomial()
+               .getCoefficient(VarList(var))
+               .isZero());
+    return afterSub;
+  }else{
+    return ti;
+  }
+}
+
+
+DioSolver::TrailIndex DioSolver::reduceByGCD(DioSolver::TrailIndex ti){
+  const SumPair& sp = d_trail[ti].d_eq;
+  Polynomial vsum = sp.getPolynomial();
+  Constant c = sp.getConstant();
+
+  Trace("arith::dio") << "reduceByGCD " << vsum.getNode() << endl;
+  Assert(!vsum.isConstant());
+  Integer g = vsum.gcd();
+  Assert(g >= 1);
+  Trace("arith::dio") << "gcd("<< vsum.getNode() <<")=" << g << " " << c.getValue() << endl;
+  if(g.divides(c.getValue().getNumerator())){
+    if(g > 1){
+      return scaleEqAtIndex(ti, g);
+    }else{
+      return ti;
+    }
+  }else{
+    raiseConflict(ti);
+    return ti;
+  }
+}
+
+bool DioSolver::triviallySat(TrailIndex i){
+  const SumPair& eq = d_trail[i].d_eq;
+  if(eq.isConstant()){
+    return eq.getConstant().isZero();
+  }else{
+    return false;
+  }
+}
+
+bool DioSolver::triviallyUnsat(DioSolver::TrailIndex i){
+  const SumPair& eq = d_trail[i].d_eq;
+  if(eq.isConstant()){
+    return !eq.getConstant().isZero();
+  }else{
+    return false;
+  }
+}
+
+
+bool DioSolver::gcdIsOne(DioSolver::TrailIndex i){
+  const SumPair& eq = d_trail[i].d_eq;
+  return eq.gcd() == Integer(1);
+}
+
+void DioSolver::subAndReduceCurrentFByIndex(DioSolver::SubIndex subIndex){
+  size_t N = d_currentF.size();
+
+  size_t readIter = 0, writeIter = 0;
+  for(; readIter < N && !inConflict(); ++readIter){
+    TrailIndex curr = d_currentF[readIter];
+    TrailIndex nextTI = applySubstitution(subIndex, curr);
+    if(nextTI == curr){
+      d_currentF[writeIter] = curr;
+      ++writeIter;
+    }else{
+      Assert(nextTI != curr);
+
+      if(triviallyUnsat(nextTI)){
+        raiseConflict(nextTI);
+      }else if(!triviallySat(nextTI)){
+        TrailIndex nextNextTI = reduceByGCD(nextTI);
+
+        if(!(inConflict() || anyCoefficientExceedsMaximum(nextNextTI))){
+          Assert(queueConditions(nextNextTI));
+          d_currentF[writeIter] = nextNextTI;
+          ++writeIter;
+        }
+      }
+    }
+  }
+  if(!inConflict() && writeIter < N){
+    d_currentF.resize(writeIter);
+  }
+}
+
+void DioSolver::addTrailElementAsLemma(TrailIndex i) {
+  if (options().arith.exportDioDecompositions)
+  {
+    d_decompositionLemmaQueue.push(i);
+  }
+}
+
+Node DioSolver::trailIndexToEquality(TrailIndex i) const {
+  const SumPair& sp = d_trail[i].d_eq;
+  Node n = sp.getNode();
+  Node zero =
+      NodeManager::currentNM()->mkConstRealOrInt(n.getType(), Rational(0));
+  Node eq = n.eqNode(zero);
+  return eq;
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/dio_solver.h b/src/theory/arith/linear/dio_solver.h
new file mode 100644 (file)
index 0000000..9887a23
--- /dev/null
@@ -0,0 +1,424 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Morgan Deters, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * A Diophantine equation solver for the theory of arithmetic.
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__DIO_SOLVER_H
+#define CVC5__THEORY__ARITH__DIO_SOLVER_H
+
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "context/cdlist.h"
+#include "context/cdmaybe.h"
+#include "context/cdo.h"
+#include "context/cdqueue.h"
+#include "smt/env_obj.h"
+#include "theory/arith/linear/normal_form.h"
+#include "util/rational.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::context {
+class Context;
+}
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class DioSolver : protected EnvObj
+{
+ private:
+  typedef size_t TrailIndex;
+  typedef size_t InputConstraintIndex;
+  typedef size_t SubIndex;
+
+  std::vector<Variable> d_proofVariablePool;
+  /** Sat context dependent. */
+  context::CDO<size_t> d_lastUsedProofVariable;
+
+  /**
+   * The set of input constraints is stored in a CDList.
+   * Each constraint point to an element of the trail.
+   */
+  struct InputConstraint {
+    Node d_reason;
+    TrailIndex d_trailPos;
+    InputConstraint(Node reason, TrailIndex pos) : d_reason(reason), d_trailPos(pos) {}
+  };
+  context::CDList<InputConstraint> d_inputConstraints;
+
+  /**
+   * This is the next input constraint to handle.
+   */
+  context::CDO<size_t> d_nextInputConstraintToEnqueue;
+
+  /**
+   * We maintain a map from the variables associated with proofs to an input constraint.
+   * These variables can then be used in polynomial manipulations.
+   */
+  typedef std::unordered_map<Node, InputConstraintIndex>
+      NodeToInputConstraintIndexMap;
+  NodeToInputConstraintIndexMap d_varToInputConstraintMap;
+
+  Node proofVariableToReason(const Variable& v) const{
+    Assert(d_varToInputConstraintMap.find(v.getNode())
+           != d_varToInputConstraintMap.end());
+    InputConstraintIndex pos = (*(d_varToInputConstraintMap.find(v.getNode()))).second;
+    Assert(pos < d_inputConstraints.size());
+    return d_inputConstraints[pos].d_reason;
+  }
+
+  /**
+   * The main work horse of the algorithm, the trail of constraints.
+   * Each constraint is a SumPair that implicitly represents an equality against 0.
+   *   d_trail[i].d_eq = (+ c (+ [(* coeff var)])) representing (+ [(* coeff var)]) = -c
+   * Each constraint has a proof in terms of a linear combination of the input constraints.
+   *   d_trail[i].d_proof
+   *
+   * Each Constraint also a monomial in d_eq.getPolynomial()
+   * of minimal absolute value by the coefficients.
+   * d_trail[i].d_minimalMonomial
+   *
+   * See Alberto's paper for how linear proofs are maintained for the abstract
+   * state machine in rules (7), (8) and (9).
+   */
+  struct Constraint {
+    SumPair d_eq;
+    Polynomial d_proof;
+    Monomial d_minimalMonomial;
+    Constraint(const SumPair& eq, const Polynomial& p) :
+      d_eq(eq), d_proof(p), d_minimalMonomial(d_eq.getPolynomial().selectAbsMinimum())
+    {}
+  };
+  context::CDList<Constraint> d_trail;
+
+  // /** Compare by d_minimal. */
+  // struct TrailMinimalCoefficientOrder {
+  //   const context::CDList<Constraint>& d_trail;
+  //   TrailMinimalCoefficientOrder(const context::CDList<Constraint>&
+  //   trail):
+  //     d_trail(trail)
+  //   {}
+
+  //   bool operator()(TrailIndex i, TrailIndex j){
+  //     return d_trail[i].d_minimalMonomial.absLessThan(d_trail[j].d_minimalMonomial);
+  //   }
+  // };
+
+  /**
+   * A substitution is stored as a constraint in the trail together with
+   * the variable to be eliminated, and a fresh variable if one was introduced.
+   * The variable d_subs[i].d_eliminated is substituted using the implicit equality in
+   * d_trail[d_subs[i].d_constraint]
+   *  - d_subs[i].d_eliminated is normalized to have coefficient -1 in
+   *    d_trail[d_subs[i].d_constraint].
+   *  - d_subs[i].d_fresh is either Node::null() or it is variable it is normalized
+   *    to have coefficient 1 in d_trail[d_subs[i].d_constraint].
+   */
+  struct Substitution {
+    Node d_fresh;
+    Variable d_eliminated;
+    TrailIndex d_constraint;
+    Substitution(Node f, const Variable& e, TrailIndex c) :
+      d_fresh(f), d_eliminated(e), d_constraint(c)
+    {}
+  };
+  context::CDList<Substitution> d_subs;
+
+  /**
+   * This is the queue of constraints to be processed in the current context level.
+   * This is to be empty upon entering solver and cleared upon leaving the solver.
+   *
+   * All elements in currentF:
+   * - are fully substituted according to d_subs.
+   * - !isConstant().
+   * - If the element is (+ constant (+ [(* coeff var)] )), then the gcd(coeff) = 1
+   */
+  std::deque<TrailIndex> d_currentF;
+  context::CDList<TrailIndex> d_savedQueue;
+  context::CDO<size_t> d_savedQueueIndex;
+  context::CDMaybe<TrailIndex> d_conflictIndex;
+
+  /**
+   * Drop derived constraints with a coefficient length larger than
+   * the maximum input constraints length than 2**MAX_GROWTH_RATE.
+   */
+  context::CDO<uint32_t> d_maxInputCoefficientLength;
+  static constexpr uint32_t MAX_GROWTH_RATE = 3;
+
+  /** Returns true if the element on the trail should be dropped.*/
+  bool anyCoefficientExceedsMaximum(TrailIndex j) const;
+
+  /**
+   * Is true if decomposeIndex has been used in this context.
+   */
+  context::CDO<bool> d_usedDecomposeIndex;
+
+  context::CDO<SubIndex> d_lastPureSubstitution;
+  context::CDO<SubIndex> d_pureSubstitionIter;
+
+  /**
+   * Decomposition lemma queue.
+   */
+  context::CDQueue<TrailIndex> d_decompositionLemmaQueue;
+
+ public:
+  /** Construct a Diophantine equation solver with the given context. */
+ DioSolver(Env& env);
+
+ /** Returns true if the substitutions use no new variables. */
+ bool hasMorePureSubstitutions() const
+ {
+   return d_pureSubstitionIter < d_lastPureSubstitution;
+  }
+
+  Node nextPureSubstitution();
+
+  /**
+   * Adds an equality to the queue of the DioSolver.
+   * orig is blamed in a conflict.
+   * orig can either be of the form (= p c) or (and ub lb).
+   * where ub is either (leq p c) or (not (> p [- c 1])), and
+   * where lb is either (geq p c) or (not (< p [+ c 1]))
+   *
+   * If eq cannot be used, this constraint is dropped.
+   */
+  void pushInputConstraint(const Comparison& eq, Node reason);
+
+  /**
+   * Processes the queue looking for any conflict.
+   * If a conflict is found, this returns conflict.
+   * Otherwise, it returns null.
+   * The conflict is guarenteed to be over literals given in addEquality.
+   */
+  Node processEquationsForConflict();
+
+  /**
+   * Processes the queue looking for an integer unsatisfiable cutting plane.
+   * If such a plane is found this returns an entailed plane using no
+   * fresh variables.
+   */
+  SumPair processEquationsForCut();
+
+private:
+  /** Returns true if the TrailIndex refers to a element in the trail. */
+  bool inRange(TrailIndex i) const{
+    return i < d_trail.size();
+  }
+
+  Node columnGcdIsOne() const;
+
+
+  /**
+   * Returns true if the context dependent flag for conflicts
+   * has been raised.
+   */
+  bool inConflict() const { return d_conflictIndex.isSet(); }
+
+  /** Raises a conflict at the index ti. */
+  void raiseConflict(TrailIndex ti){
+    Assert(!inConflict());
+    d_conflictIndex.set(ti);
+  }
+
+  /** Returns the conflict index. */
+  TrailIndex getConflictIndex() const{
+    Assert(inConflict());
+    return d_conflictIndex.get();
+  }
+
+  /**
+   * Allocates a "unique" proof variable.
+   * This variable is fresh with respect to the context.
+   * Returns index of the variable in d_variablePool;
+   */
+  size_t allocateProofVariable();
+
+
+  /** Empties the unproccessed input constraints into the queue. */
+  void enqueueInputConstraints();
+
+  /**
+   * Returns true if an input equality is in the map.
+   * This is expensive and is only for debug assertions.
+   */
+  bool debugEqualityInInputEquations(Node eq);
+
+  /** Applies the substitution at subIndex to currentF. */
+  void subAndReduceCurrentFByIndex(SubIndex d_subIndex);
+
+  /**
+   * Takes as input a TrailIndex i and an integer that divides d_trail[i].d_eq, and
+   * returns a TrailIndex j s.t.
+   *   d_trail[j].d_eq = (1/g) d_trail[i].d_eq
+   * and
+   *   d_trail[j].d_proof = (1/g) d_trail[i].d_proof.
+   *
+   * g must be non-zero.
+   *
+   * This corresponds to an application of Alberto's rule (7).
+   */
+  TrailIndex scaleEqAtIndex(TrailIndex i, const Integer& g);
+
+
+  /**
+   * Takes as input TrailIndex's i and j and Integer's q and r and a TrailIndex k s.t.
+   *   d_trail[k].d_eq == d_trail[i].d_eq * q + d_trail[j].d_eq * r
+   * and
+   *   d_trail[k].d_proof == d_trail[i].d_proof * q + d_trail[j].d_proof * r
+   *
+   * This corresponds to an application of Alberto's rule (8).
+   */
+  TrailIndex combineEqAtIndexes(TrailIndex i, const Integer& q, TrailIndex j, const Integer& r);
+
+  /**
+   * Decomposes the equation at index ti of trail by the variable
+   * with the lowest coefficient.
+   * This corresponds to an application of Alberto's rule (9).
+   *
+   * Returns a pair of a SubIndex and a TrailIndex.
+   * The SubIndex is the index of a newly introduced substition.
+   */
+  std::pair<SubIndex, TrailIndex> decomposeIndex(TrailIndex ti);
+
+  /** Solves the index at ti for the value in minimumMonomial. */
+  std::pair<SubIndex, TrailIndex> solveIndex(TrailIndex ti);
+
+  /** Prints the queue for debugging purposes to Trace("arith::dio"). */
+  void printQueue();
+
+  /**
+   * Exhaustively applies all substitutions discovered to an element of the trail.
+   * Returns a TrailIndex corresponding to the substitutions being applied.
+   */
+  TrailIndex applyAllSubstitutionsToIndex(TrailIndex i);
+
+  /**
+   * Applies a substitution to an element in the trail.
+   */
+  TrailIndex applySubstitution(SubIndex s, TrailIndex i);
+
+  /**
+   * Reduces the trail node at i by the gcd of the variables.
+   * Returns the new trail element.
+   *
+   * This raises the conflict flag if unsat is detected.
+   */
+  TrailIndex reduceByGCD(TrailIndex i);
+
+  /**
+   * Returns true if i'th element in the trail is trivially true.
+   * (0 = 0)
+   */
+  bool triviallySat(TrailIndex t);
+
+  /**
+   * Returns true if i'th element in the trail is trivially unsatisfiable.
+   * (1 = 0)
+   */
+  bool triviallyUnsat(TrailIndex t);
+
+  /** Returns true if the gcd of the i'th element of the trail is 1.*/
+  bool gcdIsOne(TrailIndex t);
+
+  bool debugAnySubstitionApplies(TrailIndex t);
+  bool debugSubstitutionApplies(SubIndex si, TrailIndex ti);
+
+
+  /** Returns true if the queue of nodes to process is empty. */
+  bool queueEmpty() const;
+
+  bool queueConditions(TrailIndex t);
+
+
+  void pushToQueueBack(TrailIndex t){
+    Assert(queueConditions(t));
+    d_currentF.push_back(t);
+  }
+
+  void pushToQueueFront(TrailIndex t){
+    Assert(queueConditions(t));
+    d_currentF.push_front(t);
+  }
+
+  /**
+   * Moves the minimum Constraint by absolute value of the minimum coefficient to
+   * the front of the queue.
+   */
+  void moveMinimumByAbsToQueueFront();
+
+  void saveQueue();
+
+  TrailIndex impliedGcdOfOne();
+
+
+  /**
+   * Processing the current set of equations.
+   *
+   * decomposeIndex() rule is only applied if allowDecomposition is true.
+   */
+  bool processEquations(bool allowDecomposition);
+
+  /**
+   * Constructs a proof from any d_trail[i] in terms of input literals.
+   */
+  Node proveIndex(TrailIndex i);
+
+  /**
+   * Returns the SumPair in d_trail[i].d_eq with all of the fresh variables purified out.
+   */
+  SumPair purifyIndex(TrailIndex i);
+
+public:
+  bool hasMoreDecompositionLemmas() const{
+    return !d_decompositionLemmaQueue.empty();
+  }
+  Node nextDecompositionLemma() {
+    Assert(hasMoreDecompositionLemmas());
+    TrailIndex front = d_decompositionLemmaQueue.front();
+    d_decompositionLemmaQueue.pop();
+    return trailIndexToEquality(front);
+  }
+private:
+  Node trailIndexToEquality(TrailIndex i) const;
+  void addTrailElementAsLemma(TrailIndex i);
+
+public:
+
+  /** These fields are designed to be accessible to TheoryArith methods. */
+  class Statistics {
+  public:
+
+    IntStat d_conflictCalls;
+    IntStat d_cutCalls;
+
+    IntStat d_cuts;
+    IntStat d_conflicts;
+
+    TimerStat d_conflictTimer;
+    TimerStat d_cutTimer;
+
+    Statistics();
+  };
+
+  Statistics d_statistics;
+}; /* class DioSolver */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__DIO_SOLVER_H */
diff --git a/src/theory/arith/linear/dual_simplex.cpp b/src/theory/arith/linear/dual_simplex.cpp
new file mode 100644 (file)
index 0000000..3ef92bc
--- /dev/null
@@ -0,0 +1,245 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ */
+#include "theory/arith/linear/dual_simplex.h"
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "smt/env.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/linear_equality.h"
+
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+DualSimplexDecisionProcedure::DualSimplexDecisionProcedure(
+    Env& env,
+    LinearEqualityModule& linEq,
+    ErrorSet& errors,
+    RaiseConflict conflictChannel,
+    TempVarMalloc tvmalloc)
+    : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
+      d_pivotsInRound(),
+      d_statistics(d_pivots)
+{ }
+
+DualSimplexDecisionProcedure::Statistics::Statistics(uint32_t& pivots)
+    : d_statUpdateConflicts(smtStatisticsRegistry().registerInt(
+        "theory::arith::dual::UpdateConflicts")),
+      d_processSignalsTime(smtStatisticsRegistry().registerTimer(
+          "theory::arith::dual::findConflictOnTheQueueTime")),
+      d_simplexConflicts(smtStatisticsRegistry().registerInt(
+          "theory::arith::dual::simplexConflicts")),
+      d_recentViolationCatches(smtStatisticsRegistry().registerInt(
+          "theory::arith::dual::recentViolationCatches")),
+      d_searchTime(smtStatisticsRegistry().registerTimer(
+          "theory::arith::dual::searchTime")),
+      d_finalCheckPivotCounter(
+          smtStatisticsRegistry().registerReference<uint32_t>(
+              "theory::arith::dual::lastPivots", pivots))
+{
+}
+
+Result::Status DualSimplexDecisionProcedure::dualFindModel(bool exactResult)
+{
+  Assert(d_conflictVariables.empty());
+
+  d_pivots = 0;
+
+  if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
+    Trace("arith::findModel") << "dualFindModel() trivial" << endl;
+    return Result::SAT;
+  }
+
+  // We need to reduce this because of
+  d_errorSet.reduceToSignals();
+  d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
+
+  if(processSignals()){
+    d_conflictVariables.purge();
+
+    Trace("arith::findModel") << "dualFindModel() early conflict" << endl;
+    return Result::UNSAT;
+  }else if(d_errorSet.errorEmpty()){
+    Trace("arith::findModel") << "dualFindModel() fixed itself" << endl;
+    Assert(!d_errorSet.moreSignals());
+    return Result::SAT;
+  }
+
+  Trace("arith::findModel") << "dualFindModel() start non-trivial" << endl;
+
+  Result::Status result = Result::UNKNOWN;
+
+  exactResult |= d_varOrderPivotLimit < 0;
+
+  uint32_t checkPeriod = options().arith.arithSimplexCheckPeriod;
+  if (result == Result::UNKNOWN)
+  {
+    uint32_t numDifferencePivots = options().arith.arithHeuristicPivots < 0
+                                       ? d_numVariables + 1
+                                       : options().arith.arithHeuristicPivots;
+    // The signed to unsigned conversion is safe.
+    if(numDifferencePivots > 0){
+
+      d_errorSet.setSelectionRule(d_heuristicRule);
+      if(searchForFeasibleSolution(numDifferencePivots)){
+        result = Result::UNSAT;
+      }
+    }
+  }
+  Assert(!d_errorSet.moreSignals());
+
+  if(!d_errorSet.errorEmpty() && result != Result::UNSAT){
+    if(exactResult){
+      d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
+      while(!d_errorSet.errorEmpty() && result != Result::UNSAT){
+        Assert(checkPeriod > 0);
+        if(searchForFeasibleSolution(checkPeriod)){
+          result = Result::UNSAT;
+        }
+      }
+    }
+    else if (d_varOrderPivotLimit > 0)
+    {
+      d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
+      if (searchForFeasibleSolution(d_varOrderPivotLimit))
+      {
+        result = Result::UNSAT;
+      }
+    }
+  }
+
+  Assert(!d_errorSet.moreSignals());
+  if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
+  {
+    result = Result::SAT;
+  }
+
+  d_pivotsInRound.purge();
+  // ensure that the conflict variable is still in the queue.
+  d_conflictVariables.purge();
+
+  Trace("arith::findModel") << "end findModel() " << result << endl;
+
+  return result;
+}
+
+//corresponds to Check() in dM06
+//template <SimplexDecisionProcedure::PreferenceFunction pf>
+bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingIterations){
+  TimerStat::CodeTimer codeTimer(d_statistics.d_searchTime);
+
+  Trace("arith") << "searchForFeasibleSolution" << endl;
+  Assert(remainingIterations > 0);
+
+  while(remainingIterations > 0 && !d_errorSet.focusEmpty()){
+    if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+    Assert(d_conflictVariables.empty());
+    ArithVar x_i = d_errorSet.topFocusVariable();
+
+    Trace("arith::update::select") << "selectSmallestInconsistentVar()=" << x_i << endl;
+    if(x_i == ARITHVAR_SENTINEL){
+      Trace("arith::update") << "No inconsistent variables" << endl;
+      return false; //sat
+    }
+
+    --remainingIterations;
+
+    bool useVarOrderPivot =
+        d_pivotsInRound.count(x_i) >= options().arith.arithPivotThreshold;
+    if(!useVarOrderPivot){
+      d_pivotsInRound.add(x_i);
+    }
+
+    Trace("arith::update") << "pivots in rounds: " << d_pivotsInRound.count(x_i)
+                           << " use " << useVarOrderPivot << " threshold "
+                           << options().arith.arithPivotThreshold << std::endl;
+
+    LinearEqualityModule::VarPreferenceFunction pf = useVarOrderPivot ?
+      &LinearEqualityModule::minVarOrder : &LinearEqualityModule::minBoundAndColLength;
+
+    //DeltaRational beta_i = d_variables.getAssignment(x_i);
+    ArithVar x_j = ARITHVAR_SENTINEL;
+
+    int32_t prevErrorSize CVC5_UNUSED = d_errorSet.errorSize();
+
+    if(d_variables.cmpAssignmentLowerBound(x_i) < 0 ){
+      x_j = d_linEq.selectSlackUpperBound(x_i, pf);
+      if(x_j == ARITHVAR_SENTINEL ){
+        Unreachable();
+        // ++(d_statistics.d_statUpdateConflicts);
+        // reportConflict(x_i);
+        // ++(d_statistics.d_simplexConflicts);
+        // Node conflict = d_linEq.generateConflictBelowLowerBound(x_i); //unsat
+        // d_conflictVariable = x_i;
+        // reportConflict(conflict);
+        // return true;
+      }else{
+        const DeltaRational& l_i = d_variables.getLowerBound(x_i);
+        d_linEq.pivotAndUpdate(x_i, x_j, l_i);
+      }
+    }else if(d_variables.cmpAssignmentUpperBound(x_i) > 0){
+      x_j = d_linEq.selectSlackLowerBound(x_i, pf);
+      if(x_j == ARITHVAR_SENTINEL ){
+        Unreachable();
+        // ++(d_statistics.d_statUpdateConflicts);
+        // reportConflict(x_i);
+        // ++(d_statistics.d_simplexConflicts);
+        // Node conflict = d_linEq.generateConflictAboveUpperBound(x_i); //unsat
+        // d_conflictVariable = x_i;
+        // reportConflict(conflict);
+        // return true;
+      }else{
+        const DeltaRational& u_i = d_variables.getUpperBound(x_i);
+        d_linEq.pivotAndUpdate(x_i, x_j, u_i);
+      }
+    }
+    Assert(x_j != ARITHVAR_SENTINEL);
+
+    bool conflict = processSignals();
+    int32_t currErrorSize CVC5_UNUSED = d_errorSet.errorSize();
+    d_pivots++;
+
+    if(TraceIsOn("arith::dual")){
+      Trace("arith::dual")
+        << "#" << d_pivots
+        << " c" << conflict
+        << " d" << (prevErrorSize - currErrorSize)
+        << " f"  << d_errorSet.inError(x_j)
+        << " h" << d_conflictVariables.isMember(x_j)
+        << " " << x_i << "->" << x_j
+        << endl;
+    }
+
+    if(conflict){
+      return true;
+    }
+  }
+  Assert(!d_errorSet.focusEmpty() || d_errorSet.errorEmpty());
+  Assert(remainingIterations == 0 || d_errorSet.focusEmpty());
+  Assert(d_errorSet.noSignals());
+
+  return false;
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/dual_simplex.h b/src/theory/arith/linear/dual_simplex.h
new file mode 100644 (file)
index 0000000..1bb40e8
--- /dev/null
@@ -0,0 +1,120 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ *
+ * This implements the Simplex module for the Simpelx for DPLL(T) decision
+ * procedure.
+ * See the Simplex for DPLL(T) technical report for more background.(citation?)
+ * This shares with the theory a Tableau, and a PartialModel that:
+ *  - satisfies the equalities in the Tableau, and
+ *  - the assignment for the non-basic variables satisfies their bounds.
+ * This is required to either produce a conflict or satisifying PartialModel.
+ * Further, we require being told when a basic variable updates its value.
+ *
+ * During the Simplex search we maintain a queue of variables.
+ * The queue is required to contain all of the basic variables that voilate
+ * their bounds.
+ * As elimination from the queue is more efficient to be done lazily,
+ * we do not maintain that the queue of variables needs to be only basic
+ * variables or only variables that satisfy their bounds.
+ *
+ * The simplex procedure roughly follows Alberto's thesis. (citation?)
+ * There is one round of selecting using a heuristic pivoting rule.
+ * (See PreferenceFunction Documentation for the available options.)
+ * The non-basic variable is the one that appears in the fewest pivots.
+ * (Bruno says that Leonardo invented this first.)
+ * After this, Bland's pivot rule is invoked.
+ *
+ * During this proccess, we periodically inspect the queue of variables to
+ * 1) remove now extraneous extries,
+ * 2) detect conflicts that are "waiting" on the queue but may not be detected
+ *    by the current queue heuristics, and
+ * 3) detect multiple conflicts.
+ *
+ * Conflicts are greedily slackened to use the weakest bounds that still
+ * produce the conflict.
+ *
+ * Extra things tracked atm: (Subject to change at Tim's whims)
+ * - A superset of all of the newly pivoted variables.
+ * - A queue of additional conflicts that were discovered by Simplex.
+ *   These are theory valid and are currently turned into lemmas
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "theory/arith/linear/simplex.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class DualSimplexDecisionProcedure : public SimplexDecisionProcedure{
+public:
+ DualSimplexDecisionProcedure(Env& env,
+                              LinearEqualityModule& linEq,
+                              ErrorSet& errors,
+                              RaiseConflict conflictChannel,
+                              TempVarMalloc tvmalloc);
+
+ Result::Status findModel(bool exactResult) override
+ {
+   return dualFindModel(exactResult);
+  }
+
+private:
+
+  /**
+   * Maps a variable to how many times they have been used as a pivot in the
+   * simplex search.
+   */
+  DenseMultiset d_pivotsInRound;
+
+  Result::Status dualFindModel(bool exactResult);
+
+  /**
+   * This is the main simplex for DPLL(T) loop.
+   * It runs for at most maxIterations.
+   *
+   * Returns true iff it has found a conflict.
+   * d_conflictVariable will be set and the conflict for this row is reported.
+   */
+  bool searchForFeasibleSolution(uint32_t maxIterations);
+  
+
+  bool processSignals(){
+    TimerStat &timer = d_statistics.d_processSignalsTime;
+    IntStat& conflictStat  = d_statistics.d_recentViolationCatches;
+    return standardProcessSignals(timer, conflictStat);
+  }
+  /** These fields are designed to be accessible to TheoryArith methods. */
+  class Statistics {
+  public:
+    IntStat d_statUpdateConflicts;
+    TimerStat d_processSignalsTime;
+    IntStat d_simplexConflicts;
+    IntStat d_recentViolationCatches;
+    TimerStat d_searchTime;
+
+    ReferenceStat<uint32_t> d_finalCheckPivotCounter;
+
+    Statistics(uint32_t& pivots);
+  } d_statistics;
+};/* class DualSimplexDecisionProcedure */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/error_set.cpp b/src/theory/arith/linear/error_set.cpp
new file mode 100644 (file)
index 0000000..025af41
--- /dev/null
@@ -0,0 +1,490 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Andres Noetzli, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/error_set.h"
+
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+ErrorInformation::ErrorInformation()
+    : d_variable(ARITHVAR_SENTINEL),
+      d_violated(NullConstraint),
+      d_sgn(0),
+      d_relaxed(false),
+      d_inFocus(false),
+      d_handle(),
+      d_amount(nullptr),
+      d_metric(0)
+{
+  Trace("arith::error::mem")
+      << "def constructor " << d_variable << " " << d_amount.get() << endl;
+}
+
+ErrorInformation::ErrorInformation(ArithVar var, ConstraintP vio, int sgn)
+    : d_variable(var),
+      d_violated(vio),
+      d_sgn(sgn),
+      d_relaxed(false),
+      d_inFocus(false),
+      d_handle(),
+      d_amount(nullptr),
+      d_metric(0)
+{
+  Assert(debugInitialized());
+  Trace("arith::error::mem")
+      << "constructor " << d_variable << " " << d_amount.get() << endl;
+}
+
+
+ErrorInformation::~ErrorInformation() {
+  Assert(d_relaxed != true);
+  if (d_amount != nullptr)
+  {
+    Trace("arith::error::mem") << d_amount.get() << endl;
+    Trace("arith::error::mem")
+        << "destroy " << d_variable << " " << d_amount.get() << endl;
+    d_amount = nullptr;
+  }
+}
+
+ErrorInformation::ErrorInformation(const ErrorInformation& ei)
+  : d_variable(ei.d_variable)
+  , d_violated(ei.d_violated)
+  , d_sgn(ei.d_sgn)
+  , d_relaxed(ei.d_relaxed)
+  , d_inFocus(ei.d_inFocus)
+  , d_handle(ei.d_handle)
+  , d_metric(0)
+{
+  if (ei.d_amount == nullptr)
+  {
+    d_amount = nullptr;
+  }
+  else
+  {
+    d_amount = std::make_unique<DeltaRational>(*ei.d_amount);
+  }
+  Trace("arith::error::mem")
+      << "copy const " << d_variable << " " << d_amount.get() << endl;
+}
+
+ErrorInformation& ErrorInformation::operator=(const ErrorInformation& ei){
+  d_variable = ei.d_variable;
+  d_violated = ei.d_violated;
+  d_sgn = ei.d_sgn;
+  d_relaxed = (ei.d_relaxed);
+  d_inFocus = (ei.d_inFocus);
+  d_handle = (ei.d_handle);
+  d_metric = ei.d_metric;
+  if (d_amount != nullptr && ei.d_amount != nullptr)
+  {
+    Trace("arith::error::mem")
+        << "assignment assign " << d_variable << " " << d_amount.get() << endl;
+    *d_amount = *ei.d_amount;
+  }
+  else if (ei.d_amount != nullptr)
+  {
+    d_amount = std::make_unique<DeltaRational>(*ei.d_amount);
+    Trace("arith::error::mem")
+        << "assignment alloc " << d_variable << " " << d_amount.get() << endl;
+  }
+  else if (d_amount != nullptr)
+  {
+    Trace("arith::error::mem")
+        << "assignment release " << d_variable << " " << d_amount.get() << endl;
+    d_amount = nullptr;
+  }
+  else
+  {
+    d_amount = nullptr;
+  }
+  return *this;
+}
+
+void ErrorInformation::reset(ConstraintP c, int sgn){
+  Assert(!isRelaxed());
+  Assert(c != NullConstraint);
+  d_violated = c;
+  d_sgn = sgn;
+
+  if (d_amount != nullptr)
+  {
+    Trace("arith::error::mem")
+        << "reset " << d_variable << " " << d_amount.get() << endl;
+    d_amount = nullptr;
+  }
+}
+
+void ErrorInformation::setAmount(const DeltaRational& am){
+  if (d_amount == nullptr)
+  {
+    d_amount = std::make_unique<DeltaRational>();
+    Trace("arith::error::mem")
+        << "setAmount " << d_variable << " " << d_amount.get() << endl;
+  }
+  (*d_amount) = am;
+}
+
+ErrorSet::Statistics::Statistics()
+    : d_enqueues(
+        smtStatisticsRegistry().registerInt("theory::arith::pqueue::enqueues")),
+      d_enqueuesCollection(smtStatisticsRegistry().registerInt(
+          "theory::arith::pqueue::enqueuesCollection")),
+      d_enqueuesDiffMode(smtStatisticsRegistry().registerInt(
+          "theory::arith::pqueue::enqueuesDiffMode")),
+      d_enqueuesVarOrderMode(smtStatisticsRegistry().registerInt(
+          "theory::arith::pqueue::enqueuesVarOrderMode")),
+      d_enqueuesCollectionDuplicates(smtStatisticsRegistry().registerInt(
+          "theory::arith::pqueue::enqueuesCollectionDuplicates")),
+      d_enqueuesVarOrderModeDuplicates(smtStatisticsRegistry().registerInt(
+          "theory::arith::pqueue::enqueuesVarOrderModeDuplicates"))
+{
+}
+
+ErrorSet::ErrorSet(ArithVariables& vars,
+                   TableauSizes tabSizes,
+                   BoundCountingLookup lookups)
+    : d_variables(vars),
+      d_errInfo(),
+      d_selectionRule(options::ErrorSelectionRule::VAR_ORDER),
+      d_focus(ComparatorPivotRule(this, d_selectionRule)),
+      d_outOfFocus(),
+      d_signals(),
+      d_tableauSizes(tabSizes),
+      d_boundLookup(lookups)
+{}
+
+options::ErrorSelectionRule ErrorSet::getSelectionRule() const
+{
+  return d_selectionRule;
+}
+
+void ErrorSet::recomputeAmount(ErrorInformation& ei,
+                               options::ErrorSelectionRule rule)
+{
+  switch(rule){
+    case options::ErrorSelectionRule::MINIMUM_AMOUNT:
+    case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
+      ei.setAmount(computeDiff(ei.getVariable()));
+      break;
+    case options::ErrorSelectionRule::SUM_METRIC:
+      ei.setMetric(sumMetric(ei.getVariable()));
+      break;
+    case options::ErrorSelectionRule::VAR_ORDER:
+      // do nothing
+      break;
+  }
+}
+
+void ErrorSet::setSelectionRule(options::ErrorSelectionRule rule)
+{
+  if(rule != getSelectionRule()){
+    FocusSet into(ComparatorPivotRule(this, rule));
+    FocusSet::const_iterator iter = d_focus.begin();
+    FocusSet::const_iterator i_end = d_focus.end();
+    for(; iter != i_end; ++iter){
+      ArithVar v = *iter;
+      ErrorInformation& ei = d_errInfo.get(v);
+      if(ei.inFocus()){
+        recomputeAmount(ei, rule);
+        FocusSetHandle handle = into.push(v);
+        ei.setHandle(handle);
+      }
+    }
+    d_focus.swap(into);
+    d_selectionRule = rule;
+  }
+  Assert(getSelectionRule() == rule);
+}
+
+ComparatorPivotRule::ComparatorPivotRule(const ErrorSet* es,
+                                         options::ErrorSelectionRule r)
+    : d_errorSet(es), d_rule(r)
+{}
+
+bool ComparatorPivotRule::operator()(ArithVar v, ArithVar u) const {
+  switch(d_rule){
+    case options::ErrorSelectionRule::VAR_ORDER:
+      // This needs to be the reverse of the minVariableOrder
+      return v > u;
+    case options::ErrorSelectionRule::SUM_METRIC:
+    {
+      uint32_t v_metric = d_errorSet->getMetric(v);
+      uint32_t u_metric = d_errorSet->getMetric(u);
+      if(v_metric == u_metric){
+        return v > u;
+      }else{
+        return v_metric > u_metric;
+      }
+    }
+    case options::ErrorSelectionRule::MINIMUM_AMOUNT:
+    {
+      const DeltaRational& vamt = d_errorSet->getAmount(v);
+      const DeltaRational& uamt = d_errorSet->getAmount(u);
+      int cmp = vamt.cmp(uamt);
+      if(cmp == 0){
+        return v > u;
+      }else{
+        return cmp > 0;
+      }
+    }
+    case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
+    {
+      const DeltaRational& vamt = d_errorSet->getAmount(v);
+      const DeltaRational& uamt = d_errorSet->getAmount(u);
+      int cmp = vamt.cmp(uamt);
+      if(cmp == 0){
+        return v > u;
+      }else{
+        return cmp < 0;
+      }
+    }
+  }
+  Unreachable();
+}
+
+void ErrorSet::update(ErrorInformation& ei){
+  if(ei.inFocus()){
+
+    switch(getSelectionRule()){
+      case options::ErrorSelectionRule::MINIMUM_AMOUNT:
+      case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
+        ei.setAmount(computeDiff(ei.getVariable()));
+        d_focus.update(ei.getHandle(), ei.getVariable());
+        break;
+      case options::ErrorSelectionRule::SUM_METRIC:
+        ei.setMetric(sumMetric(ei.getVariable()));
+        d_focus.update(ei.getHandle(), ei.getVariable());
+        break;
+      case options::ErrorSelectionRule::VAR_ORDER:
+        // do nothing
+        break;
+    }
+  }
+}
+
+/** A variable becomes satisfied. */
+void ErrorSet::transitionVariableOutOfError(ArithVar v) {
+  Assert(!inconsistent(v));
+  ErrorInformation& ei = d_errInfo.get(v);
+  Assert(ei.debugInitialized());
+  if(ei.isRelaxed()){
+    ConstraintP viol = ei.getViolated();
+    if(ei.sgn() > 0){
+      d_variables.setLowerBoundConstraint(viol);
+    }else{
+      d_variables.setUpperBoundConstraint(viol);
+    }
+    Assert(!inconsistent(v));
+    ei.setUnrelaxed();
+  }
+  if(ei.inFocus()){
+    d_focus.erase(ei.getHandle());
+    ei.setInFocus(false);
+  }
+  d_errInfo.remove(v);
+}
+
+
+void ErrorSet::transitionVariableIntoError(ArithVar v) {
+  Assert(inconsistent(v));
+  bool vilb = d_variables.cmpAssignmentLowerBound(v) < 0;
+  int sgn = vilb ? 1 : -1;
+  ConstraintP c = vilb ?
+    d_variables.getLowerBoundConstraint(v) : d_variables.getUpperBoundConstraint(v);
+  d_errInfo.set(v, ErrorInformation(v, c, sgn));
+  ErrorInformation& ei = d_errInfo.get(v);
+
+  switch(getSelectionRule()){
+    case options::ErrorSelectionRule::MINIMUM_AMOUNT:
+    case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
+      ei.setAmount(computeDiff(v));
+      break;
+    case options::ErrorSelectionRule::SUM_METRIC:
+      ei.setMetric(sumMetric(ei.getVariable()));
+      break;
+    case options::ErrorSelectionRule::VAR_ORDER:
+      // do nothing
+      break;
+  }
+  ei.setInFocus(true);
+  FocusSetHandle handle = d_focus.push(v);
+  ei.setHandle(handle);
+}
+
+void ErrorSet::dropFromFocus(ArithVar v) {
+  Assert(inError(v));
+  ErrorInformation& ei = d_errInfo.get(v);
+  Assert(ei.inFocus());
+  d_focus.erase(ei.getHandle());
+  ei.setInFocus(false);
+  d_outOfFocus.push_back(v);
+}
+
+void ErrorSet::addBackIntoFocus(ArithVar v) {
+  Assert(inError(v));
+  ErrorInformation& ei = d_errInfo.get(v);
+  Assert(!ei.inFocus());
+  switch(getSelectionRule()){
+    case options::ErrorSelectionRule::MINIMUM_AMOUNT:
+    case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
+      ei.setAmount(computeDiff(v));
+      break;
+    case options::ErrorSelectionRule::SUM_METRIC:
+      ei.setMetric(sumMetric(v));
+      break;
+    case options::ErrorSelectionRule::VAR_ORDER:
+      // do nothing
+      break;
+  }
+
+  ei.setInFocus(true);
+  FocusSetHandle handle = d_focus.push(v);
+  ei.setHandle(handle);
+}
+
+void ErrorSet::blur(){
+  while(!d_outOfFocus.empty()){
+    ArithVar v = d_outOfFocus.back();
+    d_outOfFocus.pop_back();
+
+    if(inError(v) && !inFocus(v)){
+      addBackIntoFocus(v);
+    }
+  }
+}
+
+
+
+int ErrorSet::popSignal() {
+  ArithVar back = d_signals.back();
+  d_signals.pop_back();
+
+  if(inError(back)){
+    ErrorInformation& ei = d_errInfo.get(back);
+    int prevSgn = ei.sgn();
+    int focusSgn = ei.focusSgn();
+    bool vilb = d_variables.cmpAssignmentLowerBound(back) < 0;
+    bool viub = d_variables.cmpAssignmentUpperBound(back) > 0;
+    if(vilb || viub){
+      Assert(!vilb || !viub);
+      int currSgn = vilb ? 1 : -1;
+      if(currSgn != prevSgn){
+        ConstraintP curr = vilb ?  d_variables.getLowerBoundConstraint(back)
+          : d_variables.getUpperBoundConstraint(back);
+        ei.reset(curr, currSgn);
+      }
+      update(ei);
+    }else{
+      transitionVariableOutOfError(back);
+    }
+    return focusSgn;
+  }else if(inconsistent(back)){
+    transitionVariableIntoError(back);
+  }
+  return 0;
+}
+
+void ErrorSet::clear(){
+  // Nothing should be relaxed!
+  d_signals.clear();
+  d_errInfo.purge();
+  d_focus.clear();
+}
+
+void ErrorSet::clearFocus(){
+  for(ErrorSet::focus_iterator i =focusBegin(), i_end = focusEnd(); i != i_end; ++i){
+    ArithVar f = *i;
+    ErrorInformation& fei = d_errInfo.get(f);
+    fei.setInFocus(false);
+    d_outOfFocus.push_back(f);
+  }
+  d_focus.clear();
+}
+
+void ErrorSet::reduceToSignals(){
+  for(error_iterator ei=errorBegin(), ei_end=errorEnd(); ei != ei_end; ++ei){
+    ArithVar curr = *ei;
+    signalVariable(curr);
+  }
+
+  d_errInfo.purge();
+  d_focus.clear();
+  d_outOfFocus.clear();
+}
+
+DeltaRational ErrorSet::computeDiff(ArithVar v) const{
+  Assert(inconsistent(v));
+  const DeltaRational& beta = d_variables.getAssignment(v);
+  DeltaRational diff = d_variables.cmpAssignmentLowerBound(v) < 0 ?
+    d_variables.getLowerBound(v) - beta:
+    beta - d_variables.getUpperBound(v);
+
+  Assert(diff.sgn() > 0);
+  return diff;
+}
+
+void ErrorSet::debugPrint(std::ostream& out) const {
+  out << "error set debugprint" << endl;
+  for(error_iterator i = errorBegin(), i_end = errorEnd();
+      i != i_end; ++i){
+    ArithVar e = *i;
+    const ErrorInformation& ei = d_errInfo[e];
+    ei.print(out);
+    out << "  ";
+    d_variables.printModel(e, out);
+    out << endl;
+  }
+  out << "focus ";
+  for(focus_iterator i = focusBegin(), i_end = focusEnd();
+      i != i_end; ++i){
+    out << *i << " ";
+  }
+  out << ";" << endl;
+}
+
+void ErrorSet::focusDownToJust(ArithVar v) {
+  clearFocus();
+
+  ErrorInformation& vei = d_errInfo.get(v);
+  vei.setInFocus(true);
+  FocusSetHandle handle = d_focus.push(v);
+  vei.setHandle(handle);
+}
+
+void ErrorSet::pushErrorInto(ArithVarVec& vec) const{
+  for(error_iterator i = errorBegin(), e = errorEnd(); i != e; ++i ){
+    vec.push_back(*i);
+  }
+}
+
+void ErrorSet::pushFocusInto(ArithVarVec& vec) const{
+  for(focus_iterator i = focusBegin(), e = focusEnd(); i != e; ++i ){
+    vec.push_back(*i);
+  }
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/error_set.h b/src/theory/arith/linear/error_set.h
new file mode 100644 (file)
index 0000000..448601b
--- /dev/null
@@ -0,0 +1,421 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Mathias Preiner, Andres Noetzli
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "options/arith_options.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/bound_counts.h"
+#include "theory/arith/linear/callbacks.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/arith/linear/tableau_sizes.h"
+#include "util/bin_heap.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+
+/**
+ * The priority queue has 3 different modes of operation:
+ * - Collection
+ *   This passively collects arithmetic variables that may be inconsistent.
+ *   This does not maintain any heap structure.
+ *   dequeueInconsistentBasicVariable() does not work in this mode!
+ *   Entering this mode requires the queue to be empty.
+ *
+ * - Difference Queue
+ *   This mode uses the difference between a variables and its bound
+ *   to determine which to dequeue first.
+ *
+ * - Variable Order Queue
+ *   This mode uses the variable order to determine which ArithVar is dequeued first.
+ *
+ * The transitions between the modes of operation are:
+ *  Collection => Difference Queue
+ *  Difference Queue => Variable Order Queue
+ *  Difference Queue => Collection (queue must be empty!)
+ *  Variable Order Queue => Collection (queue must be empty!)
+ *
+ * The queue begins in Collection mode.
+ */
+
+
+class ErrorSet;
+
+class ComparatorPivotRule {
+private:
+  const ErrorSet* d_errorSet;
+
+  options::ErrorSelectionRule d_rule;
+
+ public:
+  ComparatorPivotRule();
+  ComparatorPivotRule(const ErrorSet* es, options::ErrorSelectionRule r);
+
+  bool operator()(ArithVar v, ArithVar u) const;
+  options::ErrorSelectionRule getRule() const { return d_rule; }
+};
+
+// typedef boost::heap::d_ary_heap<
+//   ArithVar,
+//   boost::heap::arity<2>,
+//   boost::heap::compare<ComparatorPivotRule>,
+//   boost::heap::mutable_<true> > FocusSet;
+//
+// typedef FocusSet::handle_type FocusSetHandle;
+
+// typedef CVC5_PB_DS_NAMESPACE::priority_queue<
+//   ArithVar,
+//   ComparatorPivotRule,
+//   CVC5_PB_DS_NAMESPACE::pairing_heap_tag> FocusSet;
+
+// typedef FocusSet::point_iterator FocusSetHandle;
+
+typedef BinaryHeap<ArithVar, ComparatorPivotRule> FocusSet;
+typedef FocusSet::handle FocusSetHandle;
+
+
+class ErrorInformation {
+private:
+  /** The variable that is in error. */
+  ArithVar d_variable;
+
+  /**
+   * The constraint that was violated.
+   * This needs to be saved in case that the
+   * violated constraint
+   */
+  ConstraintP d_violated;
+
+  /**
+   * This is the sgn of the first derivate the variable must move to satisfy
+   * the bound violated.
+   * If d_sgn > 0, then d_violated was a lowerbound.
+   * If d_sgn < 0, then d_violated was an upperbound.
+   */
+  int d_sgn;
+
+  /**
+   * If this is true, then the bound is no longer set on d_variables.
+   * This MUST be undone before this is deleted.
+   */
+  bool d_relaxed;
+
+  /**
+   * If this is true, then the variable is in the focus set and the focus heap.
+   * d_handle is then a reasonable thing to interpret.
+   * If this is false, the variable is somewhere in
+   */
+  bool d_inFocus;
+  FocusSetHandle d_handle;
+
+  /**
+   * Auxillary information for storing the difference between a variable and its bound.
+   * Only set on signals.
+   */
+  std::unique_ptr<DeltaRational> d_amount;
+
+  /** */
+  uint32_t d_metric;
+
+public:
+  ErrorInformation();
+  ErrorInformation(ArithVar var, ConstraintP vio, int sgn);
+  ~ErrorInformation();
+  ErrorInformation(const ErrorInformation& ei);
+  ErrorInformation& operator=(const ErrorInformation& ei);
+
+  void reset(ConstraintP c, int sgn);
+
+  inline ArithVar getVariable() const { return d_variable; }
+
+  bool isRelaxed() const { return d_relaxed; }
+  void setRelaxed()
+  {
+    Assert(!d_relaxed);
+    d_relaxed = true;
+  }
+  void setUnrelaxed()
+  {
+    Assert(d_relaxed);
+    d_relaxed = false;
+  }
+
+  inline int sgn() const { return d_sgn; }
+
+  inline bool inFocus() const { return d_inFocus; }
+  inline int focusSgn() const {
+    return (d_inFocus) ? sgn() : 0;
+  }
+
+  inline void setInFocus(bool inFocus) { d_inFocus = inFocus; }
+
+  const DeltaRational& getAmount() const {
+    Assert(d_amount != nullptr);
+    return *d_amount;
+  }
+
+  void setAmount(const DeltaRational& am);
+  void setMetric(uint32_t m) { d_metric = m; }
+  uint32_t getMetric() const { return d_metric; }
+
+  inline void setHandle(FocusSetHandle h) {
+    Assert(d_inFocus);
+    d_handle = h;
+  }
+  inline const FocusSetHandle& getHandle() const{ return d_handle; }
+
+  inline ConstraintP getViolated() const { return d_violated; }
+
+  bool debugInitialized() const {
+    return
+      d_variable != ARITHVAR_SENTINEL &&
+      d_violated != NullConstraint &&
+      d_sgn != 0;
+  }
+  void print(std::ostream& os) const {
+    os << "{ErrorInfo: " << d_variable
+       << ", " << d_violated
+       << ", " << d_sgn
+       << ", " << d_relaxed
+       << ", " << d_inFocus;
+    if (d_amount == nullptr)
+    {
+      os << "nullptr";
+    }
+    else
+    {
+      os << (*d_amount);
+    }
+    os << "}";
+  }
+};
+
+class ErrorInfoMap : public DenseMap<ErrorInformation> {};
+
+class ErrorSet {
+private:
+  /**
+   * Reference to the arithmetic partial model for checking if a variable
+   * is consistent with its upper and lower bounds.
+   */
+  ArithVariables& d_variables;
+
+  /**
+   * The set of all variables that violate exactly one of their bounds.
+   */
+  ErrorInfoMap d_errInfo;
+
+  options::ErrorSelectionRule d_selectionRule;
+  /**
+   * The ordered heap for the variables that are in ErrorSet.
+   */
+  FocusSet d_focus;
+
+
+  /**
+   * A strict subset of the error set.
+   *   d_outOfFocus \neq d_errInfo.
+   *
+   * Its symbolic complement is Focus.
+   *   d_outOfFocus \intersect Focus == \emptyset
+   *   d_outOfFocus \union Focus == d_errInfo
+   */
+  ArithVarVec d_outOfFocus;
+
+  /**
+   * Before a variable is added to the error set, it is added to the signals list.
+   * A variable may appear on the list multiple times.
+   * This introduces a delay.
+   */
+  ArithVarVec d_signals;
+
+  TableauSizes d_tableauSizes;
+
+  BoundCountingLookup d_boundLookup;
+
+  /**
+   * Computes the difference between the assignment and its bound for x.
+   */
+public:
+  DeltaRational computeDiff(ArithVar x) const;
+private:
+ void recomputeAmount(ErrorInformation& ei, options::ErrorSelectionRule r);
+
+ void update(ErrorInformation& ei);
+ void transitionVariableOutOfError(ArithVar v);
+ void transitionVariableIntoError(ArithVar v);
+ void addBackIntoFocus(ArithVar v);
+
+public:
+
+  /** The new focus set is the entire error set. */
+  void blur();
+  void dropFromFocus(ArithVar v);
+
+  void dropFromFocusAll(const ArithVarVec& vec) {
+    for(ArithVarVec::const_iterator i = vec.begin(), i_end = vec.end(); i != i_end; ++i){
+      ArithVar v = *i;
+      dropFromFocus(v);
+    }
+  }
+
+  ErrorSet(ArithVariables& var, TableauSizes tabSizes, BoundCountingLookup boundLookup);
+
+  typedef ErrorInfoMap::const_iterator error_iterator;
+  error_iterator errorBegin() const { return d_errInfo.begin(); }
+  error_iterator errorEnd() const { return d_errInfo.end(); }
+
+  bool inError(ArithVar v) const { return d_errInfo.isKey(v); }
+  bool inFocus(ArithVar v) const { return d_errInfo[v].inFocus(); }
+
+  void pushErrorInto(ArithVarVec& vec) const;
+  void pushFocusInto(ArithVarVec& vec) const;
+
+  options::ErrorSelectionRule getSelectionRule() const;
+  void setSelectionRule(options::ErrorSelectionRule rule);
+
+  inline ArithVar topFocusVariable() const{
+    Assert(!focusEmpty());
+    return d_focus.top();
+  }
+
+  inline void signalVariable(ArithVar var){
+    d_signals.push_back(var);
+  }
+
+  inline void signalUnderCnd(ArithVar var, bool b){
+    if(b){ signalVariable(var); }
+  }
+
+  inline bool inconsistent(ArithVar var) const{
+    return !d_variables.assignmentIsConsistent(var) ;
+  }
+  inline void signalIfInconsistent(ArithVar var){
+    signalUnderCnd(var, inconsistent(var));
+  }
+
+  inline bool errorEmpty() const{
+    return d_errInfo.empty();
+  }
+  inline uint32_t errorSize() const{
+    return d_errInfo.size();
+  }
+
+  inline bool focusEmpty() const {
+    return d_focus.empty();
+  }
+  inline uint32_t focusSize() const{
+    return d_focus.size();
+  }
+
+  inline int getSgn(ArithVar x) const {
+    Assert(inError(x));
+    return d_errInfo[x].sgn();
+  }
+  inline int focusSgn(ArithVar v) const {
+    if(inError(v)){
+      return d_errInfo[v].focusSgn();
+    }else{
+      return 0;
+    }
+  }
+
+  void focusDownToJust(ArithVar v);
+
+  void clearFocus();
+
+  /** Clears the set. */
+  void clear();
+  void reduceToSignals();
+
+  bool noSignals() const {
+    return d_signals.empty();
+  }
+  bool moreSignals() const {
+    return !noSignals();
+  }
+  ArithVar topSignal() const {
+    Assert(moreSignals());
+    return d_signals.back();
+  }
+
+  /**
+   * Moves a variable out of the signals.
+   * This moves it into the error set.
+   * Return the previous focus sign.
+   */
+  int popSignal();
+
+  const DeltaRational& getAmount(ArithVar v) const {
+    return d_errInfo[v].getAmount();
+  }
+
+  uint32_t sumMetric(ArithVar a) const{
+    Assert(inError(a));
+    BoundCounts bcs = d_boundLookup.atBounds(a);
+    uint32_t count = getSgn(a) > 0 ? bcs.upperBoundCount() : bcs.lowerBoundCount();
+
+    uint32_t length = d_tableauSizes.getRowLength(a);
+
+    return (length - count);
+  }
+
+  uint32_t getMetric(ArithVar a) const {
+    return d_errInfo[a].getMetric();
+  }
+
+  ConstraintP getViolated(ArithVar a) const {
+    return d_errInfo[a].getViolated();
+  }
+
+
+  typedef FocusSet::const_iterator focus_iterator;
+  focus_iterator focusBegin() const { return d_focus.begin(); }
+  focus_iterator focusEnd() const { return d_focus.end(); }
+
+  void debugPrint(std::ostream& out) const;
+
+private:
+  class Statistics {
+  public:
+    IntStat d_enqueues;
+    IntStat d_enqueuesCollection;
+    IntStat d_enqueuesDiffMode;
+    IntStat d_enqueuesVarOrderMode;
+
+    IntStat d_enqueuesCollectionDuplicates;
+    IntStat d_enqueuesVarOrderModeDuplicates;
+
+    Statistics();
+  };
+
+  Statistics d_statistics;
+};
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/fc_simplex.cpp b/src/theory/arith/linear/fc_simplex.cpp
new file mode 100644 (file)
index 0000000..9e25541
--- /dev/null
@@ -0,0 +1,787 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T)decision procedure.
+ */
+#include "theory/arith/linear/fc_simplex.h"
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/error_set.h"
+#include "util/statistics_stats.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+FCSimplexDecisionProcedure::FCSimplexDecisionProcedure(
+    Env& env,
+    LinearEqualityModule& linEq,
+    ErrorSet& errors,
+    RaiseConflict conflictChannel,
+    TempVarMalloc tvmalloc)
+    : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
+      d_focusSize(0),
+      d_focusErrorVar(ARITHVAR_SENTINEL),
+      d_focusCoefficients(),
+      d_pivotBudget(0),
+      d_prevWitnessImprovement(AntiProductive),
+      d_witnessImprovementInARow(0),
+      d_sgnDisagreements(),
+      d_statistics("theory::arith::FC::", d_pivots)
+{ }
+
+FCSimplexDecisionProcedure::Statistics::Statistics(const std::string& name,
+                                                   uint32_t& pivots)
+    : d_initialSignalsTime(
+        smtStatisticsRegistry().registerTimer(name + "initialProcessTime")),
+      d_initialConflicts(
+          smtStatisticsRegistry().registerInt(name + "UpdateConflicts")),
+      d_fcFoundUnsat(smtStatisticsRegistry().registerInt(name + "FoundUnsat")),
+      d_fcFoundSat(smtStatisticsRegistry().registerInt(name + "FoundSat")),
+      d_fcMissed(smtStatisticsRegistry().registerInt(name + "Missed")),
+      d_fcTimer(smtStatisticsRegistry().registerTimer(name + "Timer")),
+      d_fcFocusConstructionTimer(
+          smtStatisticsRegistry().registerTimer(name + "Construction")),
+      d_selectUpdateForDualLike(smtStatisticsRegistry().registerTimer(
+          name + "selectUpdateForDualLike")),
+      d_selectUpdateForPrimal(smtStatisticsRegistry().registerTimer(
+          name + "selectUpdateForPrimal")),
+      d_finalCheckPivotCounter(
+          smtStatisticsRegistry().registerReference<uint32_t>(
+              name + "lastPivots", pivots))
+{
+}
+
+Result::Status FCSimplexDecisionProcedure::findModel(bool exactResult)
+{
+  Assert(d_conflictVariables.empty());
+  Assert(d_sgnDisagreements.empty());
+
+  d_pivots = 0;
+
+  if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
+    Trace("arith::findModel") << "fcFindModel() trivial" << endl;
+    Assert(d_conflictVariables.empty());
+    return Result::SAT;
+  }
+
+  // We need to reduce this because of
+  d_errorSet.reduceToSignals();
+
+  // We must start tracking NOW
+  d_errorSet.setSelectionRule(options::ErrorSelectionRule::SUM_METRIC);
+
+  if(initialProcessSignals()){
+    d_conflictVariables.purge();
+    Trace("arith::findModel") << "fcFindModel() early conflict" << endl;
+    Assert(d_conflictVariables.empty());
+    return Result::UNSAT;
+  }else if(d_errorSet.errorEmpty()){
+    Trace("arith::findModel") << "fcFindModel() fixed itself" << endl;
+    Assert(d_conflictVariables.empty());
+    return Result::SAT;
+  }
+
+  Trace("arith::findModel") << "fcFindModel() start non-trivial" << endl;
+
+  exactResult |= d_varOrderPivotLimit < 0;
+
+  d_prevWitnessImprovement = HeuristicDegenerate;
+  d_witnessImprovementInARow = 0;
+
+  Result::Status result = Result::UNKNOWN;
+
+  if (result == Result::UNKNOWN)
+  {
+    if(exactResult){
+      d_pivotBudget = -1;
+    }else{
+      d_pivotBudget = d_varOrderPivotLimit;
+    }
+
+    result = dualLike();
+
+    if(result ==  Result::UNSAT){
+      ++(d_statistics.d_fcFoundUnsat);
+    }else if(d_errorSet.errorEmpty()){
+      ++(d_statistics.d_fcFoundSat);
+    }else{
+      ++(d_statistics.d_fcMissed);
+    }
+  }
+
+  Assert(!d_errorSet.moreSignals());
+  if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
+  {
+    result = Result::SAT;
+  }
+
+  // ensure that the conflict variable is still in the queue.
+  d_conflictVariables.purge();
+
+  Trace("arith::findModel") << "end findModel() " << result << endl;
+
+  Assert(d_conflictVariables.empty());
+  return result;
+}
+
+
+void FCSimplexDecisionProcedure::logPivot(WitnessImprovement w){
+  if(d_pivotBudget > 0) {
+    --d_pivotBudget;
+  }
+  Assert(w != AntiProductive);
+
+  if(w == d_prevWitnessImprovement){
+    ++d_witnessImprovementInARow;
+    // ignore overflow : probably never reached
+    if(d_witnessImprovementInARow == 0){
+      --d_witnessImprovementInARow;
+    }
+  }else{
+    if(w != BlandsDegenerate){
+      d_witnessImprovementInARow = 1;
+    }
+    // if w == BlandsDegenerate do not reset the counter
+    d_prevWitnessImprovement = w;
+  }
+  if(strongImprovement(w)){
+    d_leavingCountSinceImprovement.purge();
+  }
+
+  Trace("logPivot") << "logPivot " << d_prevWitnessImprovement << " "  << d_witnessImprovementInARow << endl;
+
+}
+
+uint32_t FCSimplexDecisionProcedure::degeneratePivotsInARow() const {
+  switch(d_prevWitnessImprovement){
+  case ConflictFound:
+  case ErrorDropped:
+  case FocusImproved:
+    return 0;
+  case HeuristicDegenerate:
+  case BlandsDegenerate:
+    return d_witnessImprovementInARow;
+  // Degenerate is unreachable for its own reasons
+  case Degenerate:
+  case FocusShrank:
+  case AntiProductive:
+    Unreachable();
+    return -1;
+  }
+  Unreachable();
+}
+
+void FCSimplexDecisionProcedure::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
+  uint32_t newErrorSize = d_errorSet.errorSize();
+  uint32_t newFocusSize = d_errorSet.focusSize();
+
+  //Assert(!d_conflictVariables.empty() || newFocusSize <= d_focusSize);
+  Assert(!d_conflictVariables.empty() || newErrorSize <= d_errorSize);
+
+  if(newFocusSize == 0 || !d_conflictVariables.empty() ){
+    tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+    d_focusErrorVar = ARITHVAR_SENTINEL;
+  }else if(2*newFocusSize < d_focusSize ){
+    tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+    d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+  }else{
+    adjustInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, focusChanges);
+  }
+
+  d_errorSize = newErrorSize;
+  d_focusSize = newFocusSize;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::adjustFocusShrank(const ArithVarVec& dropped){
+  Assert(dropped.size() > 0);
+  Assert(d_errorSet.focusSize() == d_focusSize);
+  Assert(d_errorSet.focusSize() > dropped.size());
+
+  uint32_t newFocusSize = d_focusSize - dropped.size();
+  Assert(newFocusSize > 0);
+
+  if(2 * newFocusSize <= d_focusSize){
+    d_errorSet.dropFromFocusAll(dropped);
+    tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+    d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+  }else{
+    shrinkInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, dropped);
+    d_errorSet.dropFromFocusAll(dropped);
+  }
+
+  d_focusSize = newFocusSize;
+  Assert(d_errorSet.focusSize() == d_focusSize);
+  return FocusShrank;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::focusDownToJust(ArithVar v){
+  // uint32_t newErrorSize = d_errorSet.errorSize();
+  // uint32_t newFocusSize = d_errorSet.focusSize();
+  Assert(d_focusSize == d_errorSet.focusSize());
+  Assert(d_focusSize > 1);
+  Assert(d_errorSet.inFocus(v));
+
+  d_errorSet.focusDownToJust(v);
+  Assert(d_errorSet.focusSize() == 1);
+  d_focusSize = 1;
+
+  tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+  d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+
+  return FocusShrank;
+}
+
+
+
+UpdateInfo FCSimplexDecisionProcedure::selectPrimalUpdate(ArithVar basic, LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
+  UpdateInfo selected;
+
+  Trace("arith::selectPrimalUpdate")
+      << "selectPrimalUpdate" << endl
+      << basic << " " << d_tableau.basicRowLength(basic) << " "
+      << d_linEq.debugBasicAtBoundCount(basic) << endl;
+
+  static constexpr int s_maxCandidatesAfterImprove = 3;
+  bool isFocus = basic == d_focusErrorVar;
+  Assert(isFocus || d_errorSet.inError(basic));
+  int basicDir =  isFocus? 1 : d_errorSet.getSgn(basic);
+  bool dualLike = !isFocus && d_focusSize > 1;
+
+  if(!isFocus){
+    loadFocusSigns();
+  }
+
+  decreasePenalties();
+
+  typedef std::vector<Cand> CandVector;
+  CandVector candidates;
+
+  for(Tableau::RowIterator ri = d_tableau.basicRowIterator(basic); !ri.atEnd(); ++ri){
+    const Tableau::Entry& e = *ri;
+    ArithVar curr = e.getColVar();
+    if(curr == basic){ continue; }
+
+    int sgn = e.getCoefficient().sgn();
+    int curr_movement = basicDir * sgn;
+
+    bool candidate =
+      (curr_movement > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
+      (curr_movement < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
+
+    Trace("arith::selectPrimalUpdate")
+      << "storing " << basic
+      << " " << curr
+      << " " << candidate
+      << " " << e.getCoefficient()
+      << " " << curr_movement
+      << " " << focusCoefficient(curr) << endl;
+
+    if(!candidate) { continue; }
+
+    if(!isFocus){
+      const Rational& focusC = focusCoefficient(curr);
+      Assert(dualLike || !focusC.isZero());
+      if(dualLike && curr_movement != focusC.sgn()){
+        Trace("arith::selectPrimalUpdate") << "sgn disagreement " << curr << endl;
+        d_sgnDisagreements.push_back(curr);
+        continue;
+      }else{
+        candidates.push_back(Cand(curr, penalty(curr), curr_movement, &focusC));
+      }
+    }else{
+      candidates.push_back(Cand(curr, penalty(curr), curr_movement, &e.getCoefficient()));
+    }
+  }
+
+  CompPenaltyColLength colCmp(&d_linEq, options().arith.havePenalties);
+  CandVector::iterator i = candidates.begin();
+  CandVector::iterator end = candidates.end();
+  std::make_heap(i, end, colCmp);
+
+  bool checkEverything = d_pivots == 0;
+
+  int candidatesAfterFocusImprove = 0;
+  while(i != end && (checkEverything || candidatesAfterFocusImprove <= s_maxCandidatesAfterImprove)){
+    std::pop_heap(i, end, colCmp);
+    --end;
+    Cand& cand = (*end);
+    ArithVar curr = cand.d_nb;
+    const Rational& coeff = *cand.d_coeff;
+
+    LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
+    UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
+
+    Trace("arith::selectPrimalUpdate")
+      << "selected " << selected << endl
+      << "currProp " << currProposal << endl
+      << "coeff " << coeff << endl;
+
+    Assert(!currProposal.uninitialized());
+
+    if(candidatesAfterFocusImprove > 0){
+      candidatesAfterFocusImprove++;
+    }
+
+    if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
+
+      selected = currProposal;
+      WitnessImprovement w = selected.getWitness(false);
+      Trace("arith::selectPrimalUpdate") << "selected " << w << endl;
+      setPenalty(curr, w);
+      if(improvement(w)){
+        bool exitEarly;
+        switch(w){
+        case ConflictFound: exitEarly = true; break;
+        case ErrorDropped:
+          if(checkEverything){
+            exitEarly = d_errorSize + selected.errorsChange() == 0;
+            Trace("arith::selectPrimalUpdate")
+              << "ee " << d_errorSize << " "
+              << selected.errorsChange() << " "
+              << d_errorSize + selected.errorsChange() << endl;
+          }else{
+            exitEarly = true;
+          }
+          break;
+        case FocusImproved:
+          candidatesAfterFocusImprove = 1;
+          exitEarly = false;
+          break;
+        default:
+          exitEarly = false; break;
+        }
+        if(exitEarly){ break; }
+      }
+    }else{
+      Trace("arith::selectPrimalUpdate") << "dropped "<< endl;
+    }
+
+  }
+
+  if(!isFocus){
+    unloadFocusSigns();
+  }
+  return selected;
+}
+
+bool FCSimplexDecisionProcedure::debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
+  if(inf.getWitness(useBlands) == w){
+    switch(w){
+    case ConflictFound: return inf.foundConflict();
+    case ErrorDropped: return inf.errorsChange() < 0;
+    case FocusImproved: return inf.focusDirection() > 0;
+    case FocusShrank: return false; // This is not a valid output
+    case Degenerate: return false; // This is not a valid output
+    case BlandsDegenerate: return useBlands;
+    case HeuristicDegenerate: return !useBlands;
+    case AntiProductive: return false;
+    }
+  }
+  return false;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::primalImproveError(ArithVar errorVar){
+  bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
+  UpdateInfo selected = selectUpdateForPrimal (errorVar, useBlands);
+  Assert(!selected.uninitialized());
+  WitnessImprovement w = selected.getWitness(useBlands);
+  Assert(debugCheckWitness(selected, w, useBlands));
+
+  updateAndSignal(selected, w);
+  logPivot(w);
+  return w;
+}
+
+
+WitnessImprovement FCSimplexDecisionProcedure::focusUsingSignDisagreements(ArithVar basic){
+  Assert(!d_sgnDisagreements.empty());
+  Assert(d_errorSet.focusSize() >= 2);
+
+  if(TraceIsOn("arith::focus")){
+    d_errorSet.debugPrint(Trace("arith::focus"));
+  }
+
+  ArithVar nb = d_linEq.minBy(d_sgnDisagreements, &LinearEqualityModule::minColLength);
+  const Tableau::Entry& e_evar_nb = d_tableau.basicFindEntry(basic, nb);
+  int oppositeSgn = - (e_evar_nb.getCoefficient().sgn());
+  Trace("arith::focus") << "focusUsingSignDisagreements " << basic << " " << oppositeSgn << endl;
+
+  ArithVarVec dropped;
+
+  Tableau::ColIterator colIter = d_tableau.colIterator(nb);
+  for(; !colIter.atEnd(); ++colIter){
+    const Tableau::Entry& entry = *colIter;
+    Assert(entry.getColVar() == nb);
+
+    int sgn = entry.getCoefficient().sgn();
+    Trace("arith::focus")
+      << "on row "
+      << d_tableau.rowIndexToBasic(entry.getRowIndex())
+      << " "
+      << entry.getCoefficient() << endl;
+    ArithVar currRow = d_tableau.rowIndexToBasic(entry.getRowIndex());
+    if(d_errorSet.inError(currRow) && d_errorSet.inFocus(currRow)){
+      int errSgn = d_errorSet.getSgn(currRow);
+
+      if(errSgn * sgn == oppositeSgn){
+        dropped.push_back(currRow);
+        Trace("arith::focus") << "dropping from focus " << currRow << endl;
+      }
+    }
+  }
+
+  d_sgnDisagreements.clear();
+  return adjustFocusShrank(dropped);
+}
+
+bool debugSelectedErrorDropped(const UpdateInfo& selected, int32_t prevErrorSize, int32_t currErrorSize){
+  int diff = currErrorSize - prevErrorSize;
+  return selected.foundConflict() || diff == selected.errorsChange();
+}
+
+void FCSimplexDecisionProcedure::debugPrintSignal(ArithVar updated) const{
+  Trace("updateAndSignal") << "updated basic " << updated;
+  Trace("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
+  Trace("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
+  int dir = !d_variables.assignmentIsConsistent(updated) ?
+    d_errorSet.getSgn(updated) : 0;
+  Trace("updateAndSignal") << " dir " << dir;
+  Trace("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
+}
+
+bool debugUpdatedBasic(const UpdateInfo& selected, ArithVar updated){
+  if(selected.describesPivot() &&  updated == selected.leaving()){
+    return selected.foundConflict();
+  }else{
+    return true;
+  }
+}
+
+void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
+  ArithVar nonbasic = selected.nonbasic();
+
+  Trace("updateAndSignal") << "updateAndSignal " << selected << endl;
+
+  stringstream ss;
+
+  if(selected.describesPivot()){
+    ConstraintP limiting = selected.limiting();
+    ArithVar basic = limiting->getVariable();
+    Assert(d_linEq.basicIsTracked(basic));
+    d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
+  }else{
+    Assert(!selected.unbounded() || selected.errorsChange() < 0);
+
+    DeltaRational newAssignment =
+      d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
+
+    d_linEq.updateTracked(nonbasic, newAssignment);
+  }
+  d_pivots++;
+
+  increaseLeavingCount(nonbasic);
+
+  vector< pair<ArithVar, int> > focusChanges;
+  while(d_errorSet.moreSignals()){
+    ArithVar updated = d_errorSet.topSignal();
+    int prevFocusSgn = d_errorSet.popSignal();
+
+    if(d_tableau.isBasic(updated)){
+      Assert(!d_variables.assignmentIsConsistent(updated)
+             == d_errorSet.inError(updated));
+      if(TraceIsOn("updateAndSignal")){debugPrintSignal(updated);}
+      if(!d_variables.assignmentIsConsistent(updated)){
+        if(checkBasicForConflict(updated)){
+          reportConflict(updated);
+          Assert(debugUpdatedBasic(selected, updated));
+        }
+      }
+    }else{
+      Trace("updateAndSignal") << "updated nonbasic " << updated << endl;
+    }
+    int currFocusSgn = d_errorSet.focusSgn(updated);
+    if(currFocusSgn != prevFocusSgn){
+      int change = currFocusSgn - prevFocusSgn;
+      focusChanges.push_back(make_pair(updated, change));
+    }
+  }
+
+  if(TraceIsOn("error")){ d_errorSet.debugPrint(Trace("error")); }
+
+  Assert(
+      debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
+
+  adjustFocusAndError(selected, focusChanges);
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::dualLikeImproveError(ArithVar errorVar){
+  Assert(d_sgnDisagreements.empty());
+  Assert(d_focusSize > 1);
+
+  UpdateInfo selected = selectUpdateForDualLike(errorVar);
+
+  if(selected.uninitialized()){
+    // we found no proposals
+    // If this is empty, there must be an error on this variable!
+    // this should not be possible. It Should have been caught as a signal earlier
+    WitnessImprovement dropped = focusUsingSignDisagreements(errorVar);
+    Assert(d_sgnDisagreements.empty());
+
+    return dropped;
+  }else{
+    d_sgnDisagreements.clear();
+  }
+
+  Assert(d_sgnDisagreements.empty());
+  Assert(!selected.uninitialized());
+
+  if(selected.focusDirection() == 0 &&
+     d_prevWitnessImprovement == HeuristicDegenerate &&
+     d_witnessImprovementInARow >= s_focusThreshold){
+
+    Trace("focusDownToJust") << "focusDownToJust " << errorVar << endl;
+
+    return focusDownToJust(errorVar);
+  }else{
+    WitnessImprovement w = selected.getWitness(false);
+    Assert(debugCheckWitness(selected, w, false));
+    updateAndSignal(selected, w);
+    logPivot(w);
+    return w;
+  }
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::focusDownToLastHalf(){
+  Assert(d_focusSize >= 2);
+
+  Trace("focusDownToLastHalf") << "focusDownToLastHalf "
+       << d_errorSet.errorSize()  << " "
+       << d_errorSet.focusSize() << " ";
+
+  uint32_t half = d_focusSize/2;
+  ArithVarVec buf;
+  for(ErrorSet::focus_iterator i = d_errorSet.focusBegin(),
+        i_end = d_errorSet.focusEnd(); i != i_end; ++i){
+    if(half > 0){
+      --half;
+    } else{
+      buf.push_back(*i);
+    }
+  }
+  WitnessImprovement w = adjustFocusShrank(buf);
+  Trace("focusDownToLastHalf") << "-> " << d_errorSet.focusSize() << endl;
+  return w;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::selectFocusImproving() {
+  Assert(d_focusErrorVar != ARITHVAR_SENTINEL);
+  Assert(d_focusSize >= 2);
+
+  LinearEqualityModule::UpdatePreferenceFunction upf =
+    &LinearEqualityModule::preferWitness<true>;
+
+  LinearEqualityModule::VarPreferenceFunction bpf =
+    &LinearEqualityModule::minRowLength;
+
+  UpdateInfo selected = selectPrimalUpdate(d_focusErrorVar, upf, bpf);
+
+  if(selected.uninitialized()){
+    Trace("selectFocusImproving") << "focus is optimum, but we don't have sat/conflict yet" << endl;
+
+    return focusDownToLastHalf();
+  }
+  Assert(!selected.uninitialized());
+  WitnessImprovement w = selected.getWitness(false);
+  Assert(debugCheckWitness(selected, w, false));
+
+  if(degenerate(w)){
+    Trace("selectFocusImproving") << "only degenerate" << endl;
+    if(d_prevWitnessImprovement == HeuristicDegenerate &&
+       d_witnessImprovementInARow >= s_focusThreshold){
+      Trace("selectFocusImproving") << "focus down been degenerate too long" << endl;
+      return focusDownToLastHalf();
+    }else{
+      Trace("selectFocusImproving") << "taking degenerate" << endl;
+    }
+  }
+  Trace("selectFocusImproving") << "selectFocusImproving did this " << selected << endl;
+
+  updateAndSignal(selected, w);
+  logPivot(w);
+  return w;
+}
+
+bool FCSimplexDecisionProcedure::debugDualLike(WitnessImprovement w,
+                                               ostream& out,
+                                               uint32_t prevFocusSize,
+                                               uint32_t prevErrorSize) const
+{
+  out << "DLV() ";
+  switch(w){
+  case ConflictFound:
+    out << "found conflict" << endl;
+    return !d_conflictVariables.empty();
+  case ErrorDropped:
+    out << "dropped " << prevErrorSize - d_errorSize << endl;
+    return d_errorSize < prevErrorSize;
+  case FocusImproved:
+    out << "focus improved"<< endl;
+    return d_errorSize == prevErrorSize;
+  case FocusShrank:
+    out << "focus shrank"<< endl;
+    return d_errorSize == prevErrorSize && prevFocusSize > d_focusSize;
+  case BlandsDegenerate:
+    out << "bland degenerate"<< endl;
+    return true;
+  case HeuristicDegenerate:
+    out << "heuristic degenerate"<< endl;
+    return true;
+  case AntiProductive:
+    out << "focus blur" << endl;
+    return prevFocusSize == 0;
+  case Degenerate:
+    return false;
+  }
+  return false;
+}
+
+Result::Status FCSimplexDecisionProcedure::dualLike()
+{
+  TimerStat::CodeTimer codeTimer(d_statistics.d_fcTimer);
+
+  Assert(d_sgnDisagreements.empty());
+  Assert(d_pivotBudget != 0);
+  Assert(d_errorSize == d_errorSet.errorSize());
+  Assert(d_errorSize > 0);
+  Assert(d_focusSize == d_errorSet.focusSize());
+  Assert(d_focusSize > 0);
+  Assert(d_conflictVariables.empty());
+  Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
+
+  d_scores.purge();
+  d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+
+
+  while(d_pivotBudget != 0  && d_errorSize > 0 && d_conflictVariables.empty()){
+    Trace("dualLike") << "dualLike " << endl;
+
+    Assert(d_errorSet.noSignals());
+
+    WitnessImprovement w = AntiProductive;
+    uint32_t prevFocusSize = d_focusSize;
+    uint32_t prevErrorSize = d_errorSize;
+
+    if(d_focusSize == 0){
+      Assert(d_errorSize == d_errorSet.errorSize());
+      Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
+
+      d_errorSet.blur();
+
+      d_focusSize = d_errorSet.focusSize();
+
+      Assert(d_errorSize == d_focusSize);
+      Assert(d_errorSize >= 1);
+
+      d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+
+      Trace("dualLike") << "blur " << d_focusSize << endl;
+    }else if(d_focusSize == 1){
+      // Possible outcomes:
+      // - errorSet size shrunk
+      // -- fixed v
+      // -- fixed something other than v
+      // - conflict
+      // - budget was exhausted
+
+      ArithVar e = d_errorSet.topFocusVariable();
+      Trace("dualLike") << "primalImproveError " << e << endl;
+      w = primalImproveError(e);
+    }else{
+
+      // Possible outcomes:
+      // - errorSet size shrunk
+      // -- fixed v
+      // -- fixed something other than v
+      // - conflict
+      // - budget was exhausted
+      // - focus went down
+      Assert(d_focusSize > 1);
+      ArithVar e = d_errorSet.topFocusVariable();
+      static constexpr unsigned s_sumMetricThreshold = 1;
+      if(d_errorSet.sumMetric(e) <= s_sumMetricThreshold){
+        Trace("dualLike") << "dualLikeImproveError " << e << endl;
+        w = dualLikeImproveError(e);
+      }else{
+        Trace("dualLike") << "selectFocusImproving " << endl;
+        w = selectFocusImproving();
+      }
+    }
+    Trace("dualLike") << "witnessImprovement: " << w << endl;
+    Assert(d_focusSize == d_errorSet.focusSize());
+    Assert(d_errorSize == d_errorSet.errorSize());
+
+    Assert(debugDualLike(w, Trace("dualLike"), prevFocusSize, prevErrorSize));
+    Trace("dualLike") << "Focus size " << d_focusSize << " (was " << prevFocusSize << ")" << endl;
+    Trace("dualLike") << "Error size " << d_errorSize << " (was " << prevErrorSize << ")" << endl;
+  }
+
+
+  if(d_focusErrorVar != ARITHVAR_SENTINEL){
+    tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+    d_focusErrorVar = ARITHVAR_SENTINEL;
+  }
+
+  Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
+  if(!d_conflictVariables.empty()){
+    return Result::UNSAT;
+  }else if(d_errorSet.errorEmpty()){
+    Assert(d_errorSet.noSignals());
+    return Result::SAT;
+  }else{
+    Assert(d_pivotBudget == 0);
+    return Result::UNKNOWN;
+  }
+}
+
+
+void FCSimplexDecisionProcedure::loadFocusSigns(){
+  Assert(d_focusCoefficients.empty());
+  Assert(d_focusErrorVar != ARITHVAR_SENTINEL);
+  for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_focusErrorVar); !ri.atEnd(); ++ri){
+    const Tableau::Entry& e = *ri;
+    ArithVar curr = e.getColVar();
+    d_focusCoefficients.set(curr, &e.getCoefficient());
+  }
+}
+
+void FCSimplexDecisionProcedure::unloadFocusSigns(){
+  d_focusCoefficients.purge();
+}
+
+const Rational& FCSimplexDecisionProcedure::focusCoefficient(ArithVar nb) const {
+  if(d_focusCoefficients.isKey(nb)){
+    return *(d_focusCoefficients[nb]);
+  }else{
+    return d_zero;
+  }
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/fc_simplex.h b/src/theory/arith/linear/fc_simplex.h
new file mode 100644 (file)
index 0000000..1d07df9
--- /dev/null
@@ -0,0 +1,259 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T)decision procedure.
+ *
+ * This implements the Simplex module for the Simpelx for DPLL(T) decision
+ * procedure.
+ * See the Simplex for DPLL(T) technical report for more background.(citation?)
+ * This shares with the theory a Tableau, and a PartialModel that:
+ *  - satisfies the equalities in the Tableau, and
+ *  - the assignment for the non-basic variables satisfies their bounds.
+ * This is required to either produce a conflict or satisifying PartialModel.
+ * Further, we require being told when a basic variable updates its value.
+ *
+ * During the Simplex search we maintain a queue of variables.
+ * The queue is required to contain all of the basic variables that voilate
+ * their bounds.
+ * As elimination from the queue is more efficient to be done lazily,
+ * we do not maintain that the queue of variables needs to be only basic
+ * variables or only variables that satisfy their bounds.
+ *
+ * The simplex procedure roughly follows Alberto's thesis. (citation?)
+ * There is one round of selecting using a heuristic pivoting rule.
+ * (See PreferenceFunction Documentation for the available options.)
+ * The non-basic variable is the one that appears in the fewest pivots.
+ * (Bruno says that Leonardo invented this first.)
+ * After this, Bland's pivot rule is invoked.
+ *
+ * During this proccess, we periodically inspect the queue of variables to
+ * 1) remove now extraneous extries,
+ * 2) detect conflicts that are "waiting" on the queue but may not be detected
+ *    by the current queue heuristics, and
+ * 3) detect multiple conflicts.
+ *
+ * Conflicts are greedily slackened to use the weakest bounds that still
+ * produce the conflict.
+ *
+ * Extra things tracked atm: (Subject to change at Tim's whims)
+ * - A superset of all of the newly pivoted variables.
+ * - A queue of additional conflicts that were discovered by Simplex.
+ *   These are theory valid and are currently turned into lemmas
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/simplex.h"
+#include "theory/arith/linear/simplex_update.h"
+#include "util/dense_map.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class FCSimplexDecisionProcedure : public SimplexDecisionProcedure{
+public:
+ FCSimplexDecisionProcedure(Env& env,
+                            LinearEqualityModule& linEq,
+                            ErrorSet& errors,
+                            RaiseConflict conflictChannel,
+                            TempVarMalloc tvmalloc);
+
+ Result::Status findModel(bool exactResult) override;
+
+ // other error variables are dropping
+ WitnessImprovement dualLikeImproveError(ArithVar evar);
+ WitnessImprovement primalImproveError(ArithVar evar);
+
+ // dual like
+ // - found conflict
+ // - satisfied error set
+ Result::Status dualLike();
+
+private:
+ static constexpr uint32_t PENALTY = 4;
+ DenseMultiset d_scores;
+ void decreasePenalties() { d_scores.removeOneOfEverything(); }
+ uint32_t penalty(ArithVar x) const { return d_scores.count(x); }
+ void setPenalty(ArithVar x, WitnessImprovement w)
+ {
+   if (improvement(w))
+   {
+     if (d_scores.count(x) > 0)
+     {
+       d_scores.removeAll(x);
+     }
+   }
+   else
+   {
+     d_scores.setCount(x, PENALTY);
+   }
+ }
+
+  /** The size of the focus set. */
+  uint32_t d_focusSize;
+
+  /** The current error focus variable. */
+  ArithVar d_focusErrorVar;
+
+  /**
+   * The signs of the coefficients in the focus set.
+   * This is empty until this has been loaded.
+   */
+  DenseMap<const Rational*> d_focusCoefficients;
+
+  /**
+   * Loads the signs of the coefficients of the variables on the row d_focusErrorVar
+   * into d_focusSgns.
+   */
+  void loadFocusSigns();
+
+  /** Unloads the information from d_focusSgns. */
+  void unloadFocusSigns();
+
+  /**
+   * The signs of a variable in the row of d_focusErrorVar.
+   * d_focusSgns must be loaded.
+   */
+  const Rational& focusCoefficient(ArithVar nb) const;
+
+  int32_t d_pivotBudget;
+
+  WitnessImprovement d_prevWitnessImprovement;
+  uint32_t d_witnessImprovementInARow;
+
+  uint32_t degeneratePivotsInARow() const;
+
+  static constexpr uint32_t s_focusThreshold = 6;
+  static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnLeaving = 100;
+  static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnEntering = 10;
+
+  DenseMap<uint32_t> d_leavingCountSinceImprovement;
+  void increaseLeavingCount(ArithVar x){
+    if(!d_leavingCountSinceImprovement.isKey(x)){
+      d_leavingCountSinceImprovement.set(x,1);
+    }else{
+      (d_leavingCountSinceImprovement.get(x))++;
+    }
+  }
+  LinearEqualityModule::UpdatePreferenceFunction selectLeavingFunction(ArithVar x){
+    bool useBlands = d_leavingCountSinceImprovement.isKey(x) &&
+      d_leavingCountSinceImprovement[x] >= s_maxDegeneratePivotsBeforeBlandsOnEntering;
+    if(useBlands) {
+      return &LinearEqualityModule::preferWitness<false>;
+    } else {
+      return &LinearEqualityModule::preferWitness<true>;
+    }
+  }
+
+  bool debugDualLike(WitnessImprovement w, std::ostream& out,
+                     uint32_t prevFocusSize, uint32_t prevErrorSize) const;
+
+  void debugPrintSignal(ArithVar updated) const;
+
+  ArithVarVec d_sgnDisagreements;
+
+  void logPivot(WitnessImprovement w);
+
+  void updateAndSignal(const UpdateInfo& selected, WitnessImprovement w);
+
+  UpdateInfo selectPrimalUpdate(ArithVar error,
+                                LinearEqualityModule::UpdatePreferenceFunction upf,
+                                LinearEqualityModule::VarPreferenceFunction bpf);
+
+
+  UpdateInfo selectUpdateForDualLike(ArithVar basic){
+    TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForDualLike);
+
+    LinearEqualityModule::UpdatePreferenceFunction upf =
+      &LinearEqualityModule::preferWitness<true>;
+    LinearEqualityModule::VarPreferenceFunction bpf =
+      &LinearEqualityModule::minVarOrder;
+    return selectPrimalUpdate(basic, upf, bpf);
+  }
+
+  UpdateInfo selectUpdateForPrimal(ArithVar basic, bool useBlands){
+    TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForPrimal);
+
+    LinearEqualityModule::UpdatePreferenceFunction upf;
+    if(useBlands) {
+      upf = &LinearEqualityModule::preferWitness<false>;
+    } else {
+      upf = &LinearEqualityModule::preferWitness<true>;
+    }
+
+    LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
+      &LinearEqualityModule::minVarOrder :
+      &LinearEqualityModule::minRowLength;
+
+    return selectPrimalUpdate(basic, upf, bpf);
+  }
+  WitnessImprovement selectFocusImproving() ;
+
+  WitnessImprovement focusUsingSignDisagreements(ArithVar basic);
+  WitnessImprovement focusDownToLastHalf();
+  WitnessImprovement adjustFocusShrank(const ArithVarVec& drop);
+  WitnessImprovement focusDownToJust(ArithVar v);
+
+
+  void adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges);
+
+  /**
+   * This is the main simplex for DPLL(T) loop.
+   * It runs for at most maxIterations.
+   *
+   * Returns true iff it has found a conflict.
+   * d_conflictVariable will be set and the conflict for this row is reported.
+   */
+  bool searchForFeasibleSolution(uint32_t maxIterations);
+
+  bool initialProcessSignals(){
+    TimerStat &timer = d_statistics.d_initialSignalsTime;
+    IntStat& conflictStat  = d_statistics.d_initialConflicts;
+    bool res = standardProcessSignals(timer, conflictStat);
+    d_focusSize = d_errorSet.focusSize();
+    return res;
+  }
+
+  static bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands);
+
+  /** These fields are designed to be accessible to TheoryArith methods. */
+  class Statistics {
+  public:
+    TimerStat d_initialSignalsTime;
+    IntStat d_initialConflicts;
+
+    IntStat d_fcFoundUnsat;
+    IntStat d_fcFoundSat;
+    IntStat d_fcMissed;
+
+    TimerStat d_fcTimer;
+    TimerStat d_fcFocusConstructionTimer;
+
+    TimerStat d_selectUpdateForDualLike;
+    TimerStat d_selectUpdateForPrimal;
+
+    ReferenceStat<uint32_t> d_finalCheckPivotCounter;
+
+    Statistics(const std::string& name, uint32_t& pivots);
+  } d_statistics;
+};/* class FCSimplexDecisionProcedure */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/infer_bounds.cpp b/src/theory/arith/linear/infer_bounds.cpp
new file mode 100644 (file)
index 0000000..ec2843a
--- /dev/null
@@ -0,0 +1,271 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Andres Noetzli, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/infer_bounds.h"
+#include "theory/rewriter.h"
+
+using namespace cvc5::internal::kind;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+using namespace inferbounds;
+
+InferBoundAlgorithm::InferBoundAlgorithm()
+  : d_alg(None)
+{}
+
+InferBoundAlgorithm::InferBoundAlgorithm(Algorithms a)
+  : d_alg(a)
+{
+  Assert(a != Simplex);
+}
+
+InferBoundAlgorithm::InferBoundAlgorithm(
+    const std::optional<int>& simplexRounds)
+    : d_alg(Simplex)
+{}
+
+Algorithms InferBoundAlgorithm::getAlgorithm() const{
+  return d_alg;
+}
+
+const std::optional<int>& InferBoundAlgorithm::getSimplexRounds() const
+{
+  Assert(getAlgorithm() == Simplex);
+  return d_simplexRounds;
+}
+
+InferBoundAlgorithm InferBoundAlgorithm::mkLookup(){
+  return InferBoundAlgorithm(Lookup);
+}
+
+InferBoundAlgorithm InferBoundAlgorithm::mkRowSum(){
+  return InferBoundAlgorithm(RowSum);
+}
+
+InferBoundAlgorithm InferBoundAlgorithm::mkSimplex(
+    const std::optional<int>& rounds)
+{
+  return InferBoundAlgorithm(rounds);
+}
+
+ArithEntailmentCheckParameters::ArithEntailmentCheckParameters()
+    : d_algorithms()
+{}
+
+ArithEntailmentCheckParameters::~ArithEntailmentCheckParameters()
+{}
+
+
+void ArithEntailmentCheckParameters::addLookupRowSumAlgorithms(){
+  addAlgorithm(InferBoundAlgorithm::mkLookup());
+  addAlgorithm(InferBoundAlgorithm::mkRowSum());
+}
+
+void ArithEntailmentCheckParameters::addAlgorithm(const inferbounds::InferBoundAlgorithm& alg){
+  d_algorithms.push_back(alg);
+}
+
+ArithEntailmentCheckParameters::const_iterator ArithEntailmentCheckParameters::begin() const{
+  return d_algorithms.begin();
+}
+
+ArithEntailmentCheckParameters::const_iterator ArithEntailmentCheckParameters::end() const{
+  return d_algorithms.end();
+}
+
+InferBoundsResult::InferBoundsResult()
+  : d_foundBound(false)
+  , d_budgetExhausted(false)
+  , d_boundIsProvenOpt(false)
+  , d_inconsistentState(false)
+  , d_reachedThreshold(false)
+  , d_value(false)
+  , d_term(Node::null())
+  , d_upperBound(true)
+  , d_explanation(Node::null())
+{}
+
+InferBoundsResult::InferBoundsResult(Node term, bool ub)
+  : d_foundBound(false)
+  , d_budgetExhausted(false)
+  , d_boundIsProvenOpt(false)
+  , d_inconsistentState(false)
+  , d_reachedThreshold(false)
+  , d_value(false)
+  , d_term(term)
+  , d_upperBound(ub)
+  , d_explanation(Node::null())
+{}
+
+bool InferBoundsResult::foundBound() const {
+  return d_foundBound;
+}
+bool InferBoundsResult::boundIsOptimal() const {
+  return d_boundIsProvenOpt;
+}
+bool InferBoundsResult::inconsistentState() const {
+  return d_inconsistentState;
+}
+
+bool InferBoundsResult::boundIsInteger() const{
+  return foundBound() && d_value.isIntegral();
+}
+
+bool InferBoundsResult::boundIsRational() const {
+  return foundBound() && d_value.infinitesimalIsZero();
+}
+
+Integer InferBoundsResult::valueAsInteger() const{
+  Assert(boundIsInteger());
+  return getValue().floor();
+}
+const Rational& InferBoundsResult::valueAsRational() const{
+  Assert(boundIsRational());
+  return getValue().getNoninfinitesimalPart();
+}
+
+const DeltaRational& InferBoundsResult::getValue() const{
+  return d_value;
+}
+
+Node InferBoundsResult::getTerm() const { return d_term; }
+
+Node InferBoundsResult::getLiteral() const{
+  const Rational& q = getValue().getNoninfinitesimalPart();
+  NodeManager* nm = NodeManager::currentNM();
+  Node qnode = nm->mkConst(CONST_RATIONAL, q);
+
+  Kind k;
+  if(d_upperBound){
+    // x <= q + c*delta
+    Assert(getValue().infinitesimalSgn() <= 0);
+    k = boundIsRational() ? kind::LEQ : kind::LT;
+  }else{
+    // x >= q + c*delta
+    Assert(getValue().infinitesimalSgn() >= 0);
+    k = boundIsRational() ? kind::GEQ : kind::GT;
+  }
+  return nm->mkNode(k, getTerm(), qnode);
+}
+
+/* If there is a bound, this is a node that explains the bound. */
+Node InferBoundsResult::getExplanation() const{
+  return d_explanation;
+}
+
+
+void InferBoundsResult::setBound(const DeltaRational& dr, Node exp){
+  d_foundBound = true;
+  d_value = dr;
+  d_explanation = exp;
+}
+
+void InferBoundsResult::setBudgetExhausted() { d_budgetExhausted = true; }
+void InferBoundsResult::setReachedThreshold() { d_reachedThreshold = true; }
+void InferBoundsResult::setIsOptimal() { d_boundIsProvenOpt = true; }
+void InferBoundsResult::setInconsistent() { d_inconsistentState = true; }
+
+bool InferBoundsResult::thresholdWasReached() const{
+  return d_reachedThreshold;
+}
+bool InferBoundsResult::budgetIsExhausted() const{
+  return d_budgetExhausted;
+}
+
+std::ostream& operator<<(std::ostream& os, const InferBoundsResult& ibr){
+  os << "{InferBoundsResult " << std::endl;
+  os << "on " << ibr.getTerm() << ", ";
+  if(ibr.findUpperBound()){
+    os << "find upper bound, ";
+  }else{
+    os << "find lower bound, ";
+  }
+  if(ibr.foundBound()){
+    os << "found a bound: ";
+    if(ibr.boundIsInteger()){
+      os << ibr.valueAsInteger() << "(int), ";
+    }else if(ibr.boundIsRational()){
+      os << ibr.valueAsRational() << "(rat), ";
+    }else{
+      os << ibr.getValue() << "(extended), ";
+    }
+
+    os << "as term " << ibr.getLiteral() << ", ";
+    os << "explanation " << ibr.getExplanation() << ", ";
+  }else {
+    os << "did not find a bound, ";
+  }
+
+  if(ibr.boundIsOptimal()){
+    os << "(opt), ";
+  }
+
+  if(ibr.inconsistentState()){
+    os << "(inconsistent), ";
+  }
+  if(ibr.budgetIsExhausted()){
+    os << "(budget exhausted), ";
+  }
+  if(ibr.thresholdWasReached()){
+    os << "(reached threshold), ";
+  }
+  os << "}";
+  return os;
+}
+
+ArithEntailmentCheckSideEffects::ArithEntailmentCheckSideEffects()
+    : d_simplexSideEffects(NULL)
+{}
+
+ArithEntailmentCheckSideEffects::~ArithEntailmentCheckSideEffects(){
+  if(d_simplexSideEffects != NULL){
+    delete d_simplexSideEffects;
+    d_simplexSideEffects = NULL;
+  }
+}
+
+InferBoundsResult& ArithEntailmentCheckSideEffects::getSimplexSideEffects(){
+  if(d_simplexSideEffects == NULL){
+    d_simplexSideEffects = new InferBoundsResult;
+  }
+  return *d_simplexSideEffects;
+}
+
+namespace inferbounds { /* namespace arith */
+
+std::ostream& operator<<(std::ostream& os,  const Algorithms a){
+  switch(a){
+  case None:    os << "AlgNone"; break;
+  case Lookup:  os << "AlgLookup"; break;
+  case RowSum:  os << "AlgRowSum"; break;
+  case Simplex: os << "AlgSimplex"; break;
+  default:
+    Unhandled();
+  }
+
+  return os;
+}
+
+} /* namespace inferbounds */
+
+} /* namespace arith */
+} /* namespace theory */
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/infer_bounds.h b/src/theory/arith/linear/infer_bounds.h
new file mode 100644 (file)
index 0000000..de6b50e
--- /dev/null
@@ -0,0 +1,164 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Andrew Reynolds, Andres Noetzli
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <optional>
+#include <ostream>
+
+#include "expr/node.h"
+#include "theory/arith/delta_rational.h"
+#include "util/integer.h"
+#include "util/rational.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+namespace inferbounds {
+  enum Algorithms {None = 0, Lookup, RowSum, Simplex};
+  enum SimplexParamKind { Unbounded, NumVars, Direct};
+
+class InferBoundAlgorithm {
+private:
+  Algorithms d_alg;
+  std::optional<int> d_simplexRounds;
+  InferBoundAlgorithm(Algorithms a);
+  InferBoundAlgorithm(const std::optional<int>& simplexRounds);
+
+ public:
+  InferBoundAlgorithm();
+
+  Algorithms getAlgorithm() const;
+  const std::optional<int>& getSimplexRounds() const;
+
+  static InferBoundAlgorithm mkLookup();
+  static InferBoundAlgorithm mkRowSum();
+  static InferBoundAlgorithm mkSimplex(const std::optional<int>& rounds);
+};
+
+std::ostream& operator<<(std::ostream& os, const Algorithms a);
+} /* namespace inferbounds */
+
+class ArithEntailmentCheckParameters
+{
+ private:
+  typedef std::vector<inferbounds::InferBoundAlgorithm> VecInferBoundAlg;
+  VecInferBoundAlg d_algorithms;
+
+public:
+  typedef VecInferBoundAlg::const_iterator const_iterator;
+
+  ArithEntailmentCheckParameters();
+  ~ArithEntailmentCheckParameters();
+
+  void addLookupRowSumAlgorithms();
+  void addAlgorithm(const inferbounds::InferBoundAlgorithm& alg);
+
+  const_iterator begin() const;
+  const_iterator end() const;
+};
+
+
+
+class InferBoundsResult {
+public:
+  InferBoundsResult();
+  InferBoundsResult(Node term, bool ub);
+
+  void setBound(const DeltaRational& dr, Node exp);
+  bool foundBound() const;
+
+  void setIsOptimal();
+  bool boundIsOptimal() const;
+
+  void setInconsistent();
+  bool inconsistentState() const;
+
+  const DeltaRational& getValue() const;
+  bool boundIsRational() const;
+  const Rational& valueAsRational() const;
+  bool boundIsInteger() const;
+  Integer valueAsInteger() const;
+
+  Node getTerm() const;
+  Node getLiteral() const;
+  void setTerm(Node t){ d_term = t; }
+
+  /* If there is a bound, this is a node that explains the bound. */
+  Node getExplanation() const;
+
+  bool budgetIsExhausted() const;
+  void setBudgetExhausted();
+
+  bool thresholdWasReached() const;
+  void setReachedThreshold();
+
+  bool findUpperBound() const { return d_upperBound; }
+
+  void setFindLowerBound() { d_upperBound = false; }
+  void setFindUpperBound() { d_upperBound = true; }
+private:
+  /* was a bound found */
+  bool d_foundBound;
+
+  /* was the budget exhausted */
+  bool d_budgetExhausted;
+
+  /* does the bound have to be optimal*/
+  bool d_boundIsProvenOpt;
+
+  /* was this started on an inconsistent state. */
+  bool d_inconsistentState;
+
+  /* reached the threshold. */
+  bool d_reachedThreshold;
+
+  /* the value of the bound */
+  DeltaRational d_value;
+
+  /* The input term. */
+  Node d_term;
+
+  /* Was the bound found an upper or lower bound.*/
+  bool d_upperBound;
+
+  /* Explanation of the bound. */
+  Node d_explanation;
+};
+
+std::ostream& operator<<(std::ostream& os, const InferBoundsResult& ibr);
+
+class ArithEntailmentCheckSideEffects
+{
+ public:
+  ArithEntailmentCheckSideEffects();
+  ~ArithEntailmentCheckSideEffects();
+
+  InferBoundsResult& getSimplexSideEffects();
+
+private:
+  InferBoundsResult* d_simplexSideEffects;
+};
+
+
+} /* namespace arith */
+} /* namespace theory */
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/linear_equality.cpp b/src/theory/arith/linear/linear_equality.cpp
new file mode 100644 (file)
index 0000000..6fcabef
--- /dev/null
@@ -0,0 +1,1375 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Andres Noetzli
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This implements the LinearEqualityModule.
+ */
+#include "theory/arith/linear/linear_equality.h"
+
+#include "base/output.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/* Explicitly instatiate these functions. */
+
+template ArithVar LinearEqualityModule::selectSlack<true>(ArithVar x_i, VarPreferenceFunction pf) const;
+template ArithVar LinearEqualityModule::selectSlack<false>(ArithVar x_i, VarPreferenceFunction pf) const;
+
+template bool LinearEqualityModule::preferWitness<true>(const UpdateInfo& a, const UpdateInfo& b) const;
+template bool LinearEqualityModule::preferWitness<false>(const UpdateInfo& a, const UpdateInfo& b) const;
+
+
+void Border::output(std::ostream& out) const{
+  out << "{Border"
+      << ", " << d_bound->getVariable()
+      << ", " << d_bound->getValue()
+      << ", " << d_diff
+      << ", " << d_areFixing
+      << ", " << d_upperbound;
+  if(ownBorder()){
+    out << ", ownBorder";
+  }else{
+    out << ", " << d_entry->getCoefficient();
+  }
+  out << ", " << d_bound
+      << "}";
+}
+
+LinearEqualityModule::LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundInfoMap& boundsTracking, BasicVarModelUpdateCallBack f):
+  d_variables(vars),
+  d_tableau(t),
+  d_basicVariableUpdates(f),
+  d_increasing(1),
+  d_decreasing(-1),
+  d_upperBoundDifference(),
+  d_lowerBoundDifference(),
+  d_one(1),
+  d_negOne(-1),
+  d_btracking(boundsTracking),
+  d_areTracking(false),
+  d_trackCallback(this)
+{}
+
+LinearEqualityModule::Statistics::Statistics()
+    : d_statPivots(
+        smtStatisticsRegistry().registerInt("theory::arith::pivots")),
+      d_statUpdates(
+          smtStatisticsRegistry().registerInt("theory::arith::updates")),
+      d_pivotTime(
+          smtStatisticsRegistry().registerTimer("theory::arith::pivotTime")),
+      d_adjTime(
+          smtStatisticsRegistry().registerTimer("theory::arith::adjTime")),
+      d_weakeningAttempts(smtStatisticsRegistry().registerInt(
+          "theory::arith::weakening::attempts")),
+      d_weakeningSuccesses(smtStatisticsRegistry().registerInt(
+          "theory::arith::weakening::success")),
+      d_weakenings(smtStatisticsRegistry().registerInt(
+          "theory::arith::weakening::total")),
+      d_weakenTime(smtStatisticsRegistry().registerTimer(
+          "theory::arith::weakening::time")),
+      d_forceTime(
+          smtStatisticsRegistry().registerTimer("theory::arith::forcing::time"))
+{
+}
+
+void LinearEqualityModule::includeBoundUpdate(ArithVar v, const BoundsInfo& prev){
+  Assert(!d_areTracking);
+
+  BoundsInfo curr = d_variables.boundsInfo(v);
+
+  Assert(prev != curr);
+  Tableau::ColIterator basicIter = d_tableau.colIterator(v);
+  for(; !basicIter.atEnd(); ++basicIter){
+    const Tableau::Entry& entry = *basicIter;
+    Assert(entry.getColVar() == v);
+    int a_ijSgn = entry.getCoefficient().sgn();
+
+    RowIndex ridx = entry.getRowIndex();
+    BoundsInfo& counts = d_btracking.get(ridx);
+    Trace("includeBoundUpdate") << d_tableau.rowIndexToBasic(ridx) << " " << counts << " to " ;
+    counts.addInChange(a_ijSgn, prev, curr);
+    Trace("includeBoundUpdate") << counts << " " << a_ijSgn << std::endl;
+  }
+}
+
+void LinearEqualityModule::updateMany(const DenseMap<DeltaRational>& many){
+  for(DenseMap<DeltaRational>::const_iterator i = many.begin(), i_end = many.end(); i != i_end; ++i){
+    ArithVar nb = *i;
+    if(!d_tableau.isBasic(nb)){
+      Assert(!d_tableau.isBasic(nb));
+      const DeltaRational& newValue = many[nb];
+      if(newValue != d_variables.getAssignment(nb)){
+        Trace("arith::updateMany")
+          << "updateMany:" << nb << " "
+          << d_variables.getAssignment(nb) << " to "<< newValue << endl;
+        update(nb, newValue);
+      }
+    }
+  }
+}
+
+
+
+
+void LinearEqualityModule::applySolution(const DenseSet& newBasis, const DenseMap<DeltaRational>& newValues){
+  forceNewBasis(newBasis);
+  updateMany(newValues);
+}
+
+void LinearEqualityModule::forceNewBasis(const DenseSet& newBasis){
+  TimerStat::CodeTimer codeTimer(d_statistics.d_forceTime);
+  cout << "force begin" << endl;
+  DenseSet needsToBeAdded;
+  for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){
+    ArithVar b = *i;
+    if(!d_tableau.isBasic(b)){
+      needsToBeAdded.add(b);
+    }
+  }
+
+  while(!needsToBeAdded.empty()){
+    ArithVar toRemove = ARITHVAR_SENTINEL;
+    ArithVar toAdd = ARITHVAR_SENTINEL;
+    DenseSet::const_iterator i = needsToBeAdded.begin(), i_end = needsToBeAdded.end();
+    for(; toAdd == ARITHVAR_SENTINEL && i != i_end; ++i){
+      ArithVar v = *i;
+
+      Tableau::ColIterator colIter = d_tableau.colIterator(v);
+      for(; !colIter.atEnd(); ++colIter){
+        const Tableau::Entry& entry = *colIter;
+        Assert(entry.getColVar() == v);
+        ArithVar b = d_tableau.rowIndexToBasic(entry.getRowIndex());
+        if(!newBasis.isMember(b)){
+          toAdd = v;
+          if(toRemove == ARITHVAR_SENTINEL ||
+             d_tableau.basicRowLength(toRemove) > d_tableau.basicRowLength(b)){
+            toRemove = b;
+          }
+        }
+      }
+    }
+    Assert(toRemove != ARITHVAR_SENTINEL);
+    Assert(toAdd != ARITHVAR_SENTINEL);
+
+    Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl;
+    d_tableau.pivot(toRemove, toAdd, d_trackCallback);
+    d_basicVariableUpdates(toAdd);
+
+    Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
+    needsToBeAdded.remove(toAdd);
+  }
+}
+
+void LinearEqualityModule::updateUntracked(ArithVar x_i, const DeltaRational& v){
+  Assert(!d_tableau.isBasic(x_i));
+  Assert(!d_areTracking);
+  const DeltaRational& assignment_x_i = d_variables.getAssignment(x_i);
+  ++(d_statistics.d_statUpdates);
+
+
+  Trace("arith") <<"update " << x_i << ": "
+                 << assignment_x_i << "|-> " << v << endl;
+  DeltaRational diff = v - assignment_x_i;
+
+  Tableau::ColIterator colIter = d_tableau.colIterator(x_i);
+  for(; !colIter.atEnd(); ++colIter){
+    const Tableau::Entry& entry = *colIter;
+    Assert(entry.getColVar() == x_i);
+
+    ArithVar x_j = d_tableau.rowIndexToBasic(entry.getRowIndex());
+    const Rational& a_ji = entry.getCoefficient();
+
+    const DeltaRational& assignment = d_variables.getAssignment(x_j);
+    DeltaRational  nAssignment = assignment+(diff * a_ji);
+    d_variables.setAssignment(x_j, nAssignment);
+
+    d_basicVariableUpdates(x_j);
+  }
+
+  d_variables.setAssignment(x_i, v);
+
+  if(TraceIsOn("paranoid:check_tableau")){  debugCheckTableau(); }
+}
+
+void LinearEqualityModule::updateTracked(ArithVar x_i, const DeltaRational& v){
+  TimerStat::CodeTimer codeTimer(d_statistics.d_adjTime);
+
+  Assert(!d_tableau.isBasic(x_i));
+  Assert(d_areTracking);
+
+  ++(d_statistics.d_statUpdates);
+
+  DeltaRational diff =  v - d_variables.getAssignment(x_i);
+  Trace("arith") <<"update " << x_i << ": "
+                 << d_variables.getAssignment(x_i) << "|-> " << v << endl;
+
+
+  BoundCounts before = d_variables.atBoundCounts(x_i);
+  d_variables.setAssignment(x_i, v);
+  BoundCounts after = d_variables.atBoundCounts(x_i);
+
+  bool anyChange = before != after;
+
+  Tableau::ColIterator colIter = d_tableau.colIterator(x_i);
+  for(; !colIter.atEnd(); ++colIter){
+    const Tableau::Entry& entry = *colIter;
+    Assert(entry.getColVar() == x_i);
+
+    RowIndex ridx = entry.getRowIndex();
+    ArithVar x_j = d_tableau.rowIndexToBasic(ridx);
+    const Rational& a_ji = entry.getCoefficient();
+
+    const DeltaRational& assignment = d_variables.getAssignment(x_j);
+    DeltaRational  nAssignment = assignment+(diff * a_ji);
+    Trace("update") << x_j << " " << a_ji << assignment << " -> " << nAssignment << endl;
+    BoundCounts xjBefore = d_variables.atBoundCounts(x_j);
+    d_variables.setAssignment(x_j, nAssignment);
+    BoundCounts xjAfter = d_variables.atBoundCounts(x_j);
+
+    Assert(rowIndexIsTracked(ridx));
+    BoundsInfo& next_bc_k = d_btracking.get(ridx);
+    if(anyChange){
+      next_bc_k.addInAtBoundChange(a_ji.sgn(), before, after);
+    }
+    if(xjBefore != xjAfter){
+      next_bc_k.addInAtBoundChange(-1, xjBefore, xjAfter);
+    }
+
+    d_basicVariableUpdates(x_j);
+  }
+
+  if(TraceIsOn("paranoid:check_tableau")){  debugCheckTableau(); }
+}
+
+void LinearEqualityModule::pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& x_i_value){
+  Assert(x_i != x_j);
+
+  TimerStat::CodeTimer codeTimer(d_statistics.d_pivotTime);
+
+  if(TraceIsOn("arith::tracking::pre")){
+    Trace("arith::tracking") << "pre update" << endl;
+    debugCheckTracking();
+  }
+
+  if(TraceIsOn("arith::simplex:row")){ debugPivot(x_i, x_j); }
+
+  RowIndex ridx = d_tableau.basicToRowIndex(x_i);
+  const Tableau::Entry& entry_ij =  d_tableau.findEntry(ridx, x_j);
+  Assert(!entry_ij.blank());
+
+  const Rational& a_ij = entry_ij.getCoefficient();
+  const DeltaRational& betaX_i = d_variables.getAssignment(x_i);
+  DeltaRational theta = (x_i_value - betaX_i)/a_ij;
+  DeltaRational x_j_value = d_variables.getAssignment(x_j) + theta;
+
+  updateTracked(x_j, x_j_value);
+
+  if(TraceIsOn("arith::tracking::mid")){
+    Trace("arith::tracking") << "postupdate prepivot" << endl;
+    debugCheckTracking();
+  }
+
+  // Pivots
+  ++(d_statistics.d_statPivots);
+
+  d_tableau.pivot(x_i, x_j, d_trackCallback);
+
+  if(TraceIsOn("arith::tracking::post")){
+    Trace("arith::tracking") << "postpivot" << endl;
+    debugCheckTracking();
+  }
+
+  d_basicVariableUpdates(x_j);
+
+  if(TraceIsOn("matrix")){
+    d_tableau.printMatrix();
+  }
+}
+
+uint32_t LinearEqualityModule::updateProduct(const UpdateInfo& inf) const {
+  uint32_t colLen = d_tableau.getColLength(inf.nonbasic());
+  if(inf.describesPivot()){
+    Assert(inf.leaving() != inf.nonbasic());
+    return colLen + d_tableau.basicRowLength(inf.leaving());
+  }else{
+    return colLen;
+  }
+}
+
+void LinearEqualityModule::debugCheckTracking(){
+  Tableau::BasicIterator basicIter = d_tableau.beginBasic(),
+    endIter = d_tableau.endBasic();
+  for(; basicIter != endIter; ++basicIter){
+    ArithVar basic = *basicIter;
+    Trace("arith::tracking") << "arith::tracking row basic: " << basic << endl;
+
+    for(Tableau::RowIterator iter = d_tableau.basicRowIterator(basic); !iter.atEnd() && TraceIsOn("arith::tracking"); ++iter){
+      const Tableau::Entry& entry = *iter;
+
+      ArithVar var = entry.getColVar();
+      const Rational& coeff = entry.getCoefficient();
+      DeltaRational beta = d_variables.getAssignment(var);
+      Trace("arith::tracking") << var << " " << d_variables.boundsInfo(var)
+                               << " " << beta << coeff;
+      if(d_variables.hasLowerBound(var)){
+        Trace("arith::tracking") << "(lb " << d_variables.getLowerBound(var) << ")";
+      }
+      if(d_variables.hasUpperBound(var)){
+        Trace("arith::tracking") << "(up " << d_variables.getUpperBound(var) << ")";
+      }
+      Trace("arith::tracking") << endl;
+    }
+    Trace("arith::tracking") << "end row"<< endl;
+
+    if(basicIsTracked(basic)){
+      RowIndex ridx = d_tableau.basicToRowIndex(basic);
+      BoundsInfo computed = computeRowBoundInfo(ridx, false);
+      Trace("arith::tracking")
+        << "computed " << computed
+        << " tracking " << d_btracking[ridx] << endl;
+      Assert(computed == d_btracking[ridx]);
+    }
+  }
+}
+
+void LinearEqualityModule::debugPivot(ArithVar x_i, ArithVar x_j){
+  Trace("arith::pivot") << "debugPivot("<< x_i  <<"|->"<< x_j << ")" << endl;
+
+  for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+    const Tableau::Entry& entry = *iter;
+
+    ArithVar var = entry.getColVar();
+    const Rational& coeff = entry.getCoefficient();
+    DeltaRational beta = d_variables.getAssignment(var);
+    Trace("arith::pivot") << var << beta << coeff;
+    if(d_variables.hasLowerBound(var)){
+      Trace("arith::pivot") << "(lb " << d_variables.getLowerBound(var) << ")";
+    }
+    if(d_variables.hasUpperBound(var)){
+      Trace("arith::pivot") << "(up " << d_variables.getUpperBound(var) << ")";
+    }
+    Trace("arith::pivot") << endl;
+  }
+  Trace("arith::pivot") << "end row"<< endl;
+}
+
+/**
+ * This check is quite expensive.
+ * It should be wrapped in a TraceIsOn() guard.
+ *   if(TraceIsOn("paranoid:check_tableau")){
+ *      checkTableau();
+ *   }
+ */
+void LinearEqualityModule::debugCheckTableau(){
+  Tableau::BasicIterator basicIter = d_tableau.beginBasic(),
+    endIter = d_tableau.endBasic();
+  for(; basicIter != endIter; ++basicIter){
+    ArithVar basic = *basicIter;
+    DeltaRational sum;
+    Trace("paranoid:check_tableau") << "starting row" << basic << endl;
+    Tableau::RowIterator nonbasicIter = d_tableau.basicRowIterator(basic);
+    for(; !nonbasicIter.atEnd(); ++nonbasicIter){
+      const Tableau::Entry& entry = *nonbasicIter;
+      ArithVar nonbasic = entry.getColVar();
+      if(basic == nonbasic) continue;
+
+      const Rational& coeff = entry.getCoefficient();
+      DeltaRational beta = d_variables.getAssignment(nonbasic);
+      Trace("paranoid:check_tableau") << nonbasic << beta << coeff<<endl;
+      sum = sum + (beta*coeff);
+    }
+    DeltaRational shouldBe = d_variables.getAssignment(basic);
+    Trace("paranoid:check_tableau") << "ending row" << sum
+                                    << "," << shouldBe << endl;
+
+    Assert(sum == shouldBe);
+  }
+}
+
+DeltaRational LinearEqualityModule::computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const {
+  DeltaRational sum(0,0);
+  for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){
+    const Tableau::Entry& entry = (*i);
+    ArithVar v = entry.getColVar();
+    if(v == skip){ continue; }
+
+    const Rational& coeff =  entry.getCoefficient();
+    bool vUb = (rowUb == (coeff.sgn() > 0));
+
+    const DeltaRational& bound = vUb ?
+      d_variables.getUpperBound(v):
+      d_variables.getLowerBound(v);
+
+    DeltaRational diff = bound * coeff;
+    sum = sum + diff;
+  }
+  return sum;
+}
+
+/**
+ * Computes the value of a basic variable using the current assignment.
+ */
+DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe) const{
+  Assert(d_tableau.isBasic(x));
+  DeltaRational sum(0);
+
+  for(Tableau::RowIterator i = d_tableau.basicRowIterator(x); !i.atEnd(); ++i){
+    const Tableau::Entry& entry = (*i);
+    ArithVar nonbasic = entry.getColVar();
+    if(nonbasic == x) continue;
+    const Rational& coeff = entry.getCoefficient();
+
+    const DeltaRational& assignment = d_variables.getAssignment(nonbasic, useSafe);
+    sum = sum + (assignment * coeff);
+  }
+  return sum;
+}
+
+const Tableau::Entry* LinearEqualityModule::rowLacksBound(RowIndex ridx, bool rowUb, ArithVar skip){
+  Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
+  for(; !iter.atEnd(); ++iter){
+    const Tableau::Entry& entry = *iter;
+
+    ArithVar var = entry.getColVar();
+    if(var == skip) { continue; }
+
+    int sgn = entry.getCoefficient().sgn();
+    bool selectUb = (rowUb == (sgn > 0));
+    bool hasBound = selectUb ?
+      d_variables.hasUpperBound(var):
+      d_variables.hasLowerBound(var);
+    if(!hasBound){
+      return &entry;
+    }
+  }
+  return NULL;
+}
+
+void LinearEqualityModule::propagateBasicFromRow(ConstraintP c,
+                                                 bool produceProofs)
+{
+  Assert(c != NullConstraint);
+  Assert(c->isUpperBound() || c->isLowerBound());
+  Assert(!c->assertedToTheTheory());
+  Assert(!c->hasProof());
+
+  bool upperBound = c->isUpperBound();
+  ArithVar basic = c->getVariable();
+  RowIndex ridx = d_tableau.basicToRowIndex(basic);
+
+  ConstraintCPVec bounds;
+  RationalVectorP coeffs = produceProofs ? new RationalVector() : nullptr;
+  propagateRow(bounds, ridx, upperBound, c, coeffs);
+  c->impliedByFarkas(bounds, coeffs, false);
+  c->tryToPropagate();
+
+  if(coeffs != RationalVectorPSentinel) { delete coeffs; }
+}
+
+/* An explanation of the farkas coefficients.
+ *
+ * We are proving c using the other variables on the row.
+ * The proof is in terms of the other constraints and the negation of c, ~c.
+ *
+ * A row has the form:
+ *   sum a_i * x_i  = 0
+ * or
+ *   sx + sum r y + sum q z = 0
+ * where r > 0 and q < 0.
+ *
+ * If rowUp, we are proving c
+ *   g = sum r u_y + sum q l_z
+ *   and c is entailed by -sx <= g
+ * If !rowUp, we are proving c
+ *   g = sum r l_y + sum q u_z
+ *   and c is entailed by -sx >= g
+ *
+ *             | s     | c         | ~c       | u_i     | l_i
+ *   if  rowUp | s > 0 | x >= -g/s | x < -g/s | a_i > 0 | a_i < 0
+ *   if  rowUp | s < 0 | x <= -g/s | x > -g/s | a_i > 0 | a_i < 0
+ *   if !rowUp | s > 0 | x <= -g/s | x > -g/s | a_i < 0 | a_i > 0
+ *   if !rowUp | s < 0 | x >= -g/s | x < -g/s | a_i < 0 | a_i > 0
+ *
+ *
+ * Thus we treat !rowUp as multiplying the row by -1 and rowUp as 1
+ * for the entire row.
+ */
+void LinearEqualityModule::propagateRow(ConstraintCPVec& into, RowIndex ridx, bool rowUp, ConstraintP c, RationalVectorP farkas){
+  Assert(!c->assertedToTheTheory());
+  Assert(c->canBePropagated());
+  Assert(!c->hasProof());
+
+  if(farkas != RationalVectorPSentinel){
+    Assert(farkas->empty());
+    farkas->push_back(Rational(0));
+  }
+
+  ArithVar v = c->getVariable();
+  Trace("arith::propagateRow") << "LinearEqualityModule::propagateRow("
+                                   << ridx << ", " << rowUp << ", " << v << ") start" << endl;
+
+  const Rational& multiple = rowUp ? d_one : d_negOne;
+
+  Trace("arith::propagateRow") << "multiple: " << multiple << endl;
+
+  Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
+  for(; !iter.atEnd(); ++iter){
+    const Tableau::Entry& entry = *iter;
+    ArithVar nonbasic = entry.getColVar();
+    const Rational& a_ij = entry.getCoefficient();
+    int sgn = a_ij.sgn();
+    Assert(sgn != 0);
+    bool selectUb = rowUp ? (sgn > 0) : (sgn < 0);
+
+    Assert(nonbasic != v || (rowUp && a_ij.sgn() > 0 && c->isLowerBound())
+           || (rowUp && a_ij.sgn() < 0 && c->isUpperBound())
+           || (!rowUp && a_ij.sgn() > 0 && c->isUpperBound())
+           || (!rowUp && a_ij.sgn() < 0 && c->isLowerBound()));
+
+    if(TraceIsOn("arith::propagateRow")){
+      if(nonbasic == v){
+        Trace("arith::propagateRow") << "(target) "
+                                     << rowUp << " "
+                                     << a_ij.sgn() << " "
+                                     << c->isLowerBound() << " "
+                                     << c->isUpperBound() << endl;
+
+        Trace("arith::propagateRow") << "(target) ";
+      }
+      Trace("arith::propagateRow") << "propagateRow " << a_ij << " * " << nonbasic ;
+    }
+
+    if(nonbasic == v){
+      if(farkas != RationalVectorPSentinel){
+        Assert(farkas->front().isZero());
+        Rational multAij = multiple * a_ij;
+        Trace("arith::propagateRow") << "(" << multAij << ") ";
+        farkas->front() = multAij;
+      }
+
+      Trace("arith::propagateRow") << c << endl;
+    }else{
+
+      ConstraintCP bound = selectUb
+        ? d_variables.getUpperBoundConstraint(nonbasic)
+        : d_variables.getLowerBoundConstraint(nonbasic);
+
+      if(farkas != RationalVectorPSentinel){
+        Rational multAij = multiple * a_ij;
+        Trace("arith::propagateRow") << "(" << multAij << ") ";
+        farkas->push_back(multAij);
+      }
+      Assert(bound != NullConstraint);
+      Trace("arith::propagateRow") << bound << endl;
+      into.push_back(bound);
+    }
+  }
+  Trace("arith::propagateRow") << "LinearEqualityModule::propagateRow("
+                                   << ridx << ", " << rowUp << ", " << v << ") done" << endl;
+
+}
+
+ConstraintP LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic) const {
+
+  int sgn = coeff.sgn();
+  bool ub = aboveUpper?(sgn < 0) : (sgn > 0);
+
+  ConstraintP c = ub ?
+    d_variables.getUpperBoundConstraint(v) :
+    d_variables.getLowerBoundConstraint(v);
+
+  bool weakened;
+  do{
+    const DeltaRational& bound = c->getValue();
+
+    weakened = false;
+
+    ConstraintP weaker = ub?
+      c->getStrictlyWeakerUpperBound(true, true):
+      c->getStrictlyWeakerLowerBound(true, true);
+
+    if(weaker != NullConstraint){
+      const DeltaRational& weakerBound = weaker->getValue();
+
+      DeltaRational diff = aboveUpper ? bound - weakerBound : weakerBound - bound;
+      //if var == basic,
+      //  if aboveUpper, weakerBound > bound, multiply by -1
+      //  if !aboveUpper, weakerBound < bound, multiply by -1
+      diff = diff * coeff;
+      if(surplus > diff){
+        ++d_statistics.d_weakenings;
+        weakened = true;
+        anyWeakening = true;
+        surplus = surplus - diff;
+
+        Trace("arith::weak") << "found:" << endl;
+        if(v == basic){
+          Trace("arith::weak") << "  basic: ";
+        }
+        Trace("arith::weak") << "  " << surplus << " "<< diff  << endl
+                             << "  " << bound << c << endl
+                             << "  " << weakerBound << weaker << endl;
+
+        Assert(diff.sgn() > 0);
+        c = weaker;
+      }
+    }
+  }while(weakened);
+
+  return c;
+}
+
+/* An explanation of the farkas coefficients.
+ *
+ * We are proving a conflict on the basic variable x_b.
+ * If aboveUpper, then the conflict is with the constraint c : x_b <= u_b.
+ * If !aboveUpper, then the conflict is with the constraint c : x_b >= l_b.
+ *
+ * A row has the form:
+ *   -x_b sum a_i * x_i  = 0
+ * or
+ *   -x_b + sum r y + sum q z = 0,
+ *    x_b = sum r y + sum q z
+ * where r > 0 and q < 0.
+ *
+ *
+ * If !aboveUp, we are proving ~c: x_b < l_b
+ *   g = sum r u_y + sum q l_z
+ *   x_b <= g < l_b
+ *   and ~c is entailed by x_b <= g
+ *
+ * If aboveUp, we are proving ~c : x_b > u_b
+ *   g = sum r l_y + sum q u_z
+ *   x_b >= g > u_b
+ *   and ~c is entailed by x_b >= g
+ *
+ *
+ *               | s     | c         | ~c       | u_i     | l_i
+ *   if !aboveUp | s > 0 | x >= -g/s | x < -g/s | a_i > 0 | a_i < 0
+ *   if !aboveUp | s < 0 | x <= -g/s | x > -g/s | a_i > 0 | a_i < 0
+ *   if  aboveUp | s > 0 | x <= -g/s | x > -g/s | a_i < 0 | a_i > 0
+ *   if  aboveUp | s < 0 | x >= -g/s | x < -g/s | a_i < 0 | a_i > 0
+ *
+ * Thus we treat aboveUp as multiplying the row by -1 and !aboveUp as 1
+ * for the entire row.
+ */
+ConstraintCP LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, FarkasConflictBuilder& fcs) const {
+  Assert(!fcs.underConstruction());
+  TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime);
+
+  Trace("arith::weak") << "LinearEqualityModule::minimallyWeakConflict("
+                       << aboveUpper <<", "<< basicVar << ", ...) start" << endl;
+
+  const Rational& adjustSgn = aboveUpper ? d_negOne : d_one;
+  const DeltaRational& assignment = d_variables.getAssignment(basicVar);
+  DeltaRational surplus;
+  if(aboveUpper){
+    Assert(d_variables.hasUpperBound(basicVar));
+    Assert(assignment > d_variables.getUpperBound(basicVar));
+    surplus = assignment - d_variables.getUpperBound(basicVar);
+  }else{
+    Assert(d_variables.hasLowerBound(basicVar));
+    Assert(assignment < d_variables.getLowerBound(basicVar));
+    surplus = d_variables.getLowerBound(basicVar) - assignment;
+  }
+
+  bool anyWeakenings = false;
+  for(Tableau::RowIterator i = d_tableau.basicRowIterator(basicVar); !i.atEnd(); ++i){
+    const Tableau::Entry& entry = *i;
+    ArithVar v = entry.getColVar();
+    const Rational& coeff = entry.getCoefficient();
+    bool weakening = false;
+    ConstraintP c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar);
+    Trace("arith::weak") << "weak : " << weakening << " "
+                         << c->assertedToTheTheory() << " "
+                         << d_variables.getAssignment(v) << " "
+                         << c << endl;
+    anyWeakenings = anyWeakenings || weakening;
+
+    fcs.addConstraint(c, coeff, adjustSgn);
+    if(basicVar == v){
+      Assert(!c->negationHasProof());
+      fcs.makeLastConsequent();
+    }
+  }
+  Assert(fcs.consequentIsSet());
+
+  ConstraintCP conflicted = fcs.commitConflict();
+
+  ++d_statistics.d_weakeningAttempts;
+  if(anyWeakenings){
+    ++d_statistics.d_weakeningSuccesses;
+  }
+  Trace("arith::weak") << "LinearEqualityModule::minimallyWeakConflict("
+                       << aboveUpper <<", "<< basicVar << ", ...) done" << endl;
+  return conflicted;
+}
+
+ArithVar LinearEqualityModule::minVarOrder(ArithVar x, ArithVar y) const {
+  Assert(x != ARITHVAR_SENTINEL);
+  Assert(y != ARITHVAR_SENTINEL);
+  if(x <= y){
+    return x;
+  } else {
+    return y;
+  }
+}
+
+ArithVar LinearEqualityModule::minColLength(ArithVar x, ArithVar y) const {
+  Assert(x != ARITHVAR_SENTINEL);
+  Assert(y != ARITHVAR_SENTINEL);
+  Assert(!d_tableau.isBasic(x));
+  Assert(!d_tableau.isBasic(y));
+  uint32_t xLen = d_tableau.getColLength(x);
+  uint32_t yLen = d_tableau.getColLength(y);
+  if( xLen > yLen){
+     return y;
+  } else if( xLen== yLen ){
+    return minVarOrder(x,y);
+  }else{
+    return x;
+  }
+}
+
+ArithVar LinearEqualityModule::minRowLength(ArithVar x, ArithVar y) const {
+  Assert(x != ARITHVAR_SENTINEL);
+  Assert(y != ARITHVAR_SENTINEL);
+  Assert(d_tableau.isBasic(x));
+  Assert(d_tableau.isBasic(y));
+  uint32_t xLen = d_tableau.basicRowLength(x);
+  uint32_t yLen = d_tableau.basicRowLength(y);
+  if( xLen > yLen){
+     return y;
+  } else if( xLen== yLen ){
+    return minVarOrder(x,y);
+  }else{
+    return x;
+  }
+}
+
+ArithVar LinearEqualityModule::minBoundAndColLength(ArithVar x, ArithVar y) const{
+  Assert(x != ARITHVAR_SENTINEL);
+  Assert(y != ARITHVAR_SENTINEL);
+  Assert(!d_tableau.isBasic(x));
+  Assert(!d_tableau.isBasic(y));
+  if(d_variables.hasEitherBound(x) && !d_variables.hasEitherBound(y)){
+    return y;
+  }else if(!d_variables.hasEitherBound(x) && d_variables.hasEitherBound(y)){
+    return x;
+  }else {
+    return minColLength(x, y);
+  }
+}
+
+template <bool above>
+ArithVar LinearEqualityModule::selectSlack(ArithVar x_i, VarPreferenceFunction pref) const{
+  ArithVar slack = ARITHVAR_SENTINEL;
+
+  for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd();  ++iter){
+    const Tableau::Entry& entry = *iter;
+    ArithVar nonbasic = entry.getColVar();
+    if(nonbasic == x_i) continue;
+
+    const Rational& a_ij = entry.getCoefficient();
+    int sgn = a_ij.sgn();
+    if(isAcceptableSlack<above>(sgn, nonbasic)){
+      //If one of the above conditions is met, we have found an acceptable
+      //nonbasic variable to pivot x_i with.  We can now choose which one we
+      //prefer the most.
+      slack = (slack == ARITHVAR_SENTINEL) ? nonbasic : (this->*pref)(slack, nonbasic);
+    }
+  }
+
+  return slack;
+}
+
+const Tableau::Entry* LinearEqualityModule::selectSlackEntry(ArithVar x_i, bool above) const{
+  for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd();  ++iter){
+    const Tableau::Entry& entry = *iter;
+    ArithVar nonbasic = entry.getColVar();
+    if(nonbasic == x_i) continue;
+
+    const Rational& a_ij = entry.getCoefficient();
+    int sgn = a_ij.sgn();
+    if(above && isAcceptableSlack<true>(sgn, nonbasic)){
+      //If one of the above conditions is met, we have found an acceptable
+      //nonbasic variable to pivot x_i with.  We can now choose which one we
+      //prefer the most.
+      return &entry;
+    }else if(!above && isAcceptableSlack<false>(sgn, nonbasic)){
+      return &entry;
+    }
+  }
+
+  return NULL;
+}
+
+void LinearEqualityModule::startTrackingBoundCounts(){
+  Assert(!d_areTracking);
+  d_areTracking = true;
+  if(TraceIsOn("arith::tracking")){
+    debugCheckTracking();
+  }
+  Assert(d_areTracking);
+}
+
+void LinearEqualityModule::stopTrackingBoundCounts(){
+  Assert(d_areTracking);
+  d_areTracking = false;
+  if(TraceIsOn("arith::tracking")){
+    debugCheckTracking();
+  }
+  Assert(!d_areTracking);
+}
+
+
+void LinearEqualityModule::trackRowIndex(RowIndex ridx){
+  Assert(!rowIndexIsTracked(ridx));
+  BoundsInfo bi = computeRowBoundInfo(ridx, true);
+  d_btracking.set(ridx, bi);
+}
+
+BoundsInfo LinearEqualityModule::computeRowBoundInfo(RowIndex ridx, bool inQueue) const{
+  BoundsInfo bi;
+
+  Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
+  for(; !iter.atEnd();  ++iter){
+    const Tableau::Entry& entry = *iter;
+    ArithVar v = entry.getColVar();
+    const Rational& a_ij = entry.getCoefficient();
+    bi += (d_variables.selectBoundsInfo(v, inQueue)).multiplyBySgn(a_ij.sgn());
+  }
+  return bi;
+}
+
+BoundCounts LinearEqualityModule::debugBasicAtBoundCount(ArithVar x_i) const {
+  return d_btracking[d_tableau.basicToRowIndex(x_i)].atBounds();
+}
+
+/**
+ * If the pivot described in u were performed,
+ * then the row would qualify as being either at the minimum/maximum
+ * to the non-basics being at their bounds.
+ * The minimum/maximum is determined by the direction the non-basic is changing.
+ */
+bool LinearEqualityModule::basicsAtBounds(const UpdateInfo& u) const {
+  Assert(u.describesPivot());
+
+  ArithVar nonbasic = u.nonbasic();
+  ArithVar basic = u.leaving();
+  Assert(basicIsTracked(basic));
+  int coeffSgn = u.getCoefficient().sgn();
+  int nbdir = u.nonbasicDirection();
+
+  ConstraintP c = u.limiting();
+  int toUB = (c->getType() == UpperBound ||
+              c->getType() == Equality) ? 1 : 0;
+  int toLB = (c->getType() == LowerBound ||
+              c->getType() == Equality) ? 1 : 0;
+
+  RowIndex ridx = d_tableau.basicToRowIndex(basic);
+
+  BoundCounts bcs = d_btracking[ridx].atBounds();
+  // x = c*n + \sum d*m
+  // 0 = -x + c*n + \sum d*m
+  // n = 1/c * x + -1/c * (\sum d*m)
+  BoundCounts nonb = bcs - d_variables.atBoundCounts(nonbasic).multiplyBySgn(coeffSgn);
+  nonb.addInChange(-1, d_variables.atBoundCounts(basic), BoundCounts(toLB, toUB));
+  nonb = nonb.multiplyBySgn(-coeffSgn);
+
+  uint32_t length = d_tableau.basicRowLength(basic);
+  Trace("basicsAtBounds")
+    << "bcs " << bcs
+    << "nonb " << nonb
+    << "length " << length << endl;
+  // nonb has nb excluded.
+  if(nbdir < 0){
+    return nonb.lowerBoundCount() + 1 == length;
+  }else{
+    Assert(nbdir > 0);
+    return nonb.upperBoundCount() + 1 == length;
+  }
+}
+
+bool LinearEqualityModule::nonbasicsAtLowerBounds(ArithVar basic) const {
+  Assert(basicIsTracked(basic));
+  RowIndex ridx = d_tableau.basicToRowIndex(basic);
+
+  BoundCounts bcs = d_btracking[ridx].atBounds();
+  uint32_t length = d_tableau.basicRowLength(basic);
+
+  // return true if excluding the basic is every element is at its "lowerbound"
+  // The psuedo code is:
+  //   bcs -= basic.count(basic, basic's sgn)
+  //   return bcs.lowerBoundCount() + 1 == length
+  // As basic's sign is always -1, we can pull out the pieces of the count:
+  //   bcs.lowerBoundCount() - basic.atUpperBoundInd() + 1 == length
+  // basic.atUpperBoundInd() is either 0 or 1
+  uint32_t lbc = bcs.lowerBoundCount();
+  return  (lbc == length) ||
+    (lbc + 1 == length && d_variables.cmpAssignmentUpperBound(basic) != 0);
+}
+
+bool LinearEqualityModule::nonbasicsAtUpperBounds(ArithVar basic) const {
+  Assert(basicIsTracked(basic));
+  RowIndex ridx = d_tableau.basicToRowIndex(basic);
+  BoundCounts bcs = d_btracking[ridx].atBounds();
+  uint32_t length = d_tableau.basicRowLength(basic);
+  uint32_t ubc = bcs.upperBoundCount();
+  // See the comment for nonbasicsAtLowerBounds()
+
+  return (ubc == length) ||
+    (ubc + 1 == length && d_variables.cmpAssignmentLowerBound(basic) != 0);
+}
+
+void LinearEqualityModule::trackingMultiplyRow(RowIndex ridx, int sgn) {
+  Assert(rowIndexIsTracked(ridx));
+  Assert(sgn != 0);
+  if(sgn < 0){
+    BoundsInfo& bi = d_btracking.get(ridx);
+    bi = bi.multiplyBySgn(sgn);
+  }
+}
+
+void LinearEqualityModule::trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){
+  Assert(oldSgn != currSgn);
+  BoundsInfo nb_inf = d_variables.boundsInfo(nb);
+
+  Assert(rowIndexIsTracked(ridx));
+
+  BoundsInfo& row_bi = d_btracking.get(ridx);
+  row_bi.addInSgn(nb_inf, oldSgn, currSgn);
+}
+
+ArithVar LinearEqualityModule::minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const{
+  if(vec.empty()) {
+    return ARITHVAR_SENTINEL;
+  }else {
+    ArithVar sel = vec.front();
+    ArithVarVec::const_iterator i = vec.begin() + 1;
+    ArithVarVec::const_iterator i_end = vec.end();
+    for(; i != i_end; ++i){
+      sel = (this->*pf)(sel, *i);
+    }
+    return sel;
+  }
+}
+
+bool LinearEqualityModule::accumulateBorder(const Tableau::Entry& entry, bool ub){
+  ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex());
+
+  Assert(basicIsTracked(currBasic));
+
+  ConstraintP bound = ub ?
+    d_variables.getUpperBoundConstraint(currBasic):
+    d_variables.getLowerBoundConstraint(currBasic);
+
+  if(bound == NullConstraint){ return false; }
+  Assert(bound != NullConstraint);
+
+  const Rational& coeff = entry.getCoefficient();
+
+  const DeltaRational& assignment = d_variables.getAssignment(currBasic);
+  DeltaRational toBound = bound->getValue() - assignment;
+  DeltaRational nbDiff = toBound/coeff;
+
+  // if ub
+  // if toUB >= 0
+  // then ub >= currBasic
+  //   if sgn > 0,
+  //   then diff >= 0, so nb must increase for G
+  //   else diff <= 0, so nb must decrease for G
+  // else ub < currBasic
+  //   if sgn > 0,
+  //   then diff < 0, so nb must decrease for G
+  //   else diff > 0, so nb must increase for G
+
+  int diffSgn = nbDiff.sgn();
+
+  if(diffSgn != 0 && willBeInConflictAfterPivot(entry, nbDiff, ub)){
+    return true;
+  }else{
+    bool areFixing = ub ? (toBound.sgn() < 0 ) : (toBound.sgn() > 0);
+    Border border(bound, nbDiff, areFixing, &entry, ub);
+    bool increasing =
+      (diffSgn > 0) ||
+      (diffSgn == 0 && ((coeff.sgn() > 0) == ub));
+
+    // assume diffSgn == 0
+    // if coeff > 0,
+    //   if ub, inc
+    //   else, dec
+    // else coeff < 0
+    //   if ub, dec
+    //   else, inc
+
+    if(increasing){
+      Trace("handleBorders") << "push back increasing " << border << endl;
+      d_increasing.push_back(border);
+    }else{
+      Trace("handleBorders") << "push back decreasing " << border << endl;
+      d_decreasing.push_back(border);
+    }
+    return false;
+  }
+}
+
+bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entry, const DeltaRational& nbDiff, bool bToUB) const{
+  int nbSgn = nbDiff.sgn();
+  Assert(nbSgn != 0);
+
+  if(nbSgn > 0){
+    if (!d_upperBoundDifference || nbDiff <= *d_upperBoundDifference)
+    {
+      return false;
+    }
+  }else{
+    if (!d_lowerBoundDifference || nbDiff >= *d_lowerBoundDifference)
+    {
+      return false;
+    }
+  }
+
+  // Assume past this point, nb will be in error if this pivot is done
+  ArithVar nb = entry.getColVar();
+  RowIndex ridx = entry.getRowIndex();
+  ArithVar basic = d_tableau.rowIndexToBasic(ridx);
+  Assert(rowIndexIsTracked(ridx));
+  int coeffSgn = entry.getCoefficient().sgn();
+
+
+  // if bToUB, then basic is going to be set to its upperbound
+  // if not bToUB, then basic is going to be set to its lowerbound
+
+  // Different steps of solving for this:
+  // 1) y = a * x + \sum b * z
+  // 2) -a * x = -y + \sum b * z
+  // 3) x = (-1/a) * ( -y + \sum b * z)
+
+  BoundCounts bc = d_btracking[ridx].atBounds();
+
+  // 1) y = a * x + \sum b * z
+  // Get bc(\sum b * z)
+  BoundCounts sumOnly = bc - d_variables.atBoundCounts(nb).multiplyBySgn(coeffSgn);
+
+  // y's bounds in the proposed model
+  int yWillBeAtUb = (bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0;
+  int yWillBeAtLb = (!bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0;
+  BoundCounts ysBounds(yWillBeAtLb, yWillBeAtUb);
+
+  // 2) -a * x = -y + \sum b * z
+  // Get bc(-y + \sum b * z)
+  sumOnly.addInChange(-1, d_variables.atBoundCounts(basic), ysBounds);
+
+  // 3) x = (-1/a) * ( -y + \sum b * z)
+  // Get bc((-1/a) * ( -y + \sum b * z))
+  BoundCounts xsBoundsAfterPivot = sumOnly.multiplyBySgn(-coeffSgn);
+
+  uint32_t length = d_tableau.basicRowLength(basic);
+  if(nbSgn > 0){
+    // Only check for the upper bound being violated
+    return xsBoundsAfterPivot.lowerBoundCount() + 1 == length;
+  }else{
+    // Only check for the lower bound being violated
+    return xsBoundsAfterPivot.upperBoundCount() + 1 == length;
+  }
+}
+
+UpdateInfo LinearEqualityModule::mkConflictUpdate(const Tableau::Entry& entry, bool ub) const{
+  ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex());
+  ArithVar nb = entry.getColVar();
+
+  ConstraintP bound = ub ?
+    d_variables.getUpperBoundConstraint(currBasic):
+    d_variables.getLowerBoundConstraint(currBasic);
+
+
+  const Rational& coeff = entry.getCoefficient();
+  const DeltaRational& assignment = d_variables.getAssignment(currBasic);
+  DeltaRational toBound = bound->getValue() - assignment;
+  DeltaRational nbDiff = toBound/coeff;
+
+  return UpdateInfo::conflict(nb, nbDiff, coeff, bound);
+}
+
+UpdateInfo LinearEqualityModule::speculativeUpdate(ArithVar nb, const Rational& focusCoeff, UpdatePreferenceFunction pref){
+  Assert(d_increasing.empty());
+  Assert(d_decreasing.empty());
+  Assert(!d_lowerBoundDifference);
+  Assert(!d_upperBoundDifference);
+
+  int focusCoeffSgn = focusCoeff.sgn();
+
+  Trace("speculativeUpdate") << "speculativeUpdate" << endl;
+  Trace("speculativeUpdate") << "nb " << nb << endl;
+  Trace("speculativeUpdate") << "focusCoeff " << focusCoeff << endl;
+
+  if(d_variables.hasUpperBound(nb)){
+    ConstraintP ub = d_variables.getUpperBoundConstraint(nb);
+    d_upperBoundDifference = ub->getValue() - d_variables.getAssignment(nb);
+    Border border(ub, *d_upperBoundDifference, false, NULL, true);
+    Trace("handleBorders") << "push back increasing " << border << endl;
+    d_increasing.push_back(border);
+  }
+  if(d_variables.hasLowerBound(nb)){
+    ConstraintP lb = d_variables.getLowerBoundConstraint(nb);
+    d_lowerBoundDifference = lb->getValue() - d_variables.getAssignment(nb);
+    Border border(lb, *d_lowerBoundDifference, false, NULL, false);
+    Trace("handleBorders") << "push back decreasing " << border << endl;
+    d_decreasing.push_back(border);
+  }
+
+  Tableau::ColIterator colIter = d_tableau.colIterator(nb);
+  for(; !colIter.atEnd(); ++colIter){
+    const Tableau::Entry& entry = *colIter;
+    Assert(entry.getColVar() == nb);
+
+    if(accumulateBorder(entry, true)){
+      clearSpeculative();
+      return mkConflictUpdate(entry, true);
+    }
+    if(accumulateBorder(entry, false)){
+      clearSpeculative();
+      return mkConflictUpdate(entry, false);
+    }
+  }
+
+  UpdateInfo selected;
+  BorderHeap& withSgn = focusCoeffSgn > 0 ? d_increasing : d_decreasing;
+  BorderHeap& againstSgn = focusCoeffSgn > 0 ? d_decreasing : d_increasing;
+
+  handleBorders(selected, nb, focusCoeff, withSgn, 0, pref);
+  int m = 1 - selected.errorsChangeSafe(0);
+  handleBorders(selected, nb, focusCoeff, againstSgn, m, pref);
+
+  clearSpeculative();
+  return selected;
+}
+
+void LinearEqualityModule::clearSpeculative(){
+  // clear everything away
+  d_increasing.clear();
+  d_decreasing.clear();
+  d_lowerBoundDifference.reset();
+  d_upperBoundDifference.reset();
+}
+
+void LinearEqualityModule::handleBorders(UpdateInfo& selected, ArithVar nb, const Rational& focusCoeff, BorderHeap& heap, int minimumFixes, UpdatePreferenceFunction pref){
+  Assert(minimumFixes >= 0);
+
+  // The values popped off of the heap
+  // should be popped with the values closest to 0
+  // being first and larger in absolute value last
+
+
+  int fixesRemaining = heap.possibleFixes();
+
+  Trace("handleBorders")
+    << "handleBorders "
+    << "nb " << nb
+    << "fc " << focusCoeff
+    << "h.e " << heap.empty()
+    << "h.dir " << heap.direction()
+    << "h.rem " << fixesRemaining
+    << "h.0s " << heap.numZeroes()
+    << "min " << minimumFixes
+    << endl;
+
+  if(heap.empty()){
+    // if the heap is empty, return
+    return;
+  }
+
+  bool zeroesWillDominate = fixesRemaining - heap.numZeroes() < minimumFixes;
+
+  // can the number of fixes ever exceed the minimum?
+  // no more than the number of possible fixes can be fixed in total
+  // nothing can be fixed before the zeroes are taken care of
+  if(minimumFixes > 0 && zeroesWillDominate){
+    return;
+  }
+
+
+  int negErrorChange = 0;
+  int nbDir = heap.direction();
+
+  // points at the beginning of the heap
+  if(zeroesWillDominate){
+    heap.dropNonZeroes();
+  }
+  heap.make_heap();
+
+
+  // pretend like the previous block had a value of zero.
+  // The block that actually has a value of 0 must handle this.
+  const DeltaRational zero(0);
+  const DeltaRational* prevBlockValue = &zero;
+
+  /** The coefficient changes as the value crosses border. */
+  Rational effectiveCoefficient = focusCoeff;
+
+  /* Keeps track of the change to the value of the focus function.*/
+  DeltaRational totalFocusChange(0);
+
+  const int focusCoeffSgn = focusCoeff.sgn();
+
+  while(heap.more()  &&
+        (fixesRemaining + negErrorChange > minimumFixes ||
+         (fixesRemaining + negErrorChange == minimumFixes &&
+          effectiveCoefficient.sgn() == focusCoeffSgn))){
+    // There are more elements &&
+    // we can either fix at least 1 more variable in the error function
+    // or we can improve the error function
+
+
+    int brokenInBlock = 0;
+    BorderVec::const_iterator endBlock = heap.end();
+
+    pop_block(heap, brokenInBlock, fixesRemaining, negErrorChange);
+
+    // if endVec == beginVec, block starts there
+    // other wise, block starts at endVec
+    BorderVec::const_iterator startBlock
+      = heap.more() ? heap.end() : heap.begin();
+
+    const DeltaRational& blockValue = (*startBlock).d_diff;
+
+    // if decreasing
+    // blockValue < prevBlockValue
+    // diff.sgn() = -1
+    DeltaRational diff = blockValue - (*prevBlockValue);
+    DeltaRational blockChangeToFocus =  diff * effectiveCoefficient;
+    totalFocusChange += blockChangeToFocus;
+
+    Trace("handleBorders")
+      << "blockValue " << (blockValue)
+      << "diff " << diff
+      << "blockChangeToFocus " << totalFocusChange
+      << "blockChangeToFocus " << totalFocusChange
+      << "negErrorChange " << negErrorChange
+      << "brokenInBlock " << brokenInBlock
+      << "fixesRemaining " << fixesRemaining
+      << endl;
+
+    int currFocusChangeSgn = totalFocusChange.sgn();
+    for(BorderVec::const_iterator i = startBlock; i != endBlock; ++i){
+      const Border& b = *i;
+
+      Trace("handleBorders") << b << endl;
+
+      bool makesImprovement = negErrorChange > 0 ||
+        (negErrorChange == 0  && currFocusChangeSgn > 0);
+
+      if(!makesImprovement){
+        if(b.ownBorder() || minimumFixes > 0){
+          continue;
+        }
+      }
+
+      UpdateInfo proposal(nb, nbDir);
+      if(b.ownBorder()){
+        proposal.witnessedUpdate(b.d_diff, b.d_bound, -negErrorChange, currFocusChangeSgn);
+      }else{
+        proposal.update(b.d_diff, b.getCoefficient(), b.d_bound, -negErrorChange, currFocusChangeSgn);
+      }
+
+      if(selected.unbounded() || (this->*pref)(selected, proposal)){
+        selected = proposal;
+      }
+    }
+
+    effectiveCoefficient += updateCoefficient(startBlock, endBlock);
+    prevBlockValue = &blockValue;
+    negErrorChange -= brokenInBlock;
+  }
+}
+
+Rational LinearEqualityModule::updateCoefficient(BorderVec::const_iterator startBlock, BorderVec::const_iterator endBlock){
+  //update coefficient
+  Rational changeToCoefficient(0);
+  for(BorderVec::const_iterator i = startBlock; i != endBlock; ++i){
+    const Border& curr = *i;
+    if(curr.ownBorder()){// breaking its own bound
+      if(curr.d_upperbound){
+        changeToCoefficient -= 1;
+      }else{
+        changeToCoefficient += 1;
+      }
+    }else{
+      const Rational& coeff = curr.d_entry->getCoefficient();
+      if(curr.d_areFixing){
+        if(curr.d_upperbound){// fixing an upper bound
+          changeToCoefficient += coeff;
+        }else{// fixing a lower bound
+          changeToCoefficient -= coeff;
+        }
+      }else{
+        if(curr.d_upperbound){// breaking an upper bound
+          changeToCoefficient -= coeff;
+        }else{
+          // breaking a lower bound
+          changeToCoefficient += coeff;
+        }
+      }
+    }
+  }
+  return changeToCoefficient;
+}
+
+void LinearEqualityModule::pop_block(BorderHeap& heap, int& brokenInBlock, int& fixesRemaining, int& negErrorChange){
+  Assert(heap.more());
+
+  if(heap.top().d_areFixing){
+    fixesRemaining--;
+    negErrorChange++;
+  }else{
+    brokenInBlock++;
+  }
+  heap.pop_heap();
+  const DeltaRational& blockValue = (*heap.end()).d_diff;
+
+  while(heap.more()){
+    const Border& top = heap.top();
+    if(blockValue == top.d_diff){
+      // belongs to the block
+      if(top.d_areFixing){
+        fixesRemaining--;
+        negErrorChange++;
+      }else{
+        brokenInBlock++;
+      }
+      heap.pop_heap();
+    }else{
+      // does not belong to the block
+      Assert((heap.direction() > 0) ? (blockValue < top.d_diff)
+                                    : (blockValue > top.d_diff));
+      break;
+    }
+  }
+}
+
+void LinearEqualityModule::substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult){
+  d_tableau.substitutePlusTimesConstant(to, from, mult, d_trackCallback);
+}
+void LinearEqualityModule::directlyAddToCoefficient(ArithVar row, ArithVar col, const Rational& mult){
+  d_tableau.directlyAddToCoefficient(row, col, mult, d_trackCallback);
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/linear_equality.h b/src/theory/arith/linear/linear_equality.h
new file mode 100644 (file)
index 0000000..30b137d
--- /dev/null
@@ -0,0 +1,762 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This module maintains the relationship between a Tableau and
+ * PartialModel.
+ *
+ * This shares with the theory a Tableau, and a PartialModel that:
+ *  - satisfies the equalities in the Tableau, and
+ *  - the assignment for the non-basic variables satisfies their bounds.
+ * This maintains the relationship needed by the SimplexDecisionProcedure.
+ *
+ * In the language of Simplex for DPLL(T), this provides:
+ * - update()
+ * - pivotAndUpdate()
+ *
+ * This class also provides utility functions that require
+ * using both the Tableau and PartialModel.
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "options/arith_options.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/arith/linear/simplex_update.h"
+#include "theory/arith/linear/tableau.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+struct Border{
+  // The constraint for the border
+  ConstraintP d_bound;
+
+  // The change to the nonbasic to reach the border
+  DeltaRational d_diff;
+
+  // Is reach this value fixing the constraint
+  // or is going past this value hurting the constraint
+  bool d_areFixing;
+
+  // Entry into the tableau
+  const Tableau::Entry* d_entry;
+
+  // Was this an upper bound or a lower bound?
+  bool d_upperbound;
+
+  Border():
+    d_bound(NullConstraint) // ignore the other values
+  {}
+
+  Border(ConstraintP l, const DeltaRational& diff, bool areFixing, const Tableau::Entry* en, bool ub):
+    d_bound(l), d_diff(diff), d_areFixing(areFixing), d_entry(en),  d_upperbound(ub)
+  {}
+
+  Border(ConstraintP l, const DeltaRational& diff, bool areFixing, bool ub):
+    d_bound(l), d_diff(diff), d_areFixing(areFixing), d_entry(NULL),  d_upperbound(ub)
+  {}
+  bool operator<(const Border& other) const{
+    return d_diff < other.d_diff;
+  }
+
+  /** d_lim is the nonbasic variable's own bound. */
+  bool ownBorder() const { return d_entry == NULL; }
+
+  bool isZero() const { return d_diff.sgn() == 0; }
+  static bool nonZero(const Border& b) { return !b.isZero(); }
+
+  const Rational& getCoefficient() const {
+    Assert(!ownBorder());
+    return d_entry->getCoefficient();
+  }
+  void output(std::ostream& out) const;
+};
+
+inline std::ostream& operator<<(std::ostream& out, const Border& b){
+  b.output(out);
+  return out;
+}
+
+typedef std::vector<Border> BorderVec;
+
+class BorderHeap {
+  const int d_dir;
+
+  class BorderHeapCmp {
+  private:
+    int d_nbDirection;
+  public:
+    BorderHeapCmp(int dir): d_nbDirection(dir){}
+    bool operator()(const Border& a, const Border& b) const{
+      if(d_nbDirection > 0){
+        // if nb is increasing,
+        //  this needs to act like a max
+        //  in order to have a min heap
+        return b < a;
+      }else{
+        // if nb is decreasing,
+        //  this needs to act like a min
+        //  in order to have a max heap
+        return a < b;
+      }
+    }
+  };
+  const BorderHeapCmp d_cmp;
+
+  BorderVec d_vec;
+
+  BorderVec::iterator d_begin;
+
+  /**
+   * Once this is initialized the top of the heap will always
+   * be at d_end - 1
+   */
+  BorderVec::iterator d_end;
+
+  int d_possibleFixes;
+  int d_numZeroes;
+
+public:
+  BorderHeap(int dir)
+  : d_dir(dir), d_cmp(dir), d_possibleFixes(0), d_numZeroes(0)
+  {}
+
+  void push_back(const Border& b){
+    d_vec.push_back(b);
+    if(b.d_areFixing){
+      d_possibleFixes++;
+    }
+    if(b.d_diff.sgn() == 0){
+      d_numZeroes++;
+    }
+  }
+
+  int numZeroes() const { return d_numZeroes; }
+  int possibleFixes() const { return d_possibleFixes; }
+  int direction() const { return d_dir; }
+
+  void make_heap(){
+    d_begin = d_vec.begin();
+    d_end = d_vec.end();
+    std::make_heap(d_begin, d_end, d_cmp);
+  }
+
+  void dropNonZeroes(){
+    d_vec.erase(std::remove_if(d_vec.begin(), d_vec.end(), &Border::nonZero),
+                d_vec.end());
+  }
+
+  const Border& top() const {
+    Assert(more());
+    return *d_begin;
+  }
+  void pop_heap(){
+    Assert(more());
+
+    std::pop_heap(d_begin, d_end, d_cmp);
+    --d_end;
+  }
+
+  BorderVec::const_iterator end() const{
+    return BorderVec::const_iterator(d_end);
+  }
+  BorderVec::const_iterator begin() const{
+    return BorderVec::const_iterator(d_begin);
+  }
+
+  inline bool more() const{ return d_begin != d_end; }
+
+  inline bool empty() const{ return d_vec.empty(); }
+
+  void clear(){
+    d_possibleFixes = 0;
+    d_numZeroes = 0;
+    d_vec.clear();
+  }
+};
+
+
+class LinearEqualityModule {
+public:
+  typedef ArithVar (LinearEqualityModule::*VarPreferenceFunction)(ArithVar, ArithVar) const;
+
+
+  typedef bool (LinearEqualityModule::*UpdatePreferenceFunction)(const UpdateInfo&, const UpdateInfo&) const;
+
+  
+private:
+  /**
+   * Manages information about the assignment and upper and lower bounds on the
+   * variables.
+   */
+  ArithVariables& d_variables;
+
+  /** Reference to the Tableau to operate upon. */
+  Tableau& d_tableau;
+
+  /** Called whenever the value of a basic variable is updated. */
+  BasicVarModelUpdateCallBack d_basicVariableUpdates;
+
+  BorderHeap d_increasing;
+  BorderHeap d_decreasing;
+  std::optional<DeltaRational> d_upperBoundDifference;
+  std::optional<DeltaRational> d_lowerBoundDifference;
+
+  Rational d_one;
+  Rational d_negOne;
+public:
+
+  /**
+   * Initializes a LinearEqualityModule with a partial model, a tableau,
+   * and a callback function for when basic variables update their values.
+   */
+  LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundInfoMap& boundTracking, BasicVarModelUpdateCallBack f);
+
+  /**
+   * Updates the assignment of a nonbasic variable x_i to v.
+   * Also updates the assignment of basic variables accordingly.
+   */
+  void update(ArithVar x_i, const DeltaRational& v){
+    if(d_areTracking){
+      updateTracked(x_i,v);
+    }else{
+      updateUntracked(x_i,v);
+    }
+  }
+
+  /** Specialization of update if the module is not tracking yet (for Assert*). */
+  void updateUntracked(ArithVar x_i, const DeltaRational& v);
+
+  /** Specialization of update if the module is not tracking yet (for Simplex). */
+  void updateTracked(ArithVar x_i, const DeltaRational& v);
+
+
+  /**
+   * Updates the value of a basic variable x_i to v,
+   * and then pivots x_i with the nonbasic variable in its row x_j.
+   * Updates the assignment of the other basic variables accordingly.
+   */
+  void pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& v);
+
+  ArithVariables& getVariables() const{ return d_variables; }
+  Tableau& getTableau() const{ return d_tableau; }
+
+  /**
+   * Updates every non-basic to reflect the assignment in many.
+   * For use with ApproximateSimplex.
+   */
+  void updateMany(const DenseMap<DeltaRational>& many);
+  void forceNewBasis(const DenseSet& newBasis);
+  void applySolution(const DenseSet& newBasis, const DenseMap<DeltaRational>& newValues);
+
+
+  /**
+   * Returns a pointer to the first Tableau entry on the row ridx that does not
+   * have an either a lower bound/upper bound for proving a bound on skip.
+   * The variable skip is always excluded. Returns NULL if there is no such element.
+   *
+   * If skip == ARITHVAR_SENTINEL, this is equivalent to considering the whole row.
+   */
+  const Tableau::Entry* rowLacksBound(RowIndex ridx, bool upperBound, ArithVar skip);
+
+
+  void startTrackingBoundCounts();
+  void stopTrackingBoundCounts();
+
+
+  void includeBoundUpdate(ArithVar nb, const BoundsInfo& prev);
+
+
+  uint32_t updateProduct(const UpdateInfo& inf) const;
+
+  inline bool minNonBasicVarOrder(const UpdateInfo& a, const UpdateInfo& b) const{
+    return a.nonbasic() >= b.nonbasic();
+  }
+
+  /**
+   * Prefer the update that touch the fewest entries in the matrix.
+   *
+   * The intuition is that this operation will be cheaper.
+   * This strongly biases the system towards updates instead of pivots.
+   */
+  inline bool minProduct(const UpdateInfo& a, const UpdateInfo& b) const{
+    uint32_t aprod = updateProduct(a);
+    uint32_t bprod = updateProduct(b);
+
+    if(aprod == bprod){
+      return minNonBasicVarOrder(a,b);
+    }else{
+      return aprod > bprod;
+    }
+  }
+  inline bool constrainedMin(const UpdateInfo& a, const UpdateInfo& b) const{
+    if(a.describesPivot() && b.describesPivot()){
+      bool aAtBounds = basicsAtBounds(a);
+      bool bAtBounds = basicsAtBounds(b);
+      if(aAtBounds != bAtBounds){
+        return bAtBounds;
+      }
+    }
+    return minProduct(a,b);
+  }
+
+  /**
+   * If both a and b are pivots, prefer the pivot with the leaving variables that has equal bounds.
+   * The intuition is that such variables will be less likely to lead to future problems.
+   */
+  inline bool preferFrozen(const UpdateInfo& a, const UpdateInfo& b) const {
+    if(a.describesPivot() && b.describesPivot()){
+      bool aFrozen = d_variables.boundsAreEqual(a.leaving());
+      bool bFrozen = d_variables.boundsAreEqual(b.leaving());
+
+      if(aFrozen != bFrozen){
+        return bFrozen;
+      }
+    }
+    return constrainedMin(a,b);
+  }
+
+  /**
+   * Prefer pivots with entering variables that do not have bounds.
+   * The intuition is that such variables will be less likely to lead to future problems.
+   */
+  bool preferNeitherBound(const UpdateInfo& a, const UpdateInfo& b) const {
+    if(d_variables.hasEitherBound(a.nonbasic()) == d_variables.hasEitherBound(b.nonbasic())){
+      return preferFrozen(a,b);
+    }else{
+      return d_variables.hasEitherBound(a.nonbasic());
+    }
+  }
+
+  bool modifiedBlands(const UpdateInfo& a, const UpdateInfo& b) const {
+    Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
+    Assert(a.describesPivot());
+    Assert(b.describesPivot());
+    if(a.nonbasic() == b.nonbasic()){
+      bool aIsZero = a.nonbasicDelta().sgn() == 0;
+      bool bIsZero = b.nonbasicDelta().sgn() == 0;
+
+      if((aIsZero || bIsZero) && (!aIsZero || !bIsZero)){
+        return bIsZero;
+      }else{
+        return a.leaving() >= b.leaving();
+      }
+    }else{
+      return a.nonbasic() > b.nonbasic();
+    }
+  }
+
+  template <bool heuristic>
+  bool preferWitness(const UpdateInfo& a, const UpdateInfo& b) const{
+    WitnessImprovement aImp = a.getWitness(!heuristic);
+    WitnessImprovement bImp = b.getWitness(!heuristic);
+
+    if(aImp == bImp){
+      switch(aImp){
+      case ConflictFound:
+        return preferNeitherBound(a,b);
+      case ErrorDropped:
+        if(a.errorsChange() == b.errorsChange()){
+          return preferNeitherBound(a,b);
+        }else{
+          return a.errorsChange() > b.errorsChange();
+        }
+      case FocusImproved:
+        return preferNeitherBound(a,b);
+      case BlandsDegenerate:
+        Assert(a.describesPivot());
+        Assert(b.describesPivot());
+        Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
+        return modifiedBlands(a,b);
+      case HeuristicDegenerate:
+        Assert(a.describesPivot());
+        Assert(b.describesPivot());
+        Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
+        return preferNeitherBound(a,b);
+      case AntiProductive:
+        return minNonBasicVarOrder(a, b);
+      // Not valid responses
+      case Degenerate:
+      case FocusShrank:
+        Unreachable();
+      }
+      Unreachable();
+    }else{
+      return aImp > bImp;
+    }
+  }
+
+private:
+
+  /**
+   * This maps each row index to its relevant bounds info.
+   * This tracks the count for how many variables on a row have bounds
+   * and how many are assigned at their bounds.
+   */
+  BoundInfoMap& d_btracking;
+  bool d_areTracking;
+
+public:
+  /**
+   * The constraint on a basic variable b is implied by the constraints
+   * on its row.  This is a wrapper for propagateRow().
+   */
+ void propagateBasicFromRow(ConstraintP c, bool produceProofs);
+
+ /**
+  * Let v be the variable for the constraint c.
+  * Exports either the explanation of an upperbound or a lower bound
+  * of v using the other variables in the row.
+  *
+  * If farkas != RationalVectorPSentinel, this function additionally
+  * stores the farkas coefficients of the constraints stored in into.
+  * Position 0 is the coefficient of v.
+  * Position i > 0, corresponds to the order of the other constraints.
+  */
+ void propagateRow(ConstraintCPVec& into,
+                   RowIndex ridx,
+                   bool rowUp,
+                   ConstraintP c,
+                   RationalVectorP farkas);
+
+ /**
+  * Computes the value of a basic variable using the assignments
+  * of the values of the variables in the basic variable's row tableau.
+  * This can compute the value using either:
+  * - the the current assignment (useSafe=false) or
+  * - the safe assignment (useSafe = true).
+  */
+ DeltaRational computeRowValue(ArithVar x, bool useSafe) const;
+
+ /**
+  * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
+  * and 2 ArithVar variables and returns one of the ArithVar variables
+  * potentially using the internals of the SimplexDecisionProcedure.
+  */
+
+ ArithVar noPreference(ArithVar x, ArithVar y) const { return x; }
+
+ /**
+  * minVarOrder is a PreferenceFunction for selecting the smaller of the 2
+  * ArithVars. This PreferenceFunction is used during the VarOrder stage of
+  * findModel.
+  */
+ ArithVar minVarOrder(ArithVar x, ArithVar y) const;
+
+ /**
+  * minColLength is a PreferenceFunction for selecting the variable with the
+  * smaller row count in the tableau.
+  *
+  * This is a heuristic rule and should not be used during the VarOrder
+  * stage of findModel.
+  */
+ ArithVar minColLength(ArithVar x, ArithVar y) const;
+
+ /**
+  * minRowLength is a PreferenceFunction for selecting the variable with the
+  * smaller row count in the tableau.
+  *
+  * This is a heuristic rule and should not be used during the VarOrder
+  * stage of findModel.
+  */
+ ArithVar minRowLength(ArithVar x, ArithVar y) const;
+
+ /**
+  * minBoundAndRowCount is a PreferenceFunction for preferring a variable
+  * without an asserted bound over variables with an asserted bound.
+  * If both have bounds or both do not have bounds,
+  * the rule falls back to minRowCount(...).
+  *
+  * This is a heuristic rule and should not be used during the VarOrder
+  * stage of findModel.
+  */
+ ArithVar minBoundAndColLength(ArithVar x, ArithVar y) const;
+
+ template <bool above>
+ inline bool isAcceptableSlack(int sgn, ArithVar nonbasic) const
+ {
+   return (above && sgn < 0 && d_variables.strictlyBelowUpperBound(nonbasic))
+          || (above && sgn > 0 && d_variables.strictlyAboveLowerBound(nonbasic))
+          || (!above && sgn > 0
+              && d_variables.strictlyBelowUpperBound(nonbasic))
+          || (!above && sgn < 0
+              && d_variables.strictlyAboveLowerBound(nonbasic));
+  }
+
+  /**
+   * Given the basic variable x_i,
+   * this function finds the smallest nonbasic variable x_j in the row of x_i
+   * in the tableau that can "take up the slack" to let x_i satisfy its bounds.
+   * This returns ARITHVAR_SENTINEL if none exists.
+   *
+   * More formally one of the following conditions must be satisfied:
+   * -  lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j)
+   * -  lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j)
+   * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j)
+   * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j)
+   *
+   */
+  template <bool lowerBound>  ArithVar selectSlack(ArithVar x_i, VarPreferenceFunction pf) const;
+  ArithVar selectSlackLowerBound(ArithVar x_i, VarPreferenceFunction pf) const {
+    return selectSlack<true>(x_i, pf);
+  }
+  ArithVar selectSlackUpperBound(ArithVar x_i, VarPreferenceFunction pf) const {
+    return selectSlack<false>(x_i, pf);
+  }
+
+  const Tableau::Entry* selectSlackEntry(ArithVar x_i, bool above) const;
+
+  inline bool rowIndexIsTracked(RowIndex ridx) const {
+    return d_btracking.isKey(ridx);
+  }
+  inline bool basicIsTracked(ArithVar v) const {
+    return rowIndexIsTracked(d_tableau.basicToRowIndex(v));
+  }
+  void trackRowIndex(RowIndex ridx);
+  void stopTrackingRowIndex(RowIndex ridx){
+    Assert(rowIndexIsTracked(ridx));
+    d_btracking.remove(ridx);
+  }
+
+  /**
+   * If the pivot described in u were performed,
+   * then the row would qualify as being either at the minimum/maximum
+   * to the non-basics being at their bounds.
+   * The minimum/maximum is determined by the direction the non-basic is changing.
+   */
+  bool basicsAtBounds(const UpdateInfo& u) const;
+
+private:
+
+  /**
+   * Recomputes the bound info for a row using either the information
+   * in the bounds queue or the current information.
+   * O(row length of ridx)
+   */
+  BoundsInfo computeRowBoundInfo(RowIndex ridx, bool inQueue) const;
+
+public:
+  /** Debug only routine. */
+  BoundCounts debugBasicAtBoundCount(ArithVar x_i) const;
+
+  /** Track the effect of the change of coefficient for bound counting. */
+  void trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn);
+
+  /** Track the effect of multiplying a row by a sign for bound counting. */
+  void trackingMultiplyRow(RowIndex ridx, int sgn);
+
+  /** Count for how many on a row have *an* upper/lower bounds. */
+  BoundCounts hasBoundCount(RowIndex ri) const {
+    Assert(d_variables.boundsQueueEmpty());
+    return d_btracking[ri].hasBounds();
+  }
+
+  /**
+   * Are there any non-basics on x_i's row that are not at
+   * their respective lower bounds (mod sgns).
+   * O(1) time due to the atBound() count.
+   */
+  bool nonbasicsAtLowerBounds(ArithVar x_i) const;
+
+  /**
+   * Are there any non-basics on x_i's row that are not at
+   * their respective upper bounds (mod sgns).
+   * O(1) time due to the atBound() count.
+   */
+  bool nonbasicsAtUpperBounds(ArithVar x_i) const;
+
+private:
+  class TrackingCallback : public CoefficientChangeCallback {
+  private:
+    LinearEqualityModule* d_linEq;
+  public:
+    TrackingCallback(LinearEqualityModule* le) : d_linEq(le) {}
+    void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) override
+    {
+      d_linEq->trackingCoefficientChange(ridx, nb, oldSgn, currSgn);
+    }
+    void multiplyRow(RowIndex ridx, int sgn) override
+    {
+      d_linEq->trackingMultiplyRow(ridx, sgn);
+    }
+    bool canUseRow(RowIndex ridx) const override
+    {
+      ArithVar basic = d_linEq->getTableau().rowIndexToBasic(ridx);
+      return d_linEq->basicIsTracked(basic);
+    }
+ } d_trackCallback;
+
+  /**
+   * Selects the constraint for the variable v on the row for basic
+   * with the weakest possible constraint that is consistent with the surplus
+   * surplus.
+   */
+  ConstraintP weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v,
+                                const Rational& coeff, bool& anyWeakening, ArithVar basic) const;
+
+public:
+  /**
+   * Constructs a minimally weak conflict for the basic variable basicVar.
+   *
+   * Returns a constraint that is now in conflict.
+   */
+  ConstraintCP minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, FarkasConflictBuilder& rc) const;
+
+  /**
+   * Given a basic variable that is know to have a conflict on it,
+   * construct and return a conflict.
+   * Follows section 4.2 in the CAV06 paper.
+   */
+  inline ConstraintCP generateConflictAboveUpperBound(ArithVar conflictVar, FarkasConflictBuilder& rc) const {
+    return minimallyWeakConflict(true, conflictVar, rc);
+  }
+
+  inline ConstraintCP generateConflictBelowLowerBound(ArithVar conflictVar, FarkasConflictBuilder& rc) const {
+    return minimallyWeakConflict(false, conflictVar, rc);
+  }
+
+  /**
+   * Computes the sum of the upper/lower bound of row.
+   * The variable skip is not included in the sum.
+   */
+  DeltaRational computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const;
+
+public:
+  void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult);
+  void directlyAddToCoefficient(ArithVar row, ArithVar col, const Rational& mult);
+
+
+  /**
+   * Checks to make sure the assignment is consistent with the tableau.
+   * This code is for debugging.
+   */
+  void debugCheckTableau();
+
+  void debugCheckTracking();
+
+  /** Debugging information for a pivot. */
+  void debugPivot(ArithVar x_i, ArithVar x_j);
+
+  ArithVar minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const;
+
+  /**
+   * Returns true if there would be a conflict on this row after a pivot
+   * and update using its basic variable and one of the non-basic variables on
+   * the row.
+   */
+  bool willBeInConflictAfterPivot(const Tableau::Entry& entry, const DeltaRational& nbDiff, bool bToUB) const;
+  UpdateInfo mkConflictUpdate(const Tableau::Entry& entry, bool ub) const;
+
+  /**
+   * Looks more an update for fcSimplex on the nonbasic variable nb with the focus coefficient.
+   */
+  UpdateInfo speculativeUpdate(ArithVar nb, const Rational& focusCoeff, UpdatePreferenceFunction pref);
+
+private:
+
+  /**
+   * Examines the effects of pivoting the entries column variable
+   * with the row's basic variable and setting the variable s.t.
+   * the basic variable is equal to one of its bounds.
+   *
+   * If ub, then the basic variable will be equal its upper bound.
+   * If not ub,then the basic variable will be equal its lower bound.
+   *
+   * Returns iff this row will be in conflict after the pivot.
+   *
+   * If this is false, add the bound to the relevant heap.
+   * If the bound is +/-infinity, this is ignored.
+
+   *
+   * Returns true if this would be a conflict.
+   * If it returns false, this
+   */
+  bool accumulateBorder(const Tableau::Entry& entry, bool ub);
+
+  void handleBorders(UpdateInfo& selected, ArithVar nb, const Rational& focusCoeff, BorderHeap& heap, int minimumFixes, UpdatePreferenceFunction pref);
+  void pop_block(BorderHeap& heap, int& brokenInBlock, int& fixesRemaining, int& negErrorChange);
+  void clearSpeculative();
+  Rational updateCoefficient(BorderVec::const_iterator startBlock, BorderVec::const_iterator endBlock);
+
+private:
+  /** These fields are designed to be accessible to TheoryArith methods. */
+  class Statistics {
+  public:
+    IntStat d_statPivots, d_statUpdates;
+    TimerStat d_pivotTime;
+    TimerStat d_adjTime;
+
+    IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
+    TimerStat d_weakenTime;
+    TimerStat d_forceTime;
+
+    Statistics();
+  };
+  mutable Statistics d_statistics;
+
+};/* class LinearEqualityModule */
+
+struct Cand {
+  ArithVar d_nb;
+  uint32_t d_penalty;
+  int d_sgn;
+  const Rational* d_coeff;
+
+  Cand(ArithVar nb, uint32_t penalty, int s, const Rational* c) :
+    d_nb(nb), d_penalty(penalty), d_sgn(s), d_coeff(c){}
+};
+
+
+class CompPenaltyColLength {
+private:
+  LinearEqualityModule* d_mod;
+  const bool d_havePenalties;
+
+ public:
+  CompPenaltyColLength(LinearEqualityModule* mod, bool havePenalties)
+      : d_mod(mod), d_havePenalties(havePenalties)
+  {
+  }
+
+  bool operator()(const Cand& x, const Cand& y) const {
+    if (x.d_penalty == y.d_penalty || !d_havePenalties)
+    {
+      return x.d_nb == d_mod->minBoundAndColLength(x.d_nb,y.d_nb);
+    }
+    else
+    {
+      return x.d_penalty < y.d_penalty;
+    }
+  }
+};
+
+class UpdateTrackingCallback : public BoundUpdateCallback {
+private:
+  LinearEqualityModule* d_mod;
+public:
+  UpdateTrackingCallback(LinearEqualityModule* mod): d_mod(mod){}
+  void operator()(ArithVar v, const BoundsInfo& bi) override
+  {
+    d_mod->includeBoundUpdate(v, bi);
+  }
+};
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/matrix.cpp b/src/theory/arith/linear/matrix.cpp
new file mode 100644 (file)
index 0000000..3a8b3b9
--- /dev/null
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Sparse matrix implementations for different types.
+ */
+
+#include "theory/arith/linear/matrix.h"
+
+using namespace std;
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+void NoEffectCCCB::update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) {}
+void NoEffectCCCB::multiplyRow(RowIndex ridx, int sgn){}
+bool NoEffectCCCB::canUseRow(RowIndex ridx) const { return false; }
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/matrix.h b/src/theory/arith/linear/matrix.h
new file mode 100644 (file)
index 0000000..bed8aa9
--- /dev/null
@@ -0,0 +1,1002 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ * Sparse matrix implementations for different types.
+ *
+ * Sparse matrix implementations for different types.
+ * This defines Matrix<T>, IntegerEqualityTables and Tableau.
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <queue>
+#include <utility>
+#include <vector>
+
+#include "base/output.h"
+#include "theory/arith/linear/arithvar.h"
+#include "util/dense_map.h"
+#include "util/index.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+typedef Index EntryID;
+const EntryID ENTRYID_SENTINEL = std::numeric_limits<EntryID>::max();
+
+typedef Index RowIndex;
+const RowIndex ROW_INDEX_SENTINEL  = std::numeric_limits<RowIndex>::max();
+
+class CoefficientChangeCallback {
+public:
+  virtual ~CoefficientChangeCallback() {}
+  virtual void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) = 0;
+  virtual void multiplyRow(RowIndex ridx, int Sgn) = 0;
+  virtual bool canUseRow(RowIndex ridx) const = 0;
+};
+
+class NoEffectCCCB : public CoefficientChangeCallback {
+public:
+ void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) override;
+ void multiplyRow(RowIndex ridx, int Sgn) override;
+ bool canUseRow(RowIndex ridx) const override;
+};
+
+template<class T>
+class MatrixEntry {
+private:
+  RowIndex d_rowIndex;
+  ArithVar d_colVar;
+
+  EntryID d_nextRow;
+  EntryID d_nextCol;
+
+  EntryID d_prevRow;
+  EntryID d_prevCol;
+
+  T d_coefficient;
+
+public:
+  MatrixEntry():
+    d_rowIndex(ROW_INDEX_SENTINEL),
+    d_colVar(ARITHVAR_SENTINEL),
+    d_nextRow(ENTRYID_SENTINEL),
+    d_nextCol(ENTRYID_SENTINEL),
+    d_prevRow(ENTRYID_SENTINEL),
+    d_prevCol(ENTRYID_SENTINEL),
+    d_coefficient()
+  {}
+
+  MatrixEntry(RowIndex row, ArithVar col, const T& coeff):
+     d_rowIndex(row),
+     d_colVar(col),
+     d_nextRow(ENTRYID_SENTINEL),
+     d_nextCol(ENTRYID_SENTINEL),
+     d_prevRow(ENTRYID_SENTINEL),
+     d_prevCol(ENTRYID_SENTINEL),
+     d_coefficient(coeff)
+  {}
+
+private:
+  bool unusedConsistent() const {
+    return
+      (d_rowIndex == ROW_INDEX_SENTINEL && d_colVar == ARITHVAR_SENTINEL) ||
+      (d_rowIndex != ROW_INDEX_SENTINEL && d_colVar != ARITHVAR_SENTINEL);
+  }
+
+public:
+
+  EntryID getNextRowEntryID() const {
+    return d_nextRow;
+  }
+
+  EntryID getNextColEntryID() const {
+    return d_nextCol;
+  }
+  EntryID getPrevRowEntryID() const {
+    return d_prevRow;
+  }
+
+  EntryID getPrevColEntryID() const {
+    return d_prevCol;
+  }
+
+  void setNextRowEntryID(EntryID id) {
+    d_nextRow = id;
+  }
+  void setNextColEntryID(EntryID id) {
+    d_nextCol = id;
+  }
+  void setPrevRowEntryID(EntryID id) {
+    d_prevRow = id;
+  }
+  void setPrevColEntryID(EntryID id) {
+    d_prevCol = id;
+  }
+
+  RowIndex getRowIndex() const{
+    return d_rowIndex;
+  }
+
+  ArithVar getColVar() const{
+    return d_colVar;
+  }
+
+  const T& getCoefficient() const {
+    return d_coefficient;
+  }
+
+  T& getCoefficient(){
+    return d_coefficient;
+  }
+
+  void setCoefficient(const T& t){
+    d_coefficient = t;
+  }
+
+  void markBlank() {
+    d_rowIndex = ROW_INDEX_SENTINEL;
+    d_colVar = ARITHVAR_SENTINEL;
+  }
+
+  bool blank() const{
+    Assert(unusedConsistent());
+
+    return d_rowIndex == ROW_INDEX_SENTINEL;
+  }
+}; /* class MatrixEntry<T> */
+
+template<class T>
+class MatrixEntryVector {
+private:
+  typedef MatrixEntry<T> EntryType;
+  typedef std::vector<EntryType> EntryArray;
+
+  EntryArray d_entries;
+  std::queue<EntryID> d_freedEntries;
+
+  uint32_t d_size;
+
+public:
+  MatrixEntryVector():
+    d_entries(), d_freedEntries(), d_size(0)
+  {}
+
+  const EntryType& operator[](EntryID id) const{
+    Assert(inBounds(id));
+    return d_entries[id];
+  }
+
+  EntryType& get(EntryID id){
+    Assert(inBounds(id));
+    return d_entries[id];
+  }
+
+  void freeEntry(EntryID id){
+    Assert(get(id).blank());
+    Assert(d_size > 0);
+
+    d_freedEntries.push(id);
+    --d_size;
+  }
+
+  EntryID newEntry(){
+    EntryID newId;
+    if(d_freedEntries.empty()){
+      newId = d_entries.size();
+      d_entries.push_back(MatrixEntry<T>());
+    }else{
+      newId = d_freedEntries.front();
+      d_freedEntries.pop();
+    }
+    ++d_size;
+    return newId;
+  }
+
+  uint32_t size() const{ return d_size; }
+  uint32_t capacity() const{ return d_entries.capacity(); }
+
+
+private:
+  bool inBounds(EntryID id) const{
+    return id <  d_entries.size();
+  }
+}; /* class MatrixEntryVector<T> */
+
+template <class T, bool isRow>
+class MatrixVector {
+private:
+  EntryID d_head;
+  uint32_t d_size;
+
+  MatrixEntryVector<T>* d_entries;
+
+  class Iterator {
+  private:
+    EntryID d_curr;
+    const MatrixEntryVector<T>* d_entries;
+
+  public:
+    Iterator(EntryID start, const MatrixEntryVector<T>* entries) :
+      d_curr(start), d_entries(entries)
+    {}
+
+  public:
+
+    EntryID getID() const {
+      return d_curr;
+    }
+
+    const MatrixEntry<T>& operator*() const{
+      Assert(!atEnd());
+      return (*d_entries)[d_curr];
+    }
+
+    Iterator& operator++(){
+      Assert(!atEnd());
+      const MatrixEntry<T>& entry = (*d_entries)[d_curr];
+      d_curr = isRow ? entry.getNextRowEntryID() : entry.getNextColEntryID();
+      return *this;
+    }
+
+    bool atEnd() const {
+      return d_curr == ENTRYID_SENTINEL;
+    }
+
+    bool operator==(const Iterator& i) const{
+      return d_curr == i.d_curr && d_entries == i.d_entries;
+    }
+
+    bool operator!=(const Iterator& i) const{
+      return !(d_curr == i.d_curr && d_entries == i.d_entries);
+    }
+  }; /* class MatrixVector<T, isRow>::Iterator */
+
+public:
+  MatrixVector(MatrixEntryVector<T>* mev)
+    : d_head(ENTRYID_SENTINEL), d_size(0), d_entries(mev)
+  {}
+
+  MatrixVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
+    : d_head(head), d_size(size), d_entries(mev)
+  {}
+
+  typedef Iterator const_iterator;
+  const_iterator begin() const {
+    return Iterator(d_head, d_entries);
+  }
+  const_iterator end() const {
+    return Iterator(ENTRYID_SENTINEL, d_entries);
+  }
+
+  EntryID getHead() const { return d_head; }
+
+  uint32_t getSize() const { return d_size; }
+
+  void insert(EntryID newId){
+    if(isRow){
+      d_entries->get(newId).setNextRowEntryID(d_head);
+
+      if(d_head != ENTRYID_SENTINEL){
+        d_entries->get(d_head).setPrevRowEntryID(newId);
+      }
+    }else{
+      d_entries->get(newId).setNextColEntryID(d_head);
+
+      if(d_head != ENTRYID_SENTINEL){
+        d_entries->get(d_head).setPrevColEntryID(newId);
+      }
+    }
+
+    d_head = newId;
+    ++d_size;
+  }
+  void remove(EntryID id){
+    Assert(d_size > 0);
+    --d_size;
+    if(isRow){
+      EntryID prevRow = d_entries->get(id).getPrevRowEntryID();
+      EntryID nextRow = d_entries->get(id).getNextRowEntryID();
+
+      if(d_head == id){
+        d_head = nextRow;
+      }
+      if(prevRow != ENTRYID_SENTINEL){
+        d_entries->get(prevRow).setNextRowEntryID(nextRow);
+      }
+      if(nextRow != ENTRYID_SENTINEL){
+        d_entries->get(nextRow).setPrevRowEntryID(prevRow);
+      }
+    }else{
+      EntryID prevCol = d_entries->get(id).getPrevColEntryID();
+      EntryID nextCol = d_entries->get(id).getNextColEntryID();
+
+      if(d_head == id){
+        d_head = nextCol;
+      }
+
+      if(prevCol != ENTRYID_SENTINEL){
+        d_entries->get(prevCol).setNextColEntryID(nextCol);
+      }
+      if(nextCol != ENTRYID_SENTINEL){
+        d_entries->get(nextCol).setPrevColEntryID(prevCol);
+      }
+    }
+  }
+}; /* class MatrixVector<T, isRow> */
+
+template <class T>
+  class RowVector : public MatrixVector<T, true>
+{
+private:
+  typedef MatrixVector<T, true> SuperT;
+public:
+  typedef typename SuperT::const_iterator const_iterator;
+
+  RowVector(MatrixEntryVector<T>* mev) : SuperT(mev){}
+  RowVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
+    : SuperT(head, size, mev){}
+};/* class RowVector<T> */
+
+template <class T>
+  class ColumnVector : public MatrixVector<T, false>
+{
+private:
+  typedef MatrixVector<T, false> SuperT;
+public:
+  typedef typename SuperT::const_iterator const_iterator;
+
+  ColumnVector(MatrixEntryVector<T>* mev) : SuperT(mev){}
+  ColumnVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
+    : SuperT(head, size, mev){}
+};/* class ColumnVector<T> */
+
+template <class T>
+class Matrix {
+public:
+  typedef MatrixEntry<T> Entry;
+
+protected:
+ typedef RowVector<T> RowVectorT;
+ typedef ColumnVector<T> ColumnVectorT;
+
+public:
+  typedef typename RowVectorT::const_iterator RowIterator;
+  typedef typename ColumnVectorT::const_iterator ColIterator;
+
+protected:
+  // RowTable : RowID |-> RowVector
+  typedef std::vector< RowVectorT > RowTable;
+  RowTable d_rows;
+
+  // ColumnTable : ArithVar |-> ColumnVector
+  typedef std::vector< ColumnVectorT > ColumnTable;
+  ColumnTable d_columns;
+
+  /* The merge buffer is used to store a row in order to optimize row addition. */
+  typedef std::pair<EntryID, bool> PosUsedPair;
+  typedef DenseMap< PosUsedPair > RowToPosUsedPairMap;
+  RowToPosUsedPairMap d_mergeBuffer;
+
+  /* The row that is in the merge buffer. */
+  RowIndex d_rowInMergeBuffer;
+
+  uint32_t d_entriesInUse;
+  MatrixEntryVector<T> d_entries;
+
+  std::vector<RowIndex> d_pool;
+
+  T d_zero;
+
+public:
+  /**
+   * Constructs an empty Matrix.
+   */
+  Matrix()
+  : d_rows(),
+    d_columns(),
+    d_mergeBuffer(),
+    d_rowInMergeBuffer(ROW_INDEX_SENTINEL),
+    d_entriesInUse(0),
+    d_entries(),
+    d_zero(0)
+  {}
+
+  Matrix(const T& zero)
+  : d_rows(),
+    d_columns(),
+    d_mergeBuffer(),
+    d_rowInMergeBuffer(ROW_INDEX_SENTINEL),
+    d_entriesInUse(0),
+    d_entries(),
+    d_zero(zero)
+  {}
+
+  Matrix(const Matrix& m)
+  : d_rows(),
+    d_columns(),
+    d_mergeBuffer(m.d_mergeBuffer),
+    d_rowInMergeBuffer(m.d_rowInMergeBuffer),
+    d_entriesInUse(m.d_entriesInUse),
+    d_entries(m.d_entries),
+    d_zero(m.d_zero)
+  {
+    d_columns.clear();
+    for(typename ColumnTable::const_iterator c=m.d_columns.begin(), cend = m.d_columns.end(); c!=cend; ++c){
+      const ColumnVectorT& col = *c;
+      d_columns.push_back(ColumnVectorT(col.getHead(),col.getSize(),&d_entries));
+    }
+    d_rows.clear();
+    for(typename RowTable::const_iterator r=m.d_rows.begin(), rend = m.d_rows.end(); r!=rend; ++r){
+      const RowVectorT& row = *r;
+      d_rows.push_back(RowVectorT(row.getHead(),row.getSize(),&d_entries));
+    }
+  }
+
+  Matrix& operator=(const Matrix& m){
+    d_mergeBuffer = (m.d_mergeBuffer);
+    d_rowInMergeBuffer = (m.d_rowInMergeBuffer);
+    d_entriesInUse = (m.d_entriesInUse);
+    d_entries = (m.d_entries);
+    d_zero = (m.d_zero);
+    d_columns.clear();
+    for(typename ColumnTable::const_iterator c=m.d_columns.begin(), cend = m.d_columns.end(); c!=cend; ++c){
+      const ColumnVector<T>& col = *c;
+      d_columns.push_back(ColumnVector<T>(col.getHead(), col.getSize(), &d_entries));
+    }
+    d_rows.clear();
+    for(typename RowTable::const_iterator r=m.d_rows.begin(), rend = m.d_rows.end(); r!=rend; ++r){
+      const RowVector<T>& row = *r;
+      d_rows.push_back(RowVector<T>(row.getHead(), row.getSize(), &d_entries));
+    }
+    return *this;
+  }
+
+protected:
+
+  void addEntry(RowIndex row, ArithVar col, const T& coeff){
+    Trace("tableau") << "addEntry(" << row << "," << col <<"," << coeff << ")" << std::endl;
+
+    Assert(coeff != 0);
+    Assert(row < d_rows.size());
+    Assert(col < d_columns.size());
+
+    EntryID newId = d_entries.newEntry();
+    Entry& newEntry = d_entries.get(newId);
+    newEntry = Entry(row, col, coeff);
+
+    Assert(newEntry.getCoefficient() != 0);
+
+    ++d_entriesInUse;
+
+    d_rows[row].insert(newId);
+    d_columns[col].insert(newId);
+  }
+
+  void removeEntry(EntryID id){
+    Assert(d_entriesInUse > 0);
+    --d_entriesInUse;
+
+    Entry& entry = d_entries.get(id);
+
+    RowIndex ridx = entry.getRowIndex();
+    ArithVar col = entry.getColVar();
+
+    Assert(d_rows[ridx].getSize() > 0);
+    Assert(d_columns[col].getSize() > 0);
+
+    d_rows[ridx].remove(id);
+    d_columns[col].remove(id);
+
+    entry.markBlank();
+
+    d_entries.freeEntry(id);
+  }
+
+ private:
+  RowIndex requestRowIndex(){
+    if(d_pool.empty()){
+      RowIndex ridx = d_rows.size();
+      d_rows.push_back(RowVectorT(&d_entries));
+      return ridx;
+    }else{
+      RowIndex rid = d_pool.back();
+      d_pool.pop_back();
+      return rid;
+    }
+  }
+
+  void releaseRowIndex(RowIndex rid){
+    d_pool.push_back(rid);
+  }
+
+public:
+
+  size_t getNumRows() const {
+    return d_rows.size();
+  }
+
+  size_t getNumColumns() const {
+    return d_columns.size();
+  }
+
+  void increaseSize(){
+    d_columns.push_back(ColumnVector<T>(&d_entries));
+  }
+
+  void increaseSizeTo(size_t s){
+    while(getNumColumns() < s){
+      increaseSize();
+    }
+  }
+
+  const RowVector<T>& getRow(RowIndex r) const {
+    Assert(r < d_rows.size());
+    return d_rows[r];
+  }
+
+  const ColumnVector<T>& getColumn(ArithVar v) const {
+    Assert(v < d_columns.size());
+    return d_columns[v];
+  }
+
+  uint32_t getRowLength(RowIndex r) const{
+    return getRow(r).getSize();
+  }
+
+  uint32_t getColLength(ArithVar x) const{
+    return getColumn(x).getSize();
+  }
+
+  /**
+   * Adds a row to the matrix.
+   * The new row is equivalent to:
+   *   \f$\sum_i\f$ coeffs[i] * variables[i]
+   */
+  RowIndex addRow(const std::vector<T>& coeffs,
+                  const std::vector<ArithVar>& variables){
+
+    RowIndex ridx = requestRowIndex();
+
+    //RowIndex ridx = d_rows.size();
+    //d_rows.push_back(RowVectorT(&d_entries));
+
+    typename std::vector<T>::const_iterator coeffIter = coeffs.begin();
+    std::vector<ArithVar>::const_iterator varsIter = variables.begin();
+    std::vector<ArithVar>::const_iterator varsEnd = variables.end();
+
+    for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
+      const T& coeff = *coeffIter;
+      ArithVar var_i = *varsIter;
+      Assert(var_i < getNumColumns());
+      addEntry(ridx, var_i, coeff);
+    }
+
+    return ridx;
+  }
+
+
+  void loadRowIntoBuffer(RowIndex rid){
+    Assert(d_mergeBuffer.empty());
+    Assert(d_rowInMergeBuffer == ROW_INDEX_SENTINEL);
+
+    RowIterator i = getRow(rid).begin(), i_end = getRow(rid).end();
+    for(; i != i_end; ++i){
+      EntryID id = i.getID();
+      const MatrixEntry<T>& entry = *i;
+      ArithVar colVar = entry.getColVar();
+      d_mergeBuffer.set(colVar, std::make_pair(id, false));
+    }
+
+    d_rowInMergeBuffer = rid;
+  }
+
+  void clearBuffer() {
+    Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
+
+    d_rowInMergeBuffer = ROW_INDEX_SENTINEL;
+    d_mergeBuffer.purge();
+  }
+
+  /* to *= mult */
+  void multiplyRowByConstant(RowIndex to, const T& mult){
+    RowIterator i = getRow(to).begin();
+    RowIterator i_end = getRow(to).end();
+    for( ; i != i_end; ++i){
+      EntryID id = i.getID();
+      Entry& entry = d_entries.get(id);
+      T& coeff = entry.getCoefficient();
+      coeff *= mult;
+    }
+  }
+
+  /**  to += mult * from.
+   * Use the more efficient rowPlusBufferTimesConstant() for
+   * repeated use.
+   */
+  void rowPlusRowTimesConstant(RowIndex to, RowIndex from, const T& mult){
+    Assert(to != from);
+    loadRowIntoBuffer(from);
+    rowPlusBufferTimesConstant(to, mult);
+    clearBuffer();
+  }
+
+  /**  to += mult * buffer.
+   * Invalidates coefficients on the row.
+   * (mult should never be a direct copy of a coefficient!)
+   */
+  void rowPlusBufferTimesConstant(RowIndex to, const T& mult){
+    Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
+    Assert(to != ROW_INDEX_SENTINEL);
+
+    Trace("tableau") << "rowPlusRowTimesConstant("
+                     << to << "," << mult << "," << d_rowInMergeBuffer << ")"
+                     << std::endl;
+
+    Assert(debugNoZeroCoefficients(to));
+    Assert(debugNoZeroCoefficients(d_rowInMergeBuffer));
+
+    Assert(mult != 0);
+
+    RowIterator i = getRow(to).begin();
+    RowIterator i_end = getRow(to).end();
+    while(i != i_end){
+      EntryID id = i.getID();
+      Entry& entry = d_entries.get(id);
+      ArithVar colVar = entry.getColVar();
+
+      ++i;
+
+      if(d_mergeBuffer.isKey(colVar)){
+        EntryID bufferEntry = d_mergeBuffer[colVar].first;
+        Assert(!d_mergeBuffer[colVar].second);
+        d_mergeBuffer.get(colVar).second = true;
+
+        const Entry& other = d_entries.get(bufferEntry);
+        T& coeff = entry.getCoefficient();
+        coeff += mult * other.getCoefficient();
+
+        if(coeff.sgn() == 0){
+          removeEntry(id);
+        }
+      }
+    }
+
+    i = getRow(d_rowInMergeBuffer).begin();
+    i_end = getRow(d_rowInMergeBuffer).end();
+
+    for(; i != i_end; ++i){
+      const Entry& entry = *i;
+      ArithVar colVar = entry.getColVar();
+
+      if(d_mergeBuffer[colVar].second){
+        d_mergeBuffer.get(colVar).second = false;
+      }else{
+        Assert(!(d_mergeBuffer[colVar]).second);
+        T newCoeff =  mult * entry.getCoefficient();
+        addEntry(to, colVar, newCoeff);
+      }
+    }
+
+    Assert(mergeBufferIsClear());
+
+    if(TraceIsOn("matrix")) { printMatrix(); }
+  }
+
+  /**  to += mult * buffer. */
+  void rowPlusBufferTimesConstant(RowIndex to, const T& mult, CoefficientChangeCallback& cb){
+    Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
+    Assert(to != ROW_INDEX_SENTINEL);
+
+    Trace("tableau") << "rowPlusRowTimesConstant("
+                     << to << "," << mult << "," << d_rowInMergeBuffer << ")"
+                     << std::endl;
+
+    Assert(debugNoZeroCoefficients(to));
+    Assert(debugNoZeroCoefficients(d_rowInMergeBuffer));
+
+    Assert(mult != 0);
+
+    RowIterator i = getRow(to).begin();
+    RowIterator i_end = getRow(to).end();
+    while(i != i_end){
+      EntryID id = i.getID();
+      Entry& entry = d_entries.get(id);
+      ArithVar colVar = entry.getColVar();
+
+      ++i;
+
+      if(d_mergeBuffer.isKey(colVar)){
+        EntryID bufferEntry = d_mergeBuffer[colVar].first;
+        Assert(!d_mergeBuffer[colVar].second);
+        d_mergeBuffer.get(colVar).second = true;
+
+        const Entry& other = d_entries.get(bufferEntry);
+        T& coeff = entry.getCoefficient();
+        int coeffOldSgn = coeff.sgn();
+        coeff += mult * other.getCoefficient();
+        int coeffNewSgn = coeff.sgn();
+
+        if(coeffOldSgn != coeffNewSgn){
+          cb.update(to, colVar, coeffOldSgn,  coeffNewSgn);
+
+          if(coeffNewSgn == 0){
+            removeEntry(id);
+          }
+        }
+      }
+    }
+
+    i = getRow(d_rowInMergeBuffer).begin();
+    i_end = getRow(d_rowInMergeBuffer).end();
+
+    for(; i != i_end; ++i){
+      const Entry& entry = *i;
+      ArithVar colVar = entry.getColVar();
+
+      if(d_mergeBuffer[colVar].second){
+        d_mergeBuffer.get(colVar).second = false;
+      }else{
+        Assert(!(d_mergeBuffer[colVar]).second);
+        T newCoeff =  mult * entry.getCoefficient();
+        addEntry(to, colVar, newCoeff);
+
+        cb.update(to, colVar, 0,  newCoeff.sgn());
+      }
+    }
+
+    Assert(mergeBufferIsClear());
+
+    if(TraceIsOn("matrix")) { printMatrix(); }
+  }
+
+  bool mergeBufferIsClear() const{
+    RowToPosUsedPairMap::const_iterator i = d_mergeBuffer.begin();
+    RowToPosUsedPairMap::const_iterator i_end = d_mergeBuffer.end();
+    for(; i != i_end; ++i){
+      RowIndex rid = *i;
+      if(d_mergeBuffer[rid].second){
+        return false;
+      }
+    }
+    return true;
+  }
+
+protected:
+
+  EntryID findOnRow(RowIndex rid, ArithVar column) const {
+    RowIterator i = d_rows[rid].begin(), i_end = d_rows[rid].end();
+    for(; i != i_end; ++i){
+      EntryID id = i.getID();
+      const MatrixEntry<T>& entry = *i;
+      ArithVar colVar = entry.getColVar();
+
+      if(colVar == column){
+        return id;
+      }
+    }
+    return ENTRYID_SENTINEL;
+  }
+
+  EntryID findOnCol(RowIndex rid, ArithVar column) const{
+    ColIterator i = d_columns[column].begin(), i_end = d_columns[column].end();
+    for(; i != i_end; ++i){
+      EntryID id = i.getID();
+      const MatrixEntry<T>& entry = *i;
+      RowIndex currRow = entry.getRowIndex();
+
+      if(currRow == rid){
+        return id;
+      }
+    }
+    return ENTRYID_SENTINEL;
+  }
+
+  EntryID findEntryID(RowIndex rid, ArithVar col) const{
+    bool colIsShorter = getColLength(col) < getRowLength(rid);
+    EntryID id = colIsShorter ? findOnCol(rid, col) : findOnRow(rid,col);
+    return id;
+  }
+  MatrixEntry<T> d_failedFind;
+public:
+
+  /** If the find fails, isUnused is true on the entry. */
+  const MatrixEntry<T>& findEntry(RowIndex rid, ArithVar col) const{
+    EntryID id = findEntryID(rid, col);
+    if(id == ENTRYID_SENTINEL){
+      return d_failedFind;
+    }else{
+      return d_entries[id];
+    }
+  }
+
+  /**
+   * Prints the contents of the Matrix to Trace("matrix")
+   */
+  void printMatrix(std::ostream& out) const {
+    out << "Matrix::printMatrix"  << std::endl;
+
+    for(RowIndex i = 0, N = d_rows.size(); i < N; ++i){
+      printRow(i, out);
+    }
+  }
+  void printMatrix() const {
+    printMatrix(Trace("matrix"));
+  }
+
+  void printRow(RowIndex rid, std::ostream& out) const {
+    out << "{" << rid << ":";
+    const RowVector<T>& row = getRow(rid);
+    RowIterator i = row.begin();
+    RowIterator i_end = row.end();
+    for(; i != i_end; ++i){
+      printEntry(*i, out);
+      out << ",";
+    }
+    out << "}" << std::endl;
+  }
+  void printRow(RowIndex rid) const {
+    printRow(rid, Trace("matrix"));
+  }
+
+  void printEntry(const MatrixEntry<T>& entry, std::ostream& out) const {
+    out << entry.getColVar() << "*" << entry.getCoefficient();
+  }
+  void printEntry(const MatrixEntry<T>& entry) const {
+    printEntry(entry, Trace("matrix"));
+  }
+public:
+  uint32_t size() const {
+    return d_entriesInUse;
+  }
+  uint32_t getNumEntriesInTableau() const {
+    return d_entries.size();
+  }
+  uint32_t getEntryCapacity() const {
+    return d_entries.capacity();
+  }
+
+  void manipulateRowEntry(RowIndex row, ArithVar col, const T& c, CoefficientChangeCallback& cb){
+    int coeffOldSgn;
+    int coeffNewSgn;
+
+    EntryID id = findEntryID(row, col);
+    if(id == ENTRYID_SENTINEL){
+      coeffOldSgn = 0;
+      addEntry(row, col, c);
+      coeffNewSgn = c.sgn();
+    }else{
+      Entry& e = d_entries.get(id);
+      T& t = e.getCoefficient();
+      coeffOldSgn = t.sgn();
+      t += c;
+      coeffNewSgn = t.sgn();
+    }
+
+    if(coeffOldSgn != coeffNewSgn){
+      cb.update(row, col, coeffOldSgn,  coeffNewSgn);
+    }
+    if(coeffNewSgn == 0){
+      removeEntry(id);
+    }
+  }
+
+  void removeRow(RowIndex rid){
+    RowIterator i = getRow(rid).begin();
+    RowIterator i_end = getRow(rid).end();
+    for(; i != i_end; ++i){
+      EntryID id = i.getID();
+      removeEntry(id);
+    }
+    releaseRowIndex(rid);
+  }
+
+  double densityMeasure() const{
+    Assert(numNonZeroEntriesByRow() == numNonZeroEntries());
+    Assert(numNonZeroEntriesByCol() == numNonZeroEntries());
+
+    uint32_t n = getNumRows();
+    if(n == 0){
+      return 1.0;
+    }else {
+      uint32_t s = numNonZeroEntries();
+      uint32_t m = d_columns.size();
+      uint32_t divisor = (n *(m - n + 1));
+
+      Assert(n >= 1);
+      Assert(m >= n);
+      Assert(divisor > 0);
+      Assert(divisor >= s);
+
+      return (double(s)) / divisor;
+    }
+  }
+
+  void loadSignQueries(RowIndex rid, DenseMap<int>& target) const{
+
+    RowIterator i = getRow(rid).begin(), i_end = getRow(rid).end();
+    for(; i != i_end; ++i){
+      const MatrixEntry<T>& entry = *i;
+      target.set(entry.getColVar(), entry.getCoefficient().sgn());
+    }
+  }
+
+protected:
+  uint32_t numNonZeroEntries() const { return size(); }
+
+  uint32_t numNonZeroEntriesByRow() const {
+    uint32_t rowSum = 0;
+    for(RowIndex rid = 0, N = d_rows.size(); rid < N; ++rid){
+      rowSum += getRowLength(rid);
+    }
+    return rowSum;
+  }
+
+  uint32_t numNonZeroEntriesByCol() const {
+    uint32_t colSum = 0;
+    for(ArithVar v = 0, N = d_columns.size(); v < N; ++v){
+      colSum += getColLength(v);
+    }
+    return colSum;
+  }
+
+
+  bool debugNoZeroCoefficients(RowIndex ridx){
+    for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
+      const Entry& entry = *i;
+      if(entry.getCoefficient() == 0){
+        return false;
+      }
+    }
+    return true;
+  }
+  bool debugMatchingCountsForRow(RowIndex ridx){
+    for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
+      const Entry& entry = *i;
+      ArithVar colVar = entry.getColVar();
+      uint32_t count = debugCountColLength(colVar);
+      Trace("tableau") << "debugMatchingCountsForRow "
+                       << ridx << ":" << colVar << " " << count
+                       <<" "<< getColLength(colVar) << std::endl;
+      if( count != getColLength(colVar) ){
+        return false;
+      }
+    }
+    return true;
+  }
+
+  uint32_t debugCountColLength(ArithVar var){
+    Trace("tableau") << var << " ";
+    uint32_t count = 0;
+    for(ColIterator i=getColumn(var).begin(); !i.atEnd(); ++i){
+      const Entry& entry = *i;
+      Trace("tableau") << "(" << entry.getRowIndex() << ", " << i.getID() << ") ";
+      ++count;
+    }
+    Trace("tableau") << std::endl;
+    return count;
+  }
+  uint32_t debugCountRowLength(RowIndex ridx){
+    uint32_t count = 0;
+    for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
+      ++count;
+    }
+    return count;
+  }
+
+};/* class Matrix<T> */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/normal_form.cpp b/src/theory/arith/linear/normal_form.cpp
new file mode 100644 (file)
index 0000000..81bb238
--- /dev/null
@@ -0,0 +1,1427 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+#include "theory/arith/linear/normal_form.h"
+
+#include <list>
+
+#include "base/output.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/theory.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+Constant Constant::mkConstant(const Rational& rat) {
+  return Constant(mkRationalNode(rat));
+}
+
+size_t Variable::getComplexity() const{
+  return 1u;
+}
+
+size_t VarList::getComplexity() const{
+  if(empty()){
+    return 1;
+  }else if(singleton()){
+    return 1;
+  }else{
+    return size() + 1;
+  }
+}
+
+size_t Monomial::getComplexity() const{
+  return getConstant().getComplexity() + getVarList().getComplexity();
+}
+
+size_t Polynomial::getComplexity() const{
+  size_t cmp = 0;
+  iterator i = begin(), e = end();
+  for(; i != e; ++i){
+    Monomial m = *i;
+    cmp += m.getComplexity();
+  }
+  return cmp;
+}
+
+size_t Constant::getComplexity() const{
+  return getValue().complexity();
+}
+
+bool Variable::isLeafMember(Node n){
+  return (!isRelationOperator(n.getKind())) &&
+    (Theory::isLeafOf(n, theory::THEORY_ARITH));
+}
+
+VarList::VarList(Node n) : NodeWrapper(n) { Assert(isSorted(begin(), end())); }
+
+bool Variable::isIAndMember(Node n)
+{
+  return n.getKind() == kind::IAND && Polynomial::isMember(n[0])
+         && Polynomial::isMember(n[1]);
+}
+
+bool Variable::isPow2Member(Node n)
+{
+  return n.getKind() == kind::POW2 && Polynomial::isMember(n[0]);
+}
+
+bool Variable::isDivMember(Node n){
+  switch(n.getKind()){
+  case kind::DIVISION:
+  case kind::INTS_DIVISION:
+  case kind::INTS_MODULUS:
+  case kind::DIVISION_TOTAL:
+  case kind::INTS_DIVISION_TOTAL:
+  case kind::INTS_MODULUS_TOTAL:
+    return Polynomial::isMember(n[0]) && Polynomial::isMember(n[1]);
+  default:
+    return false;
+  }
+}
+
+bool Variable::isTranscendentalMember(Node n) {
+  switch(n.getKind()){
+  case kind::EXPONENTIAL:
+  case kind::SINE:
+  case kind::COSINE:
+  case kind::TANGENT:
+  case kind::COSECANT:
+  case kind::SECANT:
+  case kind::COTANGENT:
+  case kind::ARCSINE:
+  case kind::ARCCOSINE:
+  case kind::ARCTANGENT:
+  case kind::ARCCOSECANT:
+  case kind::ARCSECANT:
+  case kind::ARCCOTANGENT:
+  case kind::SQRT: return Polynomial::isMember(n[0]);
+  case kind::PI:
+    return true;
+  default:
+    return false;
+  }
+}
+
+
+bool VarList::isSorted(iterator start, iterator end) {
+  return std::is_sorted(start, end);
+}
+
+bool VarList::isMember(Node n) {
+  if(Variable::isMember(n)) {
+    return true;
+  }
+  if(n.getKind() == kind::NONLINEAR_MULT) {
+    Node::iterator curr = n.begin(), end = n.end();
+    Node prev = *curr;
+    if(!Variable::isMember(prev)) return false;
+
+    Variable::VariableNodeCmp cmp;
+
+    while( (++curr) != end) {
+      if(!Variable::isMember(*curr)) return false;
+      // prev <= curr : accept
+      // !(prev <= curr) : reject
+      // !(!(prev > curr)) : reject
+      // curr < prev : reject
+      if((cmp(*curr, prev))) return false;
+      prev = *curr;
+    }
+    return true;
+  } else {
+    return false;
+  }
+}
+
+int VarList::cmp(const VarList& vl) const {
+  int dif = this->size() - vl.size();
+  if (dif == 0) {
+    if(this->getNode() == vl.getNode()) {
+      return 0;
+    }
+
+    Assert(!empty());
+    Assert(!vl.empty());
+    if(this->size() == 1){
+      return Variable::VariableNodeCmp::cmp(this->getNode(), vl.getNode());
+    }
+
+
+    internal_iterator ii=this->internalBegin(), ie=this->internalEnd();
+    internal_iterator ci=vl.internalBegin(), ce=vl.internalEnd();
+    for(; ii != ie; ++ii, ++ci){
+      Node vi = *ii;
+      Node vc = *ci;
+      int tmp = Variable::VariableNodeCmp::cmp(vi, vc);
+      if(tmp != 0){
+        return tmp;
+      }
+    }
+    Unreachable();
+  } else if(dif < 0) {
+    return -1;
+  } else {
+    return 1;
+  }
+}
+
+VarList VarList::parseVarList(Node n) {
+  return VarList(n);
+  // if(Variable::isMember(n)) {
+  //   return VarList(Variable(n));
+  // } else {
+  //   Assert(n.getKind() == kind::MULT);
+  //   for(Node::iterator i=n.begin(), end = n.end(); i!=end; ++i) {
+  //     Assert(Variable::isMember(*i));
+  //   }
+  //   return VarList(n);
+  // }
+}
+
+VarList VarList::operator*(const VarList& other) const {
+  if(this->empty()) {
+    return other;
+  } else if(other.empty()) {
+    return *this;
+  } else {
+    vector<Node> result;
+
+    internal_iterator
+      thisBegin = this->internalBegin(),
+      thisEnd = this->internalEnd(),
+      otherBegin = other.internalBegin(),
+      otherEnd = other.internalEnd();
+
+    Variable::VariableNodeCmp cmp;
+    std::merge(thisBegin, thisEnd, otherBegin, otherEnd, std::back_inserter(result), cmp);
+
+    Assert(result.size() >= 2);
+    Node mult = NodeManager::currentNM()->mkNode(kind::NONLINEAR_MULT, result);
+    return VarList::parseVarList(mult);
+  }
+}
+
+bool Monomial::isMember(TNode n){
+  if(n.getKind() == kind::CONST_RATIONAL) {
+    return true;
+  } else if(multStructured(n)) {
+    return VarList::isMember(n[1]);
+  } else {
+    return VarList::isMember(n);
+  }
+}
+
+Monomial Monomial::mkMonomial(const Constant& c, const VarList& vl) {
+  if(c.isZero() || vl.empty() ) {
+    return Monomial(c);
+  } else if(c.isOne()) {
+    return Monomial(vl);
+  } else {
+    return Monomial(c, vl);
+  }
+}
+
+Monomial Monomial::mkMonomial(const VarList& vl) {
+  // acts like Monomial::mkMonomial( 1, vl)
+  if( vl.empty() ) {
+    return Monomial::mkOne();
+  } else if(true){
+    return Monomial(vl);
+  }
+}
+
+Monomial Monomial::parseMonomial(Node n) {
+  if(n.getKind() == kind::CONST_RATIONAL) {
+    return Monomial(Constant(n));
+  } else if(multStructured(n)) {
+    return Monomial::mkMonomial(Constant(n[0]),VarList::parseVarList(n[1]));
+  } else {
+    return Monomial(VarList::parseVarList(n));
+  }
+}
+Monomial Monomial::operator*(const Rational& q) const {
+  if(q.isZero()){
+    return mkZero();
+  }else{
+    Constant newConstant = this->getConstant() * q;
+    return Monomial::mkMonomial(newConstant, getVarList());
+  }
+}
+
+Monomial Monomial::operator*(const Constant& c) const {
+  return (*this) * c.getValue();
+  // if(c.isZero()){
+  //   return mkZero();
+  // }else{
+  //   Constant newConstant = this->getConstant() * c;
+  //   return Monomial::mkMonomial(newConstant, getVarList());
+  // }
+}
+
+Monomial Monomial::operator*(const Monomial& mono) const {
+  Constant newConstant = this->getConstant() * mono.getConstant();
+  VarList newVL = this->getVarList() * mono.getVarList();
+
+  return Monomial::mkMonomial(newConstant, newVL);
+}
+
+// vector<Monomial> Monomial::sumLikeTerms(const std::vector<Monomial> & monos)
+// {
+//   Assert(isSorted(monos));
+//   vector<Monomial> outMonomials;
+//   typedef vector<Monomial>::const_iterator iterator;
+//   for(iterator rangeIter = monos.begin(), end=monos.end(); rangeIter != end;)
+//   {
+//     Rational constant = (*rangeIter).getConstant().getValue();
+//     VarList varList  = (*rangeIter).getVarList();
+//     ++rangeIter;
+//     while(rangeIter != end && varList == (*rangeIter).getVarList()) {
+//       constant += (*rangeIter).getConstant().getValue();
+//       ++rangeIter;
+//     }
+//     if(constant != 0) {
+//       Constant asConstant = Constant::mkConstant(constant);
+//       Monomial nonZero = Monomial::mkMonomial(asConstant, varList);
+//       outMonomials.push_back(nonZero);
+//     }
+//   }
+
+//   Assert(isStrictlySorted(outMonomials));
+//   return outMonomials;
+// }
+
+void Monomial::sort(std::vector<Monomial>& m){
+  if(!isSorted(m)){
+    std::sort(m.begin(), m.end());
+  }
+}
+
+void Monomial::combineAdjacentMonomials(std::vector<Monomial>& monos) {
+  Assert(isSorted(monos));
+  size_t writePos, readPos, N;
+  for(writePos = 0, readPos = 0, N = monos.size(); readPos < N;){
+    Monomial& atRead = monos[readPos];
+    const VarList& varList  = atRead.getVarList();
+
+    size_t rangeEnd = readPos+1;
+    for(; rangeEnd < N; rangeEnd++){
+      if(!(varList == monos[rangeEnd].getVarList())){ break; }
+    }
+    // monos[i] for i in [readPos, rangeEnd) has the same var list
+    if(readPos+1 == rangeEnd){ // no addition needed
+      if(!atRead.getConstant().isZero()){
+        Monomial cpy = atRead; // being paranoid here
+        monos[writePos] = cpy;
+        writePos++;
+      }
+    }else{
+      Rational constant(monos[readPos].getConstant().getValue());
+      for(size_t i=readPos+1; i < rangeEnd; ++i){
+        constant += monos[i].getConstant().getValue();
+      }
+      if(!constant.isZero()){
+        Constant asConstant = Constant::mkConstant(constant);
+        Monomial nonZero = Monomial::mkMonomial(asConstant, varList);
+        monos[writePos] = nonZero;
+        writePos++;
+      }
+    }
+    Assert(rangeEnd > readPos);
+    readPos = rangeEnd;
+  }
+  if(writePos > 0 ){
+    Monomial cp = monos[0];
+    Assert(writePos <= N);
+    monos.resize(writePos, cp);
+  }else{
+    monos.clear();
+  }
+  Assert(isStrictlySorted(monos));
+}
+
+void Monomial::print() const {
+  Trace("normal-form") <<  getNode() << std::endl;
+}
+
+void Monomial::printList(const std::vector<Monomial>& list) {
+  for(vector<Monomial>::const_iterator i = list.begin(), end = list.end(); i != end; ++i) {
+    const Monomial& m =*i;
+    m.print();
+  }
+}
+Polynomial Polynomial::operator+(const Polynomial& vl) const {
+
+  std::vector<Monomial> sortedMonos;
+  std::merge(begin(), end(), vl.begin(), vl.end(), std::back_inserter(sortedMonos));
+
+  Monomial::combineAdjacentMonomials(sortedMonos);
+  //std::vector<Monomial> combined = Monomial::sumLikeTerms(sortedMonos);
+
+  Polynomial result = mkPolynomial(sortedMonos);
+  return result;
+}
+
+Polynomial Polynomial::exactDivide(const Integer& z) const {
+  Assert(isIntegral());
+  if(z.isOne()){
+    return (*this);
+  }else {
+    Constant invz = Constant::mkConstant(Rational(1,z));
+    Polynomial prod = (*this) * Monomial::mkMonomial(invz);
+    Assert(prod.isIntegral());
+    return prod;
+  }
+}
+
+Polynomial Polynomial::sumPolynomials(const std::vector<Polynomial>& ps){
+  if(ps.empty()){
+    return mkZero();
+  }else if(ps.size() <= 4){
+    // if there are few enough polynomials just add them
+    Polynomial p = ps[0];
+    for(size_t i = 1; i < ps.size(); ++i){
+      p = p + ps[i];
+    }
+    return p;
+  }else{
+    // general case
+    std::map<Node, Rational> coeffs;
+    for(size_t i = 0, N = ps.size(); i<N; ++i){
+      const Polynomial& p = ps[i];
+      for(iterator pi = p.begin(), pend = p.end(); pi != pend; ++pi) {
+        Monomial m = *pi;
+        coeffs[m.getVarList().getNode()] += m.getConstant().getValue();
+      }
+    }
+    std::vector<Monomial> monos;
+    std::map<Node, Rational>::const_iterator ci = coeffs.begin(), cend = coeffs.end();
+    for(; ci != cend; ++ci){
+      if(!(*ci).second.isZero()){
+        Constant c = Constant::mkConstant((*ci).second);
+        Node n = (*ci).first;
+        VarList vl = VarList::parseVarList(n);
+        monos.push_back(Monomial::mkMonomial(c, vl));
+      }
+    }
+    Monomial::sort(monos);
+    Monomial::combineAdjacentMonomials(monos);
+
+    Polynomial result = mkPolynomial(monos);
+    return result;
+  }
+}
+
+Polynomial Polynomial::operator-(const Polynomial& vl) const {
+  Constant negOne = Constant::mkConstant(Rational(-1));
+
+  return *this + (vl*negOne);
+}
+
+Polynomial Polynomial::operator*(const Rational& q) const{
+  if(q.isZero()){
+    return Polynomial::mkZero();
+  }else if(q.isOne()){
+    return *this;
+  }else{
+    std::vector<Monomial> newMonos;
+    for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
+      newMonos.push_back((*i)*q);
+    }
+
+    Assert(Monomial::isStrictlySorted(newMonos));
+    return Polynomial::mkPolynomial(newMonos);
+  }
+}
+
+Polynomial Polynomial::operator*(const Constant& c) const{
+  return (*this) * c.getValue();
+  // if(c.isZero()){
+  //   return Polynomial::mkZero();
+  // }else if(c.isOne()){
+  //   return *this;
+  // }else{
+  //   std::vector<Monomial> newMonos;
+  //   for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
+  //     newMonos.push_back((*i)*c);
+  //   }
+
+  //   Assert(Monomial::isStrictlySorted(newMonos));
+  //   return Polynomial::mkPolynomial(newMonos);
+  // }
+}
+
+Polynomial Polynomial::operator*(const Monomial& mono) const {
+  if(mono.isZero()) {
+    return Polynomial(mono); //Don't multiply by zero
+  } else {
+    std::vector<Monomial> newMonos;
+    for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
+      newMonos.push_back(mono * (*i));
+    }
+
+    // We may need to sort newMonos.
+    // Suppose this = (+ x y), mono = x, (* x y).getId() < (* x x).getId()
+    // newMonos = <(* x x), (* x y)> after this loop.
+    // This is not sorted according to the current VarList order.
+    Monomial::sort(newMonos);
+    return Polynomial::mkPolynomial(newMonos);
+  }
+}
+
+Polynomial Polynomial::operator*(const Polynomial& poly) const {
+  Polynomial res = Polynomial::mkZero();
+  for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
+    Monomial curr = *i;
+    Polynomial prod = poly * curr;
+    Polynomial sum  = res + prod;
+    res = sum;
+  }
+  return res;
+}
+
+Monomial Polynomial::selectAbsMinimum() const {
+  iterator iter = begin(), myend = end();
+  Assert(iter != myend);
+
+  Monomial min = *iter;
+  ++iter;
+  for(; iter != end(); ++iter){
+    Monomial curr = *iter;
+    if(curr.absCmp(min) < 0){
+      min = curr;
+    }
+  }
+  return min;
+}
+
+bool Polynomial::leadingCoefficientIsAbsOne() const {
+  return getHead().absCoefficientIsOne();
+}
+bool Polynomial::leadingCoefficientIsPositive() const {
+  return getHead().getConstant().isPositive();
+}
+
+bool Polynomial::denominatorLCMIsOne() const {
+  return denominatorLCM().isOne();
+}
+
+bool Polynomial::numeratorGCDIsOne() const {
+  return gcd().isOne();
+}
+
+Integer Polynomial::gcd() const {
+  Assert(isIntegral());
+  return numeratorGCD();
+}
+
+Integer Polynomial::numeratorGCD() const {
+  //We'll use the standardization that gcd(0, 0) = 0
+  //So that the gcd of the zero polynomial is gcd{0} = 0
+  iterator i=begin(), e=end();
+  Assert(i != e);
+
+  Integer d = (*i).getConstant().getValue().getNumerator().abs();
+  if(d.isOne()){
+    return d;
+  }
+  ++i;
+  for(; i!=e; ++i){
+    Integer c = (*i).getConstant().getValue().getNumerator();
+    d = d.gcd(c);
+    if(d.isOne()){
+      return d;
+    }
+  }
+  return d;
+}
+
+Integer Polynomial::denominatorLCM() const {
+  Integer tmp(1);
+  for (iterator i = begin(), e = end(); i != e; ++i) {
+    const Integer denominator = (*i).getConstant().getValue().getDenominator();
+    tmp = tmp.lcm(denominator);
+  }
+  return tmp;
+}
+
+Constant Polynomial::getCoefficient(const VarList& vl) const{
+  //TODO improve to binary search...
+  for(iterator iter=begin(), myend=end(); iter != myend; ++iter){
+    Monomial m = *iter;
+    VarList curr = m.getVarList();
+    if(curr == vl){
+      return m.getConstant();
+    }
+  }
+  return Constant::mkConstant(0);
+}
+
+Node Polynomial::computeQR(const Polynomial& p, const Integer& div){
+  Assert(p.isIntegral());
+  std::vector<Monomial> q_vec, r_vec;
+  Integer tmp_q, tmp_r;
+  for(iterator iter = p.begin(), pend = p.end(); iter != pend; ++iter){
+    Monomial curr = *iter;
+    VarList vl = curr.getVarList();
+    Constant c = curr.getConstant();
+
+    const Integer& a = c.getValue().getNumerator();
+    Integer::floorQR(tmp_q, tmp_r, a, div);
+    Constant q=Constant::mkConstant(tmp_q);
+    Constant r=Constant::mkConstant(tmp_r);
+    if(!q.isZero()){
+      q_vec.push_back(Monomial::mkMonomial(q, vl));
+    }
+    if(!r.isZero()){
+      r_vec.push_back(Monomial::mkMonomial(r, vl));
+    }
+  }
+
+  Polynomial p_q = Polynomial::mkPolynomial(q_vec);
+  Polynomial p_r = Polynomial::mkPolynomial(r_vec);
+
+  return NodeManager::currentNM()->mkNode(
+      kind::ADD, p_q.getNode(), p_r.getNode());
+}
+
+
+Monomial Polynomial::minimumVariableMonomial() const{
+  Assert(!isConstant());
+  if(singleton()){
+    return getHead();
+  }else{
+    iterator i = begin();
+    Monomial first = *i;
+    if( first.isConstant() ){
+      ++i;
+      Assert(i != end());
+      return *i;
+    }else{
+      return first;
+    }
+  }
+}
+
+bool Polynomial::variableMonomialAreStrictlyGreater(const Monomial& m) const{
+  if(isConstant()){
+    return true;
+  }else{
+    Monomial minimum = minimumVariableMonomial();
+    Trace("nf::tmp") << "minimum " << minimum.getNode() << endl;
+    Trace("nf::tmp") << "m " << m.getNode() << endl;
+    return m < minimum;
+  }
+}
+
+bool Polynomial::isMember(TNode n) {
+  if(Monomial::isMember(n)){
+    return true;
+  }
+  else if (n.getKind() == kind::ADD)
+  {
+    Assert(n.getNumChildren() >= 2);
+    Node::iterator currIter = n.begin(), end = n.end();
+    Node prev = *currIter;
+    if(!Monomial::isMember(prev)){
+      return false;
+    }
+
+    Monomial mprev = Monomial::parseMonomial(prev);
+    ++currIter;
+    for(; currIter != end; ++currIter){
+      Node curr = *currIter;
+      if(!Monomial::isMember(curr)){
+        return false;
+      }
+      Monomial mcurr = Monomial::parseMonomial(curr);
+      if(!(mprev < mcurr)){
+        return false;
+      }
+      mprev = mcurr;
+    }
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
+
+Node SumPair::computeQR(const SumPair& sp, const Integer& div){
+  Assert(sp.isIntegral());
+
+  const Integer& constant = sp.getConstant().getValue().getNumerator();
+
+  Integer constant_q, constant_r;
+  Integer::floorQR(constant_q, constant_r, constant, div);
+
+  Node p_qr = Polynomial::computeQR(sp.getPolynomial(), div);
+  Assert(p_qr.getKind() == kind::ADD);
+  Assert(p_qr.getNumChildren() == 2);
+
+  Polynomial p_q = Polynomial::parsePolynomial(p_qr[0]);
+  Polynomial p_r = Polynomial::parsePolynomial(p_qr[1]);
+
+  SumPair sp_q(p_q, Constant::mkConstant(constant_q));
+  SumPair sp_r(p_r, Constant::mkConstant(constant_r));
+
+  return NodeManager::currentNM()->mkNode(
+      kind::ADD, sp_q.getNode(), sp_r.getNode());
+}
+
+SumPair SumPair::mkSumPair(const Polynomial& p){
+  if(p.isConstant()){
+    Constant leadingConstant = p.getHead().getConstant();
+    return SumPair(Polynomial::mkZero(), leadingConstant);
+  }else if(p.containsConstant()){
+    Assert(!p.singleton());
+    return SumPair(p.getTail(), p.getHead().getConstant());
+  }else{
+    return SumPair(p, Constant::mkZero());
+  }
+}
+
+Comparison::Comparison(TNode n) : NodeWrapper(n) { Assert(isNormalForm()); }
+
+SumPair Comparison::toSumPair() const {
+  Kind cmpKind = comparisonKind();
+  switch(cmpKind){
+  case kind::LT:
+  case kind::LEQ:
+  case kind::GT:
+  case kind::GEQ:
+    {
+      TNode lit = getNode();
+      TNode atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
+      Polynomial p = Polynomial::parsePolynomial(atom[0]);
+      Constant c = Constant::mkConstant(atom[1]);
+      if(p.leadingCoefficientIsPositive()){
+        return SumPair(p, -c);
+      }else{
+        return SumPair(-p, c);
+      }
+    }
+  case kind::EQUAL:
+  case kind::DISTINCT:
+    {
+      Polynomial left = getLeft();
+      Polynomial right = getRight();
+      Trace("nf::tmp") << "left: " << left.getNode() << endl;
+      Trace("nf::tmp") << "right: " << right.getNode() << endl;
+      if(right.isConstant()){
+        return SumPair(left, -right.getHead().getConstant());
+      }else if(right.containsConstant()){
+        Assert(!right.singleton());
+
+        Polynomial noConstant = right.getTail();
+        return SumPair(left - noConstant, -right.getHead().getConstant());
+      }else{
+        return SumPair(left - right, Constant::mkZero());
+      }
+    }
+    default: Unhandled() << cmpKind;
+  }
+}
+
+Polynomial Comparison::normalizedVariablePart() const {
+  Kind cmpKind = comparisonKind();
+  switch(cmpKind){
+  case kind::LT:
+  case kind::LEQ:
+  case kind::GT:
+  case kind::GEQ:
+    {
+      TNode lit = getNode();
+      TNode atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
+      Polynomial p = Polynomial::parsePolynomial(atom[0]);
+      if(p.leadingCoefficientIsPositive()){
+        return p;
+      }else{
+        return -p;
+      }
+    }
+  case kind::EQUAL:
+  case kind::DISTINCT:
+    {
+      Polynomial left = getLeft();
+      Polynomial right = getRight();
+      if(right.isConstant()){
+        return left;
+      }else{
+        Polynomial noConstant = right.containsConstant() ? right.getTail() : right;
+        Polynomial diff = left - noConstant;
+        if(diff.leadingCoefficientIsPositive()){
+          return diff;
+        }else{
+          return -diff;
+        }
+      }
+    }
+    default: Unhandled() << cmpKind;
+  }
+}
+
+DeltaRational Comparison::normalizedDeltaRational() const {
+  Kind cmpKind = comparisonKind();
+  int delta = deltaCoeff(cmpKind);
+  switch(cmpKind){
+  case kind::LT:
+  case kind::LEQ:
+  case kind::GT:
+  case kind::GEQ:
+    {
+      Node lit = getNode();
+      Node atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
+      Polynomial left = Polynomial::parsePolynomial(atom[0]);
+      const Rational& q = atom[1].getConst<Rational>();
+      if(left.leadingCoefficientIsPositive()){
+        return DeltaRational(q, delta);
+      }else{
+        return DeltaRational(-q, -delta);
+      }
+    }
+  case kind::EQUAL:
+  case kind::DISTINCT:
+    {
+      Polynomial right = getRight();
+      Monomial firstRight = right.getHead();
+      if(firstRight.isConstant()){
+        DeltaRational c = DeltaRational(firstRight.getConstant().getValue(), 0);
+        Polynomial left = getLeft();
+        if(!left.allIntegralVariables()){
+          return c;
+          //this is a qpolynomial and the sign of the leading
+          //coefficient will not change after the diff below
+        } else{
+          // the polynomial may be a z polynomial in which case
+          // taking the diff is the simplest and obviously correct means
+          Polynomial diff = right.singleton() ? left : left - right.getTail();
+          if(diff.leadingCoefficientIsPositive()){
+            return c;
+          }else{
+            return -c;
+          }
+        }
+      }else{ // The constant is 0 sign cannot change
+        return DeltaRational(0, 0);
+      }
+    }
+    default: Unhandled() << cmpKind;
+  }
+}
+
+std::tuple<Polynomial, Kind, Constant> Comparison::decompose(
+    bool split_constant) const
+{
+  Kind rel = getNode().getKind();
+  if (rel == Kind::NOT)
+  {
+    switch (getNode()[0].getKind())
+    {
+      case kind::LEQ: rel = Kind::GT; break;
+      case kind::LT: rel = Kind::GEQ; break;
+      case kind::EQUAL: rel = Kind::DISTINCT; break;
+      case kind::DISTINCT: rel = Kind::EQUAL; break;
+      case kind::GEQ: rel = Kind::LT; break;
+      case kind::GT: rel = Kind::LEQ; break;
+      default:
+        Assert(false) << "Unsupported relation: " << getNode()[0].getKind();
+    }
+  }
+
+  Polynomial poly = getLeft() - getRight();
+
+  if (!split_constant)
+  {
+    return std::tuple<Polynomial, Kind, Constant>{
+        poly, rel, Constant::mkZero()};
+  }
+
+  Constant right = Constant::mkZero();
+  if (poly.containsConstant())
+  {
+    right = -poly.getHead().getConstant();
+    poly = poly + Polynomial::mkPolynomial(right);
+  }
+
+  Constant lcoeff = poly.getHead().getConstant();
+  if (!lcoeff.isOne())
+  {
+    Constant invlcoeff = lcoeff.inverse();
+    if (lcoeff.isNegative())
+    {
+      switch (rel)
+      {
+        case kind::LEQ: rel = Kind::GEQ; break;
+        case kind::LT: rel = Kind::GT; break;
+        case kind::EQUAL: break;
+        case kind::DISTINCT: break;
+        case kind::GEQ: rel = Kind::LEQ; break;
+        case kind::GT: rel = Kind::LT; break;
+        default: Assert(false) << "Unsupported relation: " << rel;
+      }
+    }
+    poly = poly * invlcoeff;
+    right = right * invlcoeff;
+  }
+
+  return std::tuple<Polynomial, Kind, Constant>{poly, rel, right};
+}
+
+Comparison Comparison::parseNormalForm(TNode n) {
+  Trace("polynomial") << "Comparison::parseNormalForm(" << n << ")";
+  Comparison result(n);
+  Assert(result.isNormalForm());
+  return result;
+}
+
+Node Comparison::toNode(Kind k, const Polynomial& l, const Constant& r) {
+  Assert(isRelationOperator(k));
+  switch(k) {
+  case kind::GEQ:
+  case kind::GT:
+    return NodeManager::currentNM()->mkNode(k, l.getNode(), r.getNode());
+  default: Unhandled() << k;
+  }
+}
+
+Node Comparison::toNode(Kind k, const Polynomial& l, const Polynomial& r) {
+  Assert(isRelationOperator(k));
+  switch(k) {
+  case kind::GEQ:
+  case kind::EQUAL:
+  case kind::GT:
+    return NodeManager::currentNM()->mkNode(k, l.getNode(), r.getNode());
+  case kind::LEQ:
+    return toNode(kind::GEQ, r, l).notNode();
+  case kind::LT:
+    return toNode(kind::GT, r, l).notNode();
+  case kind::DISTINCT:
+    return toNode(kind::EQUAL, r, l).notNode();
+  default:
+    Unreachable();
+  }
+}
+
+bool Comparison::rightIsConstant() const {
+  if(getNode().getKind() == kind::NOT){
+    return getNode()[0][1].getKind() == kind::CONST_RATIONAL;
+  }else{
+    return getNode()[1].getKind() == kind::CONST_RATIONAL;
+  }
+}
+
+size_t Comparison::getComplexity() const{
+  switch(comparisonKind()){
+  case kind::CONST_BOOLEAN: return 1;
+  case kind::LT:
+  case kind::LEQ:
+  case kind::DISTINCT:
+  case kind::EQUAL:
+  case kind::GT:
+  case kind::GEQ:
+    return getLeft().getComplexity() +  getRight().getComplexity();
+  default: Unhandled() << comparisonKind(); return -1;
+  }
+}
+
+Polynomial Comparison::getLeft() const {
+  TNode left;
+  Kind k = comparisonKind();
+  switch(k){
+  case kind::LT:
+  case kind::LEQ:
+  case kind::DISTINCT:
+    left = getNode()[0][0];
+    break;
+  case kind::EQUAL:
+  case kind::GT:
+  case kind::GEQ:
+    left = getNode()[0];
+    break;
+  default: Unhandled() << k;
+  }
+  return Polynomial::parsePolynomial(left);
+}
+
+Polynomial Comparison::getRight() const {
+  TNode right;
+  Kind k = comparisonKind();
+  switch(k){
+  case kind::LT:
+  case kind::LEQ:
+  case kind::DISTINCT:
+    right = getNode()[0][1];
+    break;
+  case kind::EQUAL:
+  case kind::GT:
+  case kind::GEQ:
+    right = getNode()[1];
+    break;
+  default: Unhandled() << k;
+  }
+  return Polynomial::parsePolynomial(right);
+}
+
+// Polynomial Comparison::getLeft() const {
+//   Node n = getNode();
+//   Node left = (n.getKind() == kind::NOT ? n[0]: n)[0];
+//   return Polynomial::parsePolynomial(left);
+// }
+
+// Polynomial Comparison::getRight() const {
+//   Node n = getNode();
+//   Node right = (n.getKind() == kind::NOT ? n[0]: n)[1];
+//   return Polynomial::parsePolynomial(right);
+// }
+
+bool Comparison::isNormalForm() const {
+  Node n = getNode();
+  Kind cmpKind = comparisonKind(n);
+  Trace("nf::tmp") << "isNormalForm " << n << " " << cmpKind << endl;
+  switch(cmpKind){
+  case kind::CONST_BOOLEAN:
+    return true;
+  case kind::GT:
+    return isNormalGT();
+  case kind::GEQ:
+    return isNormalGEQ();
+  case kind::EQUAL:
+    return isNormalEquality();
+  case kind::LT:
+    return isNormalLT();
+  case kind::LEQ:
+    return isNormalLEQ();
+  case kind::DISTINCT:
+    return isNormalDistinct();
+  default:
+    return false;
+  }
+}
+
+/** This must be (> qpolynomial constant) */
+bool Comparison::isNormalGT() const {
+  Node n = getNode();
+  Assert(n.getKind() == kind::GT);
+  if(!rightIsConstant()){
+    return false;
+  }else{
+    Polynomial left = getLeft();
+    if(left.containsConstant()){
+      return false;
+    }else if(!left.leadingCoefficientIsAbsOne()){
+      return false;
+    }else{
+      return !left.isIntegral();
+    }
+  }
+}
+
+/** This must be (not (> qpolynomial constant)) */
+bool Comparison::isNormalLEQ() const {
+  Node n = getNode();
+  Trace("nf::tmp") << "isNormalLEQ " << n << endl;
+  Assert(n.getKind() == kind::NOT);
+  Assert(n[0].getKind() == kind::GT);
+  if(!rightIsConstant()){
+    return false;
+  }else{
+    Polynomial left = getLeft();
+    if(left.containsConstant()){
+      return false;
+    }else if(!left.leadingCoefficientIsAbsOne()){
+      return false;
+    }else{
+      return !left.isIntegral();
+    }
+  }
+}
+
+
+/** This must be (>= qpolynomial constant) or  (>= zpolynomial constant) */
+bool Comparison::isNormalGEQ() const {
+  Node n = getNode();
+  Assert(n.getKind() == kind::GEQ);
+
+  Trace("nf::tmp") << "isNormalGEQ " << n << " " << rightIsConstant() << endl;
+
+  if(!rightIsConstant()){
+    return false;
+  }else{
+    Polynomial left = getLeft();
+    if(left.containsConstant()){
+      return false;
+    }else{
+      if(left.isIntegral()){
+        return left.signNormalizedReducedSum();
+      }else{
+        return left.leadingCoefficientIsAbsOne();
+      }
+    }
+  }
+}
+
+/** This must be (not (>= qpolynomial constant)) or (not (>= zpolynomial constant)) */
+bool Comparison::isNormalLT() const {
+  Node n = getNode();
+  Assert(n.getKind() == kind::NOT);
+  Assert(n[0].getKind() == kind::GEQ);
+
+  if(!rightIsConstant()){
+    return false;
+  }else{
+    Polynomial left = getLeft();
+    if(left.containsConstant()){
+      return false;
+    }else{
+      if(left.isIntegral()){
+        return left.signNormalizedReducedSum();
+      }else{
+        return left.leadingCoefficientIsAbsOne();
+      }
+    }
+  }
+}
+
+
+bool Comparison::isNormalEqualityOrDisequality() const {
+  Polynomial pleft = getLeft();
+
+  if(pleft.numMonomials() == 1){
+    Monomial mleft = pleft.getHead();
+    if(mleft.isConstant()){
+      return false;
+    }else{
+      Polynomial pright = getRight();
+      if(allIntegralVariables()){
+        const Rational& lcoeff = mleft.getConstant().getValue();
+        if(pright.isConstant()){
+          return pright.isIntegral() && lcoeff.isOne();
+        }
+        Polynomial varRight = pright.containsConstant() ? pright.getTail() : pright;
+        if(lcoeff.sgn() <= 0){
+          return false;
+        }else{
+          Integer lcm = lcoeff.getDenominator().lcm(varRight.denominatorLCM());
+          Integer g = lcoeff.getNumerator().gcd(varRight.numeratorGCD());
+          Trace("nf::tmp") << lcm << " " << g << endl;
+          if(!lcm.isOne()){
+            return false;
+          }else if(!g.isOne()){
+            return false;
+          }else{
+            Monomial absMinRight = varRight.selectAbsMinimum();
+            Trace("nf::tmp") << mleft.getNode() << " " << absMinRight.getNode() << endl;
+            if( mleft.absCmp(absMinRight) < 0){
+              return true;
+            }else{
+              return (!(absMinRight.absCmp(mleft)< 0)) && mleft < absMinRight;
+            }
+          }
+        }
+      }else{
+        if(mleft.coefficientIsOne()){
+          Trace("nf::tmp")
+            << "dfklj " << mleft.getNode() << endl
+            << pright.getNode() << endl
+            << pright.variableMonomialAreStrictlyGreater(mleft)
+            << endl;
+          return pright.variableMonomialAreStrictlyGreater(mleft);
+        }else{
+          return false;
+        }
+      }
+    }
+  }else{
+    return false;
+  }
+}
+
+/** This must be (= qvarlist qpolynomial) or (= zmonomial zpolynomial)*/
+bool Comparison::isNormalEquality() const {
+  Assert(getNode().getKind() == kind::EQUAL);
+  return Theory::theoryOf(getNode()[0].getType()) == THEORY_ARITH &&
+         isNormalEqualityOrDisequality();
+}
+
+/**
+ * This must be (not (= qvarlist qpolynomial)) or
+ * (not (= zmonomial zpolynomial)).
+ */
+bool Comparison::isNormalDistinct() const {
+  Assert(getNode().getKind() == kind::NOT);
+  Assert(getNode()[0].getKind() == kind::EQUAL);
+
+  return Theory::theoryOf(getNode()[0][0].getType()) == THEORY_ARITH &&
+         isNormalEqualityOrDisequality();
+}
+
+Node Comparison::mkRatEquality(const Polynomial& p){
+  Assert(!p.isConstant());
+  Assert(!p.allIntegralVariables());
+
+  Monomial minimalVList = p.minimumVariableMonomial();
+  Constant coeffInv = -(minimalVList.getConstant().inverse());
+
+  Polynomial newRight = (p - minimalVList) * coeffInv;
+  Polynomial newLeft(Monomial::mkMonomial(minimalVList.getVarList()));
+
+  return toNode(kind::EQUAL, newLeft, newRight);
+}
+
+Node Comparison::mkRatInequality(Kind k, const Polynomial& p){
+  Assert(k == kind::GEQ || k == kind::GT);
+  Assert(!p.isConstant());
+  Assert(!p.allIntegralVariables());
+
+  SumPair sp = SumPair::mkSumPair(p);
+  Polynomial left = sp.getPolynomial();
+  Constant right = - sp.getConstant();
+
+  Monomial minimalVList = left.getHead();
+  Assert(!minimalVList.isConstant());
+
+  Constant coeffInv = minimalVList.getConstant().inverse().abs();
+  Polynomial newLeft = left * coeffInv;
+  Constant newRight = right * (coeffInv);
+
+  return toNode(k, newLeft, newRight);
+}
+
+Node Comparison::mkIntInequality(Kind k, const Polynomial& p){
+  Assert(kind::GT == k || kind::GEQ == k);
+  Assert(!p.isConstant());
+  Assert(p.allIntegralVariables());
+
+  SumPair sp = SumPair::mkSumPair(p);
+  Polynomial left = sp.getPolynomial();
+  Rational right = - (sp.getConstant().getValue());
+
+
+  Monomial m = left.getHead();
+  Assert(!m.isConstant());
+
+  Integer lcm = left.denominatorLCM();
+  Integer g = left.numeratorGCD();
+  Rational mult(lcm,g);
+
+  Polynomial newLeft = left * mult;
+  Rational rightMult = right * mult;
+
+  bool negateResult = false;
+  if(!newLeft.leadingCoefficientIsPositive()){
+    // multiply by -1
+    // a: left >= right or b: left > right
+    // becomes
+    // a: -left <= -right or b: -left < -right
+    // a: not (-left > -right) or b: (not -left >= -right)
+    newLeft = -newLeft;
+    rightMult = -rightMult;
+    k = (kind::GT == k) ? kind::GEQ : kind::GT;
+    negateResult = true;
+    // the later stages handle:
+    // a: not (-left >= -right + 1) or b: (not -left >= -right)
+  }
+
+  Node result = Node::null();
+  if(rightMult.isIntegral()){
+    if(k == kind::GT){
+      // (> p z)
+      // (>= p (+ z 1))
+      Constant rightMultPlusOne = Constant::mkConstant(rightMult + 1);
+      result = toNode(kind::GEQ, newLeft, rightMultPlusOne);
+    }else{
+      Constant newRight = Constant::mkConstant(rightMult);
+      result = toNode(kind::GEQ, newLeft, newRight);
+    }
+  }else{
+    //(>= l (/ n d))
+    //(>= l (ceil (/ n d)))
+    //This also hold for GT as (ceil (/ n d)) > (/ n d)
+    Integer ceilr = rightMult.ceiling();
+    Constant ceilRight = Constant::mkConstant(ceilr);
+    result = toNode(kind::GEQ, newLeft, ceilRight);
+  }
+  Assert(!result.isNull());
+  if(negateResult){
+    return result.notNode();
+  }else{
+    return result;
+  }
+}
+
+Node Comparison::mkIntEquality(const Polynomial& p){
+  Assert(!p.isConstant());
+  Assert(p.allIntegralVariables());
+
+  SumPair sp = SumPair::mkSumPair(p);
+  Polynomial varPart = sp.getPolynomial();
+  Constant constPart = sp.getConstant();
+
+  Integer lcm = varPart.denominatorLCM();
+  Integer g = varPart.numeratorGCD();
+  Constant mult = Constant::mkConstant(Rational(lcm,g));
+
+  Constant constMult = constPart * mult;
+
+  if(constMult.isIntegral()){
+    Polynomial varPartMult = varPart * mult;
+
+    Monomial m = varPartMult.selectAbsMinimum();
+    bool mIsPositive =  m.getConstant().isPositive();
+
+    Polynomial noM = (varPartMult + (- m)) + Polynomial::mkPolynomial(constMult);
+
+    // m + noM = 0
+    Polynomial newRight = mIsPositive ? -noM : noM;
+    Polynomial newLeft  = mIsPositive ? m  : -m;
+
+    Assert(newRight.isIntegral());
+    return toNode(kind::EQUAL, newLeft, newRight);
+  }else{
+    return mkBoolNode(false);
+  }
+}
+
+Comparison Comparison::mkComparison(Kind k, const Polynomial& l, const Polynomial& r){
+
+  //Make this special case fast for sharing!
+  if((k == kind::EQUAL || k == kind::DISTINCT) && l.isVarList() && r.isVarList()){
+    VarList vLeft = l.asVarList();
+    VarList vRight = r.asVarList();
+
+    if(vLeft == vRight){
+      // return true for equalities and false for disequalities
+      return Comparison(k == kind::EQUAL);
+    }else{
+      Node eqNode = vLeft < vRight ? toNode( kind::EQUAL, l, r) : toNode( kind::EQUAL, r, l);
+      Node forK = (k == kind::DISTINCT) ? eqNode.notNode() : eqNode;
+      return Comparison(forK);
+    }
+  }
+
+  //General case
+  Polynomial diff = l - r;
+  if(diff.isConstant()){
+    bool res = evaluateConstantPredicate(k, diff.asConstant(), Rational(0));
+    return Comparison(res);
+  }else{
+    Node result = Node::null();
+    bool isInteger = diff.allIntegralVariables();
+    switch(k){
+    case kind::EQUAL:
+      result = isInteger ? mkIntEquality(diff) : mkRatEquality(diff);
+      break;
+    case kind::DISTINCT:
+      {
+        Node eq = isInteger ? mkIntEquality(diff) : mkRatEquality(diff);
+        result = eq.notNode();
+      }
+      break;
+    case kind::LEQ:
+    case kind::LT:
+      {
+        Polynomial neg = - diff;
+        Kind negKind = (k == kind::LEQ ? kind::GEQ : kind::GT);
+        result = isInteger ?
+          mkIntInequality(negKind, neg) : mkRatInequality(negKind, neg);
+      }
+      break;
+    case kind::GEQ:
+    case kind::GT:
+      result = isInteger ?
+        mkIntInequality(k, diff) : mkRatInequality(k, diff);
+      break;
+    default: Unhandled() << k;
+    }
+    Assert(!result.isNull());
+    if(result.getKind() == kind::NOT && result[0].getKind() == kind::CONST_BOOLEAN){
+      return Comparison(!(result[0].getConst<bool>()));
+    }else{
+      Comparison cmp(result);
+      Assert(cmp.isNormalForm());
+      return cmp;
+    }
+  }
+}
+
+bool Comparison::isBoolean() const {
+  return getNode().getKind() == kind::CONST_BOOLEAN;
+}
+
+
+bool Comparison::debugIsIntegral() const{
+  return getLeft().isIntegral() && getRight().isIntegral();
+}
+
+Kind Comparison::comparisonKind(TNode literal){
+  switch(literal.getKind()){
+  case kind::CONST_BOOLEAN:
+  case kind::GT:
+  case kind::GEQ:
+  case kind::EQUAL:
+    return literal.getKind();
+  case  kind::NOT:
+    {
+      TNode negatedAtom = literal[0];
+      switch(negatedAtom.getKind()){
+      case kind::GT: //(not (GT x c)) <=> (LEQ x c)
+        return kind::LEQ;
+      case kind::GEQ: //(not (GEQ x c)) <=> (LT x c)
+        return kind::LT;
+      case kind::EQUAL:
+        return kind::DISTINCT;
+      default:
+        return  kind::UNDEFINED_KIND;
+      }
+    }
+  default:
+    return kind::UNDEFINED_KIND;
+  }
+}
+
+
+Node Polynomial::makeAbsCondition(Variable v, Polynomial p){
+  Polynomial zerop = Polynomial::mkZero();
+
+  Polynomial varp = Polynomial::mkPolynomial(v);
+  Comparison pLeq0 = Comparison::mkComparison(kind::LEQ, p, zerop);
+  Comparison negP = Comparison::mkComparison(kind::EQUAL, varp, -p);
+  Comparison posP = Comparison::mkComparison(kind::EQUAL, varp, p);
+
+  Node absCnd = (pLeq0.getNode()).iteNode(negP.getNode(), posP.getNode());
+  return absCnd;
+}
+
+bool Polynomial::isNonlinear() const {
+
+  for(iterator i=begin(), iend =end(); i != iend; ++i){
+    Monomial m = *i;
+    if(m.isNonlinear()){
+      return true;
+    }
+  }
+  return false;
+}
+
+} //namespace arith
+} //namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/normal_form.h b/src/theory/arith/linear/normal_form.h
new file mode 100644 (file)
index 0000000..9656e28
--- /dev/null
@@ -0,0 +1,1466 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__NORMAL_FORM_H
+#define CVC5__THEORY__ARITH__NORMAL_FORM_H
+
+#include <algorithm>
+
+#include "base/output.h"
+#include "expr/node.h"
+#include "expr/node_self_iterator.h"
+#include "theory/arith/delta_rational.h"
+#include "util/rational.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/***********************************************/
+/***************** Normal Form *****************/
+/***********************************************/
+/***********************************************/
+
+/**
+ * Section 1: Languages
+ * The normal form for arithmetic nodes is defined by the language
+ * accepted by the following BNFs with some guard conditions.
+ * (The guard conditions are in Section 3 for completeness.)
+ *
+ * variable := n
+ *   where
+ *     n.isVar() or is foreign
+ *     n.getType() \in {Integer, Real}
+ *
+ * constant := n
+ *   where
+ *     n.getKind() == kind::CONST_RATIONAL
+ *
+ * var_list := variable | (* [variable])
+ *   where
+ *     len [variable] >= 2
+ *     isSorted varOrder [variable]
+ *
+ * monomial := constant | var_list | (* constant' var_list')
+ *   where
+ *     \f$ constant' \not\in {0,1} \f$
+ *
+ * polynomial := monomial' | (+ [monomial])
+ *   where
+ *     len [monomial] >= 2
+ *     isStrictlySorted monoOrder [monomial]
+ *     forall (\x -> x != 0) [monomial]
+ *
+ * rational_cmp := (|><| qpolynomial constant)
+ *   where
+ *     |><| is GEQ, or GT
+ *     not (exists constantMonomial (monomialList qpolynomial))
+ *     (exists realMonomial (monomialList qpolynomial))
+ *     abs(monomialCoefficient (head (monomialList qpolynomial))) == 1
+ *
+ * integer_cmp := (>= zpolynomial constant)
+ *   where
+ *     not (exists constantMonomial (monomialList zpolynomial))
+ *     (forall integerMonomial (monomialList zpolynomial))
+ *     the gcd of all numerators of coefficients is 1
+ *     the denominator of all coefficients and the constant is 1
+ *     the leading coefficient is positive
+ *
+ * rational_eq := (= qvarlist qpolynomial)
+ *   where
+ *     let allMonomials = (cons qvarlist (monomialList zpolynomial))
+ *     let variableMonomials = (drop constantMonomial allMonomials)
+ *     isStrictlySorted variableMonomials
+ *     exists realMonomial variableMonomials
+ *     is not empty qvarlist
+ *
+ * integer_eq := (= zmonomial zpolynomial)
+ *   where
+ *     let allMonomials = (cons zmonomial (monomialList zpolynomial))
+ *     let variableMonomials = (drop constantMonomial allMonomials)
+ *     not (constantMonomial zmonomial)
+ *     (forall integerMonomial allMonomials)
+ *     isStrictlySorted variableMonomials
+ *     the gcd of all numerators of coefficients is 1
+ *     the denominator of all coefficients and the constant is 1
+ *     the coefficient of monomial is positive
+ *     the value of the coefficient of monomial is minimal in variableMonomials
+ *
+ * comparison := TRUE | FALSE
+ *   | rational_cmp | (not rational_cmp)
+ *   | rational_eq | (not rational_eq)
+ *   | integer_cmp | (not integer_cmp)
+ *   | integer_eq | (not integer_eq)
+ *
+ * Normal Form for terms := polynomial
+ * Normal Form for atoms := comparison
+ */
+
+/**
+ * Section 2: Helper Classes
+ * The langauges accepted by each of these defintions
+ * roughly corresponds to one of the following helper classes:
+ *  Variable
+ *  Constant
+ *  VarList
+ *  Monomial
+ *  Polynomial
+ *  Comparison
+ *
+ * Each of the classes obeys the following contracts/design decisions:
+ * -Calling isMember(Node node) on a node returns true iff that node is a
+ *  a member of the language. Note: isMember is O(n).
+ * -Calling isNormalForm() on a helper class object returns true iff that
+ *  helper class currently represents a normal form object.
+ * -If isNormalForm() is false, then this object must have been made
+ *  using a mk*() factory function.
+ * -If isNormalForm() is true, calling getNode() on all of these classes
+ *  returns a node that would be accepted by the corresponding language.
+ *  And if isNormalForm() is false, returns Node::null().
+ * -Each of the classes is immutable.
+ * -Public facing constuctors have a 1-to-1 correspondence with one of
+ *  production rules in the above grammar.
+ * -Public facing constuctors are required to fail in debug mode when the
+ *  guards of the production rule are not strictly met.
+ *  For example: Monomial(Constant(1),VarList(Variable(x))) must fail.
+ * -When a class has a Class parseClass(Node node) function,
+ *  if isMember(node) is true, the function is required to return an instance
+ *  of the helper class, instance, s.t. instance.getNode() == node.
+ *  And if isMember(node) is false, this throws an assertion failure in debug
+ *  mode and has undefined behaviour if not in debug mode.
+ * -Only public facing constructors, parseClass(node), and mk*() functions are
+ *  considered privileged functions for the helper class.
+ * -Only privileged functions may use private constructors, and access
+ *  private data members.
+ * -All non-privileged functions are considered utility functions and
+ *  must use a privileged function in order to create an instance of the class.
+ */
+
+/**
+ * Section 3: Guard Conditions Misc.
+ *
+ *
+ *  variable_order x y =
+ *    if (meta_kind_variable x) and (meta_kind_variable y)
+ *    then node_order x y
+ *    else if (meta_kind_variable x)
+ *    then false
+ *    else if (meta_kind_variable y)
+ *    then true
+ *    else node_order x y
+ *
+ *  var_list_len vl =
+ *    match vl with
+ *       variable -> 1
+ *     | (* [variable]) -> len [variable]
+ *
+ *  order res =
+ *    match res with
+ *       Empty -> (0,Node::null())
+ *     | NonEmpty(vl) -> (var_list_len vl, vl)
+ *
+ *  var_listOrder a b = tuple_cmp (order a) (order b)
+ *
+ *  monomialVarList monomial =
+ *    match monomial with
+ *        constant -> Empty
+ *      | var_list -> NonEmpty(var_list)
+ *      | (* constant' var_list') -> NonEmpty(var_list')
+ *
+ *  monoOrder m0 m1 = var_listOrder (monomialVarList m0) (monomialVarList m1)
+ *
+ *  integerMonomial mono =
+ *    forall varHasTypeInteger (monomialVarList mono)
+ *
+ *  realMonomial mono = not (integerMonomial mono)
+ *
+ *  constantMonomial monomial =
+ *    match monomial with
+ *        constant -> true
+ *      | var_list -> false
+ *      | (* constant' var_list') -> false
+ *
+ *  monomialCoefficient monomial =
+ *    match monomial with
+ *        constant -> constant
+ *      | var_list -> Constant(1)
+ *      | (* constant' var_list') -> constant'
+ *
+ *  monomialList polynomial =
+ *    match polynomial with
+ *        monomial -> monomial::[]
+ *      | (+ [monomial]) -> [monomial]
+ */
+
+/**
+ * A NodeWrapper is a class that is a thinly veiled container of a Node object.
+ */
+class NodeWrapper {
+private:
+  Node node;
+public:
+  NodeWrapper(Node n) : node(n) {}
+  const Node& getNode() const { return node; }
+};/* class NodeWrapper */
+
+
+class Variable : public NodeWrapper {
+public:
+ Variable(Node n) : NodeWrapper(n) { Assert(isMember(getNode())); }
+
+ // TODO: check if it's a theory leaf also
+ static bool isMember(Node n)
+ {
+   Kind k = n.getKind();
+   switch (k)
+   {
+     case kind::CONST_RATIONAL: return false;
+     case kind::INTS_DIVISION:
+     case kind::INTS_MODULUS:
+     case kind::DIVISION:
+     case kind::INTS_DIVISION_TOTAL:
+     case kind::INTS_MODULUS_TOTAL:
+     case kind::DIVISION_TOTAL: return isDivMember(n);
+     case kind::IAND: return isIAndMember(n);
+     case kind::POW2: return isPow2Member(n);
+     case kind::EXPONENTIAL:
+     case kind::SINE:
+     case kind::COSINE:
+     case kind::TANGENT:
+     case kind::COSECANT:
+     case kind::SECANT:
+     case kind::COTANGENT:
+     case kind::ARCSINE:
+     case kind::ARCCOSINE:
+     case kind::ARCTANGENT:
+     case kind::ARCCOSECANT:
+     case kind::ARCSECANT:
+     case kind::ARCCOTANGENT:
+     case kind::SQRT:
+     case kind::PI: return isTranscendentalMember(n);
+     case kind::ABS:
+     case kind::TO_INTEGER:
+       // Treat to_int as a variable; it is replaced in early preprocessing
+       // by a variable.
+       return true;
+     default: return isLeafMember(n);
+   }
+ }
+
+  static bool isLeafMember(Node n);
+  static bool isIAndMember(Node n);
+  static bool isPow2Member(Node n);
+  static bool isDivMember(Node n);
+  bool isDivLike() const{
+    return isDivMember(getNode());
+  }
+  static bool isTranscendentalMember(Node n);
+
+  bool isNormalForm() { return isMember(getNode()); }
+
+  bool isIntegral() const {
+    return getNode().getType().isInteger();
+  }
+
+  bool isMetaKindVariable() const {
+    return getNode().isVar();
+  }
+
+  bool operator<(const Variable& v) const {
+    VariableNodeCmp cmp;
+    return cmp(this->getNode(), v.getNode());
+  }
+
+  struct VariableNodeCmp {
+    static inline int cmp(const Node& n, const Node& m) {
+      if ( n == m ) { return 0; }
+
+      // RAN < real var < int var < non-variable
+
+      bool nIsRAN = n.getKind() == Kind::REAL_ALGEBRAIC_NUMBER;
+      bool mIsRAN = m.getKind() == Kind::REAL_ALGEBRAIC_NUMBER;
+
+      if (mIsRAN != nIsRAN)
+      {
+        return nIsRAN ? -1 : 1;
+      }
+
+      bool nIsInteger = n.getType().isInteger();
+      bool mIsInteger = m.getType().isInteger();
+
+      if(nIsInteger == mIsInteger){
+        bool nIsVariable = n.isVar();
+        bool mIsVariable = m.isVar();
+
+        if(nIsVariable == mIsVariable){
+          if(n < m){
+            return -1;
+          }else{
+            Assert(n != m);
+            return 1;
+          }
+        }else{
+          if(nIsVariable){
+            return -1; // nIsVariable => !mIsVariable
+          }else{
+            return 1; // !nIsVariable => mIsVariable
+          }
+        }
+      }else{
+        Assert(nIsInteger != mIsInteger);
+        if(nIsInteger){
+          return 1; // nIsInteger => !mIsInteger
+        }else{
+          return -1; // !nIsInteger => mIsInteger
+        }
+      }
+    }
+
+    bool operator()(const Node& n, const Node& m) const {
+      return VariableNodeCmp::cmp(n,m) < 0;
+    }
+  };
+
+  bool operator==(const Variable& v) const { return getNode() == v.getNode();}
+
+  size_t getComplexity() const;
+};/* class Variable */
+
+class Constant : public NodeWrapper {
+public:
+ Constant(Node n) : NodeWrapper(n) { Assert(isMember(getNode())); }
+
+ static bool isMember(Node n) { return n.getKind() == kind::CONST_RATIONAL; }
+
+ bool isNormalForm() { return isMember(getNode()); }
+
+ static Constant mkConstant(Node n)
+ {
+   Assert(n.getKind() == kind::CONST_RATIONAL);
+   return Constant(n);
+ }
+
+  static Constant mkConstant(const Rational& rat);
+
+  static Constant mkZero() {
+    return mkConstant(Rational(0));
+  }
+
+  static Constant mkOne() {
+    return mkConstant(Rational(1));
+  }
+
+  const Rational& getValue() const {
+    return getNode().getConst<Rational>();
+  }
+
+  static int absCmp(const Constant& a, const Constant& b);
+  bool isIntegral() const { return getValue().isIntegral(); }
+
+  int sgn() const { return getValue().sgn(); }
+
+  bool isZero() const { return sgn() == 0; }
+  bool isNegative() const { return sgn() < 0; }
+  bool isPositive() const { return sgn() > 0; }
+
+  bool isOne() const { return getValue() == 1; }
+
+  Constant operator*(const Rational& other) const {
+    return mkConstant(getValue() * other);
+  }
+
+  Constant operator*(const Constant& other) const {
+    return mkConstant(getValue() * other.getValue());
+  }
+  Constant operator+(const Constant& other) const {
+    return mkConstant(getValue() + other.getValue());
+  }
+  Constant operator-() const {
+    return mkConstant(-getValue());
+  }
+
+  Constant inverse() const{
+    Assert(!isZero());
+    return mkConstant(getValue().inverse());
+  }
+
+  bool operator<(const Constant& other) const {
+    return getValue() < other.getValue();
+  }
+
+  bool operator==(const Constant& other) const {
+    //Rely on node uniqueness.
+    return getNode() == other.getNode();
+  }
+
+  Constant abs() const {
+    if(isNegative()){
+      return -(*this);
+    }else{
+      return (*this);
+    }
+  }
+
+  uint32_t length() const{
+    Assert(isIntegral());
+    return getValue().getNumerator().length();
+  }
+
+  size_t getComplexity() const;
+
+};/* class Constant */
+
+
+template <class GetNodeIterator>
+inline Node makeNode(Kind k, GetNodeIterator start, GetNodeIterator end) {
+  NodeBuilder nb(k);
+
+  while(start != end) {
+    nb << (*start).getNode();
+    ++start;
+  }
+
+  return Node(nb);
+}/* makeNode<GetNodeIterator>(Kind, iterator, iterator) */
+
+/**
+ * A VarList is a sorted list of variables representing a product.
+ * If the VarList is empty, it represents an empty product or 1.
+ * If the VarList has size 1, it represents a single variable.
+ *
+ * A non-sorted VarList can never be successfully made in debug mode.
+ */
+class VarList : public NodeWrapper {
+private:
+
+  static Node multList(const std::vector<Variable>& list) {
+    Assert(list.size() >= 2);
+
+    return makeNode(kind::NONLINEAR_MULT, list.begin(), list.end());
+  }
+
+  VarList() : NodeWrapper(Node::null()) {}
+
+  VarList(Node n);
+
+  typedef expr::NodeSelfIterator internal_iterator;
+
+  internal_iterator internalBegin() const {
+    if(singleton()){
+      return expr::NodeSelfIterator::self(getNode());
+    }else{
+      return getNode().begin();
+    }
+  }
+
+  internal_iterator internalEnd() const {
+    if(singleton()){
+      return expr::NodeSelfIterator::selfEnd(getNode());
+    }else{
+      return getNode().end();
+    }
+  }
+
+public:
+
+  class iterator {
+  private:
+    internal_iterator d_iter;
+
+  public:
+    /* The following types are required by trait std::iterator_traits */
+
+    /** Iterator tag */
+    using iterator_category = std::forward_iterator_tag;
+
+    /** The type of the item */
+    using value_type = Variable;
+
+    /** The pointer type of the item */
+    using pointer = Variable*;
+
+    /** The reference type of the item */
+    using reference = Variable&;
+
+    /** The type returned when two iterators are subtracted */
+    using difference_type = std::ptrdiff_t;
+
+    /* End of std::iterator_traits required types */
+
+    explicit iterator(internal_iterator i) : d_iter(i) {}
+
+    inline Variable operator*() {
+      return Variable(*d_iter);
+    }
+
+    bool operator==(const iterator& i) {
+      return d_iter == i.d_iter;
+    }
+
+    bool operator!=(const iterator& i) {
+      return d_iter != i.d_iter;
+    }
+
+    iterator operator++() {
+      ++d_iter;
+      return *this;
+    }
+
+    iterator operator++(int) {
+      return iterator(d_iter++);
+    }
+  };
+
+  iterator begin() const {
+    return iterator(internalBegin());
+  }
+
+  iterator end() const {
+    return iterator(internalEnd());
+  }
+
+  Variable getHead() const {
+    Assert(!empty());
+    return *(begin());
+  }
+
+  VarList(Variable v) : NodeWrapper(v.getNode()) {
+    Assert(isSorted(begin(), end()));
+  }
+
+  VarList(const std::vector<Variable>& l) : NodeWrapper(multList(l)) {
+    Assert(l.size() >= 2);
+    Assert(isSorted(begin(), end()));
+  }
+
+  static bool isMember(Node n);
+
+  bool isNormalForm() const {
+    return !empty();
+  }
+
+  static VarList mkEmptyVarList() {
+    return VarList();
+  }
+
+
+  /** There are no restrictions on the size of l */
+  static VarList mkVarList(const std::vector<Variable>& l) {
+    if(l.size() == 0) {
+      return mkEmptyVarList();
+    } else if(l.size() == 1) {
+      return VarList((*l.begin()).getNode());
+    } else {
+      return VarList(l);
+    }
+  }
+
+  bool empty() const { return getNode().isNull(); }
+  bool singleton() const {
+    return !empty() && getNode().getKind() != kind::NONLINEAR_MULT;
+  }
+
+  int size() const {
+    if(singleton())
+      return 1;
+    else
+      return getNode().getNumChildren();
+  }
+
+  static VarList parseVarList(Node n);
+
+  VarList operator*(const VarList& vl) const;
+
+  int cmp(const VarList& vl) const;
+
+  bool operator<(const VarList& vl) const { return cmp(vl) < 0; }
+
+  bool operator==(const VarList& vl) const { return cmp(vl) == 0; }
+
+  bool isIntegral() const {
+    for(iterator i = begin(), e=end(); i != e; ++i ){
+      Variable var = *i;
+      if(!var.isIntegral()){
+        return false;
+      }
+    }
+    return true;
+  }
+  size_t getComplexity() const;
+
+private:
+  bool isSorted(iterator start, iterator end);
+
+};/* class VarList */
+
+
+/** Constructors have side conditions. Use the static mkMonomial functions instead. */ 
+class Monomial : public NodeWrapper {
+private:
+  Constant constant;
+  VarList varList;
+  Monomial(Node n, const Constant& c, const VarList& vl):
+    NodeWrapper(n), constant(c), varList(vl)
+  {
+    Assert(!c.isZero() || vl.empty());
+    Assert(c.isZero() || !vl.empty());
+
+    Assert(!c.isOne() || !multStructured(n));
+  }
+
+  static Node makeMultNode(const Constant& c, const VarList& vl) {
+    Assert(!c.isZero());
+    Assert(!c.isOne());
+    Assert(!vl.empty());
+    return NodeManager::currentNM()->mkNode(kind::MULT, c.getNode(), vl.getNode());
+  }
+
+  static bool multStructured(Node n) {
+    return n.getKind() ==  kind::MULT &&
+      n[0].getKind() == kind::CONST_RATIONAL &&
+      n.getNumChildren() == 2;
+  }
+
+  Monomial(const Constant& c):
+    NodeWrapper(c.getNode()), constant(c), varList(VarList::mkEmptyVarList())
+  { }
+  
+  Monomial(const VarList& vl):
+    NodeWrapper(vl.getNode()), constant(Constant::mkConstant(1)), varList(vl)
+  {
+    Assert(!varList.empty());
+  }
+
+  Monomial(const Constant& c, const VarList& vl):
+    NodeWrapper(makeMultNode(c,vl)), constant(c), varList(vl)
+  {
+    Assert(!c.isZero());
+    Assert(!c.isOne());
+    Assert(!varList.empty());
+
+    Assert(multStructured(getNode()));
+  }
+public:
+  static bool isMember(TNode n);
+
+  /** Makes a monomial with no restrictions on c and vl. */
+  static Monomial mkMonomial(const Constant& c, const VarList& vl);
+
+  /** If vl is empty, this make one. */
+  static Monomial mkMonomial(const VarList& vl);
+
+  static Monomial mkMonomial(const Constant& c){
+    return Monomial(c);
+  }
+  
+  static Monomial mkMonomial(const Variable& v){
+    return Monomial(VarList(v));
+  }
+
+  static Monomial parseMonomial(Node n);
+
+  static Monomial mkZero() {
+    return Monomial(Constant::mkConstant(0));
+  }
+  static Monomial mkOne() {
+    return Monomial(Constant::mkConstant(1));
+  }
+  const Constant& getConstant() const { return constant; }
+  const VarList& getVarList() const { return varList; }
+  
+  bool isConstant() const {
+    return varList.empty();
+  }
+
+  bool isZero() const {
+    return constant.isZero();
+  }
+
+  bool coefficientIsOne() const {
+    return constant.isOne();
+  }
+
+  bool absCoefficientIsOne() const {
+    return coefficientIsOne() || constant.getValue() == -1;
+  }
+
+  bool constantIsPositive() const {
+    return getConstant().isPositive();
+  }
+
+  Monomial operator*(const Rational& q) const;
+  Monomial operator*(const Constant& c) const;
+  Monomial operator*(const Monomial& mono) const;
+
+  Monomial operator-() const{
+    return (*this) * Rational(-1);
+  }
+
+
+  int cmp(const Monomial& mono) const {
+    return getVarList().cmp(mono.getVarList());
+  }
+
+  bool operator<(const Monomial& vl) const {
+    return cmp(vl) < 0;
+  }
+
+  bool operator==(const Monomial& vl) const {
+    return cmp(vl) == 0;
+  }
+
+  static bool isSorted(const std::vector<Monomial>& m) {
+    return std::is_sorted(m.begin(), m.end());
+  }
+
+  static bool isStrictlySorted(const std::vector<Monomial>& m) {
+    return isSorted(m) && std::adjacent_find(m.begin(),m.end()) == m.end();
+  }
+
+  static void sort(std::vector<Monomial>& m);
+  static void combineAdjacentMonomials(std::vector<Monomial>& m);
+
+  /**
+   * The variable product
+   */
+  bool integralVariables() const {
+    return getVarList().isIntegral();
+  }
+
+  /**
+   * The coefficient of the monomial is integral.
+   */
+  bool integralCoefficient() const {
+    return getConstant().isIntegral();
+  }
+
+  /**
+   * A Monomial is an "integral" monomial if the constant is integral.
+   */
+  bool isIntegral() const {
+    return integralCoefficient() && integralVariables();
+  }
+
+  /** Returns true if the VarList is a product of at least 2 Variables.*/
+  bool isNonlinear() const {
+    return getVarList().size() >= 2;
+  }
+
+  /**
+   * Given a sorted list of monomials, this function transforms this
+   * into a strictly sorted list of monomials that does not contain zero.
+   */
+  //static std::vector<Monomial> sumLikeTerms(const std::vector<Monomial>& monos);
+
+  int absCmp(const Monomial& other) const{
+    return getConstant().getValue().absCmp(other.getConstant().getValue());
+  }
+  // bool absLessThan(const Monomial& other) const{
+  //   return getConstant().abs() < other.getConstant().abs();
+  // }
+
+  uint32_t coefficientLength() const{
+    return getConstant().length();
+  }
+
+  void print() const;
+  static void printList(const std::vector<Monomial>& list);
+
+  size_t getComplexity() const;
+};/* class Monomial */
+
+class SumPair;
+class Comparison;;
+
+class Polynomial : public NodeWrapper {
+private:
+  bool d_singleton;
+
+  Polynomial(TNode n) : NodeWrapper(n), d_singleton(Monomial::isMember(n)) {
+    Assert(isMember(getNode()));
+  }
+
+  static Node makePlusNode(const std::vector<Monomial>& m) {
+    Assert(m.size() >= 2);
+
+    return makeNode(kind::ADD, m.begin(), m.end());
+  }
+
+  typedef expr::NodeSelfIterator internal_iterator;
+
+  internal_iterator internalBegin() const {
+    if(singleton()){
+      return expr::NodeSelfIterator::self(getNode());
+    }else{
+      return getNode().begin();
+    }
+  }
+
+  internal_iterator internalEnd() const {
+    if(singleton()){
+      return expr::NodeSelfIterator::selfEnd(getNode());
+    }else{
+      return getNode().end();
+    }
+  }
+
+  bool singleton() const { return d_singleton; }
+
+public:
+  static bool isMember(TNode n);
+
+  class iterator {
+  private:
+    internal_iterator d_iter;
+
+  public:
+    /* The following types are required by trait std::iterator_traits */
+
+    /** Iterator tag */
+    using iterator_category = std::forward_iterator_tag;
+
+    /** The type of the item */
+    using value_type = Monomial;
+
+    /** The pointer type of the item */
+    using pointer = Monomial*;
+
+    /** The reference type of the item */
+    using reference = Monomial&;
+
+    /** The type returned when two iterators are subtracted */
+    using difference_type = std::ptrdiff_t;
+
+    /* End of std::iterator_traits required types */
+
+    explicit iterator(internal_iterator i) : d_iter(i) {}
+
+    inline Monomial operator*() {
+      return Monomial::parseMonomial(*d_iter);
+    }
+
+    bool operator==(const iterator& i) {
+      return d_iter == i.d_iter;
+    }
+
+    bool operator!=(const iterator& i) {
+      return d_iter != i.d_iter;
+    }
+
+    iterator operator++() {
+      ++d_iter;
+      return *this;
+    }
+
+    iterator operator++(int) {
+      return iterator(d_iter++);
+    }
+  };
+
+  iterator begin() const { return iterator(internalBegin()); }
+  iterator end() const {  return iterator(internalEnd()); }
+
+  Polynomial(const Monomial& m):
+    NodeWrapper(m.getNode()), d_singleton(true)
+  {}
+
+  Polynomial(const std::vector<Monomial>& m):
+    NodeWrapper(makePlusNode(m)), d_singleton(false)
+  {
+    Assert(m.size() >= 2);
+    Assert(Monomial::isStrictlySorted(m));
+  }
+
+  static Polynomial mkPolynomial(const Constant& c){
+    return Polynomial(Monomial::mkMonomial(c));
+  }
+
+  static Polynomial mkPolynomial(const Variable& v){
+    return Polynomial(Monomial::mkMonomial(v));
+  }
+
+  static Polynomial mkPolynomial(const std::vector<Monomial>& m) {
+    if(m.size() == 0) {
+      return Polynomial(Monomial::mkZero());
+    } else if(m.size() == 1) {
+      return Polynomial((*m.begin()));
+    } else {
+      return Polynomial(m);
+    }
+  }
+
+  static Polynomial parsePolynomial(Node n) {
+    return Polynomial(n);
+  }
+
+  static Polynomial mkZero() {
+    return Polynomial(Monomial::mkZero());
+  }
+  static Polynomial mkOne() {
+    return Polynomial(Monomial::mkOne());
+  }
+  bool isZero() const {
+    return singleton() && (getHead().isZero());
+  }
+
+  bool isConstant() const {
+    return singleton() && (getHead().isConstant());
+  }
+
+  bool containsConstant() const {
+    return getHead().isConstant();
+  }
+
+  uint32_t size() const{
+    if(singleton()){
+      return 1;
+    }else{
+      Assert(getNode().getKind() == kind::ADD);
+      return getNode().getNumChildren();
+    }
+  }
+
+  Monomial getHead() const {
+    return *(begin());
+  }
+
+  Polynomial getTail() const {
+    Assert(!singleton());
+
+    iterator tailStart = begin();
+    ++tailStart;
+    std::vector<Monomial> subrange;
+    std::copy(tailStart, end(), std::back_inserter(subrange));
+    return mkPolynomial(subrange);
+  }
+
+  Monomial minimumVariableMonomial() const;
+  bool variableMonomialAreStrictlyGreater(const Monomial& m) const;
+
+  void printList() const {
+    if(TraceIsOn("normal-form")){
+      Trace("normal-form") << "start list" << std::endl;
+      for(iterator i = begin(), oend = end(); i != oend; ++i) {
+        const Monomial& m =*i;
+        m.print();
+      }
+      Trace("normal-form") << "end list" << std::endl;
+    }
+  }
+
+  /** A Polynomial is an "integral" polynomial if all of the monomials are integral. */
+  bool allIntegralVariables() const {
+    for(iterator i = begin(), e=end(); i!=e; ++i){
+      if(!(*i).integralVariables()){
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * A Polynomial is an "integral" polynomial if all of the monomials are integral
+   * and all of the coefficients are Integral. */
+  bool isIntegral() const {
+    for(iterator i = begin(), e=end(); i!=e; ++i){
+      if(!(*i).isIntegral()){
+        return false;
+      }
+    }
+    return true;
+  }
+
+  static Polynomial sumPolynomials(const std::vector<Polynomial>& polynomials);
+
+  /** Returns true if the polynomial contains a non-linear monomial.*/
+  bool isNonlinear() const;
+
+  /** Check whether this polynomial is only a single variable. */
+  bool isVariable() const
+  {
+    return singleton() && getHead().getVarList().singleton()
+           && getHead().coefficientIsOne();
+  }
+  /** Return the variable, given that isVariable() holds. */
+  Variable getVariable() const
+  {
+    Assert(isVariable());
+    return getHead().getVarList().getHead();
+  }
+
+  /**
+   * Selects a minimal monomial in the polynomial by the absolute value of
+   * the coefficient.
+   */
+  Monomial selectAbsMinimum() const;
+
+  /** Returns true if the absolute value of the head coefficient is one. */
+  bool leadingCoefficientIsAbsOne() const;
+  bool leadingCoefficientIsPositive() const;
+  bool denominatorLCMIsOne() const;
+  bool numeratorGCDIsOne() const;
+
+  bool signNormalizedReducedSum() const {
+    return leadingCoefficientIsPositive() && denominatorLCMIsOne() && numeratorGCDIsOne();
+  }
+
+  /**
+   * Returns the Least Common Multiple of the denominators of the coefficients
+   * of the monomials.
+   */
+  Integer denominatorLCM() const;
+
+  /**
+   * Returns the GCD of the numerators of the monomials.
+   * Requires this to be an isIntegral() polynomial.
+   */
+  Integer numeratorGCD() const;
+
+  /**
+   * Returns the GCD of the coefficients of the monomials.
+   * Requires this to be an isIntegral() polynomial.
+   */
+  Integer gcd() const;
+
+  /** z must divide all of the coefficients of the polynomial. */
+  Polynomial exactDivide(const Integer& z) const;
+
+  Polynomial operator+(const Polynomial& vl) const;
+  Polynomial operator-(const Polynomial& vl) const;
+  Polynomial operator-() const{
+    return (*this) * Rational(-1);
+  }
+
+  Polynomial operator*(const Rational& q) const;
+  Polynomial operator*(const Constant& c) const;
+  Polynomial operator*(const Monomial& mono) const;
+
+  Polynomial operator*(const Polynomial& poly) const;
+
+  /**
+   * Viewing the integer polynomial as a list [(* coeff_i mono_i)]
+   * The quotient and remainder of p divided by the non-zero integer z is:
+   *   q := [(* floor(coeff_i/z) mono_i )]
+   *   r := [(* rem(coeff_i/z) mono_i)]
+   * computeQR(p,z) returns the node (+ q r).
+   *
+   * q and r are members of the Polynomial class.
+   * For example:
+   * computeQR( p = (+ 5 (* 3 x) (* 8 y)) , z = 2) returns
+   *   (+ (+ 2 x (* 4 y)) (+ 1 x))
+   */
+  static Node computeQR(const Polynomial& p, const Integer& z);
+
+  /** Returns the coefficient associated with the VarList in the polynomial. */
+  Constant getCoefficient(const VarList& vl) const;
+
+  uint32_t maxLength() const{
+    iterator i = begin(), e=end();
+    if( i == e){
+      return 1;
+    }else{
+      uint32_t max = (*i).coefficientLength();
+      ++i;
+      for(; i!=e; ++i){
+        uint32_t curr = (*i).coefficientLength();
+        if(curr > max){
+          max = curr;
+        }
+      }
+      return max;
+    }
+  }
+
+  uint32_t numMonomials() const {
+    if (getNode().getKind() == kind::ADD)
+    {
+      return getNode().getNumChildren();
+    }
+    else if (isZero())
+    {
+      return 0;
+    }
+    else
+    {
+      return 1;
+    }
+  }
+
+  const Rational& asConstant() const{
+    Assert(isConstant());
+    return getNode().getConst<Rational>();
+    //return getHead().getConstant().getValue();
+  }
+
+  bool isVarList() const {
+    if(singleton()){
+      return VarList::isMember(getNode());
+    }else{
+      return false;
+    }
+  }
+
+  VarList asVarList() const {
+    Assert(isVarList());
+    return getHead().getVarList();
+  }
+
+  size_t getComplexity() const;
+
+  friend class SumPair;
+  friend class Comparison;
+
+  /** Returns a node that if asserted ensures v is the abs of this polynomial.*/
+  Node makeAbsCondition(Variable v){
+    return makeAbsCondition(v, *this);
+  }
+
+  /** Returns a node that if asserted ensures v is the abs of p.*/
+  static Node makeAbsCondition(Variable v, Polynomial p);
+
+};/* class Polynomial */
+
+
+/**
+ * SumPair is a utility class that extends polynomials for use in computations.
+ * A SumPair is always a combination of (+ p c) where
+ *  c is a constant and p is a polynomial such that p = 0 or !p.containsConstant().
+ *
+ * These are a useful utility for representing the equation p = c as (+ p -c) where the pair
+ * is known to implicitly be equal to 0.
+ *
+ * SumPairs do not have unique representations due to the potential for p = 0.
+ * This makes them inappropriate for normal forms.
+ */
+class SumPair : public NodeWrapper {
+private:
+  static Node toNode(const Polynomial& p, const Constant& c){
+    return NodeManager::currentNM()->mkNode(
+        kind::ADD, p.getNode(), c.getNode());
+  }
+
+  SumPair(TNode n) : NodeWrapper(n) { Assert(isNormalForm()); }
+
+ public:
+  SumPair(const Polynomial& p):
+    NodeWrapper(toNode(p, Constant::mkConstant(0)))
+  {
+    Assert(isNormalForm());
+  }
+
+  SumPair(const Polynomial& p, const Constant& c):
+    NodeWrapper(toNode(p, c))
+  {
+    Assert(isNormalForm());
+  }
+
+  static bool isMember(TNode n) {
+    if (n.getKind() == kind::ADD && n.getNumChildren() == 2)
+    {
+      if(Constant::isMember(n[1])){
+        if(Polynomial::isMember(n[0])){
+          Polynomial p = Polynomial::parsePolynomial(n[0]);
+          return p.isZero() || (!p.containsConstant());
+        }else{
+          return false;
+        }
+      }else{
+        return false;
+      }
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  bool isNormalForm() const {
+    return isMember(getNode());
+  }
+
+  Polynomial getPolynomial() const {
+    return Polynomial::parsePolynomial(getNode()[0]);
+  }
+
+  Constant getConstant() const {
+    return Constant::mkConstant((getNode())[1]);
+  }
+
+  SumPair operator+(const SumPair& other) const {
+    return SumPair(getPolynomial() + other.getPolynomial(),
+                   getConstant() + other.getConstant());
+  }
+
+  SumPair operator*(const Constant& c) const {
+    return SumPair(getPolynomial() * c, getConstant() * c);
+  }
+
+  SumPair operator-(const SumPair& other) const {
+    return (*this) + (other * Constant::mkConstant(-1));
+  }
+
+  static SumPair mkSumPair(const Polynomial& p);
+
+  static SumPair mkSumPair(const Variable& var){
+    return SumPair(Polynomial::mkPolynomial(var));
+  }
+
+  static SumPair parseSumPair(TNode n){
+    return SumPair(n);
+  }
+
+  bool isIntegral() const{
+    return getConstant().isIntegral() && getPolynomial().isIntegral();
+  }
+
+  bool isConstant() const {
+    return getPolynomial().isZero();
+  }
+
+  bool isZero() const {
+    return getConstant().isZero() && isConstant();
+  }
+
+  uint32_t size() const{
+    return getPolynomial().size();
+  }
+
+  bool isNonlinear() const{
+    return getPolynomial().isNonlinear();
+  }
+
+  /**
+   * Returns the greatest common divisor of gcd(getPolynomial()) and getConstant().
+   * The SumPair must be integral.
+   */
+  Integer gcd() const {
+    Assert(isIntegral());
+    return (getPolynomial().gcd()).gcd(getConstant().getValue().getNumerator());
+  }
+
+  uint32_t maxLength() const {
+    Assert(isIntegral());
+    return std::max(getPolynomial().maxLength(), getConstant().length());
+  }
+
+  static SumPair mkZero() {
+    return SumPair(Polynomial::mkZero(), Constant::mkConstant(0));
+  }
+
+  static Node computeQR(const SumPair& sp, const Integer& div);
+
+};/* class SumPair */
+
+/* class OrderedPolynomialPair { */
+/* private: */
+/*   Polynomial d_first; */
+/*   Polynomial d_second; */
+/* public: */
+/*   OrderedPolynomialPair(const Polynomial& f, const Polynomial& s) */
+/*     : d_first(f), */
+/*       d_second(s) */
+/*   {} */
+
+/*   /\** Returns the first part of the pair. *\/ */
+/*   const Polynomial& getFirst() const { */
+/*     return d_first; */
+/*   } */
+
+/*   /\** Returns the second part of the pair. *\/ */
+/*   const Polynomial& getSecond() const { */
+/*     return d_second; */
+/*   } */
+
+/*   OrderedPolynomialPair operator*(const Constant& c) const; */
+/*   OrderedPolynomialPair operator+(const Polynomial& p) const; */
+
+/*   /\** Returns true if both of the polynomials are constant. *\/ */
+/*   bool isConstant() const; */
+
+/*   /\** */
+/*    * Evaluates an isConstant() ordered pair as if */
+/*    *   (k getFirst() getRight()) */
+/*    *\/ */
+/*   bool evaluateConstant(Kind k) const; */
+
+/*   /\** */
+/*    * Returns the Least Common Multiple of the monomials */
+/*    * on the lefthand side and the constant on the right. */
+/*    *\/ */
+/*   Integer denominatorLCM() const; */
+
+/*   /\** Constructs a SumPair. *\/ */
+/*   SumPair toSumPair() const; */
+
+
+/*   OrderedPolynomialPair divideByGCD() const; */
+/*   OrderedPolynomialPair multiplyConstant(const Constant& c) const; */
+
+/*   /\** */
+/*    * Returns true if all of the variables are integers, */
+/*    * and the coefficients are integers. */
+/*    *\/ */
+/*   bool isIntegral() const; */
+
+/*   /\** Returns true if all of the variables are integers. *\/ */
+/*   bool allIntegralVariables() const { */
+/*     return getFirst().allIntegralVariables() && getSecond().allIntegralVariables(); */
+/*   } */
+/* }; */
+
+class Comparison : public NodeWrapper {
+private:
+
+  static Node toNode(Kind k, const Polynomial& l, const Constant& c);
+  static Node toNode(Kind k, const Polynomial& l, const Polynomial& r);
+
+  Comparison(TNode n);
+
+  /**
+   * Creates a node in normal form equivalent to (= l 0).
+   * All variables in l are integral.
+   */
+  static Node mkIntEquality(const Polynomial& l);
+
+  /**
+   * Creates a comparison equivalent to (k l 0).
+   * k is either GT or GEQ.
+   * All variables in l are integral.
+   */
+  static Node mkIntInequality(Kind k, const Polynomial& l);
+
+  /**
+   * Creates a node equivalent to (= l 0).
+   * It is not the case that all variables in l are integral.
+   */
+  static Node mkRatEquality(const Polynomial& l);
+
+  /**
+   * Creates a comparison equivalent to (k l 0).
+   * k is either GT or GEQ.
+   * It is not the case that all variables in l are integral.
+   */
+  static Node mkRatInequality(Kind k, const Polynomial& l);
+
+public:
+
+  Comparison(bool val) :
+    NodeWrapper(NodeManager::currentNM()->mkConst(val))
+  { }
+
+  /**
+   * Given a literal to TheoryArith return a single kind to
+   * to indicate its underlying structure.
+   * The function returns the following in each case:
+   * - (K left right)           -> K where is either EQUAL, GT, or GEQ
+   * - (CONST_BOOLEAN b)        -> CONST_BOOLEAN
+   * - (NOT (EQUAL left right)) -> DISTINCT
+   * - (NOT (GT left right))    -> LEQ
+   * - (NOT (GEQ left right))   -> LT
+   * If none of these match, it returns UNDEFINED_KIND.
+   */
+  static Kind comparisonKind(TNode literal);
+
+  Kind comparisonKind() const { return comparisonKind(getNode()); }
+
+  static Comparison mkComparison(Kind k, const Polynomial& l, const Polynomial& r);
+
+  /** Returns true if the comparison is a boolean constant. */
+  bool isBoolean() const;
+
+  /**
+   * Returns true if the comparison is either a boolean term,
+   * in integer normal form or mixed normal form.
+   */
+  bool isNormalForm() const;
+
+private:
+  bool isNormalGT() const;
+  bool isNormalGEQ() const;
+
+  bool isNormalLT() const;
+  bool isNormalLEQ() const;
+
+  bool isNormalEquality() const;
+  bool isNormalDistinct() const;
+  bool isNormalEqualityOrDisequality() const;
+
+  bool allIntegralVariables() const {
+    return getLeft().allIntegralVariables() && getRight().allIntegralVariables();
+  }
+  bool rightIsConstant() const;
+
+public:
+  Polynomial getLeft() const;
+  Polynomial getRight() const;
+
+  /* /\** Normal form check if at least one variable is real. *\/ */
+  /* bool isMixedCompareNormalForm() const; */
+
+  /* /\** Normal form check if at least one variable is real. *\/ */
+  /* bool isMixedEqualsNormalForm() const; */
+
+  /* /\** Normal form check is all variables are integer.*\/ */
+  /* bool isIntegerCompareNormalForm() const; */
+
+  /* /\** Normal form check is all variables are integer.*\/ */
+  /* bool isIntegerEqualsNormalForm() const; */
+
+
+  /**
+   * Returns true if all of the variables are integers, the coefficients are integers,
+   * and the right hand coefficient is an integer.
+   */
+  bool debugIsIntegral() const;
+
+  static Comparison parseNormalForm(TNode n);
+
+  inline static bool isNormalAtom(TNode n){
+    Comparison parse = Comparison::parseNormalForm(n);
+    return parse.isNormalForm();
+  }
+
+  size_t getComplexity() const;
+
+  SumPair toSumPair() const;
+
+  Polynomial normalizedVariablePart() const;
+  DeltaRational normalizedDeltaRational() const;
+
+  /**
+   * Transforms a Comparison object into a stronger normal form:
+   *    Polynomial ~Kind~ Constant
+   * 
+   * From the comparison, this method resolved a negation (if present) and
+   * moves everything to the left side.
+   * If split_constant is false, the constant is always zero.
+   * If split_constant is true, the polynomial has no constant term and is
+   * normalized to have leading coefficient one.
+   */
+  std::tuple<Polynomial, Kind, Constant> decompose(
+      bool split_constant = false) const;
+
+};/* class Comparison */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__NORMAL_FORM_H */
diff --git a/src/theory/arith/linear/partial_model.cpp b/src/theory/arith/linear/partial_model.cpp
new file mode 100644 (file)
index 0000000..58beca8
--- /dev/null
@@ -0,0 +1,691 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "base/output.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/normal_form.h"
+#include "theory/arith/linear/partial_model.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+ArithVariables::ArithVariables(context::Context* c,
+                               DeltaComputeCallback deltaComputingFunc)
+    : d_vars(),
+      d_safeAssignment(),
+      d_numberOfVariables(0),
+      d_pool(),
+      d_released(),
+      d_nodeToArithVarMap(),
+      d_boundsQueue(),
+      d_enqueueingBoundCounts(true),
+      d_lbRevertHistory(c, true, LowerBoundCleanUp(this)),
+      d_ubRevertHistory(c, true, UpperBoundCleanUp(this)),
+      d_deltaIsSafe(false),
+      d_delta(-1, 1),
+      d_deltaComputingFunc(deltaComputingFunc)
+{ }
+
+ArithVar ArithVariables::getNumberOfVariables() const {
+  return d_numberOfVariables;
+}
+
+
+bool ArithVariables::hasArithVar(TNode x) const {
+  return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end();
+}
+
+bool ArithVariables::hasNode(ArithVar a) const {
+  return d_vars.isKey(a);
+}
+
+ArithVar ArithVariables::asArithVar(TNode x) const{
+  Assert(hasArithVar(x));
+  Assert((d_nodeToArithVarMap.find(x))->second <= ARITHVAR_SENTINEL);
+  return (d_nodeToArithVarMap.find(x))->second;
+}
+
+Node ArithVariables::asNode(ArithVar a) const{
+  Assert(hasNode(a));
+  return d_vars[a].d_node;
+}
+
+ArithVariables::var_iterator::var_iterator()
+  : d_vars(NULL)
+  , d_wrapped()
+{}
+
+ArithVariables::var_iterator::var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci)
+  : d_vars(vars), d_wrapped(ci)
+{
+  nextInitialized();
+}
+
+ArithVariables::var_iterator& ArithVariables::var_iterator::operator++(){
+  ++d_wrapped;
+  nextInitialized();
+  return *this;
+}
+bool ArithVariables::var_iterator::operator==(const ArithVariables::var_iterator& other) const{
+  return d_wrapped == other.d_wrapped;
+}
+bool ArithVariables::var_iterator::operator!=(const ArithVariables::var_iterator& other) const{
+  return d_wrapped != other.d_wrapped;
+}
+ArithVar ArithVariables::var_iterator::operator*() const{
+  return *d_wrapped;
+}
+
+void ArithVariables::var_iterator::nextInitialized(){
+  VarInfoVec::const_iterator end = d_vars->end();
+  while(d_wrapped != end &&
+        !((*d_vars)[*d_wrapped].initialized())){
+    ++d_wrapped;
+  }
+}
+
+ArithVariables::var_iterator ArithVariables::var_begin() const {
+  return var_iterator(&d_vars, d_vars.begin());
+}
+
+ArithVariables::var_iterator ArithVariables::var_end() const {
+  return var_iterator(&d_vars, d_vars.end());
+}
+bool ArithVariables::isInteger(ArithVar x) const {
+  return d_vars[x].d_type >= ArithType::Integer;
+}
+
+/** Is the assignment to x integral? */
+bool ArithVariables::integralAssignment(ArithVar x) const {
+  return getAssignment(x).isIntegral();
+}
+bool ArithVariables::isAuxiliary(ArithVar x) const {
+  return d_vars[x].d_auxiliary;
+}
+
+bool ArithVariables::isIntegerInput(ArithVar x) const {
+  return isInteger(x) && !isAuxiliary(x);
+}
+
+ArithVariables::VarInfo::VarInfo()
+    : d_var(ARITHVAR_SENTINEL),
+      d_assignment(0),
+      d_lb(NullConstraint),
+      d_ub(NullConstraint),
+      d_cmpAssignmentLB(1),
+      d_cmpAssignmentUB(-1),
+      d_pushCount(0),
+      d_type(ArithType::Unset),
+      d_node(Node::null()),
+      d_auxiliary(false) {}
+
+bool ArithVariables::VarInfo::initialized() const {
+  return d_var != ARITHVAR_SENTINEL;
+}
+
+void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool aux){
+  Assert(!initialized());
+  Assert(d_lb == NullConstraint);
+  Assert(d_ub == NullConstraint);
+  Assert(d_cmpAssignmentLB > 0);
+  Assert(d_cmpAssignmentUB < 0);
+  d_var = v;
+  d_node = n;
+  d_auxiliary = aux;
+
+  if(d_auxiliary){
+    //The type computation is not quite accurate for Rationals that are
+    //integral.
+    //We'll use the isIntegral check from the polynomial package instead.
+    Polynomial p = Polynomial::parsePolynomial(n);
+    d_type = p.isIntegral() ? ArithType::Integer : ArithType::Real;
+  }else{
+    d_type = n.getType().isInteger() ? ArithType::Integer : ArithType::Real;
+  }
+
+  Assert(initialized());
+}
+
+void ArithVariables::VarInfo::uninitialize(){
+  d_var = ARITHVAR_SENTINEL;
+  d_node = Node::null();
+}
+
+bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundsInfo& prev){
+  Assert(initialized());
+  d_assignment = a;
+  int cmpUB = (d_ub == NullConstraint) ? -1 :
+    d_assignment.cmp(d_ub->getValue());
+
+  int cmpLB = (d_lb == NullConstraint) ? 1 :
+    d_assignment.cmp(d_lb->getValue());
+
+  bool lbChanged = cmpLB != d_cmpAssignmentLB &&
+    (cmpLB == 0 || d_cmpAssignmentLB == 0);
+  bool ubChanged = cmpUB != d_cmpAssignmentUB &&
+    (cmpUB == 0 || d_cmpAssignmentUB == 0);
+
+  if(lbChanged || ubChanged){
+    prev = boundsInfo();
+  }
+
+  d_cmpAssignmentUB = cmpUB;
+  d_cmpAssignmentLB = cmpLB;
+  return lbChanged || ubChanged;
+}
+
+void ArithVariables::releaseArithVar(ArithVar v){
+  VarInfo& vi = d_vars.get(v);
+
+  size_t removed CVC5_UNUSED = d_nodeToArithVarMap.erase(vi.d_node);
+  Assert(removed == 1);
+
+  vi.uninitialize();
+
+  if(d_safeAssignment.isKey(v)){
+    d_safeAssignment.remove(v);
+  }
+  if(vi.canBeReclaimed()){
+    d_pool.push_back(v);
+  }else{
+    d_released.push_back(v);
+  }
+}
+
+bool ArithVariables::VarInfo::setUpperBound(ConstraintP ub, BoundsInfo& prev){
+  Assert(initialized());
+  bool wasNull = d_ub == NullConstraint;
+  bool isNull = ub == NullConstraint;
+
+  int cmpUB = isNull ? -1 : d_assignment.cmp(ub->getValue());
+  bool ubChanged = (wasNull != isNull) ||
+    (cmpUB != d_cmpAssignmentUB && (cmpUB == 0 || d_cmpAssignmentUB == 0));
+  if(ubChanged){
+    prev = boundsInfo();
+  }
+  d_ub = ub;
+  d_cmpAssignmentUB = cmpUB;
+  return ubChanged;
+}
+
+bool ArithVariables::VarInfo::setLowerBound(ConstraintP lb, BoundsInfo& prev){
+  Assert(initialized());
+  bool wasNull = d_lb == NullConstraint;
+  bool isNull = lb == NullConstraint;
+
+  int cmpLB = isNull ? 1 : d_assignment.cmp(lb->getValue());
+
+  bool lbChanged = (wasNull != isNull) ||
+    (cmpLB != d_cmpAssignmentLB && (cmpLB == 0 || d_cmpAssignmentLB == 0));
+  if(lbChanged){
+    prev = boundsInfo();
+  }
+  d_lb = lb;
+  d_cmpAssignmentLB = cmpLB;
+  return lbChanged;
+}
+
+BoundCounts ArithVariables::VarInfo::atBoundCounts() const {
+  uint32_t lbIndc = (d_cmpAssignmentLB == 0) ? 1 : 0;
+  uint32_t ubIndc = (d_cmpAssignmentUB == 0) ? 1 : 0;
+  return BoundCounts(lbIndc, ubIndc);
+}
+
+BoundCounts ArithVariables::VarInfo::hasBoundCounts() const {
+  uint32_t lbIndc = (d_lb != NullConstraint) ? 1 : 0;
+  uint32_t ubIndc = (d_ub != NullConstraint) ? 1 : 0;
+  return BoundCounts(lbIndc, ubIndc);
+}
+
+BoundsInfo ArithVariables::VarInfo::boundsInfo() const{
+  return BoundsInfo(atBoundCounts(), hasBoundCounts());
+}
+
+bool ArithVariables::VarInfo::canBeReclaimed() const{
+  return d_pushCount == 0;
+}
+
+bool ArithVariables::canBeReleased(ArithVar v) const{
+  return d_vars[v].canBeReclaimed();
+}
+
+void ArithVariables::attemptToReclaimReleased(){
+  size_t readPos = 0, writePos = 0, N = d_released.size();
+  for(; readPos < N; ++readPos){
+    ArithVar v = d_released[readPos];
+    if(canBeReleased(v)){
+      d_pool.push_back(v);
+    }else{
+      d_released[writePos] = v;
+      writePos++;
+    }
+  }
+  d_released.resize(writePos);
+}
+
+ArithVar ArithVariables::allocateVariable(){
+  if(d_pool.empty()){
+    attemptToReclaimReleased();
+  }
+  bool reclaim = !d_pool.empty();
+
+  ArithVar varX;
+  if(reclaim){
+    varX = d_pool.back();
+    d_pool.pop_back();
+  }else{
+    varX = d_numberOfVariables;
+    ++d_numberOfVariables;
+  }
+  d_vars.set(varX, VarInfo());
+  return varX;
+}
+
+
+const Rational& ArithVariables::getDelta(){
+  if(!d_deltaIsSafe){
+    Rational nextDelta = d_deltaComputingFunc();
+    setDelta(nextDelta);
+  }
+  Assert(d_deltaIsSafe);
+  return d_delta;
+}
+
+bool ArithVariables::boundsAreEqual(ArithVar x) const{
+  if(hasLowerBound(x) && hasUpperBound(x)){
+    return getUpperBound(x) == getLowerBound(x);
+  }else{
+    return false;
+  }
+}
+
+
+std::pair<ConstraintP, ConstraintP> ArithVariables::explainEqualBounds(ArithVar x) const{
+  Assert(boundsAreEqual(x));
+
+  ConstraintP lb = getLowerBoundConstraint(x);
+  ConstraintP ub = getUpperBoundConstraint(x);
+  if(lb->isEquality()){
+    return make_pair(lb, NullConstraint);
+  }else if(ub->isEquality()){
+    return make_pair(ub, NullConstraint);
+  }else{
+    return make_pair(lb, ub);
+  }
+}
+
+void ArithVariables::setAssignment(ArithVar x, const DeltaRational& r){
+  Trace("partial_model") << "pm: updating the assignment to" << x
+                         << " now " << r <<endl;
+  VarInfo& vi = d_vars.get(x);
+  if(!d_safeAssignment.isKey(x)){
+    d_safeAssignment.set(x, vi.d_assignment);
+  }
+  invalidateDelta();
+
+  BoundsInfo prev;
+  if(vi.setAssignment(r, prev)){
+    addToBoundQueue(x, prev);
+  }
+}
+
+void ArithVariables::setAssignment(ArithVar x, const DeltaRational& safe, const DeltaRational& r){
+  Trace("partial_model") << "pm: updating the assignment to" << x
+                         << " now " << r <<endl;
+  if(safe == r){
+    if(d_safeAssignment.isKey(x)){
+      d_safeAssignment.remove(x);
+    }
+  }else{
+    d_safeAssignment.set(x, safe);
+  }
+
+  invalidateDelta();
+  VarInfo& vi = d_vars.get(x);
+  BoundsInfo prev;
+  if(vi.setAssignment(r, prev)){
+    addToBoundQueue(x, prev);
+  }
+}
+
+void ArithVariables::initialize(ArithVar x, Node n, bool aux){
+  VarInfo& vi = d_vars.get(x);
+  vi.initialize(x, n, aux);
+  d_nodeToArithVarMap[n] = x;
+}
+
+ArithVar ArithVariables::allocate(Node n, bool aux){
+  ArithVar v = allocateVariable();
+  initialize(v, n, aux);
+  return v;
+}
+
+// void ArithVariables::initialize(ArithVar x, const DeltaRational& r){
+//   Assert(x == d_mapSize);
+//   Assert(equalSizes());
+//   ++d_mapSize;
+
+//   // Is worth mentioning that this is not strictly necessary, but this maintains the internal invariant
+//   // that when d_assignment is set this gets set.
+//   invalidateDelta();
+//   d_assignment.push_back( r );
+
+//   d_boundRel.push_back(BetweenBounds);
+
+//   d_ubc.push_back(NullConstraint);
+//   d_lbc.push_back(NullConstraint);
+// }
+
+/** Must know that the bound exists both calling this! */
+const DeltaRational& ArithVariables::getUpperBound(ArithVar x) const {
+  Assert(inMaps(x));
+  Assert(hasUpperBound(x));
+
+  return getUpperBoundConstraint(x)->getValue();
+}
+
+const DeltaRational& ArithVariables::getLowerBound(ArithVar x) const {
+  Assert(inMaps(x));
+  Assert(hasLowerBound(x));
+
+  return getLowerBoundConstraint(x)->getValue();
+}
+
+const DeltaRational& ArithVariables::getSafeAssignment(ArithVar x) const{
+  Assert(inMaps(x));
+  if(d_safeAssignment.isKey(x)){
+    return d_safeAssignment[x];
+  }else{
+    return d_vars[x].d_assignment;
+  }
+}
+
+const DeltaRational& ArithVariables::getAssignment(ArithVar x, bool safe) const{
+  Assert(inMaps(x));
+  if(safe && d_safeAssignment.isKey(x)){
+    return d_safeAssignment[x];
+  }else{
+    return d_vars[x].d_assignment;
+  }
+}
+
+const DeltaRational& ArithVariables::getAssignment(ArithVar x) const{
+  Assert(inMaps(x));
+  return d_vars[x].d_assignment;
+}
+
+
+void ArithVariables::setLowerBoundConstraint(ConstraintP c){
+  AssertArgument(c != NullConstraint, "Cannot set a lower bound to NullConstraint.");
+  AssertArgument(c->isEquality() || c->isLowerBound(),
+                 "Constraint type must be set to an equality or UpperBound.");
+  ArithVar x = c->getVariable();
+  Trace("partial_model") << "setLowerBoundConstraint(" << x << ":" << c << ")" << endl;
+  Assert(inMaps(x));
+  Assert(greaterThanLowerBound(x, c->getValue()));
+
+  invalidateDelta();
+  VarInfo& vi = d_vars.get(x);
+  pushLowerBound(vi);
+  BoundsInfo prev;
+  if(vi.setLowerBound(c, prev)){
+    addToBoundQueue(x, prev);
+  }
+}
+
+void ArithVariables::setUpperBoundConstraint(ConstraintP c){
+  AssertArgument(c != NullConstraint, "Cannot set a upper bound to NullConstraint.");
+  AssertArgument(c->isEquality() || c->isUpperBound(),
+                 "Constraint type must be set to an equality or UpperBound.");
+
+  ArithVar x = c->getVariable();
+  Trace("partial_model") << "setUpperBoundConstraint(" << x << ":" << c << ")" << endl;
+  Assert(inMaps(x));
+  Assert(lessThanUpperBound(x, c->getValue()));
+
+  invalidateDelta();
+  VarInfo& vi = d_vars.get(x);
+  pushUpperBound(vi);
+  BoundsInfo prev;
+  if(vi.setUpperBound(c, prev)){
+    addToBoundQueue(x, prev);
+  }
+}
+
+int ArithVariables::cmpToLowerBound(ArithVar x, const DeltaRational& c) const{
+  if(!hasLowerBound(x)){
+    // l = -\intfy
+    // ? c < -\infty |-  _|_
+    return 1;
+  }else{
+    return c.cmp(getLowerBound(x));
+  }
+}
+
+int ArithVariables::cmpToUpperBound(ArithVar x, const DeltaRational& c) const{
+  if(!hasUpperBound(x)){
+    //u = \intfy
+    // ? c > \infty |-  _|_
+    return -1;
+  }else{
+    return c.cmp(getUpperBound(x));
+  }
+}
+
+bool ArithVariables::equalsLowerBound(ArithVar x, const DeltaRational& c){
+  if(!hasLowerBound(x)){
+    return false;
+  }else{
+    return c == getLowerBound(x);
+  }
+}
+bool ArithVariables::equalsUpperBound(ArithVar x, const DeltaRational& c){
+  if(!hasUpperBound(x)){
+    return false;
+  }else{
+    return c == getUpperBound(x);
+  }
+}
+
+bool ArithVariables::hasEitherBound(ArithVar x) const{
+  return hasLowerBound(x) || hasUpperBound(x);
+}
+
+bool ArithVariables::strictlyBelowUpperBound(ArithVar x) const{
+  return d_vars[x].d_cmpAssignmentUB < 0;
+}
+
+bool ArithVariables::strictlyAboveLowerBound(ArithVar x) const{
+  return d_vars[x].d_cmpAssignmentLB > 0;
+}
+
+bool ArithVariables::assignmentIsConsistent(ArithVar x) const{
+  return
+    d_vars[x].d_cmpAssignmentLB >= 0 &&
+    d_vars[x].d_cmpAssignmentUB <= 0;
+}
+
+
+void ArithVariables::clearSafeAssignments(bool revert){
+
+  if(revert && !d_safeAssignment.empty()){
+    invalidateDelta();
+  }
+
+  while(!d_safeAssignment.empty()){
+    ArithVar atBack = d_safeAssignment.back();
+    if(revert){
+      VarInfo& vi = d_vars.get(atBack);
+      BoundsInfo prev;
+      if(vi.setAssignment(d_safeAssignment[atBack], prev)){
+        addToBoundQueue(atBack, prev);
+      }
+    }
+    d_safeAssignment.pop_back();
+  }
+}
+
+void ArithVariables::revertAssignmentChanges(){
+  clearSafeAssignments(true);
+}
+void ArithVariables::commitAssignmentChanges(){
+  clearSafeAssignments(false);
+}
+
+bool ArithVariables::lowerBoundIsZero(ArithVar x){
+  return hasLowerBound(x) && getLowerBound(x).sgn() == 0;
+}
+
+bool ArithVariables::upperBoundIsZero(ArithVar x){
+  return hasUpperBound(x) && getUpperBound(x).sgn() == 0;
+}
+
+void ArithVariables::printEntireModel(std::ostream& out) const{
+  out << "---Printing Model ---" << std::endl;
+  for(var_iterator i = var_begin(), iend = var_end(); i != iend; ++i){
+    printModel(*i, out);
+  }
+  out << "---Done Model ---" << std::endl;
+}
+
+void ArithVariables::printModel(ArithVar x, std::ostream& out) const{
+  out << "model" << x << ": "
+      << asNode(x) << " "
+      << getAssignment(x) << " ";
+  if(!hasLowerBound(x)){
+    out << "no lb ";
+  }else{
+    out << getLowerBound(x) << " ";
+    out << getLowerBoundConstraint(x) << " ";
+  }
+  if(!hasUpperBound(x)){
+    out << "no ub ";
+  }else{
+    out << getUpperBound(x) << " ";
+    out << getUpperBoundConstraint(x) << " ";
+  }
+
+  if(isInteger(x) && !integralAssignment(x)){
+    out << "(not an integer)" << endl;
+  }
+  out << endl;
+}
+
+void ArithVariables::printModel(ArithVar x) const{
+  printModel(x,  Trace("model"));
+}
+
+void ArithVariables::pushUpperBound(VarInfo& vi){
+  ++vi.d_pushCount;
+  d_ubRevertHistory.push_back(make_pair(vi.d_var, vi.d_ub));
+}
+void ArithVariables::pushLowerBound(VarInfo& vi){
+  ++vi.d_pushCount;
+  d_lbRevertHistory.push_back(make_pair(vi.d_var, vi.d_lb));
+}
+
+void ArithVariables::popUpperBound(AVCPair* c){
+  ArithVar x = c->first;
+  VarInfo& vi = d_vars.get(x);
+  BoundsInfo prev;
+  if(vi.setUpperBound(c->second, prev)){
+    addToBoundQueue(x, prev);
+  }
+  --vi.d_pushCount;
+}
+
+void ArithVariables::popLowerBound(AVCPair* c){
+  ArithVar x = c->first;
+  VarInfo& vi = d_vars.get(x);
+  BoundsInfo prev;
+  if(vi.setLowerBound(c->second, prev)){
+    addToBoundQueue(x, prev);
+  }
+  --vi.d_pushCount;
+}
+
+void ArithVariables::addToBoundQueue(ArithVar v, const BoundsInfo& prev){
+  if(d_enqueueingBoundCounts && !d_boundsQueue.isKey(v)){
+    d_boundsQueue.set(v, prev);
+  }
+}
+
+BoundsInfo ArithVariables::selectBoundsInfo(ArithVar v, bool old) const {
+  if(old && d_boundsQueue.isKey(v)){
+    return d_boundsQueue[v];
+  }else{
+    return boundsInfo(v);
+  }
+}
+
+bool ArithVariables::boundsQueueEmpty() const {
+  return d_boundsQueue.empty();
+}
+
+void ArithVariables::processBoundsQueue(BoundUpdateCallback& changed){
+  while(!boundsQueueEmpty()){
+    ArithVar v = d_boundsQueue.back();
+    BoundsInfo prev = d_boundsQueue[v];
+    d_boundsQueue.pop_back();
+    BoundsInfo curr = boundsInfo(v);
+    if(prev != curr){
+      changed(v, prev);
+    }
+  }
+}
+
+void ArithVariables::invalidateDelta() {
+  d_deltaIsSafe = false;
+}
+
+void ArithVariables::setDelta(const Rational& d){
+  d_delta = d;
+  d_deltaIsSafe = true;
+}
+
+void ArithVariables::startQueueingBoundCounts(){
+  d_enqueueingBoundCounts = true;
+}
+void ArithVariables::stopQueueingBoundCounts(){
+  d_enqueueingBoundCounts = false;
+}
+
+bool ArithVariables::inMaps(ArithVar x) const{
+  return x < getNumberOfVariables();
+}
+
+ArithVariables::LowerBoundCleanUp::LowerBoundCleanUp(ArithVariables* pm)
+  : d_pm(pm)
+{}
+void ArithVariables::LowerBoundCleanUp::operator()(AVCPair* p){
+  d_pm->popLowerBound(p);
+}
+
+ArithVariables::UpperBoundCleanUp::UpperBoundCleanUp(ArithVariables* pm)
+  : d_pm(pm)
+{}
+void ArithVariables::UpperBoundCleanUp::operator()(AVCPair* p){
+  d_pm->popUpperBound(p);
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/partial_model.h b/src/theory/arith/linear/partial_model.h
new file mode 100644 (file)
index 0000000..349478b
--- /dev/null
@@ -0,0 +1,421 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Morgan Deters, Aina Niemetz
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Datastructures that track variable by variable information.
+ *
+ * This is a datastructure that tracks variable specific information.
+ * This is partially context dependent to back track upper/lower bounds
+ * and information derived from these.
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__PARTIAL_MODEL_H
+#define CVC5__THEORY__ARITH__PARTIAL_MODEL_H
+
+#include <vector>
+
+#include "context/cdlist.h"
+#include "expr/node.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/arithvar_node_map.h"
+#include "theory/arith/linear/bound_counts.h"
+#include "theory/arith/linear/callbacks.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/arith/delta_rational.h"
+
+namespace cvc5::context {
+class Context;
+}
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/**
+ * (For the moment) the type hierarchy goes as:
+ * Integer <: Real
+ * The type number of a variable is an integer representing the most specific
+ * type of the variable. The possible values of type number are:
+ */
+enum class ArithType {
+  Unset,
+  Real,
+  Integer,
+};
+
+class ArithVariables {
+private:
+
+  class VarInfo {
+    friend class ArithVariables;
+    ArithVar d_var;
+
+    DeltaRational d_assignment;
+    ConstraintP d_lb;
+    ConstraintP d_ub;
+    int d_cmpAssignmentLB;
+    int d_cmpAssignmentUB;
+
+    unsigned d_pushCount;
+    ArithType d_type;
+    Node d_node;
+    bool d_auxiliary;
+
+  public:
+    VarInfo();
+
+    bool setAssignment(const DeltaRational& r, BoundsInfo& prev);
+    bool setLowerBound(ConstraintP c, BoundsInfo& prev);
+    bool setUpperBound(ConstraintP c, BoundsInfo& prev);
+
+    /** Returns true if this VarInfo has been initialized. */
+    bool initialized() const;
+
+    /**
+     * Initializes the VarInfo with the ArithVar index it is associated with,
+     * the node that the variable represents, and whether it is an auxillary
+     * variable.
+     */
+    void initialize(ArithVar v, Node n, bool aux);
+
+    /** Uninitializes the VarInfo. */
+    void uninitialize();
+
+    bool canBeReclaimed() const;
+
+    /** Indicator variables for if the assignment is equal to the upper
+     * and lower bounds. */
+    BoundCounts atBoundCounts() const;
+
+    /** Combination of indicator variables for whether it has upper and
+     * lower bounds.  */
+    BoundCounts hasBoundCounts() const;
+
+    /** Stores both atBoundCounts() and hasBoundCounts().  */
+    BoundsInfo boundsInfo() const;
+  };
+
+  /**Maps from ArithVar -> VarInfo */
+  typedef DenseMap<VarInfo> VarInfoVec;
+
+  /** This maps an ArithVar to its Variable information.*/
+  VarInfoVec d_vars;
+
+  /** Partial Map from Arithvar -> PreviousAssignment */
+  DenseMap<DeltaRational> d_safeAssignment;
+
+  /** if d_vars.isKey(x), then x < d_numberOfVariables */
+  ArithVar d_numberOfVariables;
+
+  /** [0, d_numberOfVariables) \intersect d_vars.keys == d_pool */
+  // Everything in the pool is fair game.
+  // There must be NO outstanding assertions
+  std::vector<ArithVar> d_pool;
+  std::vector<ArithVar> d_released;
+  //std::list<ArithVar>::iterator d_releasedIterator;
+
+  // Reverse Map from Node to ArithVar
+  // Inverse of d_vars[x].d_node
+  NodeToArithVarMap d_nodeToArithVarMap;
+
+
+  /** The queue of constraints where the assignment is at the bound.*/
+  DenseMap<BoundsInfo> d_boundsQueue;
+
+  /**
+   * If this is true, record the incoming changes to the bound information.
+   * If this is false, the responsibility of recording the changes is
+   * LinearEqualities's.
+   */
+  bool d_enqueueingBoundCounts;
+
+ public:
+
+  /** Returns the number of variables. */
+  ArithVar getNumberOfVariables() const;
+
+  /** Returns true if the node has an associated variables. */
+  bool hasArithVar(TNode x) const;
+
+  /** Returns true if the variable has a defining node. */
+  bool hasNode(ArithVar a) const;
+
+  /** Returns the ArithVar associated with a node. */
+  ArithVar asArithVar(TNode x) const;
+
+  /** Returns the node associated with an ArithVar. */
+  Node asNode(ArithVar a) const;
+
+  /** Allocates a freshly allocated variables. */
+  ArithVar allocateVariable();
+
+  class var_iterator {
+  private:
+    const VarInfoVec* d_vars;
+    VarInfoVec::const_iterator d_wrapped;
+  public:
+    var_iterator();
+    var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci);
+    var_iterator& operator++();
+
+    bool operator==(const var_iterator& other) const;
+    bool operator!=(const var_iterator& other) const;
+    ArithVar operator*() const;
+
+  private:
+    void nextInitialized();
+  };
+
+  var_iterator var_begin() const;
+  var_iterator var_end() const;
+
+
+  bool canBeReleased(ArithVar v) const;
+  void releaseArithVar(ArithVar v);
+  void attemptToReclaimReleased();
+
+  /** Is this variable guaranteed to have an integer assignment?
+   * (Should agree with the type system.) */
+  bool isInteger(ArithVar x) const;
+
+  /** Is the assignment to x integral? */
+  bool integralAssignment(ArithVar x) const;
+
+  /* Is this variable defined as a linear sum of other variables? */
+  bool isAuxiliary(ArithVar x) const;
+
+  /* Is the variable both input and not auxiliary? */
+  bool isIntegerInput(ArithVar x) const;
+
+ private:
+
+  typedef std::pair<ArithVar, ConstraintP> AVCPair;
+  class LowerBoundCleanUp {
+  private:
+    ArithVariables* d_pm;
+  public:
+    LowerBoundCleanUp(ArithVariables* pm);
+    void operator()(AVCPair* restore);
+  };
+
+  class UpperBoundCleanUp {
+  private:
+    ArithVariables* d_pm;
+  public:
+    UpperBoundCleanUp(ArithVariables* pm);
+    void operator()(AVCPair* restore);
+  };
+
+  typedef context::CDList<AVCPair, LowerBoundCleanUp> LBReverts;
+  LBReverts d_lbRevertHistory;
+
+  typedef context::CDList<AVCPair, UpperBoundCleanUp> UBReverts;
+  UBReverts d_ubRevertHistory;
+
+  void pushUpperBound(VarInfo&);
+  void popUpperBound(AVCPair*);
+  void pushLowerBound(VarInfo&);
+  void popLowerBound(AVCPair*);
+
+  // This is true when setDelta() is called, until invalidateDelta is called
+  bool d_deltaIsSafe;
+  // Cache of a value of delta to ensure a total order.
+  Rational d_delta;
+  // Function to call if the value of delta needs to be recomputed.
+  DeltaComputeCallback d_deltaComputingFunc;
+
+
+public:
+ ArithVariables(context::Context* c, DeltaComputeCallback deltaComputation);
+
+ /**
+  * This sets the lower bound for a variable in the current context.
+  * This must be stronger the previous constraint.
+  */
+ void setLowerBoundConstraint(ConstraintP lb);
+
+ /**
+  * This sets the upper bound for a variable in the current context.
+  * This must be stronger the previous constraint.
+  */
+ void setUpperBoundConstraint(ConstraintP ub);
+
+ /** Returns the constraint for the upper bound of a variable. */
+ inline ConstraintP getUpperBoundConstraint(ArithVar x) const
+ {
+   return d_vars[x].d_ub;
+ }
+  /** Returns the constraint for the lower bound of a variable. */
+  inline ConstraintP getLowerBoundConstraint(ArithVar x) const{
+    return d_vars[x].d_lb;
+  }
+
+  /* Initializes a variable to a safe value.*/
+  void initialize(ArithVar x, Node n, bool aux);
+
+  ArithVar allocate(Node n, bool aux = false);
+
+  /* Gets the last assignment to a variable that is known to be consistent. */
+  const DeltaRational& getSafeAssignment(ArithVar x) const;
+  const DeltaRational& getAssignment(ArithVar x, bool safe) const;
+
+  /* Reverts all variable assignments to their safe values. */
+  void revertAssignmentChanges();
+
+  /* Commits all variables assignments as safe.*/
+  void commitAssignmentChanges();
+
+
+  bool lowerBoundIsZero(ArithVar x);
+  bool upperBoundIsZero(ArithVar x);
+
+  bool boundsAreEqual(ArithVar x) const;
+
+  /* Sets an unsafe variable assignment */
+  void setAssignment(ArithVar x, const DeltaRational& r);
+  void setAssignment(ArithVar x, const DeltaRational& safe, const DeltaRational& r);
+
+
+  /** Must know that the bound exists before calling this! */
+  const DeltaRational& getUpperBound(ArithVar x) const;
+  const DeltaRational& getLowerBound(ArithVar x) const;
+  const DeltaRational& getAssignment(ArithVar x) const;
+
+
+  bool equalsLowerBound(ArithVar x, const DeltaRational& c);
+  bool equalsUpperBound(ArithVar x, const DeltaRational& c);
+
+  /**
+   * If lowerbound > - \infty:
+   *   return getAssignment(x).cmp(getLowerBound(x))
+   * If lowerbound = - \infty:
+   *   return 1
+   */
+  int cmpToLowerBound(ArithVar x, const DeltaRational& c) const;
+
+  inline bool strictlyLessThanLowerBound(ArithVar x, const DeltaRational& c) const{
+    return cmpToLowerBound(x, c) < 0;
+  }
+  inline bool lessThanLowerBound(ArithVar x, const DeltaRational& c) const{
+    return cmpToLowerBound(x, c) <= 0;
+  }
+
+  inline bool strictlyGreaterThanLowerBound(ArithVar x, const DeltaRational& c) const{
+    return cmpToLowerBound(x, c) > 0;
+  }
+
+  inline bool greaterThanLowerBound(ArithVar x, const DeltaRational& c) const{
+    return cmpToLowerBound(x, c) >= 0;
+  }
+  /**
+   * If upperbound < \infty:
+   *   return getAssignment(x).cmp(getUpperBound(x))
+   * If upperbound = \infty:
+   *   return -1
+   */
+  int cmpToUpperBound(ArithVar x, const DeltaRational& c) const;
+
+  inline bool strictlyLessThanUpperBound(ArithVar x, const DeltaRational& c) const{
+    return cmpToUpperBound(x, c) < 0;
+  }
+
+  inline bool lessThanUpperBound(ArithVar x, const DeltaRational& c) const{
+    return cmpToUpperBound(x, c) <= 0;
+  }
+
+  inline bool strictlyGreaterThanUpperBound(ArithVar x, const DeltaRational& c) const{
+    return cmpToUpperBound(x, c) > 0;
+  }
+
+  inline bool greaterThanUpperBound(ArithVar x, const DeltaRational& c) const{
+    return cmpToUpperBound(x, c) >= 0;
+  }
+
+  inline int cmpAssignmentLowerBound(ArithVar x) const{
+    return d_vars[x].d_cmpAssignmentLB;
+  }
+  inline int cmpAssignmentUpperBound(ArithVar x) const{
+    return d_vars[x].d_cmpAssignmentUB;
+  }
+
+  inline BoundCounts atBoundCounts(ArithVar x) const {
+    return d_vars[x].atBoundCounts();
+  }
+  inline BoundCounts hasBoundCounts(ArithVar x) const {
+    return d_vars[x].hasBoundCounts();
+  }
+  inline BoundsInfo boundsInfo(ArithVar x) const{
+    return d_vars[x].boundsInfo();
+  }
+
+  bool strictlyBelowUpperBound(ArithVar x) const;
+  bool strictlyAboveLowerBound(ArithVar x) const;
+  bool assignmentIsConsistent(ArithVar x) const;
+
+  void printModel(ArithVar x, std::ostream& out) const;
+  void printModel(ArithVar x) const;
+
+  /** returns true iff x has both a lower and upper bound. */
+  bool hasEitherBound(ArithVar x) const;
+  inline bool hasLowerBound(ArithVar x) const{
+    return d_vars[x].d_lb != NullConstraint;
+  }
+  inline bool hasUpperBound(ArithVar x) const{
+    return d_vars[x].d_ub != NullConstraint;
+  }
+
+  const Rational& getDelta();
+
+  void invalidateDelta();
+
+  void setDelta(const Rational& d);
+
+  void startQueueingBoundCounts();
+  void stopQueueingBoundCounts();
+  void addToBoundQueue(ArithVar v, const BoundsInfo& prev);
+
+  BoundsInfo selectBoundsInfo(ArithVar v, bool old) const;
+
+  bool boundsQueueEmpty() const;
+  void processBoundsQueue(BoundUpdateCallback& changed);
+
+  void printEntireModel(std::ostream& out) const;
+
+
+  /**
+   * Precondition: assumes boundsAreEqual(x).
+   * If the either the lower/ upper bound is an equality, eq,
+   * this returns make_pair(eq, NullConstraint).
+   * Otherwise, this returns make_pair(lb, ub).
+   */
+  std::pair<ConstraintP, ConstraintP> explainEqualBounds(ArithVar x) const;
+
+private:
+
+  /**
+   * This function implements the mostly identical:
+   * revertAssignmentChanges() and commitAssignmentChanges().
+   */
+  void clearSafeAssignments(bool revert);
+
+  bool debugEqualSizes();
+
+  bool inMaps(ArithVar x) const;
+
+};/* class ArithVariables */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__PARTIAL_MODEL_H */
diff --git a/src/theory/arith/linear/simplex.cpp b/src/theory/arith/linear/simplex.cpp
new file mode 100644 (file)
index 0000000..6e20ccc
--- /dev/null
@@ -0,0 +1,290 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ */
+
+#include "theory/arith/linear/simplex.h"
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "options/smt_options.h"
+#include "smt/env.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/tableau.h"
+#include "util/statistics_value.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+SimplexDecisionProcedure::SimplexDecisionProcedure(
+    Env& env,
+    LinearEqualityModule& linEq,
+    ErrorSet& errors,
+    RaiseConflict conflictChannel,
+    TempVarMalloc tvmalloc)
+    : EnvObj(env),
+      d_pivots(0),
+      d_conflictVariables(),
+      d_linEq(linEq),
+      d_variables(d_linEq.getVariables()),
+      d_tableau(d_linEq.getTableau()),
+      d_errorSet(errors),
+      d_numVariables(0),
+      d_conflictChannel(conflictChannel),
+      d_conflictBuilder(NULL),
+      d_arithVarMalloc(tvmalloc),
+      d_errorSize(0),
+      d_zero(0),
+      d_posOne(1),
+      d_negOne(-1)
+{
+  d_heuristicRule = options().arith.arithErrorSelectionRule;
+  d_errorSet.setSelectionRule(d_heuristicRule);
+  d_conflictBuilder = new FarkasConflictBuilder(options().smt.produceProofs);
+}
+
+SimplexDecisionProcedure::~SimplexDecisionProcedure(){
+  delete d_conflictBuilder;
+}
+
+
+bool SimplexDecisionProcedure::standardProcessSignals(TimerStat &timer, IntStat& conflicts) {
+  TimerStat::CodeTimer codeTimer(timer);
+  Assert(d_conflictVariables.empty());
+
+  while(d_errorSet.moreSignals()){
+    ArithVar curr = d_errorSet.topSignal();
+    if(d_tableau.isBasic(curr) && !d_variables.assignmentIsConsistent(curr)){
+      Assert(d_linEq.basicIsTracked(curr));
+
+      if(!d_conflictVariables.isMember(curr) && checkBasicForConflict(curr)){
+
+        Trace("recentlyViolated")
+          << "It worked? "
+          << conflicts.get()
+          << " " << curr
+          << " "  << checkBasicForConflict(curr) << endl;
+        reportConflict(curr);
+        ++conflicts;
+      }
+    }
+    // Pop signal afterwards in case d_linEq.trackVariable(curr);
+    // is needed for for the ErrorSet
+    d_errorSet.popSignal();
+  }
+  d_errorSize = d_errorSet.errorSize();
+
+  Assert(d_errorSet.noSignals());
+  return !d_conflictVariables.empty();
+}
+
+/** Reports a conflict to on the output channel. */
+void SimplexDecisionProcedure::reportConflict(ArithVar basic){
+  Assert(!d_conflictVariables.isMember(basic));
+  Assert(checkBasicForConflict(basic));
+
+  ConstraintCP conflicted = generateConflictForBasic(basic);
+  Assert(conflicted != NullConstraint);
+  d_conflictChannel.raiseConflict(conflicted, InferenceId::ARITH_CONF_SIMPLEX);
+
+  d_conflictVariables.add(basic);
+}
+
+ConstraintCP SimplexDecisionProcedure::generateConflictForBasic(ArithVar basic) const {
+  Assert(d_tableau.isBasic(basic));
+  Assert(checkBasicForConflict(basic));
+
+  if(d_variables.cmpAssignmentLowerBound(basic) < 0){
+    Assert(d_linEq.nonbasicsAtUpperBounds(basic));
+    return d_linEq.generateConflictBelowLowerBound(basic, *d_conflictBuilder);
+  }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){
+    Assert(d_linEq.nonbasicsAtLowerBounds(basic));
+    return d_linEq.generateConflictAboveUpperBound(basic, *d_conflictBuilder);
+  }else{
+    Unreachable();
+    return NullConstraint;
+  }
+}
+bool SimplexDecisionProcedure::maybeGenerateConflictForBasic(ArithVar basic) const {
+  if(checkBasicForConflict(basic)){
+    ConstraintCP conflicted = generateConflictForBasic(basic);
+    d_conflictChannel.raiseConflict(conflicted, InferenceId::UNKNOWN);
+    return true;
+  }else{
+    return false;
+  }
+}
+
+bool SimplexDecisionProcedure::checkBasicForConflict(ArithVar basic) const {
+  Assert(d_tableau.isBasic(basic));
+  Assert(d_linEq.basicIsTracked(basic));
+
+  if(d_variables.cmpAssignmentLowerBound(basic) < 0){
+    if(d_linEq.nonbasicsAtUpperBounds(basic)){
+      return true;
+    }
+  }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){
+    if(d_linEq.nonbasicsAtLowerBounds(basic)){
+      return true;
+    }
+  }
+  return false;
+}
+
+void SimplexDecisionProcedure::tearDownInfeasiblityFunction(TimerStat& timer, ArithVar tmp){
+  TimerStat::CodeTimer codeTimer(timer);
+  Assert(tmp != ARITHVAR_SENTINEL);
+  Assert(d_tableau.isBasic(tmp));
+
+  RowIndex ri = d_tableau.basicToRowIndex(tmp);
+  d_linEq.stopTrackingRowIndex(ri);
+  d_tableau.removeBasicRow(tmp);
+  releaseVariable(tmp);
+}
+
+void SimplexDecisionProcedure::shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped){
+  TimerStat::CodeTimer codeTimer(timer);
+  for(ArithVarVec::const_iterator i=dropped.begin(), i_end = dropped.end(); i != i_end; ++i){
+    ArithVar back = *i;
+
+    int focusSgn = d_errorSet.focusSgn(back);
+    Rational chg(-focusSgn);
+
+    d_linEq.substitutePlusTimesConstant(inf, back, chg);
+  }
+}
+
+void SimplexDecisionProcedure::adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges){
+  TimerStat::CodeTimer codeTimer(timer);
+  for(AVIntPairVec::const_iterator i=focusChanges.begin(), i_end = focusChanges.end(); i != i_end; ++i){
+    ArithVar v = (*i).first;
+    int focusChange = (*i).second;
+
+    Rational chg(focusChange);
+    if(d_tableau.isBasic(v)){
+      d_linEq.substitutePlusTimesConstant(inf, v, chg);
+    }else{
+      d_linEq.directlyAddToCoefficient(inf, v, chg);
+    }
+  }
+}
+
+void SimplexDecisionProcedure::addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e){
+  AVIntPairVec justE;
+  int sgn  = d_errorSet.getSgn(e);
+  justE.push_back(make_pair(e, sgn));
+  adjustInfeasFunc(timer, inf, justE);
+}
+
+void SimplexDecisionProcedure::removeFromInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e){
+  AVIntPairVec justE;
+  int opSgn  = -d_errorSet.getSgn(e);
+  justE.push_back(make_pair(e, opSgn));
+  adjustInfeasFunc(timer, inf, justE);
+}
+
+ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set){
+  Trace("constructInfeasiblityFunction") << "constructInfeasiblityFunction start" << endl;
+
+  TimerStat::CodeTimer codeTimer(timer);
+  Assert(!d_errorSet.focusEmpty());
+  Assert(debugIsASet(set));
+
+  ArithVar inf = requestVariable();
+  Assert(inf != ARITHVAR_SENTINEL);
+
+  std::vector<Rational> coeffs;
+  std::vector<ArithVar> variables;
+
+  for(ArithVarVec::const_iterator iter = set.begin(), iend = set.end(); iter != iend; ++iter){
+    ArithVar e = *iter;
+
+    Assert(d_tableau.isBasic(e));
+    Assert(!d_variables.assignmentIsConsistent(e));
+
+    int sgn = d_errorSet.getSgn(e);
+    Assert(sgn == -1 || sgn == 1);
+    const Rational& violatedCoeff = sgn < 0 ? d_negOne : d_posOne;
+    coeffs.push_back(violatedCoeff);
+    variables.push_back(e);
+
+    Trace("constructInfeasiblityFunction") << violatedCoeff << " " << e << endl;
+
+  }
+  d_tableau.addRow(inf, coeffs, variables);
+  DeltaRational newAssignment = d_linEq.computeRowValue(inf, false);
+  d_variables.setAssignment(inf, newAssignment);
+
+  //d_linEq.trackVariable(inf);
+  d_linEq.trackRowIndex(d_tableau.basicToRowIndex(inf));
+
+  Trace("constructInfeasiblityFunction") << inf << " " << newAssignment << endl;
+  Trace("constructInfeasiblityFunction") << "constructInfeasiblityFunction done" << endl;
+  return inf;
+}
+
+ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer){
+  ArithVarVec inError;
+  d_errorSet.pushFocusInto(inError);
+  return constructInfeasiblityFunction(timer, inError);
+}
+
+ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, ArithVar e){
+  ArithVarVec justE;
+  justE.push_back(e);
+  return constructInfeasiblityFunction(timer, justE);
+}
+
+void SimplexDecisionProcedure::addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic){
+  pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
+  sgns[p].push_back(basic);
+}
+
+void SimplexDecisionProcedure::addRowSgns(sgn_table& sgns, ArithVar basic, int norm){
+  for(Tableau::RowIterator i = d_tableau.basicRowIterator(basic); !i.atEnd(); ++i){
+    const Tableau::Entry& entry = *i;
+    ArithVar v = entry.getColVar();
+    int sgn = (entry.getCoefficient().sgn());
+    addSgn(sgns, v, norm * sgn, basic);
+  }
+}
+
+ArithVar SimplexDecisionProcedure::find_basic_in_sgns(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m, bool inside){
+  pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
+  sgn_table::const_iterator i = sgns.find(p);
+
+  if(i != sgns.end()){
+    const ArithVarVec& vec = (*i).second;
+    for(ArithVarVec::const_iterator viter = vec.begin(), vend = vec.end(); viter != vend; ++viter){
+      ArithVar curr = *viter;
+      if(inside == m.isMember(curr)){
+        return curr;
+      }
+    }
+  }
+  return ARITHVAR_SENTINEL;
+}
+
+SimplexDecisionProcedure::sgn_table::const_iterator SimplexDecisionProcedure::find_sgns(const sgn_table& sgns, ArithVar col, int sgn){
+  pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
+  return sgns.find(p);
+}
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/simplex.h b/src/theory/arith/linear/simplex.h
new file mode 100644 (file)
index 0000000..e739ac8
--- /dev/null
@@ -0,0 +1,235 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ *
+ * This implements the Simplex module for the Simpelx for DPLL(T) decision
+ * procedure.
+ * See the Simplex for DPLL(T) technical report for more background.(citation?)
+ * This shares with the theory a Tableau, and a PartialModel that:
+ *  - satisfies the equalities in the Tableau, and
+ *  - the assignment for the non-basic variables satisfies their bounds.
+ * This is required to either produce a conflict or satisifying PartialModel.
+ * Further, we require being told when a basic variable updates its value.
+ *
+ * During the Simplex search we maintain a queue of variables.
+ * The queue is required to contain all of the basic variables that voilate
+ * their bounds.
+ * As elimination from the queue is more efficient to be done lazily,
+ * we do not maintain that the queue of variables needs to be only basic
+ * variables or only variables that satisfy their bounds.
+ *
+ * The simplex procedure roughly follows Alberto's thesis. (citation?)
+ * There is one round of selecting using a heuristic pivoting rule.
+ * (See PreferenceFunction Documentation for the available options.)
+ * The non-basic variable is the one that appears in the fewest pivots.
+ * (Bruno says that Leonardo invented this first.)
+ * After this, Bland's pivot rule is invoked.
+ *
+ * During this proccess, we periodically inspect the queue of variables to
+ * 1) remove now extraneous extries,
+ * 2) detect conflicts that are "waiting" on the queue but may not be detected
+ *    by the current queue heuristics, and
+ * 3) detect multiple conflicts.
+ *
+ * Conflicts are greedily slackened to use the weakest bounds that still
+ * produce the conflict.
+ *
+ * Extra things tracked atm: (Subject to change at Tim's whims)
+ * - A superset of all of the newly pivoted variables.
+ * - A queue of additional conflicts that were discovered by Simplex.
+ *   These are theory valid and are currently turned into lemmas
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <unordered_map>
+
+#include "options/arith_options.h"
+#include "smt/env_obj.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/partial_model.h"
+#include "util/dense_map.h"
+#include "util/result.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class ErrorSet;
+class LinearEqualityModule;
+class Tableau;
+
+class SimplexDecisionProcedure : protected EnvObj
+{
+ protected:
+  typedef std::vector< std::pair<ArithVar, int> > AVIntPairVec;
+
+  /** Pivot count of the current round of pivoting. */
+  uint32_t d_pivots;
+
+  /** The set of variables that are in conflict in this round. */
+  DenseSet d_conflictVariables;
+
+  /** The rule to use for heuristic selection mode. */
+  options::ErrorSelectionRule d_heuristicRule;
+
+  /** Linear equality module. */
+  LinearEqualityModule& d_linEq;
+
+  /**
+   * Manages information about the assignment and upper and lower bounds on
+   * variables.
+   * Partial model matches that in LinearEqualityModule.
+   */
+  ArithVariables& d_variables;
+
+  /**
+   * Stores the linear equalities used by Simplex.
+   * Tableau from the LinearEquality module.
+   */
+  Tableau& d_tableau;
+
+  /** Contains a superset of the basic variables in violation of their bounds. */
+  ErrorSet& d_errorSet;
+
+  /** Number of variables in the system. This is used for tuning heuristics. */
+  ArithVar d_numVariables;
+
+  /** This is the call back channel for Simplex to report conflicts. */
+  RaiseConflict d_conflictChannel;
+
+  /** This is the call back channel for Simplex to report conflicts. */
+  FarkasConflictBuilder* d_conflictBuilder;
+
+  /** Used for requesting d_opt, bound and error variables for primal.*/
+  TempVarMalloc d_arithVarMalloc;
+
+  /** The size of the error set. */
+  uint32_t d_errorSize;
+
+  /** A local copy of 0. */
+  const Rational d_zero;
+
+  /** A local copy of 1. */
+  const Rational d_posOne;
+
+  /** A local copy of -1. */
+  const Rational d_negOne;
+
+  /**
+   * Locally cached value of arithStandardCheckVarOrderPivots option. It is
+   * cached here to allow for single runs with a different (lower) limit.
+   */
+  int64_t d_varOrderPivotLimit = -1;
+
+  ArithVar constructInfeasiblityFunction(TimerStat& timer);
+  ArithVar constructInfeasiblityFunction(TimerStat& timer, ArithVar e);
+  ArithVar constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set);
+
+  void tearDownInfeasiblityFunction(TimerStat& timer, ArithVar inf);
+  void adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges);
+  void addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e);
+  void removeFromInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e);
+  void shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped);
+
+public:
+ SimplexDecisionProcedure(Env& env,
+                          LinearEqualityModule& linEq,
+                          ErrorSet& errors,
+                          RaiseConflict conflictChannel,
+                          TempVarMalloc tvmalloc);
+ virtual ~SimplexDecisionProcedure();
+
+ /**
+  * Tries to update the assignments of variables such that all of the
+  * assignments are consistent with their bounds.
+  * This is done by a simplex search through the possible bases of the tableau.
+  *
+  * If all of the variables can be made consistent with their bounds
+  * SAT is returned. Otherwise UNSAT is returned, and at least 1 conflict
+  * was reported on the conflictCallback passed to the Module.
+  *
+  * Tableau pivoting is performed so variables may switch from being basic to
+  * nonbasic and vice versa.
+  *
+  * Corresponds to the "check()" procedure in [Cav06].
+  */
+ virtual Result::Status findModel(bool exactResult) = 0;
+
+ void increaseMax() { d_numVariables++; }
+
+ uint32_t getPivots() const { return d_pivots; }
+
+ /** Set the variable ordering pivot limit */
+ void setVarOrderPivotLimit(int64_t value) { d_varOrderPivotLimit = value; }
+
+protected:
+ /** Reports a conflict to on the output channel. */
+ void reportConflict(ArithVar basic);
+
+ /**
+  * Checks a basic variable, b, to see if it is in conflict.
+  * If a conflict is discovered a node summarizing the conflict is returned.
+  * Otherwise, Node::null() is returned.
+  */
+ bool maybeGenerateConflictForBasic(ArithVar basic) const;
+
+ /** Returns true if a tracked basic variable has a conflict on it. */
+ bool checkBasicForConflict(ArithVar b) const;
+
+ /**
+  * If a basic variable has a conflict on its row,
+  * this produces a minimized row on the conflict channel.
+  */
+ ConstraintCP generateConflictForBasic(ArithVar basic) const;
+
+ /** Gets a fresh variable from TheoryArith. */
+ ArithVar requestVariable() { return d_arithVarMalloc.request(); }
+
+ /** Releases a requested variable from TheoryArith.*/
+ void releaseVariable(ArithVar v) { d_arithVarMalloc.release(v); }
+
+ /** Post condition: !d_queue.moreSignals() */
+ bool standardProcessSignals(TimerStat& timer, IntStat& conflictStat);
+
+ struct ArithVarIntPairHashFunc
+ {
+   size_t operator()(const std::pair<ArithVar, int>& p) const
+   {
+     size_t h1 = std::hash<ArithVar>()(p.first);
+     size_t h2 = std::hash<int>()(p.second);
+     return h1 + 3389 * h2;
+   }
+  };
+
+  typedef std::unordered_map< std::pair<ArithVar, int>, ArithVarVec, ArithVarIntPairHashFunc> sgn_table;
+
+  static inline int determinizeSgn(int sgn){
+    return sgn < 0 ? -1 : (sgn == 0 ? 0 : 1);
+  }
+
+  void addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic);
+  void addRowSgns(sgn_table& sgns, ArithVar basic, int norm);
+  ArithVar find_basic_in_sgns(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m, bool inside);
+
+  sgn_table::const_iterator find_sgns(const sgn_table& sgns, ArithVar col, int sgn);
+
+}; /* class SimplexDecisionProcedure */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/simplex_update.cpp b/src/theory/arith/linear/simplex_update.cpp
new file mode 100644 (file)
index 0000000..9e7a551
--- /dev/null
@@ -0,0 +1,207 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Andres Noetzli, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This implements the UpdateInfo.
+ */
+
+#include "theory/arith/linear/simplex_update.h"
+
+#include "theory/arith/linear/constraint.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/*
+ * Generates a string representation of std::optional and inserts it into a
+ * stream.
+ *
+ * Note: We define this function here in the cvc5::internal::theory::arith namespace,
+ * because it would otherwise not be found for std::optional<int>. This is due
+ * to the argument-dependent lookup rules.
+ *
+ * @param out The stream
+ * @param m The value
+ * @return The stream
+ */
+std::ostream& operator<<(std::ostream& out, const std::optional<int>& m)
+{
+  return cvc5::internal::operator<<(out, m);
+}
+
+UpdateInfo::UpdateInfo():
+  d_nonbasic(ARITHVAR_SENTINEL),
+  d_nonbasicDirection(0),
+  d_nonbasicDelta(),
+  d_foundConflict(false),
+  d_errorsChange(),
+  d_focusDirection(),
+  d_tableauCoefficient(),
+  d_limiting(NullConstraint),
+  d_witness(AntiProductive)
+{}
+
+UpdateInfo::UpdateInfo(ArithVar nb, int dir):
+  d_nonbasic(nb),
+  d_nonbasicDirection(dir),
+  d_nonbasicDelta(),
+  d_foundConflict(false),
+  d_errorsChange(),
+  d_focusDirection(),
+  d_tableauCoefficient(),
+  d_limiting(NullConstraint),
+  d_witness(AntiProductive)
+{
+  Assert(dir == 1 || dir == -1);
+}
+
+UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP c):
+  d_nonbasic(nb),
+  d_nonbasicDirection(delta.sgn()),
+  d_nonbasicDelta(delta),
+  d_foundConflict(true),
+  d_errorsChange(),
+  d_focusDirection(),
+  d_tableauCoefficient(&r),
+  d_limiting(c),
+  d_witness(ConflictFound)
+{
+  Assert(conflict);
+}
+
+UpdateInfo UpdateInfo::conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim){
+  return UpdateInfo(true, nb, delta, r, lim);
+}
+
+void UpdateInfo::updateUnbounded(const DeltaRational& delta, int ec, int f){
+  d_limiting = NullConstraint;
+  d_nonbasicDelta = delta;
+  d_errorsChange = ec;
+  d_focusDirection = f;
+  d_tableauCoefficient.reset();
+  updateWitness();
+  Assert(unbounded());
+  Assert(improvement(d_witness));
+  Assert(!describesPivot());
+  Assert(debugSgnAgreement());
+}
+void UpdateInfo::updatePureFocus(const DeltaRational& delta, ConstraintP c){
+  d_limiting = c;
+  d_nonbasicDelta = delta;
+  d_errorsChange.reset();
+  d_focusDirection = 1;
+  d_tableauCoefficient.reset();
+  updateWitness();
+  Assert(!describesPivot());
+  Assert(improvement(d_witness));
+  Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c){
+  d_limiting = c;
+  d_nonbasicDelta = delta;
+  d_errorsChange.reset();
+  d_focusDirection.reset();
+  updateWitness();
+  Assert(describesPivot());
+  Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec){
+  d_limiting = c;
+  d_nonbasicDelta = delta;
+  d_errorsChange = ec;
+  d_focusDirection.reset();
+  d_tableauCoefficient = &r;
+  updateWitness();
+  Assert(describesPivot());
+  Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::witnessedUpdate(const DeltaRational& delta, ConstraintP c, int ec, int fd){
+  d_limiting = c;
+  d_nonbasicDelta = delta;
+  d_errorsChange = ec;
+  d_focusDirection = fd;
+  d_tableauCoefficient.reset();
+  updateWitness();
+  Assert(describesPivot() || improvement(d_witness));
+  Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::update(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec, int fd){
+  d_limiting = c;
+  d_nonbasicDelta = delta;
+  d_errorsChange = ec;
+  d_focusDirection = fd;
+  d_tableauCoefficient = &r;
+  updateWitness();
+  Assert(describesPivot() || improvement(d_witness));
+  Assert(debugSgnAgreement());
+}
+
+bool UpdateInfo::describesPivot() const {
+  return !unbounded() && d_nonbasic != d_limiting->getVariable();
+}
+
+void UpdateInfo::output(std::ostream& out) const{
+  out << "{UpdateInfo"
+      << ", nb = " << d_nonbasic
+      << ", dir = " << d_nonbasicDirection
+      << ", delta = " << d_nonbasicDelta
+      << ", conflict = " << d_foundConflict
+      << ", errorChange = " << d_errorsChange
+      << ", focusDir = " << d_focusDirection
+      << ", witness = " << d_witness
+      << ", limiting = " << d_limiting
+      << "}";
+}
+
+ArithVar UpdateInfo::leaving() const{
+  Assert(describesPivot());
+
+  return d_limiting->getVariable();
+}
+
+std::ostream& operator<<(std::ostream& out, const UpdateInfo& up){
+  up.output(out);
+  return out;
+}
+
+
+std::ostream& operator<<(std::ostream& out,  WitnessImprovement w){
+  switch(w){
+  case ConflictFound:
+    out << "ConflictFound"; break;
+  case ErrorDropped:
+    out << "ErrorDropped"; break;
+  case FocusImproved:
+    out << "FocusImproved"; break;
+  case FocusShrank:
+    out << "FocusShrank"; break;
+  case Degenerate:
+    out << "Degenerate"; break;
+  case BlandsDegenerate:
+    out << "BlandsDegenerate"; break;
+  case HeuristicDegenerate:
+    out << "HeuristicDegenerate"; break;
+  case AntiProductive:
+    out << "AntiProductive"; break;
+  }
+  return out;
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/simplex_update.h b/src/theory/arith/linear/simplex_update.h
new file mode 100644 (file)
index 0000000..0a4c90e
--- /dev/null
@@ -0,0 +1,360 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Andres Noetzli, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This provides a class for summarizing pivot proposals.
+ *
+ * This shares with the theory a Tableau, and a PartialModel that:
+ *  - satisfies the equalities in the Tableau, and
+ *  - the assignment for the non-basic variables satisfies their bounds.
+ * This maintains the relationship needed by the SimplexDecisionProcedure.
+ *
+ * In the language of Simplex for DPLL(T), this provides:
+ * - update()
+ * - pivotAndUpdate()
+ *
+ * This class also provides utility functions that require
+ * using both the Tableau and PartialModel.
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <optional>
+
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/arith/delta_rational.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+enum WitnessImprovement {
+  ConflictFound = 0,
+  ErrorDropped = 1,
+  FocusImproved = 2,
+  FocusShrank = 3,
+  Degenerate = 4,
+  BlandsDegenerate = 5,
+  HeuristicDegenerate = 6,
+  AntiProductive = 7
+};
+
+inline bool strongImprovement(WitnessImprovement w){
+  return w <= FocusImproved;
+}
+
+inline bool improvement(WitnessImprovement w){
+  return w <= FocusShrank;
+}
+
+inline bool degenerate(WitnessImprovement w){
+  switch(w){
+  case Degenerate:
+  case BlandsDegenerate:
+  case HeuristicDegenerate:
+    return true;
+  default:
+    return false;
+  }
+}
+
+std::ostream& operator<<(std::ostream& out,  WitnessImprovement w);
+
+/**
+ * This class summarizes both potential:
+ * - pivot-and-update operations or
+ * - a pure update operation.
+ * This stores enough information for the various algorithms  hat consider these operations.
+ * These require slightly different pieces of information at different points
+ * so they are a bit verbose and paranoid.
+ */
+class UpdateInfo {
+private:
+
+  /**
+   * The nonbasic variables under consideration.
+   * This is either the entering variable on a pivot and update
+   * or the variable being updated.
+   * This can only be set in the constructor or assignment.
+   *
+   * If this uninitialized, then this is ARITHVAR_SENTINEL.
+   */
+  ArithVar d_nonbasic;
+
+  /**
+   * The sgn of the "intended" derivative (delta) of the update to d_nonbasic.
+   * This is either 1, -1, or 0.
+   * It is "intended" as the delta is always allowed to be 0.
+   * (See debugSgnAgreement().)
+   *
+   * If this uninitialized, then this is 0.
+   * If this is initialized, then it is -1 or 1.
+   *
+   * This can only be set in the constructor or assignment.
+   */
+  int d_nonbasicDirection;
+
+  /**
+   * The change in the assignment of d_nonbasic.
+   * This is changed via the updateProposal(...) methods.
+   * The value needs to satisfy debugSgnAgreement() or it is in conflict.
+   */
+  std::optional<DeltaRational> d_nonbasicDelta;
+
+  /**
+   * This is true if the pivot-and-update is *known* to cause a conflict.
+   * This can only be true if it was constructed through the static conflict(...) method.
+   */
+  bool d_foundConflict;
+
+  /** This is the change in the size of the error set. */
+  std::optional<int> d_errorsChange;
+
+  /** This is the sgn of the change in the value of the focus set.*/
+  std::optional<int> d_focusDirection;
+
+  /** This is the sgn of the change in the value of the focus set.*/
+  std::optional<DeltaRational> d_focusChange;
+
+  /** This is the coefficient in the tableau for the entry.*/
+  std::optional<const Rational*> d_tableauCoefficient;
+
+  /**
+   * This is the constraint that nonbasic is basic is updating s.t. its variable is against it.
+   * This has 3 different possibilities:
+   * - Unbounded : then this is NullConstraint and unbounded() is true.
+   * - Pivot-And-Update: then this is not NullConstraint and the variable is not d_nonbasic.
+   * - Update: then this is not NullConstraint and the variable is d_nonbasic.
+   */
+  ConstraintP d_limiting;
+
+  WitnessImprovement d_witness;
+
+  /**
+   * This returns true if
+   * d_nonbasicDelta is zero() or its sgn() must agree with d_nonbasicDirection.
+   */
+  bool debugSgnAgreement() const {
+    int deltaSgn = d_nonbasicDelta.value().sgn();
+    return deltaSgn == 0 || deltaSgn == d_nonbasicDirection;
+  }
+
+  /** This private constructor allows for setting conflict to true. */
+  UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim);
+
+public:
+
+  /** This constructs an uninitialized UpdateInfo. */
+  UpdateInfo();
+
+  /**
+   * This constructs an initialized UpdateInfo.
+   * dir must be 1 or -1.
+   */
+  UpdateInfo(ArithVar nb, int dir);
+
+  /**
+   * This updates the nonBasicDelta to d and limiting to NullConstraint.
+   * This describes an unbounded() update.
+   */
+  void updateUnbounded(const DeltaRational& d, int ec, int f);
+
+
+  void updatePureFocus(const DeltaRational& d, ConstraintP c);
+  //void updatePureError(const DeltaRational& d, Constraint c, int e);
+  //void updatePure(const DeltaRational& d, Constraint c, int e, int f);
+
+  /**
+   * This updates the nonBasicDelta to d and limiting to c.
+   * This clears errorChange() and focusDir().
+   */
+  void updatePivot(const DeltaRational& d, const Rational& r,  ConstraintP c);
+
+  /**
+   * This updates the nonBasicDelta to d, limiting to c, and errorChange to e.
+   * This clears focusDir().
+   */
+  void updatePivot(const DeltaRational& d, const Rational& r, ConstraintP c, int e);
+
+  /**
+   * This updates the nonBasicDelta to d, limiting to c, errorChange to e and
+   * focusDir to f.
+   */
+  void witnessedUpdate(const DeltaRational& d, ConstraintP c, int e, int f);
+  void update(const DeltaRational& d, const Rational& r, ConstraintP c, int e, int f);
+
+
+  static UpdateInfo conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim);
+
+  inline ArithVar nonbasic() const { return d_nonbasic; }
+  inline bool uninitialized() const {
+    return d_nonbasic == ARITHVAR_SENTINEL;
+  }
+
+  /**
+   * There is no limiting value to the improvement of the focus.
+   * If this is true, this never describes an update.
+   */
+  inline bool unbounded() const {
+    return d_limiting == NullConstraint;
+  }
+
+  /**
+   * The update either describes a pivotAndUpdate operation
+   * or it describes just an update.
+   */
+  bool describesPivot() const;
+
+  /** Returns the . describesPivot() must be true. */
+  ArithVar leaving() const;
+
+  /**
+   * Returns true if this is *known* to find a conflict.
+   * If true, this must have been made through the static conflict(...) function.
+   */
+  bool foundConflict() const { return d_foundConflict; }
+
+  /** Returns the direction nonbasic is supposed to move. */
+  inline int nonbasicDirection() const{  return d_nonbasicDirection; }
+
+  /** Requires errorsChange to be set through setErrorsChange or updateProposal. */
+  inline int errorsChange() const { return d_errorsChange.value(); }
+
+  /**
+   * If errorsChange has been set, return errorsChange().
+   * Otherwise, return def.
+   */
+  inline int errorsChangeSafe(int def) const {
+    if (d_errorsChange)
+    {
+      return d_errorsChange.value();
+    }
+    else
+    {
+      return def;
+    }
+  }
+
+  /** Sets the errorChange. */
+  void setErrorsChange(int ec){
+    d_errorsChange = ec;
+    updateWitness();
+  }
+
+
+  /** Requires errorsChange to be set through setErrorsChange or updateProposal. */
+  inline int focusDirection() const { return d_focusDirection.value(); }
+
+  /** Sets the focusDirection. */
+  void setFocusDirection(int fd){
+    Assert(-1 <= fd && fd <= 1);
+    d_focusDirection = fd;
+    updateWitness();
+  }
+
+  /**
+   * nonbasicDirection must be the same as the sign for the focus function's
+   * coefficient for this to be safe.
+   * The burden for this being safe is on the user!
+   */
+  void determineFocusDirection(){
+    const int deltaSgn = d_nonbasicDelta.value().sgn();
+    setFocusDirection(deltaSgn * d_nonbasicDirection);
+  }
+
+  /** Requires nonbasicDelta to be set through updateProposal(...). */
+  const DeltaRational& nonbasicDelta() const { return d_nonbasicDelta.value(); }
+  const Rational& getCoefficient() const {
+    Assert(describesPivot());
+    Assert(d_tableauCoefficient.value() != NULL);
+    return *(d_tableauCoefficient.value());
+  }
+  int basicDirection() const {
+    return nonbasicDirection() * (getCoefficient().sgn());
+  }
+
+  /** Returns the limiting constraint. */
+  inline ConstraintP limiting() const {
+    return d_limiting;
+  }
+
+  WitnessImprovement getWitness(bool useBlands = false) const{
+    Assert(d_witness == computeWitness());
+
+    if(d_witness == Degenerate){
+      if(useBlands){
+        return BlandsDegenerate;
+      }else{
+        return HeuristicDegenerate;
+      }
+    }else{
+      return d_witness;
+    }
+  }
+
+  const DeltaRational& focusChange() const { return d_focusChange.value(); }
+  void setFocusChange(const DeltaRational& fc) {
+    d_focusChange = fc;
+  }
+
+  /** Outputs the UpdateInfo into out. */
+  void output(std::ostream& out) const;
+
+private:
+  void updateWitness() {
+    d_witness = computeWitness();
+    Assert(describesPivot() || improvement(d_witness));
+  }
+
+  /**
+   * Determines the appropriate WitnessImprovement for the update.
+   * useBlands breaks ties for degenerate pivots.
+   *
+   * This is safe if:
+   * - d_foundConflict is true, or
+   * - d_foundConflict is false and d_errorsChange has been set and d_errorsChange < 0, or
+   * - d_foundConflict is false and d_errorsChange has been set and d_errorsChange >= 0 and d_focusDirection has been set.
+   */
+  WitnessImprovement computeWitness() const {
+    if(d_foundConflict){
+      return ConflictFound;
+    }
+    else if (d_errorsChange && d_errorsChange.value() < 0)
+    {
+      return ErrorDropped;
+    }
+    else if (d_errorsChange.value_or(0) == 0)
+    {
+      if (d_focusDirection)
+      {
+        if (*d_focusDirection > 0)
+        {
+          return FocusImproved;
+        }
+        else if (*d_focusDirection == 0)
+        {
+          return Degenerate;
+        }
+      }
+    }
+    return AntiProductive;
+  }
+
+};
+
+std::ostream& operator<<(std::ostream& out, const UpdateInfo& up);
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/soi_simplex.cpp b/src/theory/arith/linear/soi_simplex.cpp
new file mode 100644 (file)
index 0000000..0ba3e34
--- /dev/null
@@ -0,0 +1,912 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ */
+#include "theory/arith/linear/soi_simplex.h"
+
+#include <algorithm>
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/tableau.h"
+#include "util/statistics_stats.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+SumOfInfeasibilitiesSPD::SumOfInfeasibilitiesSPD(Env& env,
+                                                 LinearEqualityModule& linEq,
+                                                 ErrorSet& errors,
+                                                 RaiseConflict conflictChannel,
+                                                 TempVarMalloc tvmalloc)
+    : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
+      d_soiVar(ARITHVAR_SENTINEL),
+      d_pivotBudget(0),
+      d_prevWitnessImprovement(AntiProductive),
+      d_witnessImprovementInARow(0),
+      d_sgnDisagreements(),
+      d_statistics("theory::arith::SOI", d_pivots)
+{ }
+
+SumOfInfeasibilitiesSPD::Statistics::Statistics(const std::string& name,
+                                                uint32_t& pivots)
+    : d_initialSignalsTime(
+        smtStatisticsRegistry().registerTimer(name + "initialProcessTime")),
+      d_initialConflicts(
+          smtStatisticsRegistry().registerInt(name + "UpdateConflicts")),
+      d_soiFoundUnsat(smtStatisticsRegistry().registerInt(name + "FoundUnsat")),
+      d_soiFoundSat(smtStatisticsRegistry().registerInt(name + "FoundSat")),
+      d_soiMissed(smtStatisticsRegistry().registerInt(name + "Missed")),
+      d_soiConflicts(
+          smtStatisticsRegistry().registerInt(name + "ConfMin::num")),
+      d_hasToBeMinimal(
+          smtStatisticsRegistry().registerInt(name + "HasToBeMin")),
+      d_maybeNotMinimal(
+          smtStatisticsRegistry().registerInt(name + "MaybeNotMin")),
+      d_soiTimer(smtStatisticsRegistry().registerTimer(name + "Time")),
+      d_soiFocusConstructionTimer(
+          smtStatisticsRegistry().registerTimer(name + "Construction")),
+      d_soiConflictMinimization(smtStatisticsRegistry().registerTimer(
+          name + "Conflict::Minimization")),
+      d_selectUpdateForSOI(
+          smtStatisticsRegistry().registerTimer(name + "selectSOI")),
+      d_finalCheckPivotCounter(
+          smtStatisticsRegistry().registerReference<uint32_t>(
+              name + "lastPivots", pivots))
+{
+}
+
+Result::Status SumOfInfeasibilitiesSPD::findModel(bool exactResult)
+{
+  Assert(d_conflictVariables.empty());
+  Assert(d_sgnDisagreements.empty());
+
+  d_pivots = 0;
+
+  if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
+    Trace("soi::findModel") << "soiFindModel() trivial" << endl;
+    Assert(d_conflictVariables.empty());
+    return Result::SAT;
+  }
+
+  // We need to reduce this because of
+  d_errorSet.reduceToSignals();
+
+  // We must start tracking NOW
+  d_errorSet.setSelectionRule(options::ErrorSelectionRule::SUM_METRIC);
+
+  if(initialProcessSignals()){
+    d_conflictVariables.purge();
+    Trace("soi::findModel") << "fcFindModel() early conflict" << endl;
+    Assert(d_conflictVariables.empty());
+    return Result::UNSAT;
+  }else if(d_errorSet.errorEmpty()){
+    Trace("soi::findModel") << "fcFindModel() fixed itself" << endl;
+    Assert(!d_errorSet.moreSignals());
+    Assert(d_conflictVariables.empty());
+    return Result::SAT;
+  }
+
+  Trace("soi::findModel") << "fcFindModel() start non-trivial" << endl;
+
+  exactResult |= d_varOrderPivotLimit < 0;
+
+  d_prevWitnessImprovement = HeuristicDegenerate;
+  d_witnessImprovementInARow = 0;
+
+  Result::Status result = Result::UNKNOWN;
+
+  if (result == Result::UNKNOWN)
+  {
+    if(exactResult){
+      d_pivotBudget = -1;
+    }else{
+      d_pivotBudget = d_varOrderPivotLimit;
+    }
+
+    result = sumOfInfeasibilities();
+
+    if(result ==  Result::UNSAT){
+      ++(d_statistics.d_soiFoundUnsat);
+    }else if(d_errorSet.errorEmpty()){
+      ++(d_statistics.d_soiFoundSat);
+    }else{
+      ++(d_statistics.d_soiMissed);
+    }
+  }
+
+  Assert(!d_errorSet.moreSignals());
+  if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
+  {
+    result = Result::SAT;
+  }
+
+  // ensure that the conflict variable is still in the queue.
+  d_conflictVariables.purge();
+
+  Trace("soi::findModel") << "end findModel() " << result << endl;
+
+  Assert(d_conflictVariables.empty());
+  return result;
+}
+
+
+void SumOfInfeasibilitiesSPD::logPivot(WitnessImprovement w){
+  if(d_pivotBudget > 0) {
+    --d_pivotBudget;
+  }
+  Assert(w != AntiProductive);
+
+  if(w == d_prevWitnessImprovement){
+    ++d_witnessImprovementInARow;
+    if(d_witnessImprovementInARow == 0){
+      --d_witnessImprovementInARow;
+    }
+  }else{
+    if(w != BlandsDegenerate){
+      d_witnessImprovementInARow = 1;
+    }
+    d_prevWitnessImprovement = w;
+  }
+  if(strongImprovement(w)){
+    d_leavingCountSinceImprovement.purge();
+  }
+
+  Trace("logPivot") << "logPivot " << d_prevWitnessImprovement << " "  << d_witnessImprovementInARow << endl;
+}
+
+uint32_t SumOfInfeasibilitiesSPD::degeneratePivotsInARow() const {
+  switch(d_prevWitnessImprovement){
+  case ConflictFound:
+  case ErrorDropped:
+  case FocusImproved:
+    return 0;
+  case HeuristicDegenerate:
+  case BlandsDegenerate:
+    return d_witnessImprovementInARow;
+  // Degenerate is unreachable for its own reasons
+  case Degenerate:
+  case FocusShrank:
+  case AntiProductive:
+    Unreachable();
+    return -1;
+  }
+  Unreachable();
+}
+
+void SumOfInfeasibilitiesSPD::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
+  uint32_t newErrorSize = d_errorSet.errorSize();
+  adjustInfeasFunc(d_statistics.d_soiFocusConstructionTimer, d_soiVar, focusChanges);
+  d_errorSize = newErrorSize;
+}
+
+
+UpdateInfo SumOfInfeasibilitiesSPD::selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
+  UpdateInfo selected;
+
+  Trace("soi::selectPrimalUpdate")
+      << "selectPrimalUpdate " << endl
+      << d_soiVar << " " << d_tableau.basicRowLength(d_soiVar) << " "
+      << d_linEq.debugBasicAtBoundCount(d_soiVar) << endl;
+
+  typedef std::vector<Cand> CandVector;
+  CandVector candidates;
+
+  for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_soiVar); !ri.atEnd(); ++ri){
+    const Tableau::Entry& e = *ri;
+    ArithVar curr = e.getColVar();
+    if(curr == d_soiVar){ continue; }
+
+    int sgn = e.getCoefficient().sgn();
+    bool candidate =
+      (sgn > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
+      (sgn < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
+
+    Trace("soi::selectPrimalUpdate")
+      << "storing " << d_soiVar
+      << " " << curr
+      << " " << candidate
+      << " " << e.getCoefficient()
+      << " " << sgn << endl;
+
+    if(candidate) {
+      candidates.push_back(Cand(curr, 0, sgn, &e.getCoefficient()));
+    }
+  }
+
+  CompPenaltyColLength colCmp(&d_linEq, options().arith.havePenalties);
+  CandVector::iterator i = candidates.begin();
+  CandVector::iterator end = candidates.end();
+  std::make_heap(i, end, colCmp);
+
+  // For the first 3 pivots take the best
+  // After that, once an improvement is found on look at a
+  // small number of pivots after finding an improvement
+  // the longer the search to more willing we are to look at more candidates
+  int maxCandidatesAfterImprove =
+    (d_pivots <= 2) ?  std::numeric_limits<int>::max() : d_pivots/5;
+
+  int candidatesAfterFocusImprove = 0;
+  while(i != end && candidatesAfterFocusImprove <= maxCandidatesAfterImprove){
+    std::pop_heap(i, end, colCmp);
+    --end;
+    Cand& cand = (*end);
+    ArithVar curr = cand.d_nb;
+    const Rational& coeff = *cand.d_coeff;
+
+    LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
+    UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
+
+    Trace("soi::selectPrimalUpdate")
+      << "selected " << selected << endl
+      << "currProp " << currProposal << endl
+      << "coeff " << coeff << endl;
+
+    Assert(!currProposal.uninitialized());
+
+    if(candidatesAfterFocusImprove > 0){
+      candidatesAfterFocusImprove++;
+    }
+
+    if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
+      selected = currProposal;
+      WitnessImprovement w = selected.getWitness(false);
+      Trace("soi::selectPrimalUpdate") << "selected " << w << endl;
+      //setPenalty(curr, w);
+      if(improvement(w)){
+        bool exitEarly;
+        switch(w){
+        case ConflictFound: exitEarly = true; break;
+        case FocusImproved:
+          candidatesAfterFocusImprove = 1;
+          exitEarly = false;
+          break;
+        default:
+          exitEarly = false; break;
+        }
+        if(exitEarly){ break; }
+      }
+    }else{
+      Trace("soi::selectPrimalUpdate") << "dropped "<< endl;
+    }
+
+  }
+  return selected;
+}
+
+bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
+  if(inf.getWitness(useBlands) == w){
+    switch(w){
+    case ConflictFound: return inf.foundConflict();
+    case ErrorDropped: return inf.errorsChange() < 0;
+    case FocusImproved: return inf.focusDirection() > 0;
+    case FocusShrank: return false; // This is not a valid output
+    case Degenerate: return false; // This is not a valid output
+    case BlandsDegenerate: return useBlands;
+    case HeuristicDegenerate: return !useBlands;
+    case AntiProductive: return false;
+    }
+  }
+  return false;
+}
+
+
+void SumOfInfeasibilitiesSPD::debugPrintSignal(ArithVar updated) const{
+  Trace("updateAndSignal") << "updated basic " << updated;
+  Trace("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
+  Trace("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
+  int dir = !d_variables.assignmentIsConsistent(updated) ?
+    d_errorSet.getSgn(updated) : 0;
+  Trace("updateAndSignal") << " dir " << dir;
+  Trace("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
+}
+
+
+void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
+  ArithVar nonbasic = selected.nonbasic();
+
+  Trace("updateAndSignal") << "updateAndSignal " << selected << endl;
+
+  if(selected.describesPivot()){
+    ConstraintP limiting = selected.limiting();
+    ArithVar basic = limiting->getVariable();
+    Assert(d_linEq.basicIsTracked(basic));
+    d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
+  }else{
+    Assert(!selected.unbounded() || selected.errorsChange() < 0);
+
+    DeltaRational newAssignment =
+      d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
+
+    d_linEq.updateTracked(nonbasic, newAssignment);
+  }
+  d_pivots++;
+
+  increaseLeavingCount(nonbasic);
+
+  vector< pair<ArithVar, int> > focusChanges;
+  while(d_errorSet.moreSignals()){
+    ArithVar updated = d_errorSet.topSignal();
+    int prevFocusSgn = d_errorSet.popSignal();
+
+    if(d_tableau.isBasic(updated)){
+      Assert(!d_variables.assignmentIsConsistent(updated)
+             == d_errorSet.inError(updated));
+      if(TraceIsOn("updateAndSignal")){debugPrintSignal(updated);}
+      if(!d_variables.assignmentIsConsistent(updated)){
+        if(checkBasicForConflict(updated)){
+          reportConflict(updated);
+          //Assert(debugUpdatedBasic(selected, updated));
+        }
+      }
+    }else{
+      Trace("updateAndSignal") << "updated nonbasic " << updated << endl;
+    }
+    int currFocusSgn = d_errorSet.focusSgn(updated);
+    if(currFocusSgn != prevFocusSgn){
+      int change = currFocusSgn - prevFocusSgn;
+      focusChanges.push_back(make_pair(updated, change));
+    }
+  }
+
+  if(TraceIsOn("error")){ d_errorSet.debugPrint(Trace("error")); }
+
+  //Assert(debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
+
+  adjustFocusAndError(selected, focusChanges);
+}
+
+void SumOfInfeasibilitiesSPD::qeAddRange(uint32_t begin, uint32_t end){
+  Assert(!d_qeInSoi.empty());
+  for(uint32_t i = begin; i != end; ++i){
+    ArithVar v = d_qeConflict[i];
+    addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v);
+    d_qeInSoi.add(v);
+  }
+}
+
+void SumOfInfeasibilitiesSPD::qeRemoveRange(uint32_t begin, uint32_t end){
+  for(uint32_t i = begin; i != end; ++i){
+    ArithVar v = d_qeConflict[i];
+    removeFromInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v);
+    d_qeInSoi.remove(v);
+  }
+  Assert(!d_qeInSoi.empty());
+}
+
+void SumOfInfeasibilitiesSPD::qeSwapRange(uint32_t N, uint32_t r, uint32_t s){
+  for(uint32_t i = 0; i < N; ++i){
+    std::swap(d_qeConflict[r+i], d_qeConflict[s+i]);
+  }
+}
+
+/**
+ * Region notation:
+ * A region is either
+ *  - A single element X@i with the name X at the position i
+ *  - A sequence of indices X@[i,j) with the name X and the elements between i [inclusive] and j exclusive
+ *  - A concatenation of regions R1 and R2, R1;R2
+ *
+ * Given the fixed assumptions C @ [0,cEnd) and a set of candidate minimizations U@[cEnd, uEnd)
+ * s.t. C \cup U is known to be in conflict ([0,uEnd) has a conflict), find a minimal
+ * subset of U, Delta, s.t. C \cup Delta is in conflict.
+ *
+ * Pre:
+ *  [0, uEnd) is a set and is in conflict.
+ *    uEnd <= assumptions.size()
+ *  [0, cEnd) is in d_inSoi.
+ *
+ * Invariants: [0,cEnd) is never modified
+ *
+ * Post:
+ *  [0, cEnd); [cEnd, deltaEnd) is in conflict
+ *  [0, deltaEnd) is a set
+ *  [0, deltaEnd) is in d_inSoi
+ */
+uint32_t SumOfInfeasibilitiesSPD::quickExplainRec(uint32_t cEnd, uint32_t uEnd){
+  Assert(cEnd <= uEnd);
+  Assert(d_qeInUAndNotInSoi.empty());
+  Assert(d_qeGreedyOrder.empty());
+
+  const Tableau::Entry* spoiler = NULL;
+
+  if(d_soiVar != ARITHVAR_SENTINEL && d_linEq.selectSlackEntry(d_soiVar, false) == NULL){
+    // already in conflict
+    return cEnd;
+  }
+
+  Assert(cEnd < uEnd);
+
+  // Phase 1 : Construct the conflict greedily
+
+  for(uint32_t i = cEnd; i < uEnd; ++i){
+    d_qeInUAndNotInSoi.add(d_qeConflict[i]);
+  }
+  if(d_soiVar == ARITHVAR_SENTINEL){ // special case for d_soiVar being empty
+    ArithVar first = d_qeConflict[cEnd];
+    d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, first);
+    d_qeInSoi.add(first);
+    d_qeInUAndNotInSoi.remove(first);
+    d_qeGreedyOrder.push_back(first);
+  }
+  while((spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
+    Assert(!d_qeInUAndNotInSoi.empty());
+
+    ArithVar nb = spoiler->getColVar();
+    int oppositeSgn = -(spoiler->getCoefficient().sgn());
+    Assert(oppositeSgn != 0);
+
+    ArithVar basicWithOp = find_basic_in_sgns(d_qeSgns, nb, oppositeSgn, d_qeInUAndNotInSoi, true);
+    Assert(basicWithOp != ARITHVAR_SENTINEL);
+
+    addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
+    d_qeInSoi.add(basicWithOp);
+    d_qeInUAndNotInSoi.remove(basicWithOp);
+    d_qeGreedyOrder.push_back(basicWithOp);
+  }
+  Assert(spoiler == NULL);
+
+  // Compact the set u
+  uint32_t newEnd = cEnd + d_qeGreedyOrder.size();
+  std::copy(d_qeGreedyOrder.begin(), d_qeGreedyOrder.end(), d_qeConflict.begin()+cEnd);
+
+  d_qeInUAndNotInSoi.purge();
+  d_qeGreedyOrder.clear();
+
+   // Phase 2 : Recursively determine the minimal set of rows
+
+  uint32_t xPos = cEnd;
+  std::swap(d_qeGreedyOrder[xPos], d_qeGreedyOrder[newEnd - 1]);
+  uint32_t uBegin = xPos + 1;
+  uint32_t split = (newEnd - uBegin)/2 + uBegin;
+
+  //assumptions : C @ [0, cEnd); X @ xPos; U1 @ [u1Begin, split); U2 @ [split, newEnd)
+  // [0, newEnd) == d_inSoi
+
+  uint32_t compactU2;
+  if(split == newEnd){ // U2 is empty
+    compactU2 = newEnd;
+  }else{
+    // Remove U2 from Soi
+    qeRemoveRange(split, newEnd);
+    // [0, split) == d_inSoi
+
+    // pre assumptions: C + X + U1 @ [0,split); U2 [split, newEnd)
+    compactU2 = quickExplainRec(split, newEnd);
+    // post:
+    //  assumptions: C + X + U1 @ [0, split); delta2 @ [split - compactU2)
+    //  d_inSoi = [0, compactU2)
+  }
+  uint32_t deltaSize = compactU2 - split;
+  qeSwapRange(deltaSize, uBegin, split);
+  uint32_t d2End = uBegin+deltaSize;
+  // assumptions : C @ [0, cEnd); X @ xPos; delta2 @ [uBegin, d2End); U1 @ [d2End, compactU2)
+  //  d_inSoi == [0, compactU2)
+
+  uint32_t d1End;
+  if(d2End == compactU2){ // U1 is empty
+    d1End = d2End;
+  }else{
+    qeRemoveRange(d2End, compactU2);
+
+    //pre assumptions : C + X + delta2 @ [0, d2End); U1 @ [d2End, compactU2);
+    d1End = quickExplainRec(d2End, compactU2);
+    //post:
+    // assumptions : C + X + delta2 @ [0, d2End); delta1 @ [d2End, d1End);
+    // d_inSoi = [0, d1End)
+  }
+  //After both:
+  // d_inSoi == [0, d1End), C @ [0, cEnd); X + delta2 + delta 1 @ [xPos, d1End);
+
+  Assert(d_qeInUAndNotInSoi.empty());
+  Assert(d_qeGreedyOrder.empty());
+  return d1End;
+}
+
+void SumOfInfeasibilitiesSPD::quickExplain(){
+  Assert(d_qeInSoi.empty());
+  Assert(d_qeInUAndNotInSoi.empty());
+  Assert(d_qeGreedyOrder.empty());
+  Assert(d_soiVar == ARITHVAR_SENTINEL);
+  Assert(d_qeSgns.empty());
+
+  d_qeConflict.clear();
+  d_errorSet.pushFocusInto(d_qeConflict);
+
+  //cout <<  d_qeConflict.size() << " ";
+  uint32_t size = d_qeConflict.size();
+
+  if(size > 2){
+    for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
+      ArithVar e = *iter;
+      addRowSgns(d_qeSgns, e, d_errorSet.getSgn(e));
+    }
+    uint32_t end = quickExplainRec(0u, size);
+    Assert(end <= d_qeConflict.size());
+    Assert(d_soiVar != ARITHVAR_SENTINEL);
+    Assert(!d_qeInSoi.empty());
+
+    d_qeConflict.resize(end);
+    tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+    d_soiVar = ARITHVAR_SENTINEL;
+    d_qeInSoi.purge();
+    d_qeSgns.clear();
+  }
+
+  //cout << d_qeConflict.size() << endl;
+
+  Assert(d_qeInSoi.empty());
+  Assert(d_qeInUAndNotInSoi.empty());
+  Assert(d_qeGreedyOrder.empty());
+  Assert(d_soiVar == ARITHVAR_SENTINEL);
+  Assert(d_qeSgns.empty());
+}
+
+unsigned SumOfInfeasibilitiesSPD::trySet(const ArithVarVec& set){
+  Assert(d_soiVar == ARITHVAR_SENTINEL);
+  bool success = false;
+  if(set.size() >= 2){
+    d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, set);
+    success = d_linEq.selectSlackEntry(d_soiVar, false) == NULL;
+
+    tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+    d_soiVar = ARITHVAR_SENTINEL;
+  }
+  return success ? set.size() : std::numeric_limits<int>::max();
+}
+
+std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){
+  Trace("arith::greedyConflictSubsets") << "greedyConflictSubsets start" << endl;
+
+  std::vector< ArithVarVec > subsets;
+  Assert(d_soiVar == ARITHVAR_SENTINEL);
+
+  if(d_errorSize <= 2){
+    ArithVarVec inError;
+    d_errorSet.pushFocusInto(inError);
+
+    Assert(debugIsASet(inError));
+    subsets.push_back(inError);
+    return subsets;
+  }
+  Assert(d_errorSize > 2);
+
+  //sgns_table< <nonbasic,sgn>, [basics] >;
+  // Phase 0: Construct the sgns table
+  sgn_table sgns;
+  DenseSet hasParticipated; //Has participated in a conflict
+  for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
+    ArithVar e = *iter;
+    addRowSgns(sgns, e, d_errorSet.getSgn(e));
+
+    Trace("arith::greedyConflictSubsets") << "basic error var: " << e << endl;
+    if(TraceIsOn("arith::greedyConflictSubsets")){
+      d_tableau.debugPrintIsBasic(e);
+      d_tableau.printBasicRow(e, Trace("arith::greedyConflictSubsets"));
+    }
+  }
+
+  // Phase 1: Try to find at least 1 pair for every element
+  ArithVarVec tmp;
+  tmp.push_back(0);
+  tmp.push_back(0);
+  for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
+    ArithVar e = *iter;
+    tmp[0] = e;
+
+    int errSgn = d_errorSet.getSgn(e);
+    bool decreasing = errSgn < 0;
+    const Tableau::Entry* spoiler = d_linEq.selectSlackEntry(e, decreasing);
+    Assert(spoiler != NULL);
+    ArithVar nb = spoiler->getColVar();
+    int oppositeSgn = -(errSgn * (spoiler->getCoefficient().sgn()));
+
+    sgn_table::const_iterator opposites = find_sgns(sgns, nb, oppositeSgn);
+    Assert(opposites != sgns.end());
+
+    const ArithVarVec& choices = (*opposites).second;
+    for(ArithVarVec::const_iterator j = choices.begin(), jend = choices.end(); j != jend; ++j){
+      ArithVar b = *j;
+      if(b < e){ continue; }
+      tmp[0] = e;
+      tmp[1] = b;
+      if(trySet(tmp) == 2){
+        Trace("arith::greedyConflictSubsets")  << "found a pair " << b << " " << e << endl;
+        hasParticipated.softAdd(b);
+        hasParticipated.softAdd(e);
+        Assert(debugIsASet(tmp));
+        subsets.push_back(tmp);
+        ++(d_statistics.d_soiConflicts);
+        ++(d_statistics.d_hasToBeMinimal);
+      }
+    }
+  }
+
+
+  // Phase 2: If there is a variable that has not participated attempt to start a conflict
+  ArithVarVec possibleStarts; //List of elements that can be tried for starts.
+  d_errorSet.pushFocusInto(possibleStarts);
+  while(!possibleStarts.empty()){
+    Assert(d_soiVar == ARITHVAR_SENTINEL);
+
+    ArithVar v = possibleStarts.back();
+    possibleStarts.pop_back();
+    if(hasParticipated.isMember(v)){ continue; }
+
+    hasParticipated.add(v);
+
+    Assert(d_soiVar == ARITHVAR_SENTINEL);
+    //d_soiVar's row =  \sumofinfeasibilites underConstruction
+    ArithVarVec underConstruction;
+    underConstruction.push_back(v);
+    d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, v);
+
+    Trace("arith::greedyConflictSubsets") << "trying " << v << endl;
+
+    const Tableau::Entry* spoiler = NULL;
+    while( (spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
+      ArithVar nb = spoiler->getColVar();
+      int oppositeSgn = -(spoiler->getCoefficient().sgn());
+      Assert(oppositeSgn != 0);
+
+      Trace("arith::greedyConflictSubsets") << "looking for " << nb << " " << oppositeSgn << endl;
+
+      ArithVar basicWithOp = find_basic_in_sgns(sgns, nb, oppositeSgn, hasParticipated, false);
+
+      if(basicWithOp == ARITHVAR_SENTINEL){
+        Trace("arith::greedyConflictSubsets") << "search did not work  for " << nb << endl;
+        // greedy construction has failed
+        break;
+      }else{
+        Trace("arith::greedyConflictSubsets") << "found  " << basicWithOp << endl;
+
+        addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
+        hasParticipated.softAdd(basicWithOp);
+        underConstruction.push_back(basicWithOp);
+      }
+    }
+    if(spoiler == NULL){
+      Trace("arith::greedyConflictSubsets") << "success" << endl;
+      //then underConstruction contains a conflicting subset
+      Assert(debugIsASet(underConstruction));
+      subsets.push_back(underConstruction);
+      ++d_statistics.d_soiConflicts;
+      if(underConstruction.size() == 3){
+        ++d_statistics.d_hasToBeMinimal;
+      }else{
+        ++d_statistics.d_maybeNotMinimal;
+      }
+    }else{
+      Trace("arith::greedyConflictSubsets") << "failure" << endl;
+    }
+    tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+    d_soiVar = ARITHVAR_SENTINEL;
+    // if(false && spoiler == NULL){
+    //   ArithVarVec tmp;
+    //   int smallest = tryAllSubsets(underConstruction, 0, tmp);
+    //   cout << underConstruction.size() << " " << smallest << endl;
+    //   Assert(smallest >= underConstruction.size());
+    //   if(smallest < underConstruction.size()){
+    //     exit(-1);
+    //   }
+    // }
+  }
+
+  Assert(d_soiVar == ARITHVAR_SENTINEL);
+  Trace("arith::greedyConflictSubsets") << "greedyConflictSubsets done" << endl;
+  return subsets;
+}
+
+bool SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){
+  Assert(d_soiVar == ARITHVAR_SENTINEL);
+  d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, subset);
+  Assert(!subset.empty());
+  Assert(!d_conflictBuilder->underConstruction());
+
+  Trace("arith::generateSOIConflict") << "SumOfInfeasibilitiesSPD::generateSOIConflict(...) start" << endl;
+
+  bool success = false;
+    
+  for(ArithVarVec::const_iterator iter = subset.begin(), end = subset.end(); iter != end; ++iter){
+    ArithVar e = *iter;
+    ConstraintP violated = d_errorSet.getViolated(e);
+    Assert(violated != NullConstraint);
+
+    int sgn = d_errorSet.getSgn(e);
+    const Rational& violatedCoeff = sgn > 0 ? d_negOne : d_posOne;
+    Trace("arith::generateSOIConflict") << "basic error var: "
+                                        << "(" <<  violatedCoeff << ")"
+                                        << " " << violated
+                                        << endl;
+
+
+    d_conflictBuilder->addConstraint(violated, violatedCoeff);
+    Assert(violated->hasProof());
+    if(!success && !violated->negationHasProof()){
+      success = true;
+      d_conflictBuilder->makeLastConsequent();
+    }
+  }
+  
+  if(!success){
+    // failure
+    d_conflictBuilder->reset();
+  } else {
+    // pick a violated constraint arbitrarily. any of them may be selected for the conflict
+    Assert(d_conflictBuilder->underConstruction());
+    Assert(d_conflictBuilder->consequentIsSet());
+
+    for(Tableau::RowIterator i = d_tableau.basicRowIterator(d_soiVar); !i.atEnd(); ++i){
+      const Tableau::Entry& entry = *i;
+      ArithVar v = entry.getColVar();
+      if(v == d_soiVar){ continue; }
+      const Rational& coeff = entry.getCoefficient();
+
+      ConstraintP c = (coeff.sgn() > 0) ?
+        d_variables.getUpperBoundConstraint(v) :
+        d_variables.getLowerBoundConstraint(v);
+      
+      Trace("arith::generateSOIConflict") << "non-basic var: "
+                                          << "(" <<  coeff << ")"
+                                          << " " << c
+                                          << endl;
+      d_conflictBuilder->addConstraint(c, coeff);
+    }
+    ConstraintCP conflicted = d_conflictBuilder->commitConflict();
+    d_conflictChannel.raiseConflict(conflicted,
+                                    InferenceId::ARITH_CONF_SOI_SIMPLEX);
+  }
+
+  tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+  d_soiVar = ARITHVAR_SENTINEL;
+  Trace("arith::generateSOIConflict") << "SumOfInfeasibilitiesSPD::generateSOIConflict(...) done" << endl;
+  Assert(d_soiVar == ARITHVAR_SENTINEL);
+  Assert(!d_conflictBuilder->underConstruction());
+  return success;
+}
+
+
+WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){
+  Trace("arith::SOIConflict") << "SumOfInfeasibilitiesSPD::SOIConflict() start "
+                              << ": |E| = " << d_errorSize << endl;
+  if(TraceIsOn("arith::SOIConflict")){
+    d_errorSet.debugPrint(cout);
+  }
+  Trace("arith::SOIConflict") << endl;
+
+  tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+  d_soiVar = ARITHVAR_SENTINEL;
+
+  if (options().arith.soiQuickExplain)
+  {
+    quickExplain();
+    generateSOIConflict(d_qeConflict);
+  }
+  else
+  {
+    vector<ArithVarVec> subsets = greedyConflictSubsets();
+    Assert(d_soiVar == ARITHVAR_SENTINEL);
+    bool anySuccess = false;
+    Assert(!subsets.empty());
+    for(vector<ArithVarVec>::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){
+      const ArithVarVec& subset = *i;
+      Assert(debugIsASet(subset));
+      anySuccess = generateSOIConflict(subset) || anySuccess;
+      //Node conflict = generateSOIConflict(subset);
+      //cout << conflict << endl;
+
+      //reportConflict(conf); do not do this. We need a custom explanations!
+      //d_conflictChannel(conflict);
+    }
+    Assert(anySuccess);
+  }
+  Assert(d_soiVar == ARITHVAR_SENTINEL);
+  d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization);
+
+  //reportConflict(conf); do not do this. We need a custom explanations!
+  d_conflictVariables.add(d_soiVar);
+
+  Trace("arith::SOIConflict")
+      << "SumOfInfeasibilitiesSPD::SOIConflict() end" << endl;
+  return ConflictFound;
+}
+
+WitnessImprovement SumOfInfeasibilitiesSPD::soiRound() {
+  Assert(d_soiVar != ARITHVAR_SENTINEL);
+
+  bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
+  LinearEqualityModule::UpdatePreferenceFunction upf;
+  if(useBlands) {
+    upf = &LinearEqualityModule::preferWitness<false>;
+  } else {
+    upf = &LinearEqualityModule::preferWitness<true>;
+  }
+
+  LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
+    &LinearEqualityModule::minVarOrder :
+    &LinearEqualityModule::minRowLength;
+  bpf = &LinearEqualityModule::minVarOrder;
+
+  UpdateInfo selected = selectUpdate(upf, bpf);
+
+  if(selected.uninitialized()){
+    Trace("selectFocusImproving") << "SOI is optimum, but we don't have sat/conflict yet" << endl;
+    return SOIConflict();
+  }else{
+    Assert(!selected.uninitialized());
+    WitnessImprovement w = selected.getWitness(false);
+    Assert(debugCheckWitness(selected, w, false));
+
+    updateAndSignal(selected, w);
+    logPivot(w);
+    return w;
+  }
+}
+
+Result::Status SumOfInfeasibilitiesSPD::sumOfInfeasibilities()
+{
+  TimerStat::CodeTimer codeTimer(d_statistics.d_soiTimer);
+
+  Assert(d_sgnDisagreements.empty());
+  Assert(d_pivotBudget != 0);
+  Assert(d_errorSize == d_errorSet.errorSize());
+  Assert(d_errorSize > 0);
+  Assert(d_conflictVariables.empty());
+  Assert(d_soiVar == ARITHVAR_SENTINEL);
+
+  //d_scores.purge();
+  d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer);
+
+
+  while(d_pivotBudget != 0  && d_errorSize > 0 && d_conflictVariables.empty()){
+    Trace("dualLike") << "dualLike" << endl;
+
+    Assert(d_errorSet.noSignals());
+    // Possible outcomes:
+    // - conflict
+    // - budget was exhausted
+    // - focus went down
+    WitnessImprovement w = soiRound();
+    Trace("dualLike") << "selectFocusImproving -> " << w << endl;
+
+    Assert(d_errorSize == d_errorSet.errorSize());
+  }
+
+
+  if(d_soiVar != ARITHVAR_SENTINEL){
+    tearDownInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer, d_soiVar);
+    d_soiVar = ARITHVAR_SENTINEL;
+  }
+
+  Assert(d_soiVar == ARITHVAR_SENTINEL);
+  if(!d_conflictVariables.empty()){
+    return Result::UNSAT;
+  }else if(d_errorSet.errorEmpty()){
+    Assert(d_errorSet.noSignals());
+    return Result::SAT;
+  }else{
+    Assert(d_pivotBudget == 0);
+    return Result::UNKNOWN;
+  }
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/soi_simplex.h b/src/theory/arith/linear/soi_simplex.h
new file mode 100644 (file)
index 0000000..c8cfc64
--- /dev/null
@@ -0,0 +1,227 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ *
+ * This implements the Simplex module for the Simpelx for DPLL(T) decision
+ * procedure.
+ * See the Simplex for DPLL(T) technical report for more background.(citation?)
+ * This shares with the theory a Tableau, and a PartialModel that:
+ *  - satisfies the equalities in the Tableau, and
+ *  - the assignment for the non-basic variables satisfies their bounds.
+ * This is required to either produce a conflict or satisifying PartialModel.
+ * Further, we require being told when a basic variable updates its value.
+ *
+ * During the Simplex search we maintain a queue of variables.
+ * The queue is required to contain all of the basic variables that voilate
+ * their bounds.
+ * As elimination from the queue is more efficient to be done lazily,
+ * we do not maintain that the queue of variables needs to be only basic
+ * variables or only variables that satisfy their bounds.
+ *
+ * The simplex procedure roughly follows Alberto's thesis. (citation?)
+ * There is one round of selecting using a heuristic pivoting rule.
+ * (See PreferenceFunction Documentation for the available options.)
+ * The non-basic variable is the one that appears in the fewest pivots.
+ * (Bruno says that Leonardo invented this first.)
+ * After this, Bland's pivot rule is invoked.
+ *
+ * During this proccess, we periodically inspect the queue of variables to
+ * 1) remove now extraneous extries,
+ * 2) detect conflicts that are "waiting" on the queue but may not be detected
+ *    by the current queue heuristics, and
+ * 3) detect multiple conflicts.
+ *
+ * Conflicts are greedily slackened to use the weakest bounds that still
+ * produce the conflict.
+ *
+ * Extra things tracked atm: (Subject to change at Tim's whims)
+ * - A superset of all of the newly pivoted variables.
+ * - A queue of additional conflicts that were discovered by Simplex.
+ *   These are theory valid and are currently turned into lemmas
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/simplex.h"
+#include "theory/arith/linear/simplex_update.h"
+#include "util/dense_map.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class SumOfInfeasibilitiesSPD : public SimplexDecisionProcedure {
+public:
+ SumOfInfeasibilitiesSPD(Env& env,
+                         LinearEqualityModule& linEq,
+                         ErrorSet& errors,
+                         RaiseConflict conflictChannel,
+                         TempVarMalloc tvmalloc);
+
+ Result::Status findModel(bool exactResult) override;
+
+ // other error variables are dropping
+ WitnessImprovement dualLikeImproveError(ArithVar evar);
+ WitnessImprovement primalImproveError(ArithVar evar);
+
+private:
+  /** The current sum of infeasibilities variable. */
+  ArithVar d_soiVar;
+
+  // dual like
+  // - found conflict
+  // - satisfied error set
+  Result::Status sumOfInfeasibilities();
+
+  int32_t d_pivotBudget;
+
+  WitnessImprovement d_prevWitnessImprovement;
+  uint32_t d_witnessImprovementInARow;
+
+  uint32_t degeneratePivotsInARow() const;
+
+  static constexpr uint32_t s_focusThreshold = 6;
+  static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnLeaving = 100;
+  static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnEntering = 10;
+
+  DenseMap<uint32_t> d_leavingCountSinceImprovement;
+  void increaseLeavingCount(ArithVar x){
+    if(!d_leavingCountSinceImprovement.isKey(x)){
+      d_leavingCountSinceImprovement.set(x,1);
+    }else{
+      (d_leavingCountSinceImprovement.get(x))++;
+    }
+  }
+  LinearEqualityModule::UpdatePreferenceFunction selectLeavingFunction(ArithVar x){
+    bool useBlands = d_leavingCountSinceImprovement.isKey(x) &&
+      d_leavingCountSinceImprovement[x] >= s_maxDegeneratePivotsBeforeBlandsOnEntering;
+    if(useBlands) {
+      return &LinearEqualityModule::preferWitness<false>;
+    } else {
+      return &LinearEqualityModule::preferWitness<true>;
+    }
+  }
+
+  void debugPrintSignal(ArithVar updated) const;
+
+  ArithVarVec d_sgnDisagreements;
+
+  void logPivot(WitnessImprovement w);
+
+  void updateAndSignal(const UpdateInfo& selected, WitnessImprovement w);
+
+  UpdateInfo selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf,
+                          LinearEqualityModule::VarPreferenceFunction bpf);
+
+
+  // UpdateInfo selectUpdateForDualLike(ArithVar basic){
+  //   TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForDualLike);
+
+  //   LinearEqualityModule::UpdatePreferenceFunction upf =
+  //     &LinearEqualityModule::preferWitness<true>;
+  //   LinearEqualityModule::VarPreferenceFunction bpf =
+  //     &LinearEqualityModule::minVarOrder;
+  //   return selectPrimalUpdate(basic, upf, bpf);
+  // }
+
+  // UpdateInfo selectUpdateForPrimal(ArithVar basic, bool useBlands){
+  //   TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForPrimal);
+
+  //   LinearEqualityModule::UpdatePreferenceFunction upf = useBlands ?
+  //     &LinearEqualityModule::preferWitness<false>:
+  //     &LinearEqualityModule::preferWitness<true>;
+
+  //   LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
+  //     &LinearEqualityModule::minVarOrder :
+  //     &LinearEqualityModule::minRowLength;
+  //   bpf = &LinearEqualityModule::minVarOrder;
+
+  //   return selectPrimalUpdate(basic, upf, bpf);
+  // }
+  // WitnessImprovement selectFocusImproving() ;
+  WitnessImprovement soiRound();
+  WitnessImprovement SOIConflict();
+  std::vector< ArithVarVec > greedyConflictSubsets();
+  bool generateSOIConflict(const ArithVarVec& subset);
+
+  // WitnessImprovement focusUsingSignDisagreements(ArithVar basic);
+  // WitnessImprovement focusDownToLastHalf();
+  // WitnessImprovement adjustFocusShrank(const ArithVarVec& drop);
+  // WitnessImprovement focusDownToJust(ArithVar v);
+
+
+  void adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges);
+
+  /**
+   * This is the main simplex for DPLL(T) loop.
+   * It runs for at most maxIterations.
+   *
+   * Returns true iff it has found a conflict.
+   * d_conflictVariable will be set and the conflict for this row is reported.
+   */
+  bool searchForFeasibleSolution(uint32_t maxIterations);
+
+  bool initialProcessSignals(){
+    TimerStat &timer = d_statistics.d_initialSignalsTime;
+    IntStat& conflictStat  = d_statistics.d_initialConflicts;
+    return standardProcessSignals(timer, conflictStat);
+  }
+
+  void quickExplain();
+  DenseSet d_qeInSoi;
+  DenseSet d_qeInUAndNotInSoi;
+  ArithVarVec d_qeConflict;
+  ArithVarVec d_qeGreedyOrder;
+  sgn_table d_qeSgns;
+
+  uint32_t quickExplainRec(uint32_t cEnd, uint32_t uEnd);
+  void qeAddRange(uint32_t begin, uint32_t end);
+  void qeRemoveRange(uint32_t begin, uint32_t end);
+  void qeSwapRange(uint32_t N, uint32_t r, uint32_t s);
+
+  unsigned trySet(const ArithVarVec& set);
+  unsigned tryAllSubsets(const ArithVarVec& set, unsigned depth, ArithVarVec& tmp);
+
+  /** These fields are designed to be accessible to TheoryArith methods. */
+  class Statistics {
+  public:
+    TimerStat d_initialSignalsTime;
+    IntStat d_initialConflicts;
+
+    IntStat d_soiFoundUnsat;
+    IntStat d_soiFoundSat;
+    IntStat d_soiMissed;
+
+    IntStat d_soiConflicts;
+    IntStat d_hasToBeMinimal;
+    IntStat d_maybeNotMinimal;
+
+    TimerStat d_soiTimer;
+    TimerStat d_soiFocusConstructionTimer;
+    TimerStat d_soiConflictMinimization;
+    TimerStat d_selectUpdateForSOI;
+
+    ReferenceStat<uint32_t> d_finalCheckPivotCounter;
+
+    Statistics(const std::string& name, uint32_t& pivots);
+  } d_statistics;
+};/* class FCSimplexDecisionProcedure */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/tableau.cpp b/src/theory/arith/linear/tableau.cpp
new file mode 100644 (file)
index 0000000..324f5df
--- /dev/null
@@ -0,0 +1,196 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "base/output.h"
+#include "theory/arith/linear/tableau.h"
+
+using namespace std;
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+
+void Tableau::pivot(ArithVar oldBasic, ArithVar newBasic, CoefficientChangeCallback& cb){
+  Assert(isBasic(oldBasic));
+  Assert(!isBasic(newBasic));
+  Assert(d_mergeBuffer.empty());
+
+  Trace("tableau") << "Tableau::pivot(" <<  oldBasic <<", " << newBasic <<")"  << endl;
+
+  RowIndex ridx = basicToRowIndex(oldBasic);
+
+  rowPivot(oldBasic, newBasic, cb);
+  Assert(ridx == basicToRowIndex(newBasic));
+
+  loadRowIntoBuffer(ridx);
+
+  ColIterator colIter = colIterator(newBasic);
+  while(!colIter.atEnd()){
+    EntryID id = colIter.getID();
+    Entry& entry = d_entries.get(id);
+
+    ++colIter; //needs to be incremented before the variable is removed
+    if(entry.getRowIndex() == ridx){ continue; }
+
+    RowIndex to = entry.getRowIndex();
+    Rational coeff = entry.getCoefficient();
+    if(cb.canUseRow(to)){
+      rowPlusBufferTimesConstant(to, coeff, cb);
+    }else{
+      rowPlusBufferTimesConstant(to, coeff);
+    }
+  }
+  clearBuffer();
+
+  //Clear the column for used for this variable
+
+  Assert(d_mergeBuffer.empty());
+  Assert(!isBasic(oldBasic));
+  Assert(isBasic(newBasic));
+  Assert(getColLength(newBasic) == 1);
+}
+
+/**
+ * Changes basic to newbasic (a variable on the row).
+ */
+void Tableau::rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb){
+  Assert(isBasic(basicOld));
+  Assert(!isBasic(basicNew));
+
+  RowIndex rid = basicToRowIndex(basicOld);
+
+  EntryID newBasicID = findOnRow(rid, basicNew);
+
+  Assert(newBasicID != ENTRYID_SENTINEL);
+
+  Tableau::Entry& newBasicEntry = d_entries.get(newBasicID);
+  const Rational& a_rs = newBasicEntry.getCoefficient();
+  int a_rs_sgn = a_rs.sgn();
+  Rational negInverseA_rs = -(a_rs.inverse());
+
+  for(RowIterator i = basicRowIterator(basicOld); !i.atEnd(); ++i){
+    EntryID id = i.getID();
+    Tableau::Entry& entry = d_entries.get(id);
+
+    entry.getCoefficient() *=  negInverseA_rs;
+  }
+
+  d_basic2RowIndex.remove(basicOld);
+  d_basic2RowIndex.set(basicNew, rid);
+  d_rowIndex2basic.set(rid, basicNew);
+
+  cb.multiplyRow(rid, -a_rs_sgn);
+}
+
+void Tableau::addRow(ArithVar basic,
+                     const std::vector<Rational>& coefficients,
+                     const std::vector<ArithVar>& variables)
+{
+  Assert(basic < getNumColumns());
+  Assert(debugIsASet(variables));
+  Assert(coefficients.size() == variables.size());
+  Assert(!isBasic(basic));
+
+  RowIndex newRow = Matrix<Rational>::addRow(coefficients, variables);
+  addEntry(newRow, basic, Rational(-1));
+
+  Assert(!d_basic2RowIndex.isKey(basic));
+  Assert(!d_rowIndex2basic.isKey(newRow));
+
+  d_basic2RowIndex.set(basic, newRow);
+  d_rowIndex2basic.set(newRow, basic);
+
+
+  if(TraceIsOn("matrix")){ printMatrix(); }
+
+  NoEffectCCCB noeffect;
+  NoEffectCCCB* nep = &noeffect;
+  CoefficientChangeCallback* cccb = static_cast<CoefficientChangeCallback*>(nep);
+
+  vector<Rational>::const_iterator coeffIter = coefficients.begin();
+  vector<ArithVar>::const_iterator varsIter = variables.begin();
+  vector<ArithVar>::const_iterator varsEnd = variables.end();
+  for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
+    ArithVar var = *varsIter;
+
+    if(isBasic(var)){
+      Rational coeff = *coeffIter;
+
+      RowIndex ri = basicToRowIndex(var);
+
+      loadRowIntoBuffer(ri);
+      rowPlusBufferTimesConstant(newRow, coeff, *cccb);
+      clearBuffer();
+    }
+  }
+
+  if(TraceIsOn("matrix")) { printMatrix(); }
+
+  Assert(debugNoZeroCoefficients(newRow));
+  Assert(debugMatchingCountsForRow(newRow));
+  Assert(getColLength(basic) == 1);
+}
+
+void Tableau::removeBasicRow(ArithVar basic){
+  RowIndex rid = basicToRowIndex(basic);
+
+  removeRow(rid);
+  d_basic2RowIndex.remove(basic);
+  d_rowIndex2basic.remove(rid);
+}
+
+void Tableau::substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult,  CoefficientChangeCallback& cb){
+  if(!mult.isZero()){
+    RowIndex to_idx = basicToRowIndex(to);
+    addEntry(to_idx, from, mult); // Add an entry to be cancelled out
+    RowIndex from_idx = basicToRowIndex(from);
+
+    cb.update(to_idx, from, 0, mult.sgn());
+
+    loadRowIntoBuffer(from_idx);
+    rowPlusBufferTimesConstant(to_idx, mult, cb);
+    clearBuffer();
+  }
+}
+
+uint32_t Tableau::rowComplexity(ArithVar basic) const{
+  uint32_t complexity = 0;
+  for(RowIterator i = basicRowIterator(basic); !i.atEnd(); ++i){
+    const Entry& e = *i;
+    complexity += e.getCoefficient().complexity();
+  }
+  return complexity;
+}
+
+double Tableau::avgRowComplexity() const{
+  double sum = 0;
+  uint32_t rows = 0;
+  for(BasicIterator i = beginBasic(), i_end = endBasic(); i != i_end; ++i){
+    sum += rowComplexity(*i);
+    rows++;
+  }
+  return (rows == 0) ? 0 : (sum/rows);
+}
+
+void Tableau::printBasicRow(ArithVar basic, std::ostream& out){
+  printRow(basicToRowIndex(basic), out);
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/tableau.h b/src/theory/arith/linear/tableau.h
new file mode 100644 (file)
index 0000000..7bbc0d6
--- /dev/null
@@ -0,0 +1,163 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <vector>
+
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/matrix.h"
+#include "util/dense_map.h"
+#include "util/rational.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/**
+ * A Tableau is a Rational matrix that keeps its rows in solved form.
+ * Each row has a basic variable with coefficient -1 that is solved.
+ * Tableau is optimized for pivoting.
+ * The tableau should only be updated via pivot calls.
+ */
+class Tableau : public Matrix<Rational> {
+public:
+private:
+  typedef DenseMap<RowIndex> BasicToRowMap;
+  // Set of all of the basic variables in the tableau.
+  // ArithVarMap<RowIndex> : ArithVar |-> RowIndex
+  BasicToRowMap d_basic2RowIndex;
+
+  // RowIndex |-> Basic Variable
+  typedef DenseMap<ArithVar> RowIndexToBasicMap;
+  RowIndexToBasicMap d_rowIndex2basic;
+
+public:
+
+  Tableau() : Matrix<Rational>(Rational(0)) {}
+
+  typedef Matrix<Rational>::ColIterator ColIterator;
+  typedef Matrix<Rational>::RowIterator RowIterator;
+  typedef BasicToRowMap::const_iterator BasicIterator;
+
+  typedef MatrixEntry<Rational> Entry;
+
+  bool isBasic(ArithVar v) const{
+    return d_basic2RowIndex.isKey(v);
+  }
+
+  void debugPrintIsBasic(ArithVar v) const {
+    if(isBasic(v)){
+      Trace("model") << v << " is basic." << std::endl;
+    }else{
+      Trace("model") << v << " is non-basic." << std::endl;
+    }
+  }
+
+  BasicIterator beginBasic() const {
+    return d_basic2RowIndex.begin();
+  }
+  BasicIterator endBasic() const {
+    return d_basic2RowIndex.end();
+  }
+
+  RowIndex basicToRowIndex(ArithVar x) const {
+    return d_basic2RowIndex[x];
+  }
+
+  ArithVar rowIndexToBasic(RowIndex rid) const {
+    Assert(d_rowIndex2basic.isKey(rid));
+    return d_rowIndex2basic[rid];
+  }
+
+  ColIterator colIterator(ArithVar x) const {
+    return getColumn(x).begin();
+  }
+
+  RowIterator ridRowIterator(RowIndex rid) const {
+    return getRow(rid).begin();
+  }
+
+  RowIterator basicRowIterator(ArithVar basic) const {
+    return ridRowIterator(basicToRowIndex(basic));
+  }
+
+  const Entry& basicFindEntry(ArithVar basic, ArithVar col) const {
+    return findEntry(basicToRowIndex(basic), col);
+  }
+
+  /**
+   * Adds a row to the tableau.
+   * The new row is equivalent to:
+   *   basicVar = \f$\sum_i\f$ coeffs[i] * variables[i]
+   * preconditions:
+   *   basicVar is already declared to be basic
+   *   basicVar does not have a row associated with it in the tableau.
+   *
+   * Note: each variables[i] does not have to be non-basic.
+   * Pivoting will be mimicked if it is basic.
+   */
+  void addRow(ArithVar basicVar,
+              const std::vector<Rational>& coeffs,
+              const std::vector<ArithVar>& variables);
+
+  /**
+   * preconditions:
+   *   x_r is basic,
+   *   x_s is non-basic, and
+   *   a_rs != 0.
+   */
+  void pivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb);
+
+  void removeBasicRow(ArithVar basic);
+
+  uint32_t basicRowLength(ArithVar basic) const{
+    RowIndex ridx = basicToRowIndex(basic);
+    return getRowLength(ridx);
+  }
+
+  /**
+   *  to += mult * from
+   * replacing from with its row.
+   */
+  void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult,  CoefficientChangeCallback& cb);
+
+  void directlyAddToCoefficient(ArithVar rowVar, ArithVar col, const Rational& mult,  CoefficientChangeCallback& cb){
+    RowIndex ridx = basicToRowIndex(rowVar);
+    manipulateRowEntry(ridx, col, mult, cb);
+  }
+
+  /* Returns the complexity of a row in the tableau. */
+  uint32_t rowComplexity(ArithVar basic) const;
+
+  /* Returns the average complexity of the rows in the tableau. */
+  double avgRowComplexity() const;
+
+  void printBasicRow(ArithVar basic, std::ostream& out);
+
+private:
+  /* Changes the basic variable on the row for basicOld to basicNew. */
+  void rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb);
+
+};/* class Tableau */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/tableau_sizes.cpp b/src/theory/arith/linear/tableau_sizes.cpp
new file mode 100644 (file)
index 0000000..a7c8f9e
--- /dev/null
@@ -0,0 +1,37 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "base/output.h"
+#include "theory/arith/linear/tableau_sizes.h"
+#include "theory/arith/linear/tableau.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+uint32_t TableauSizes::getRowLength(ArithVar b) const {
+  return d_tab->basicRowLength(b);
+}
+
+uint32_t TableauSizes::getColumnLength(ArithVar x) const {
+  return d_tab->getColLength(x);
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/tableau_sizes.h b/src/theory/arith/linear/tableau_sizes.h
new file mode 100644 (file)
index 0000000..3fbd0c1
--- /dev/null
@@ -0,0 +1,43 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "theory/arith/linear/arithvar.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class Tableau;
+
+class TableauSizes {
+private:
+  const Tableau* d_tab;
+public:
+  TableauSizes(const Tableau* tab): d_tab(tab){}
+
+  uint32_t getRowLength(ArithVar b) const;
+  uint32_t getColumnLength(ArithVar x) const;
+}; /* TableauSizes */
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/theory_arith_private.cpp b/src/theory/arith/linear/theory_arith_private.cpp
new file mode 100644 (file)
index 0000000..04ebdef
--- /dev/null
@@ -0,0 +1,4999 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Gereon Kremer, Alex Ozdemir
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/theory_arith_private.h"
+
+#include <map>
+#include <optional>
+#include <queue>
+#include <vector>
+
+#include "base/output.h"
+#include "context/cdhashset.h"
+#include "context/cdinsert_hashmap.h"
+#include "context/cdlist.h"
+#include "context/cdqueue.h"
+#include "context/context.h"
+#include "expr/kind.h"
+#include "expr/metakind.h"
+#include "expr/node.h"
+#include "expr/node_algorithm.h"
+#include "expr/node_builder.h"
+#include "expr/skolem_manager.h"
+#include "options/arith_options.h"
+#include "options/base_options.h"
+#include "options/smt_options.h"
+#include "preprocessing/util/ite_utilities.h"
+#include "proof/proof_generator.h"
+#include "proof/proof_node_manager.h"
+#include "proof/proof_rule.h"
+#include "smt/logic_exception.h"
+#include "smt/smt_statistics_registry.h"
+#include "smt_util/boolean_simplification.h"
+#include "theory/arith/linear/approx_simplex.h"
+#include "theory/arith/arith_rewriter.h"
+#include "theory/arith/linear/arith_static_learner.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/congruence_manager.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/cut_log.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/linear/dio_solver.h"
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/matrix.h"
+#include "theory/arith/nl/nonlinear_extension.h"
+#include "theory/arith/linear/normal_form.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/arith/linear/simplex.h"
+#include "theory/arith/theory_arith.h"
+#include "theory/ext_theory.h"
+#include "theory/quantifiers/fmf/bounded_integers.h"
+#include "theory/rewriter.h"
+#include "theory/theory_model.h"
+#include "theory/trust_substitutions.h"
+#include "theory/valuation.h"
+#include "util/dense_map.h"
+#include "util/integer.h"
+#include "util/random.h"
+#include "util/rational.h"
+#include "util/result.h"
+#include "util/statistics_stats.h"
+
+using namespace std;
+using namespace cvc5::internal::kind;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+static Node toSumNode(const ArithVariables& vars, const DenseMap<Rational>& sum);
+static bool complexityBelow(const DenseMap<Rational>& row, uint32_t cap);
+
+TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing,
+                                       Env& env,
+                                       BranchAndBound& bab)
+    : EnvObj(env),
+      d_containing(containing),
+      d_foundNl(false),
+      d_rowTracking(),
+      d_bab(bab),
+      d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
+                                           : nullptr),
+      d_checker(),
+      d_pfGen(new EagerProofGenerator(d_pnm, userContext())),
+      d_constraintDatabase(d_env,
+                           d_partialModel,
+                           d_congruenceManager,
+                           RaiseConflict(*this),
+                           d_pfGen.get()),
+      d_qflraStatus(Result::UNKNOWN),
+      d_unknownsInARow(0),
+      d_hasDoneWorkSinceCut(false),
+      d_learner(userContext()),
+      d_assertionsThatDoNotMatchTheirLiterals(context()),
+      d_nextIntegerCheckVar(0),
+      d_constantIntegerVariables(context()),
+      d_diseqQueue(context(), false),
+      d_currentPropagationList(),
+      d_learnedBounds(context()),
+      d_preregisteredNodes(context()),
+      d_partialModel(context(), DeltaComputeCallback(*this)),
+      d_errorSet(
+          d_partialModel, TableauSizes(&d_tableau), BoundCountingLookup(*this)),
+      d_tableau(),
+      d_linEq(d_partialModel,
+              d_tableau,
+              d_rowTracking,
+              BasicVarModelUpdateCallBack(*this)),
+      d_diosolver(env),
+      d_restartsCounter(0),
+      d_tableauSizeHasBeenModified(false),
+      d_tableauResetDensity(1.6),
+      d_tableauResetPeriod(10),
+      d_conflicts(context()),
+      d_blackBoxConflict(context(), Node::null()),
+      d_blackBoxConflictPf(context(), std::shared_ptr<ProofNode>(nullptr)),
+      d_congruenceManager(d_env,
+                          d_constraintDatabase,
+                          SetupLiteralCallBack(*this),
+                          d_partialModel,
+                          RaiseEqualityEngineConflict(*this)),
+      d_cmEnabled(context(), options().arith.arithCongMan),
+
+      d_dualSimplex(
+          env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+      d_fcSimplex(
+          env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+      d_soiSimplex(
+          env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+      d_attemptSolSimplex(
+          env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+      d_pass1SDP(NULL),
+      d_otherSDP(NULL),
+      d_lastContextIntegerAttempted(context(), -1),
+
+      d_DELTA_ZERO(0),
+      d_approxCuts(context()),
+      d_fullCheckCounter(0),
+      d_cutCount(context(), 0),
+      d_cutInContext(context()),
+      d_likelyIntegerInfeasible(context(), false),
+      d_guessedCoeffSet(context(), false),
+      d_guessedCoeffs(),
+      d_treeLog(NULL),
+      d_replayVariables(),
+      d_replayConstraints(),
+      d_lhsTmp(),
+      d_approxStats(NULL),
+      d_attemptSolveIntTurnedOff(userContext(), 0),
+      d_dioSolveResources(0),
+      d_solveIntMaybeHelp(0u),
+      d_solveIntAttempts(0u),
+      d_newFacts(false),
+      d_previousStatus(Result::UNKNOWN),
+      d_statistics(statisticsRegistry(), "theory::arith::")
+{
+}
+
+TheoryArithPrivate::~TheoryArithPrivate(){
+  if(d_treeLog != NULL){ delete d_treeLog; }
+  if(d_approxStats != NULL) { delete d_approxStats; }
+}
+
+bool TheoryArithPrivate::needsEqualityEngine(EeSetupInfo& esi)
+{
+  if (!d_cmEnabled)
+  {
+    return false;
+  }
+  return d_congruenceManager.needsEqualityEngine(esi);
+}
+void TheoryArithPrivate::finishInit()
+{
+  if (d_cmEnabled)
+  {
+    eq::EqualityEngine* ee = d_containing.getEqualityEngine();
+    Assert(ee != nullptr);
+    d_congruenceManager.finishInit(ee);
+  }
+}
+
+static bool contains(const ConstraintCPVec& v, ConstraintP con){
+  for(unsigned i = 0, N = v.size(); i < N; ++i){
+    if(v[i] == con){
+      return true;
+    }
+  }
+  return false;
+}
+static void drop( ConstraintCPVec& v, ConstraintP con){
+  size_t readPos, writePos, N;
+  for(readPos = 0, writePos = 0, N = v.size(); readPos < N; ++readPos){
+    ConstraintCP curr = v[readPos];
+    if(curr != con){
+      v[writePos] = curr;
+      writePos++;
+    }
+  }
+  v.resize(writePos);
+}
+
+
+static void resolve(ConstraintCPVec& buf, ConstraintP c, const ConstraintCPVec& pos, const ConstraintCPVec& neg){
+  unsigned posPos CVC5_UNUSED = pos.size();
+  for(unsigned i = 0, N = pos.size(); i < N; ++i){
+    if(pos[i] == c){
+      posPos = i;
+    }else{
+      buf.push_back(pos[i]);
+    }
+  }
+  Assert(posPos < pos.size());
+  ConstraintP negc = c->getNegation();
+  unsigned negPos CVC5_UNUSED = neg.size();
+  for(unsigned i = 0, N = neg.size(); i < N; ++i){
+    if(neg[i] == negc){
+      negPos = i;
+    }else{
+      buf.push_back(neg[i]);
+    }
+  }
+  Assert(negPos < neg.size());
+}
+
+TheoryArithPrivate::ModelException::ModelException(TNode n, const char* msg)
+{
+  stringstream ss;
+  ss << "Cannot construct a model for " << n << " as " << endl << msg;
+  setMessage(ss.str());
+}
+TheoryArithPrivate::ModelException::~ModelException() {}
+
+TheoryArithPrivate::Statistics::Statistics(StatisticsRegistry& reg,
+                                           const std::string& name)
+    : d_statAssertUpperConflicts(
+        reg.registerInt(name + "AssertUpperConflicts")),
+      d_statAssertLowerConflicts(
+          reg.registerInt(name + "AssertLowerConflicts")),
+      d_statUserVariables(reg.registerInt(name + "UserVariables")),
+      d_statAuxiliaryVariables(reg.registerInt(name + "AuxiliaryVariables")),
+      d_statDisequalitySplits(reg.registerInt(name + "DisequalitySplits")),
+      d_statDisequalityConflicts(
+          reg.registerInt(name + "DisequalityConflicts")),
+      d_simplifyTimer(reg.registerTimer(name + "simplifyTimer")),
+      d_staticLearningTimer(reg.registerTimer(name + "staticLearningTimer")),
+      d_presolveTime(reg.registerTimer(name + "presolveTime")),
+      d_newPropTime(reg.registerTimer(name + "newPropTimer")),
+      d_externalBranchAndBounds(
+          reg.registerInt(name + "externalBranchAndBounds")),
+      d_initialTableauSize(reg.registerInt(name + "initialTableauSize")),
+      d_currSetToSmaller(reg.registerInt(name + "currSetToSmaller")),
+      d_smallerSetToCurr(reg.registerInt(name + "smallerSetToCurr")),
+      d_restartTimer(reg.registerTimer(name + "restartTimer")),
+      d_boundComputationTime(reg.registerTimer(name + "bound::time")),
+      d_boundComputations(reg.registerInt(name + "bound::boundComputations")),
+      d_boundPropagations(reg.registerInt(name + "bound::boundPropagations")),
+      d_unknownChecks(reg.registerInt(name + "status::unknowns")),
+      d_maxUnknownsInARow(reg.registerInt(name + "status::maxUnknownsInARow")),
+      d_avgUnknownsInARow(
+          reg.registerAverage(name + "status::avgUnknownsInARow")),
+      d_revertsOnConflicts(
+          reg.registerInt(name + "status::revertsOnConflicts")),
+      d_commitsOnConflicts(
+          reg.registerInt(name + "status::commitsOnConflicts")),
+      d_nontrivialSatChecks(
+          reg.registerInt(name + "status::nontrivialSatChecks")),
+      d_replayLogRecCount(reg.registerInt(name + "z::approx::replay::rec")),
+      d_replayLogRecConflictEscalation(
+          reg.registerInt(name + "z::approx::replay::rec::escalation")),
+      d_replayLogRecEarlyExit(
+          reg.registerInt(name + "z::approx::replay::rec::earlyexit")),
+      d_replayBranchCloseFailures(reg.registerInt(
+          name + "z::approx::replay::rec::branch::closefailures")),
+      d_replayLeafCloseFailures(reg.registerInt(
+          name + "z::approx::replay::rec::leaf::closefailures")),
+      d_replayBranchSkips(
+          reg.registerInt(name + "z::approx::replay::rec::branch::skips")),
+      d_mirCutsAttempted(
+          reg.registerInt(name + "z::approx::cuts::mir::attempted")),
+      d_gmiCutsAttempted(
+          reg.registerInt(name + "z::approx::cuts::gmi::attempted")),
+      d_branchCutsAttempted(
+          reg.registerInt(name + "z::approx::cuts::branch::attempted")),
+      d_cutsReconstructed(
+          reg.registerInt(name + "z::approx::cuts::reconstructed")),
+      d_cutsReconstructionFailed(
+          reg.registerInt(name + "z::approx::cuts::reconstructed::failed")),
+      d_cutsProven(reg.registerInt(name + "z::approx::cuts::proofs")),
+      d_cutsProofFailed(
+          reg.registerInt(name + "z::approx::cuts::proofs::failed")),
+      d_mipReplayLemmaCalls(
+          reg.registerInt(name + "z::approx::external::calls")),
+      d_mipExternalCuts(reg.registerInt(name + "z::approx::external::cuts")),
+      d_mipExternalBranch(
+          reg.registerInt(name + "z::approx::external::branches")),
+      d_inSolveInteger(reg.registerInt(name + "z::approx::inSolverInteger")),
+      d_branchesExhausted(
+          reg.registerInt(name + "z::approx::exhausted::branches")),
+      d_execExhausted(reg.registerInt(name + "z::approx::exhausted::exec")),
+      d_pivotsExhausted(reg.registerInt(name + "z::approx::exhausted::pivots")),
+      d_panicBranches(reg.registerInt(name + "z::arith::paniclemmas")),
+      d_relaxCalls(reg.registerInt(name + "z::arith::relax::calls")),
+      d_relaxLinFeas(reg.registerInt(name + "z::arith::relax::feasible::res")),
+      d_relaxLinFeasFailures(
+          reg.registerInt(name + "z::arith::relax::feasible::failures")),
+      d_relaxLinInfeas(reg.registerInt(name + "z::arith::relax::infeasible")),
+      d_relaxLinInfeasFailures(
+          reg.registerInt(name + "z::arith::relax::infeasible::failures")),
+      d_relaxLinExhausted(reg.registerInt(name + "z::arith::relax::exhausted")),
+      d_relaxOthers(reg.registerInt(name + "z::arith::relax::other")),
+      d_applyRowsDeleted(
+          reg.registerInt(name + "z::arith::cuts::applyRowsDeleted")),
+      d_replaySimplexTimer(
+          reg.registerTimer(name + "z::approx::replay::simplex::timer")),
+      d_replayLogTimer(
+          reg.registerTimer(name + "z::approx::replay::log::timer")),
+      d_solveIntTimer(reg.registerTimer(name + "z::solveInt::timer")),
+      d_solveRealRelaxTimer(
+          reg.registerTimer(name + "z::solveRealRelax::timer")),
+      d_solveIntCalls(reg.registerInt(name + "z::solveInt::calls")),
+      d_solveStandardEffort(
+          reg.registerInt(name + "z::solveInt::calls::standardEffort")),
+      d_approxDisabled(reg.registerInt(name + "z::approxDisabled")),
+      d_replayAttemptFailed(reg.registerInt(name + "z::replayAttemptFailed")),
+      d_cutsRejectedDuringReplay(
+          reg.registerInt(name + "z::approx::replay::cuts::rejected")),
+      d_cutsRejectedDuringLemmas(
+          reg.registerInt(name + "z::approx::external::cuts::rejected")),
+      d_satPivots(reg.registerHistogram<uint32_t>(name + "pivots::sat")),
+      d_unsatPivots(reg.registerHistogram<uint32_t>(name + "pivots::unsat")),
+      d_unknownPivots(
+          reg.registerHistogram<uint32_t>(name + "pivots::unknown")),
+      d_solveIntModelsAttempts(
+          reg.registerInt(name + "z::solveInt::models::attempts")),
+      d_solveIntModelsSuccessful(
+          reg.registerInt(name + "zzz::solveInt::models::successful")),
+      d_mipTimer(reg.registerTimer(name + "z::approx::mip::timer")),
+      d_lpTimer(reg.registerTimer(name + "z::approx::lp::timer")),
+      d_mipProofsAttempted(reg.registerInt(name + "z::mip::proofs::attempted")),
+      d_mipProofsSuccessful(
+          reg.registerInt(name + "z::mip::proofs::successful")),
+      d_numBranchesFailed(
+          reg.registerInt(name + "z::mip::branch::proof::failed"))
+{
+}
+
+bool complexityBelow(const DenseMap<Rational>& row, uint32_t cap){
+  DenseMap<Rational>::const_iterator riter, rend;
+  for(riter=row.begin(), rend=row.end(); riter != rend; ++riter){
+    ArithVar v = *riter;
+    const Rational& q = row[v];
+    if(q.complexity() > cap){
+      return false;
+    }
+  }
+  return true;
+}
+
+bool TheoryArithPrivate::isProofEnabled() const
+{
+  return d_pnm != nullptr;
+}
+
+void TheoryArithPrivate::raiseConflict(ConstraintCP a, InferenceId id){
+  Assert(a->inConflict());
+  Assert(id != InferenceId::UNKNOWN)
+      << "Must provide an inference id in TheoryArithPrivate::raiseConflict";
+  d_conflicts.push_back(std::make_pair(a, id));
+}
+
+void TheoryArithPrivate::raiseBlackBoxConflict(Node bb,
+                                               std::shared_ptr<ProofNode> pf)
+{
+  Trace("arith::bb") << "raiseBlackBoxConflict: " << bb << std::endl;
+  if (d_blackBoxConflict.get().isNull())
+  {
+    if (isProofEnabled())
+    {
+      Trace("arith::bb") << "  with proof " << pf << std::endl;
+      d_blackBoxConflictPf.set(pf);
+    }
+    d_blackBoxConflict = bb;
+  }
+}
+
+bool TheoryArithPrivate::anyConflict() const
+{
+  return !conflictQueueEmpty() || !d_blackBoxConflict.get().isNull();
+}
+
+void TheoryArithPrivate::revertOutOfConflict(){
+  d_partialModel.revertAssignmentChanges();
+  clearUpdates();
+  d_currentPropagationList.clear();
+}
+
+void TheoryArithPrivate::clearUpdates(){
+  d_updatedBounds.purge();
+}
+
+void TheoryArithPrivate::zeroDifferenceDetected(ArithVar x){
+  if(d_cmEnabled){
+    Assert(d_congruenceManager.isWatchedVariable(x));
+    Assert(d_partialModel.upperBoundIsZero(x));
+    Assert(d_partialModel.lowerBoundIsZero(x));
+
+    ConstraintP lb = d_partialModel.getLowerBoundConstraint(x);
+    ConstraintP ub = d_partialModel.getUpperBoundConstraint(x);
+
+    if(lb->isEquality()){
+      d_congruenceManager.watchedVariableIsZero(lb);
+    }else if(ub->isEquality()){
+      d_congruenceManager.watchedVariableIsZero(ub);
+    }else{
+      d_congruenceManager.watchedVariableIsZero(lb, ub);
+    }
+  }
+}
+
+bool TheoryArithPrivate::getSolveIntegerResource(){
+  if(d_attemptSolveIntTurnedOff > 0){
+    d_attemptSolveIntTurnedOff = d_attemptSolveIntTurnedOff - 1;
+    return false;
+  }else{
+    return true;
+  }
+}
+
+bool TheoryArithPrivate::getDioCuttingResource(){
+  if(d_dioSolveResources > 0){
+    d_dioSolveResources--;
+    if(d_dioSolveResources == 0){
+      d_dioSolveResources = -options().arith.rrTurns;
+    }
+    return true;
+  }else{
+    d_dioSolveResources++;
+    if(d_dioSolveResources >= 0){
+      d_dioSolveResources = options().arith.dioSolverTurns;
+    }
+    return false;
+  }
+}
+
+/* procedure AssertLower( x_i >= c_i ) */
+bool TheoryArithPrivate::AssertLower(ConstraintP constraint){
+  Assert(constraint != NullConstraint);
+  Assert(constraint->isLowerBound());
+  Assert(constraint->isTrue());
+  Assert(!constraint->negationHasProof());
+
+  ArithVar x_i = constraint->getVariable();
+  const DeltaRational& c_i = constraint->getValue();
+
+  Trace("arith") << "AssertLower(" << x_i << " " << c_i << ")"<< std::endl;
+
+  Assert(!isInteger(x_i) || c_i.isIntegral());
+
+  //TODO Relax to less than?
+  if(d_partialModel.lessThanLowerBound(x_i, c_i)){
+    return false; //sat
+  }
+
+  int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
+  if(cmpToUB > 0){ //  c_i < \lowerbound(x_i)
+    ConstraintP ubc = d_partialModel.getUpperBoundConstraint(x_i);
+    ConstraintP negation = constraint->getNegation();
+    negation->impliedByUnate(ubc, true);
+
+    raiseConflict(constraint, InferenceId::ARITH_CONF_LOWER);
+
+    ++(d_statistics.d_statAssertLowerConflicts);
+    return true;
+  }else if(cmpToUB == 0){
+    if(isInteger(x_i)){
+      d_constantIntegerVariables.push_back(x_i);
+      Trace("dio::push") << "dio::push " << x_i << endl;
+    }
+    ConstraintP ub = d_partialModel.getUpperBoundConstraint(x_i);
+
+    if(d_cmEnabled){
+      if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){
+        // if it is not a watched variable report it
+        // if it is is a watched variable and c_i == 0,
+        // let zeroDifferenceDetected(x_i) catch this
+        d_congruenceManager.equalsConstant(constraint, ub);
+      }
+    }
+
+    const ValueCollection& vc = constraint->getValueCollection();
+    if(vc.hasEquality()){
+      Assert(vc.hasDisequality());
+      ConstraintP eq = vc.getEquality();
+      ConstraintP diseq = vc.getDisequality();
+      // x <= b, x >= b |= x = b
+      // (x > b or x < b or x = b)
+      Trace("arith::eq") << "lb == ub, propagate eq" << eq << endl;
+      bool triConflict = diseq->isTrue();
+
+      if(!eq->isTrue()){
+        eq->impliedByTrichotomy(constraint, ub, triConflict);
+        eq->tryToPropagate();
+      }
+      if(triConflict){
+        ++(d_statistics.d_statDisequalityConflicts);
+        raiseConflict(eq, InferenceId::ARITH_CONF_TRICHOTOMY);
+        return true;
+      }
+    }
+  }else{
+    // l <= x <= u and l < u
+    Assert(cmpToUB < 0);
+    const ValueCollection& vc = constraint->getValueCollection();
+
+    if(vc.hasDisequality()){
+      const ConstraintP diseq = vc.getDisequality();
+      if(diseq->isTrue()){
+        const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
+        ConstraintP negUb = ub->getNegation();
+
+        // l <= x, l != x |= l < x
+        // |= not (l >= x)
+        bool ubInConflict = ub->hasProof();
+        bool learnNegUb = !(negUb->hasProof());
+        if(learnNegUb){
+          negUb->impliedByTrichotomy(constraint, diseq, ubInConflict);
+          negUb->tryToPropagate();
+        }
+        if(ubInConflict){
+          raiseConflict(ub, InferenceId::ARITH_CONF_TRICHOTOMY);
+          return true;
+        }else if(learnNegUb){
+          d_learnedBounds.push_back(negUb);
+        }
+      }
+    }
+  }
+
+  d_currentPropagationList.push_back(constraint);
+  d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
+
+  d_partialModel.setLowerBoundConstraint(constraint);
+
+  if(d_cmEnabled){
+    if(d_congruenceManager.isWatchedVariable(x_i)){
+      int sgn = c_i.sgn();
+      if(sgn > 0){
+        d_congruenceManager.watchedVariableCannotBeZero(constraint);
+      }else if(sgn == 0 && d_partialModel.upperBoundIsZero(x_i)){
+        zeroDifferenceDetected(x_i);
+      }
+    }
+  }
+
+  d_updatedBounds.softAdd(x_i);
+
+  if(TraceIsOn("model")) {
+    Trace("model") << "before" << endl;
+    d_partialModel.printModel(x_i);
+    d_tableau.debugPrintIsBasic(x_i);
+  }
+
+  if(!d_tableau.isBasic(x_i)){
+    if(d_partialModel.getAssignment(x_i) < c_i){
+      d_linEq.update(x_i, c_i);
+    }
+  }else{
+    d_errorSet.signalVariable(x_i);
+  }
+
+  if(TraceIsOn("model")) {
+    Trace("model") << "after" << endl;
+    d_partialModel.printModel(x_i);
+    d_tableau.debugPrintIsBasic(x_i);
+ }
+
+  return false; //sat
+}
+
+/* procedure AssertUpper( x_i <= c_i) */
+bool TheoryArithPrivate::AssertUpper(ConstraintP constraint){
+  Assert(constraint != NullConstraint);
+  Assert(constraint->isUpperBound());
+  Assert(constraint->isTrue());
+  Assert(!constraint->negationHasProof());
+
+  ArithVar x_i = constraint->getVariable();
+  const DeltaRational& c_i = constraint->getValue();
+
+  Trace("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
+
+
+  //Too strong because of rounding with integers
+  //Assert(!constraint->hasLiteral() || original == constraint->getLiteral());
+  Assert(!isInteger(x_i) || c_i.isIntegral());
+
+  Trace("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
+
+  if(d_partialModel.greaterThanUpperBound(x_i, c_i) ){ // \upperbound(x_i) <= c_i
+    return false; //sat
+  }
+
+  // cmpToLb =  \lowerbound(x_i).cmp(c_i)
+  int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
+  if( cmpToLB < 0 ){ //  \upperbound(x_i) < \lowerbound(x_i)
+    // l_i <= x_i and c_i < l_i |= c_i < x_i
+    // or ... |= not (x_i <= c_i)
+    ConstraintP lbc = d_partialModel.getLowerBoundConstraint(x_i);
+    ConstraintP negConstraint = constraint->getNegation();
+    negConstraint->impliedByUnate(lbc, true);
+    raiseConflict(constraint, InferenceId::ARITH_CONF_UPPER);
+    ++(d_statistics.d_statAssertUpperConflicts);
+    return true;
+  }else if(cmpToLB == 0){ // \lowerBound(x_i) == \upperbound(x_i)
+    if(isInteger(x_i)){
+      d_constantIntegerVariables.push_back(x_i);
+      Trace("dio::push") << "dio::push " << x_i << endl;
+    }
+
+    const ValueCollection& vc = constraint->getValueCollection();
+    ConstraintP lb = d_partialModel.getLowerBoundConstraint(x_i);
+    if(d_cmEnabled){
+      if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){
+        // if it is not a watched variable report it
+        // if it is is a watched variable and c_i == 0,
+        // let zeroDifferenceDetected(x_i) catch this
+        d_congruenceManager.equalsConstant(lb, constraint);
+      }
+    }
+
+    if(vc.hasDisequality()){
+      Assert(vc.hasDisequality());
+      ConstraintP eq = vc.getEquality();
+      ConstraintP diseq = vc.getDisequality();
+      // x <= b, x >= b |= x = b
+      // (x > b or x < b or x = b)
+      Trace("arith::eq") << "lb == ub, propagate eq" << eq << endl;
+      bool triConflict = diseq->isTrue();
+      if(!eq->isTrue()){
+        eq->impliedByTrichotomy(constraint, lb, triConflict);
+        eq->tryToPropagate();
+      }
+      if(triConflict){
+        ++(d_statistics.d_statDisequalityConflicts);
+        raiseConflict(eq, InferenceId::ARITH_CONF_TRICHOTOMY);
+        return true;
+      }
+    }
+  }else if(cmpToLB > 0){
+    // l <= x <= u and l < u
+    Assert(cmpToLB > 0);
+    const ValueCollection& vc = constraint->getValueCollection();
+
+    if(vc.hasDisequality()){
+      const ConstraintP diseq = vc.getDisequality();
+      if(diseq->isTrue()){
+        const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
+        ConstraintP negLb = lb->getNegation();
+
+        // x <= u, u != x |= u < x
+        // |= not (u >= x)
+        bool lbInConflict = lb->hasProof();
+        bool learnNegLb = !(negLb->hasProof());
+        if(learnNegLb){
+          negLb->impliedByTrichotomy(constraint, diseq, lbInConflict);
+          negLb->tryToPropagate();
+        }
+        if(lbInConflict){
+          raiseConflict(lb, InferenceId::ARITH_CONF_TRICHOTOMY);
+          return true;
+        }else if(learnNegLb){
+          d_learnedBounds.push_back(negLb);
+        }
+      }
+    }
+  }
+
+  d_currentPropagationList.push_back(constraint);
+  d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
+  //It is fine if this is NullConstraint
+
+  d_partialModel.setUpperBoundConstraint(constraint);
+
+  if(d_cmEnabled){
+    if(d_congruenceManager.isWatchedVariable(x_i)){
+      int sgn = c_i.sgn();
+      if(sgn < 0){
+        d_congruenceManager.watchedVariableCannotBeZero(constraint);
+      }else if(sgn == 0 && d_partialModel.lowerBoundIsZero(x_i)){
+        zeroDifferenceDetected(x_i);
+      }
+    }
+  }
+
+  d_updatedBounds.softAdd(x_i);
+
+  if(TraceIsOn("model")) {
+    Trace("model") << "before" << endl;
+    d_partialModel.printModel(x_i);
+    d_tableau.debugPrintIsBasic(x_i);
+  }
+
+  if(!d_tableau.isBasic(x_i)){
+    if(d_partialModel.getAssignment(x_i) > c_i){
+      d_linEq.update(x_i, c_i);
+    }
+  }else{
+    d_errorSet.signalVariable(x_i);
+  }
+
+  if(TraceIsOn("model")) {
+    Trace("model") << "after" << endl;
+    d_partialModel.printModel(x_i);
+    d_tableau.debugPrintIsBasic(x_i);
+  }
+
+  return false; //sat
+}
+
+
+/* procedure AssertEquality( x_i == c_i ) */
+bool TheoryArithPrivate::AssertEquality(ConstraintP constraint){
+  Assert(constraint != NullConstraint);
+  Assert(constraint->isEquality());
+  Assert(constraint->isTrue());
+  Assert(!constraint->negationHasProof());
+
+  ArithVar x_i = constraint->getVariable();
+  const DeltaRational& c_i = constraint->getValue();
+
+  Trace("arith") << "AssertEquality(" << x_i << " " << c_i << ")"<< std::endl;
+
+  //Should be fine in integers
+  Assert(!isInteger(x_i) || c_i.isIntegral());
+
+  int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
+  int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
+
+  // u_i <= c_i <= l_i
+  // This can happen if both c_i <= x_i and x_i <= c_i are in the system.
+  if(cmpToUB >= 0 && cmpToLB <= 0){
+    return false; //sat
+  }
+
+  if(cmpToUB > 0 || cmpToLB < 0){
+    ConstraintP cb = (cmpToUB > 0) ?  d_partialModel.getUpperBoundConstraint(x_i) :
+      d_partialModel.getLowerBoundConstraint(x_i);
+    ConstraintP diseq = constraint->getNegation();
+    Assert(!diseq->isTrue());
+    diseq->impliedByUnate(cb, true);
+    raiseConflict(constraint, InferenceId::ARITH_CONF_EQ);
+    return true;
+  }
+
+  Assert(cmpToUB <= 0);
+  Assert(cmpToLB >= 0);
+  Assert(cmpToUB < 0 || cmpToLB > 0);
+
+  if(isInteger(x_i)){
+    d_constantIntegerVariables.push_back(x_i);
+    Trace("dio::push") << "dio::push " << x_i << endl;
+  }
+
+  // Don't bother to check whether x_i != c_i is in d_diseq
+  // The a and (not a) should never be on the fact queue
+  d_currentPropagationList.push_back(constraint);
+  d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
+  d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
+
+  d_partialModel.setUpperBoundConstraint(constraint);
+  d_partialModel.setLowerBoundConstraint(constraint);
+
+  if(d_cmEnabled){
+    if(d_congruenceManager.isWatchedVariable(x_i)){
+      int sgn = c_i.sgn();
+      if(sgn == 0){
+        zeroDifferenceDetected(x_i);
+      }else{
+        d_congruenceManager.watchedVariableCannotBeZero(constraint);
+        d_congruenceManager.equalsConstant(constraint);
+      }
+    }else{
+      d_congruenceManager.equalsConstant(constraint);
+    }
+  }
+
+  d_updatedBounds.softAdd(x_i);
+
+  if(TraceIsOn("model")) {
+    Trace("model") << "before" << endl;
+    d_partialModel.printModel(x_i);
+    d_tableau.debugPrintIsBasic(x_i);
+  }
+
+  if(!d_tableau.isBasic(x_i)){
+    if(!(d_partialModel.getAssignment(x_i) == c_i)){
+      d_linEq.update(x_i, c_i);
+    }
+  }else{
+    d_errorSet.signalVariable(x_i);
+  }
+
+  if(TraceIsOn("model")) {
+    Trace("model") << "after" << endl;
+    d_partialModel.printModel(x_i);
+    d_tableau.debugPrintIsBasic(x_i);
+  }
+
+  return false;
+}
+
+
+/* procedure AssertDisequality( x_i != c_i ) */
+bool TheoryArithPrivate::AssertDisequality(ConstraintP constraint){
+  Assert(constraint != NullConstraint);
+  Assert(constraint->isDisequality());
+  Assert(constraint->isTrue());
+  Assert(!constraint->negationHasProof());
+
+  ArithVar x_i = constraint->getVariable();
+  const DeltaRational& c_i = constraint->getValue();
+  Trace("arith") << "AssertDisequality(" << x_i << " " << c_i << ")"<< std::endl;
+
+  //Should be fine in integers
+  Assert(!isInteger(x_i) || c_i.isIntegral());
+
+  if(d_cmEnabled){
+    if(d_congruenceManager.isWatchedVariable(x_i)){
+      int sgn = c_i.sgn();
+      if(sgn == 0){
+        d_congruenceManager.watchedVariableCannotBeZero(constraint);
+      }
+    }
+  }
+
+  const ValueCollection& vc = constraint->getValueCollection();
+  if(vc.hasLowerBound() && vc.hasUpperBound()){
+    const ConstraintP lb = vc.getLowerBound();
+    const ConstraintP ub = vc.getUpperBound();
+    if(lb->isTrue() && ub->isTrue()){
+      ConstraintP eq = constraint->getNegation();
+      eq->impliedByTrichotomy(lb, ub, true);
+      raiseConflict(constraint, InferenceId::ARITH_CONF_TRICHOTOMY);
+      //in conflict
+      ++(d_statistics.d_statDisequalityConflicts);
+      return true;
+    }
+  }
+  if(vc.hasLowerBound() ){
+    const ConstraintP lb = vc.getLowerBound();
+    if(lb->isTrue()){
+      const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
+      Assert(!ub->isTrue());
+      Trace("arith::eq") << "propagate UpperBound " << constraint << lb << ub << endl;
+      const ConstraintP negUb = ub->getNegation();
+      if(!negUb->isTrue()){
+        negUb->impliedByTrichotomy(constraint, lb, false);
+        negUb->tryToPropagate();
+        d_learnedBounds.push_back(negUb);
+      }
+    }
+  }
+  if(vc.hasUpperBound()){
+    const ConstraintP ub = vc.getUpperBound();
+    if(ub->isTrue()){
+      const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
+      Assert(!lb->isTrue());
+
+      Trace("arith::eq") << "propagate LowerBound " << constraint << lb << ub << endl;
+      const ConstraintP negLb = lb->getNegation();
+      if(!negLb->isTrue()){
+        negLb->impliedByTrichotomy(constraint, ub, false);
+        negLb->tryToPropagate();
+        d_learnedBounds.push_back(negLb);
+      }
+    }
+  }
+
+  bool split = constraint->isSplit();
+
+  if(!split && c_i == d_partialModel.getAssignment(x_i)){
+    Trace("arith::eq") << "lemma now! " << constraint << endl;
+    outputTrustedLemma(constraint->split(), InferenceId::ARITH_SPLIT_DEQ);
+    return false;
+  }else if(d_partialModel.strictlyLessThanLowerBound(x_i, c_i)){
+    Trace("arith::eq") << "can drop as less than lb" << constraint << endl;
+  }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, c_i)){
+    Trace("arith::eq") << "can drop as less than ub" << constraint << endl;
+  }else if(!split){
+    Trace("arith::eq") << "push back" << constraint << endl;
+    d_diseqQueue.push(constraint);
+    d_partialModel.invalidateDelta();
+  }else{
+    Trace("arith::eq") << "skipping already split " << constraint << endl;
+  }
+  return false;
+}
+
+void TheoryArithPrivate::notifySharedTerm(TNode n)
+{
+  Trace("arith::notifySharedTerm") << "notifySharedTerm: " << n << endl;
+  if(n.isConst()){
+    d_partialModel.invalidateDelta();
+  }
+  if(!n.isConst() && !isSetup(n)){
+    Polynomial poly = Polynomial::parsePolynomial(n);
+    Polynomial::iterator it = poly.begin();
+    Polynomial::iterator it_end = poly.end();
+    for (; it != it_end; ++ it) {
+      Monomial m = *it;
+      if (!m.isConstant() && !isSetup(m.getVarList().getNode())) {
+        setupVariableList(m.getVarList());
+      }
+    }
+  }
+}
+
+Node TheoryArithPrivate::getModelValue(TNode term) {
+  try{
+    const DeltaRational drv = getDeltaValue(term);
+    const Rational& delta = d_partialModel.getDelta();
+    const Rational qmodel = drv.substituteDelta( delta );
+    return NodeManager::currentNM()->mkConstRealOrInt(term.getType(), qmodel);
+  } catch (DeltaRationalException& dr) {
+    return Node::null();
+  } catch (ModelException& me) {
+    return Node::null();
+  }
+}
+
+Theory::PPAssertStatus TheoryArithPrivate::ppAssert(
+    TrustNode tin, TrustSubstitutionMap& outSubstitutions)
+{
+  TimerStat::CodeTimer codeTimer(d_statistics.d_simplifyTimer);
+  TNode in = tin.getNode();
+  Trace("simplify") << "TheoryArithPrivate::solve(" << in << ")" << endl;
+
+
+  // Solve equalities
+  Rational minConstant = 0;
+  Node minMonomial;
+  Node minVar;
+  if (in.getKind() == kind::EQUAL &&
+      Theory::theoryOf(in[0].getType()) == THEORY_ARITH) {
+    Comparison cmp = Comparison::parseNormalForm(in);
+
+    Polynomial left = cmp.getLeft();
+
+    Monomial m = left.getHead();
+    if (m.getVarList().singleton()){
+      VarList vl = m.getVarList();
+      Node var = vl.getNode();
+      if (var.isVar())
+      {
+        // if vl.isIntegral then m.getConstant().isOne()
+        if(!vl.isIntegral() || m.getConstant().isOne()){
+          minVar = var;
+        }
+      }
+    }
+
+    // Solve for variable
+    if (!minVar.isNull()) {
+      Polynomial right = cmp.getRight();
+      Node elim = right.getNode();
+      // ax + p = c -> (ax + p) -ax - c = -ax
+      // x = (p - ax - c) * -1/a
+      // Add the substitution if not recursive
+      Assert(elim == rewrite(elim));
+
+      if (right.size() > options().arith.ppAssertMaxSubSize)
+      {
+        Trace("simplify")
+            << "TheoryArithPrivate::solve(): did not substitute due to the "
+               "right hand side containing too many terms: "
+            << minVar << ":" << elim << endl;
+        Trace("simplify") << right.size() << endl;
+      }
+      else if (d_containing.isLegalElimination(minVar, elim))
+      {
+        // cannot eliminate integers here unless we know the resulting
+        // substitution is integral
+        Trace("simplify") << "TheoryArithPrivate::solve(): substitution "
+                          << minVar << " |-> " << elim << endl;
+
+        outSubstitutions.addSubstitutionSolved(minVar, elim, tin);
+        return Theory::PP_ASSERT_STATUS_SOLVED;
+      }
+      else
+      {
+        Trace("simplify") << "TheoryArithPrivate::solve(): can't substitute "
+                          << minVar << ":" << minVar.getType() << " |-> "
+                          << elim << ":" << elim.getType() << endl;
+      }
+    }
+  }
+
+  // If a relation, remember the bound
+  switch(in.getKind()) {
+  case kind::LEQ:
+  case kind::LT:
+  case kind::GEQ:
+  case kind::GT:
+    if (in[0].isVar()) {
+      d_learner.addBound(in);
+    }
+    break;
+  default:
+    // Do nothing
+    break;
+  }
+
+  return Theory::PP_ASSERT_STATUS_UNSOLVED;
+}
+
+void TheoryArithPrivate::ppStaticLearn(TNode n, NodeBuilder& learned)
+{
+  TimerStat::CodeTimer codeTimer(d_statistics.d_staticLearningTimer);
+
+  d_learner.staticLearning(n, learned);
+}
+
+ArithVar TheoryArithPrivate::findShortestBasicRow(ArithVar variable){
+  ArithVar bestBasic = ARITHVAR_SENTINEL;
+  uint64_t bestRowLength = std::numeric_limits<uint64_t>::max();
+
+  Tableau::ColIterator basicIter = d_tableau.colIterator(variable);
+  for(; !basicIter.atEnd(); ++basicIter){
+    const Tableau::Entry& entry = *basicIter;
+    Assert(entry.getColVar() == variable);
+    RowIndex ridx = entry.getRowIndex();
+    ArithVar basic = d_tableau.rowIndexToBasic(ridx);
+    uint32_t rowLength = d_tableau.getRowLength(ridx);
+    if((rowLength < bestRowLength) ||
+       (rowLength == bestRowLength && basic < bestBasic)){
+      bestBasic = basic;
+      bestRowLength = rowLength;
+    }
+  }
+  Assert(bestBasic == ARITHVAR_SENTINEL
+         || bestRowLength < std::numeric_limits<uint32_t>::max());
+  return bestBasic;
+}
+
+void TheoryArithPrivate::setupVariable(const Variable& x){
+  Node n = x.getNode();
+
+  Assert(!isSetup(n));
+
+  ++(d_statistics.d_statUserVariables);
+  requestArithVar(n, false,  false);
+  //ArithVar varN = requestArithVar(n,false);
+  //setupInitialValue(varN);
+
+  markSetup(n);
+}
+
+void TheoryArithPrivate::setupVariableList(const VarList& vl){
+  Assert(!vl.empty());
+
+  TNode vlNode = vl.getNode();
+  Assert(!isSetup(vlNode));
+  Assert(!d_partialModel.hasArithVar(vlNode));
+
+  for(VarList::iterator i = vl.begin(), end = vl.end(); i != end; ++i){
+    Variable var = *i;
+
+    if(!isSetup(var.getNode())){
+      setupVariable(var);
+    }
+  }
+
+  if(!vl.singleton()){
+    // vl is the product of at least 2 variables
+    // vl : (* v1 v2 ...)
+    if (logicInfo().isLinear())
+    {
+      throw LogicException("A non-linear fact was asserted to arithmetic in a linear logic.");
+    }
+    d_foundNl = true;
+
+    ++(d_statistics.d_statUserVariables);
+    requestArithVar(vlNode, false, false);
+    //ArithVar av = requestArithVar(vlNode, false);
+    //setupInitialValue(av);
+
+    markSetup(vlNode);
+  }
+  else if (vlNode.getKind() == kind::EXPONENTIAL
+           || vlNode.getKind() == kind::SINE || vlNode.getKind() == kind::COSINE
+           || vlNode.getKind() == kind::TANGENT)
+  {
+    d_foundNl = true;
+  }
+
+  /* Note:
+   * Only call markSetup if the VarList is not a singleton.
+   * See the comment in setupPolynomail for more.
+   */
+}
+
+void TheoryArithPrivate::cautiousSetupPolynomial(const Polynomial& p){
+  if(p.containsConstant()){
+    if(!p.isConstant()){
+      Polynomial noConstant = p.getTail();
+      if(!isSetup(noConstant.getNode())){
+        setupPolynomial(noConstant);
+      }
+    }
+  }else if(!isSetup(p.getNode())){
+    setupPolynomial(p);
+  }
+}
+
+
+void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) {
+  Assert(!poly.containsConstant());
+  TNode polyNode = poly.getNode();
+  Assert(!isSetup(polyNode));
+  Assert(!d_partialModel.hasArithVar(polyNode));
+
+  for(Polynomial::iterator i = poly.begin(), end = poly.end(); i != end; ++i){
+    Monomial mono = *i;
+    const VarList& vl = mono.getVarList();
+    if(!isSetup(vl.getNode())){
+      setupVariableList(vl);
+    }
+  }
+
+  if (polyNode.getKind() == ADD)
+  {
+    d_tableauSizeHasBeenModified = true;
+
+    vector<ArithVar> variables;
+    vector<Rational> coefficients;
+    asVectors(poly, coefficients, variables);
+
+    ArithVar varSlack = requestArithVar(polyNode, true, false);
+    d_tableau.addRow(varSlack, coefficients, variables);
+    setupBasicValue(varSlack);
+    d_linEq.trackRowIndex(d_tableau.basicToRowIndex(varSlack));
+
+    //Add differences to the difference manager
+    Polynomial::iterator i = poly.begin(), end = poly.end();
+    if(i != end){
+      Monomial first = *i;
+      ++i;
+      if(i != end){
+        Monomial second = *i;
+        ++i;
+        if(i == end){
+          if(first.getConstant().isOne() && second.getConstant().getValue() == -1){
+            VarList vl0 = first.getVarList();
+            VarList vl1 = second.getVarList();
+            if(vl0.singleton() && vl1.singleton()){
+              d_congruenceManager.addWatchedPair(varSlack, vl0.getNode(), vl1.getNode());
+            }
+          }
+        }
+      }
+    }
+
+    ++(d_statistics.d_statAuxiliaryVariables);
+    markSetup(polyNode);
+  }
+
+  /* Note:
+   * It is worth documenting that polyNode should only be marked as
+   * being setup by this function if it has kind ADD.
+   * Other kinds will be marked as being setup by lower levels of setup
+   * specifically setupVariableList.
+   */
+}
+
+void TheoryArithPrivate::setupAtom(TNode atom) {
+  Assert(isRelationOperator(atom.getKind())) << atom;
+  Assert(Comparison::isNormalAtom(atom));
+  Assert(!isSetup(atom));
+  Assert(!d_constraintDatabase.hasLiteral(atom));
+
+  Comparison cmp = Comparison::parseNormalForm(atom);
+  Polynomial nvp = cmp.normalizedVariablePart();
+  Assert(!nvp.isZero());
+
+  if(!isSetup(nvp.getNode())){
+    setupPolynomial(nvp);
+  }
+
+  d_constraintDatabase.addLiteral(atom);
+
+  markSetup(atom);
+}
+
+void TheoryArithPrivate::preRegisterTerm(TNode n) {
+  Trace("arith::preregister") <<"begin arith::preRegisterTerm("<< n <<")"<< endl;
+
+  d_preregisteredNodes.insert(n);
+
+  try {
+    if(isRelationOperator(n.getKind())){
+      if(!isSetup(n)){
+        setupAtom(n);
+      }
+      ConstraintP c = d_constraintDatabase.lookup(n);
+      Assert(c != NullConstraint);
+
+      Trace("arith::preregister") << "setup constraint" << c << endl;
+      Assert(!c->canBePropagated());
+      c->setPreregistered();
+    }
+  } catch(LogicException& le) {
+    std::stringstream ss;
+    ss << le.getMessage() << endl << "The fact in question: " << n << endl;
+    throw LogicException(ss.str());
+  }
+
+  Trace("arith::preregister") << "end arith::preRegisterTerm("<< n <<")" << endl;
+}
+
+void TheoryArithPrivate::releaseArithVar(ArithVar v){
+  //Assert(d_partialModel.hasNode(v));
+
+  d_constraintDatabase.removeVariable(v);
+  d_partialModel.releaseArithVar(v);
+}
+
+ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool aux, bool internal){
+  //TODO : The VarList trick is good enough?
+  Kind xk = x.getKind();
+  Assert(isLeaf(x) || VarList::isMember(x) || xk == ADD || internal);
+  if (logicInfo().isLinear()
+      && (Variable::isDivMember(x) || xk == IAND || isTranscendentalKind(xk)))
+  {
+    stringstream ss;
+    ss << "A non-linear fact was asserted to "
+          "arithmetic in a linear logic: "
+       << x << std::endl;
+    throw LogicException(ss.str());
+  }
+  Assert(!d_partialModel.hasArithVar(x));
+  Assert(x.getType().isRealOrInt());  // real or integer
+
+  ArithVar max = d_partialModel.getNumberOfVariables();
+  ArithVar varX = d_partialModel.allocate(x, aux);
+
+  bool reclaim =  max >= d_partialModel.getNumberOfVariables();;
+
+  if(!reclaim){
+    d_dualSimplex.increaseMax();
+
+    d_tableau.increaseSize();
+    d_tableauSizeHasBeenModified = true;
+  }
+  d_constraintDatabase.addVariable(varX);
+
+  Trace("arith::arithvar") << "@" << context()->getLevel() << " " << x
+                           << " |-> " << varX << "(relaiming " << reclaim << ")"
+                           << endl;
+
+  Assert(!d_partialModel.hasUpperBound(varX));
+  Assert(!d_partialModel.hasLowerBound(varX));
+
+  return varX;
+}
+
+void TheoryArithPrivate::asVectors(const Polynomial& p, std::vector<Rational>& coeffs, std::vector<ArithVar>& variables) {
+  for(Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i){
+    const Monomial& mono = *i;
+    const Constant& constant = mono.getConstant();
+    const VarList& variable = mono.getVarList();
+
+    Node n = variable.getNode();
+
+    Trace("arith::asVectors") << "should be var: " << n << endl;
+
+    // TODO: This VarList::isMember(n) can be stronger
+    Assert(isLeaf(n) || VarList::isMember(n));
+    Assert(theoryOf(n) != THEORY_ARITH || d_partialModel.hasArithVar(n));
+
+    Assert(d_partialModel.hasArithVar(n));
+    ArithVar av = d_partialModel.asArithVar(n);
+
+    coeffs.push_back(constant.getValue());
+    variables.push_back(av);
+  }
+}
+
+/* Requirements:
+ * For basic variables the row must have been added to the tableau.
+ */
+void TheoryArithPrivate::setupBasicValue(ArithVar x){
+  Assert(d_tableau.isBasic(x));
+  //If the variable is basic, assertions may have already happened and updates
+  //may have occured before setting this variable up.
+
+  //This can go away if the tableau creation is done at preregister
+  //time instead of register
+  DeltaRational safeAssignment = d_linEq.computeRowValue(x, true);
+  DeltaRational assignment = d_linEq.computeRowValue(x, false);
+  d_partialModel.setAssignment(x,safeAssignment,assignment);
+
+  Trace("arith") << "setupVariable("<<x<<")"<<std::endl;
+}
+
+ArithVar TheoryArithPrivate::determineArithVar(const Polynomial& p) const{
+  Assert(!p.containsConstant());
+  Assert(p.getHead().constantIsPositive());
+  TNode n = p.getNode();
+  Trace("determineArithVar") << "determineArithVar(" << n << ")" << endl;
+  return d_partialModel.asArithVar(n);
+}
+
+ArithVar TheoryArithPrivate::determineArithVar(TNode assertion) const{
+  Trace("determineArithVar") << "determineArithVar " << assertion << endl;
+  Comparison cmp = Comparison::parseNormalForm(assertion);
+  Polynomial variablePart = cmp.normalizedVariablePart();
+  return determineArithVar(variablePart);
+}
+
+
+bool TheoryArithPrivate::canSafelyAvoidEqualitySetup(TNode equality){
+  Assert(equality.getKind() == EQUAL);
+  return d_partialModel.hasArithVar(equality[0]);
+}
+
+Comparison TheoryArithPrivate::mkIntegerEqualityFromAssignment(ArithVar v){
+  const DeltaRational& beta = d_partialModel.getAssignment(v);
+
+  Assert(beta.isIntegral());
+  Polynomial betaAsPolynomial = Polynomial::mkPolynomial( Constant::mkConstant(beta.floor()) );
+
+  TNode var = d_partialModel.asNode(v);
+  Polynomial varAsPolynomial = Polynomial::parsePolynomial(var);
+  return Comparison::mkComparison(EQUAL, varAsPolynomial, betaAsPolynomial);
+}
+
+TrustNode TheoryArithPrivate::dioCutting()
+{
+  context::Context::ScopedPush speculativePush(context());
+  //DO NOT TOUCH THE OUTPUTSTREAM
+
+  for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+    ArithVar v = *vi;
+    if(isInteger(v)){
+      if(d_partialModel.cmpAssignmentUpperBound(v) == 0 ||
+         d_partialModel.cmpAssignmentLowerBound(v) == 0){
+        if(!d_partialModel.boundsAreEqual(v)){
+          // If the bounds are equal this is already in the dioSolver
+          //Add v = dr as a speculation.
+          Comparison eq = mkIntegerEqualityFromAssignment(v);
+          Trace("dio::push") << "dio::push " << v << " " <<  eq.getNode() << endl;
+          Assert(!eq.isBoolean());
+          d_diosolver.pushInputConstraint(eq, eq.getNode());
+          // It does not matter what the explanation of eq is.
+          // It cannot be used in a conflict
+        }
+      }
+    }
+  }
+
+  SumPair plane = d_diosolver.processEquationsForCut();
+  if(plane.isZero()){
+    return TrustNode::null();
+  }else{
+    Polynomial p = plane.getPolynomial();
+    Polynomial c = Polynomial::mkPolynomial(plane.getConstant() * Constant::mkConstant(-1));
+    Integer gcd = p.gcd();
+    Assert(p.isIntegral());
+    Assert(c.isIntegral());
+    Assert(gcd > 1);
+    Assert(!gcd.divides(c.asConstant().getNumerator()));
+    Comparison leq = Comparison::mkComparison(LEQ, p, c);
+    Comparison geq = Comparison::mkComparison(GEQ, p, c);
+    Node lemma = NodeManager::currentNM()->mkNode(OR, leq.getNode(), geq.getNode());
+    Node rewrittenLemma = rewrite(lemma);
+    Trace("arith::dio::ex") << "dioCutting found the plane: " << plane.getNode() << endl;
+    Trace("arith::dio::ex") << "resulting in the cut: " << lemma << endl;
+    Trace("arith::dio::ex") << "rewritten " << rewrittenLemma << endl;
+    Trace("arith::dio") << "dioCutting found the plane: " << plane.getNode() << endl;
+    Trace("arith::dio") << "resulting in the cut: " << lemma << endl;
+    Trace("arith::dio") << "rewritten " << rewrittenLemma << endl;
+    if (proofsEnabled())
+    {
+      NodeManager* nm = NodeManager::currentNM();
+      Node gt = nm->mkNode(kind::GT, p.getNode(), c.getNode());
+      Node lt = nm->mkNode(kind::LT, p.getNode(), c.getNode());
+      TypeNode type = gt[0].getType();
+
+      Pf pfNotLeq = d_pnm->mkAssume(leq.getNode().negate());
+      Pf pfGt =
+          d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pfNotLeq}, {gt});
+      Pf pfNotGeq = d_pnm->mkAssume(geq.getNode().negate());
+      Pf pfLt =
+          d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pfNotGeq}, {lt});
+      Pf pfSum = d_pnm->mkNode(
+          PfRule::MACRO_ARITH_SCALE_SUM_UB,
+          {pfGt, pfLt},
+          {nm->mkConstRealOrInt(type, -1), nm->mkConstRealOrInt(type, 1)});
+      Pf pfBot = d_pnm->mkNode(
+          PfRule::MACRO_SR_PRED_TRANSFORM, {pfSum}, {nm->mkConst<bool>(false)});
+      std::vector<Node> assumptions = {leq.getNode().negate(),
+                                       geq.getNode().negate()};
+      Pf pfNotAndNot = d_pnm->mkScope(pfBot, assumptions);
+      Pf pfOr = d_pnm->mkNode(PfRule::NOT_AND, {pfNotAndNot}, {});
+      Pf pfRewritten = d_pnm->mkNode(
+          PfRule::MACRO_SR_PRED_TRANSFORM, {pfOr}, {rewrittenLemma});
+      return d_pfGen->mkTrustNode(rewrittenLemma, pfRewritten);
+    }
+    else
+    {
+      return TrustNode::mkTrustLemma(rewrittenLemma, nullptr);
+    }
+  }
+}
+
+Node TheoryArithPrivate::callDioSolver(){
+  while(!d_constantIntegerVariables.empty()){
+    ArithVar v = d_constantIntegerVariables.front();
+    d_constantIntegerVariables.pop();
+
+    Trace("arith::dio")  << "callDioSolver " << v << endl;
+
+    Assert(isInteger(v));
+    Assert(d_partialModel.boundsAreEqual(v));
+
+    ConstraintP lb = d_partialModel.getLowerBoundConstraint(v);
+    ConstraintP ub = d_partialModel.getUpperBoundConstraint(v);
+
+    Node orig = Node::null();
+    if(lb->isEquality()){
+      orig = Constraint::externalExplainByAssertions({lb});
+    }else if(ub->isEquality()){
+      orig = Constraint::externalExplainByAssertions({ub});
+    }else {
+      orig = Constraint::externalExplainByAssertions(ub, lb);
+    }
+
+    Assert(d_partialModel.assignmentIsConsistent(v));
+
+    Comparison eq = mkIntegerEqualityFromAssignment(v);
+
+    if(eq.isBoolean()){
+      //This can only be a conflict
+      Assert(!eq.getNode().getConst<bool>());
+
+      //This should be handled by the normal form earlier in the case of equality
+      Assert(orig.getKind() != EQUAL);
+      return orig;
+    }else{
+      Trace("dio::push") << "dio::push " << v << " " << eq.getNode() << " with reason " << orig << endl;
+      d_diosolver.pushInputConstraint(eq, orig);
+    }
+  }
+
+  return d_diosolver.processEquationsForConflict();
+}
+
+ConstraintP TheoryArithPrivate::constraintFromFactQueue(TNode assertion)
+{
+  Kind simpleKind = Comparison::comparisonKind(assertion);
+  ConstraintP constraint = d_constraintDatabase.lookup(assertion);
+  if(constraint == NullConstraint){
+    Assert(simpleKind == EQUAL || simpleKind == DISTINCT);
+    bool isDistinct = simpleKind == DISTINCT;
+    Node eq = (simpleKind == DISTINCT) ? assertion[0] : assertion;
+    Assert(!isSetup(eq));
+    Node reEq = rewrite(eq);
+    Trace("arith::distinct::const") << "Assertion: " << assertion << std::endl;
+    Trace("arith::distinct::const") << "Eq       : " << eq << std::endl;
+    Trace("arith::distinct::const") << "reEq     : " << reEq << std::endl;
+    if(reEq.getKind() == CONST_BOOLEAN){
+      if(reEq.getConst<bool>() == isDistinct){
+        // if is (not true), or false
+        Assert((reEq.getConst<bool>() && isDistinct)
+               || (!reEq.getConst<bool>() && !isDistinct));
+        if (proofsEnabled())
+        {
+          Pf assume = d_pnm->mkAssume(assertion);
+          std::vector<Node> assumptions = {assertion};
+          Pf pf = d_pnm->mkScope(d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
+                                               {d_pnm->mkAssume(assertion)},
+                                               {}),
+                                 assumptions);
+          raiseBlackBoxConflict(assertion, pf);
+        }
+        else
+        {
+          raiseBlackBoxConflict(assertion);
+        }
+      }
+      return NullConstraint;
+    }
+    Assert(reEq.getKind() != CONST_BOOLEAN);
+    if(!isSetup(reEq)){
+      setupAtom(reEq);
+    }
+    Node reAssertion = isDistinct ? reEq.notNode() : reEq;
+    constraint = d_constraintDatabase.lookup(reAssertion);
+
+    if(assertion != reAssertion){
+      Trace("arith::nf") << "getting non-nf assertion " << assertion << " |-> " <<  reAssertion << endl;
+      Assert(constraint != NullConstraint);
+      d_assertionsThatDoNotMatchTheirLiterals.insert(assertion, constraint);
+    }
+  }
+
+  Assert(constraint != NullConstraint);
+
+  if(constraint->assertedToTheTheory()){
+    //Do nothing
+    return NullConstraint;
+  }
+  Assert(!constraint->assertedToTheTheory());
+  bool inConflict = constraint->negationHasProof();
+  constraint->setAssertedToTheTheory(assertion, inConflict);
+
+  if(!constraint->hasProof()){
+    Trace("arith::constraint") << "marking as constraint as self explaining " << endl;
+    constraint->setAssumption(inConflict);
+  } else {
+    Trace("arith::constraint")
+        << "already has proof: "
+        << Constraint::externalExplainByAssertions({constraint});
+  }
+
+  if(TraceIsOn("arith::negatedassumption") && inConflict){
+    ConstraintP negation = constraint->getNegation();
+    if(TraceIsOn("arith::negatedassumption") && negation->isAssumption()){
+      debugPrintFacts();
+    }
+    Trace("arith::eq") << "negation has proof" << endl;
+    Trace("arith::eq") << constraint << endl;
+    Trace("arith::eq") << negation << endl;
+  }
+
+  if(inConflict){
+    ConstraintP negation = constraint->getNegation();
+    if(TraceIsOn("arith::negatedassumption") && negation->isAssumption()){
+      debugPrintFacts();
+    }
+    Trace("arith::eq") << "negation has proof" << endl;
+    Trace("arith::eq") << constraint << endl;
+    Trace("arith::eq") << negation << endl;
+    raiseConflict(negation, InferenceId::ARITH_CONF_FACT_QUEUE);
+    return NullConstraint;
+  }else{
+    return constraint;
+  }
+}
+
+bool TheoryArithPrivate::assertionCases(ConstraintP constraint){
+  Assert(constraint->hasProof());
+  Assert(!constraint->negationHasProof());
+
+  ArithVar x_i = constraint->getVariable();
+
+  switch(constraint->getType()){
+  case UpperBound:
+    if(isInteger(x_i) && constraint->isStrictUpperBound()){
+      ConstraintP floorConstraint = constraint->getFloor();
+      if(!floorConstraint->isTrue()){
+        bool inConflict = floorConstraint->negationHasProof();
+        if (TraceIsOn("arith::intbound")) {
+          Trace("arith::intbound") << "literal, before: " << constraint->getLiteral() << std::endl;
+          Trace("arith::intbound") << "constraint, after: " << floorConstraint << std::endl;
+        }
+        floorConstraint->impliedByIntTighten(constraint, inConflict);
+        floorConstraint->tryToPropagate();
+        if(inConflict){
+          raiseConflict(floorConstraint, InferenceId::ARITH_TIGHTEN_FLOOR);
+          return true;
+        }
+      }
+      return AssertUpper(floorConstraint);
+    }else{
+      return AssertUpper(constraint);
+    }
+  case LowerBound:
+    if(isInteger(x_i) && constraint->isStrictLowerBound()){
+      ConstraintP ceilingConstraint = constraint->getCeiling();
+      if(!ceilingConstraint->isTrue()){
+        bool inConflict = ceilingConstraint->negationHasProof();
+        if (TraceIsOn("arith::intbound")) {
+          Trace("arith::intbound") << "literal, before: " << constraint->getLiteral() << std::endl;
+          Trace("arith::intbound") << "constraint, after: " << ceilingConstraint << std::endl;
+        }
+        ceilingConstraint->impliedByIntTighten(constraint, inConflict);
+        ceilingConstraint->tryToPropagate();
+        if(inConflict){
+          raiseConflict(ceilingConstraint, InferenceId::ARITH_TIGHTEN_CEIL);
+          return true;
+        }
+      }
+      return AssertLower(ceilingConstraint);
+    }else{
+      return AssertLower(constraint);
+    }
+  case Equality:
+    return AssertEquality(constraint);
+  case Disequality:
+    return AssertDisequality(constraint);
+  default:
+    Unreachable();
+    return false;
+  }
+}
+/**
+ * Looks for through the variables starting at d_nextIntegerCheckVar
+ * for the first integer variable that is between its upper and lower bounds
+ * that has a non-integer assignment.
+ *
+ * If assumeBounds is true, skip the check that the variable is in bounds.
+ *
+ * If there is no such variable, returns ARITHVAR_SENTINEL;
+ */
+ArithVar TheoryArithPrivate::nextIntegerViolation(bool assumeBounds) const
+{
+  ArithVar numVars = d_partialModel.getNumberOfVariables();
+  ArithVar v = d_nextIntegerCheckVar;
+  if (numVars > 0)
+  {
+    const ArithVar rrEnd = d_nextIntegerCheckVar;
+    do
+    {
+      if (isIntegerInput(v))
+      {
+        if (!d_partialModel.integralAssignment(v))
+        {
+          if (assumeBounds || d_partialModel.assignmentIsConsistent(v))
+          {
+            return v;
+          }
+        }
+      }
+      v = (1 + v == numVars) ? 0 : (1 + v);
+    } while (v != rrEnd);
+  }
+  return ARITHVAR_SENTINEL;
+}
+
+/**
+ * Checks the set of integer variables I to see if each variable
+ * in I has an integer assignment.
+ */
+bool TheoryArithPrivate::hasIntegerModel()
+{
+  ArithVar next = nextIntegerViolation(true);
+  if (next != ARITHVAR_SENTINEL)
+  {
+    d_nextIntegerCheckVar = next;
+    if (TraceIsOn("arith::hasIntegerModel"))
+    {
+      Trace("arith::hasIntegerModel") << "has int model? " << next << endl;
+      d_partialModel.printModel(next, Trace("arith::hasIntegerModel"));
+    }
+    return false;
+  }
+  else
+  {
+    return true;
+  }
+}
+
+Node flattenAndSort(Node n){
+  Kind k = n.getKind();
+  switch(k){
+  case kind::OR:
+  case kind::AND:
+  case kind::ADD:
+  case kind::MULT:
+    break;
+  default:
+    return n;
+  }
+
+  std::vector<Node> out;
+  std::vector<Node> process;
+  process.push_back(n);
+  while(!process.empty()){
+    Node b = process.back();
+    process.pop_back();
+    if(b.getKind() == k){
+      for(Node::iterator i=b.begin(), end=b.end(); i!=end; ++i){
+        process.push_back(*i);
+      }
+    } else {
+      out.push_back(b);
+    }
+  }
+  Assert(out.size() >= 2);
+  std::sort(out.begin(), out.end());
+  return NodeManager::currentNM()->mkNode(k, out);
+}
+
+
+
+/** Outputs conflicts to the output channel. */
+void TheoryArithPrivate::outputConflicts(){
+  Trace("arith::conflict") << "outputting conflicts" << std::endl;
+  Assert(anyConflict());
+
+  if(!conflictQueueEmpty()){
+    Assert(!d_conflicts.empty());
+    for(size_t i = 0, i_end = d_conflicts.size(); i < i_end; ++i){
+      const std::pair<ConstraintCP, InferenceId>& conf = d_conflicts[i];
+      const ConstraintCP& confConstraint = conf.first;
+      bool hasProof = confConstraint->hasProof();
+      Assert(confConstraint->inConflict());
+      const ConstraintRule& pf = confConstraint->getConstraintRule();
+      if (TraceIsOn("arith::conflict"))
+      {
+        pf.print(std::cout, options().smt.produceProofs);
+        std::cout << std::endl;
+      }
+      if (TraceIsOn("arith::pf::tree"))
+      {
+        Trace("arith::pf::tree") << "\n\nTree:\n";
+        confConstraint->printProofTree(Trace("arith::pf::tree"));
+        confConstraint->getNegation()->printProofTree(Trace("arith::pf::tree"));
+      }
+
+      TrustNode trustedConflict = confConstraint->externalExplainConflict();
+      Node conflict = trustedConflict.getNode();
+
+      Trace("arith::conflict")
+          << "d_conflicts[" << i << "] " << conflict
+          << " has proof: " << hasProof << ", id = " << conf.second << endl;
+      if(TraceIsOn("arith::normalize::external")){
+        conflict = flattenAndSort(conflict);
+        Trace("arith::conflict") << "(normalized to) " << conflict << endl;
+      }
+
+      if (isProofEnabled())
+      {
+        outputTrustedConflict(trustedConflict, conf.second);
+      }
+      else
+      {
+        outputConflict(conflict, conf.second);
+      }
+    }
+  }
+  if(!d_blackBoxConflict.get().isNull()){
+    Node bb = d_blackBoxConflict.get();
+    Trace("arith::conflict") << "black box conflict" << bb
+                             << endl;
+    if(TraceIsOn("arith::normalize::external")){
+      bb = flattenAndSort(bb);
+      Trace("arith::conflict") << "(normalized to) " << bb << endl;
+    }
+    if (isProofEnabled() && d_blackBoxConflictPf.get())
+    {
+      auto confPf = d_blackBoxConflictPf.get();
+      outputTrustedConflict(d_pfGen->mkTrustNode(bb, confPf, true), InferenceId::ARITH_BLACK_BOX);
+    }
+    else
+    {
+      outputConflict(bb, InferenceId::ARITH_BLACK_BOX);
+    }
+  }
+}
+
+bool TheoryArithPrivate::outputTrustedLemma(TrustNode lemma, InferenceId id)
+{
+  Trace("arith::channel") << "Arith trusted lemma: " << lemma << std::endl;
+  return d_containing.d_im.trustedLemma(lemma, id);
+}
+
+bool TheoryArithPrivate::outputLemma(TNode lem, InferenceId id) {
+  Trace("arith::channel") << "Arith lemma: " << lem << std::endl;
+  return d_containing.d_im.lemma(lem, id);
+}
+
+void TheoryArithPrivate::outputTrustedConflict(TrustNode conf, InferenceId id)
+{
+  Trace("arith::channel") << "Arith trusted conflict: " << conf << std::endl;
+  d_containing.d_im.trustedConflict(conf, id);
+}
+
+void TheoryArithPrivate::outputConflict(TNode lit, InferenceId id) {
+  Trace("arith::channel") << "Arith conflict: " << lit << std::endl;
+  d_containing.d_im.conflict(lit, id);
+}
+
+void TheoryArithPrivate::outputPropagate(TNode lit) {
+  Trace("arith::channel") << "Arith propagation: " << lit << std::endl;
+  // call the propagate lit method of the
+  d_containing.d_im.propagateLit(lit);
+}
+
+void TheoryArithPrivate::outputRestart() {
+  Trace("arith::channel") << "Arith restart!" << std::endl;
+  (d_containing.d_out)->demandRestart();
+}
+
+bool TheoryArithPrivate::attemptSolveInteger(Theory::Effort effortLevel, bool emmmittedLemmaOrSplit){
+  int level = context()->getLevel();
+  Trace("approx")
+    << "attemptSolveInteger " << d_qflraStatus
+    << " " << emmmittedLemmaOrSplit
+    << " " << effortLevel
+    << " " << d_lastContextIntegerAttempted
+    << " " << level
+    << endl;
+
+  if(d_qflraStatus == Result::UNSAT){ return false; }
+  if(emmmittedLemmaOrSplit){ return false; }
+  if (!options().arith.useApprox)
+  {
+    return false;
+  }
+  if(!ApproximateSimplex::enabled()){ return false; }
+
+  if(Theory::fullEffort(effortLevel)){
+    if(hasIntegerModel()){
+      return false;
+    }else{
+      return getSolveIntegerResource();
+    }
+  }
+
+  if(d_lastContextIntegerAttempted <= 0){
+    if(hasIntegerModel()){
+      d_lastContextIntegerAttempted = context()->getLevel();
+      return false;
+    }else{
+      return getSolveIntegerResource();
+    }
+  }
+
+  if (!options().arith.trySolveIntStandardEffort)
+  {
+    return false;
+  }
+
+  if (d_lastContextIntegerAttempted <= (level >> 2))
+  {
+    double d = (double)(d_solveIntMaybeHelp + 1)
+               / (d_solveIntAttempts + 1 + level * level);
+    if (Random::getRandom().pickWithProb(d))
+    {
+      return getSolveIntegerResource();
+    }
+  }
+  return false;
+}
+
+bool TheoryArithPrivate::replayLog(ApproximateSimplex* approx){
+  TimerStat::CodeTimer codeTimer(d_statistics.d_replayLogTimer);
+
+  ++d_statistics.d_mipProofsAttempted;
+
+  Assert(d_replayVariables.empty());
+  Assert(d_replayConstraints.empty());
+
+  size_t enteringPropN = d_currentPropagationList.size();
+  Assert(conflictQueueEmpty());
+  TreeLog& tl = getTreeLog();
+  //tl.applySelected(); /* set row ids */
+
+  d_replayedLemmas = false;
+
+  /* use the try block for the purpose of pushing the sat context */
+  context::Context::ScopedPush speculativePush(context());
+  d_cmEnabled = false;
+  std::vector<ConstraintCPVec> res =
+      replayLogRec(approx, tl.getRootId(), NullConstraint, 1);
+
+  if(res.empty()){
+    ++d_statistics.d_replayAttemptFailed;
+  }else{
+    unsigned successes = 0;
+    for(size_t i =0, N = res.size(); i < N; ++i){
+      ConstraintCPVec& vec = res[i];
+      Assert(vec.size() >= 2);
+      for(size_t j=0, M = vec.size(); j < M; ++j){
+        ConstraintCP at_j = vec[j];
+        Assert(at_j->isTrue());
+        if(!at_j->negationHasProof()){
+          successes++;
+          vec[j] = vec.back();
+          vec.pop_back();
+          ConstraintP neg_at_j = at_j->getNegation();
+
+          Trace("approx::replayLog") << "Setting the proof for the replayLog conflict on:" << endl
+                                     << "  (" << neg_at_j->isTrue() <<") " << neg_at_j << endl
+                                     << "  (" << at_j->isTrue() <<") " << at_j << endl;
+          neg_at_j->impliedByIntHole(vec, true);
+          raiseConflict(at_j, InferenceId::ARITH_CONF_REPLAY_LOG);
+          break;
+        }
+      }
+    }
+    if(successes > 0){
+      ++d_statistics.d_mipProofsSuccessful;
+    }
+  }
+
+  if(d_currentPropagationList.size() > enteringPropN){
+    d_currentPropagationList.resize(enteringPropN);
+  }
+
+  /* It is not clear what the d_qflraStatus is at this point */
+  d_qflraStatus = Result::UNKNOWN;
+
+  Assert(d_replayVariables.empty());
+  Assert(d_replayConstraints.empty());
+
+  return !conflictQueueEmpty();
+}
+
+std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(const DenseMap<Rational>& lhs, Kind k, const Rational& rhs, bool branch)
+{
+  ArithVar added = ARITHVAR_SENTINEL;
+  Node sum = toSumNode(d_partialModel, lhs);
+  if(sum.isNull()){ return make_pair(NullConstraint, added); }
+
+  Trace("approx::constraint") << "replayGetConstraint " << sum
+                              << " " << k
+                              << " " << rhs
+                              << endl;
+
+  Assert(k == kind::LEQ || k == kind::GEQ);
+
+  NodeManager* nm = NodeManager::currentNM();
+  Node comparison =
+      nm->mkNode(k, sum, nm->mkConstRealOrInt(sum.getType(), rhs));
+  Node rewritten = rewrite(comparison);
+  if(!(Comparison::isNormalAtom(rewritten))){
+    return make_pair(NullConstraint, added);
+  }
+
+  Comparison cmp = Comparison::parseNormalForm(rewritten);
+  if(cmp.isBoolean()){ return make_pair(NullConstraint, added); }
+
+  Polynomial nvp =  cmp.normalizedVariablePart();
+  if(nvp.isZero()){ return make_pair(NullConstraint, added); }
+
+  Node norm = nvp.getNode();
+
+  ConstraintType t = Constraint::constraintTypeOfComparison(cmp);
+  DeltaRational dr = cmp.normalizedDeltaRational();
+
+  Trace("approx::constraint") << "rewriting " << rewritten << endl
+                              << " |-> " << norm << " " << t << " " << dr << endl;
+
+  Assert(!branch || d_partialModel.hasArithVar(norm));
+  ArithVar v = ARITHVAR_SENTINEL;
+  if(d_partialModel.hasArithVar(norm)){
+
+    v = d_partialModel.asArithVar(norm);
+    Trace("approx::constraint")
+        << "replayGetConstraint found " << norm << " |-> " << v << " @ "
+        << context()->getLevel() << endl;
+    Assert(!branch || d_partialModel.isIntegerInput(v));
+  }else{
+    v = requestArithVar(norm, true, true);
+    d_replayVariables.push_back(v);
+
+    added = v;
+
+    Trace("approx::constraint")
+        << "replayGetConstraint adding " << norm << " |-> " << v << " @ "
+        << context()->getLevel() << endl;
+
+    Polynomial poly = Polynomial::parsePolynomial(norm);
+    vector<ArithVar> variables;
+    vector<Rational> coefficients;
+    asVectors(poly, coefficients, variables);
+    d_tableau.addRow(v, coefficients, variables);
+    setupBasicValue(v);
+    d_linEq.trackRowIndex(d_tableau.basicToRowIndex(v));
+  }
+  Assert(d_partialModel.hasArithVar(norm));
+  Assert(d_partialModel.asArithVar(norm) == v);
+  Assert(d_constraintDatabase.variableDatabaseIsSetup(v));
+
+  ConstraintP imp = d_constraintDatabase.getBestImpliedBound(v, t, dr);
+  if(imp != NullConstraint){
+    if(imp->getValue() == dr){
+      Assert(added == ARITHVAR_SENTINEL);
+      return make_pair(imp, added);
+    }
+  }
+
+  ConstraintP newc = d_constraintDatabase.getConstraint(v, t, dr);
+  d_replayConstraints.push_back(newc);
+  return make_pair(newc, added);
+}
+
+std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(
+    ApproximateSimplex* approx, const NodeLog& nl)
+{
+  Assert(nl.isBranch());
+  Assert(d_lhsTmp.empty());
+
+  ArithVar v = approx->getBranchVar(nl);
+  if(v != ARITHVAR_SENTINEL && d_partialModel.isIntegerInput(v)){
+    if(d_partialModel.hasNode(v)){
+      d_lhsTmp.set(v, Rational(1));
+      double dval = nl.branchValue();
+      std::optional<Rational> maybe_value =
+          ApproximateSimplex::estimateWithCFE(dval);
+      if (!maybe_value)
+      {
+        return make_pair(NullConstraint, ARITHVAR_SENTINEL);
+      }
+      Rational fl(maybe_value.value().floor());
+      pair<ConstraintP, ArithVar> p;
+      p = replayGetConstraint(d_lhsTmp, kind::LEQ, fl, true);
+      d_lhsTmp.purge();
+      return p;
+    }
+  }
+  return make_pair(NullConstraint, ARITHVAR_SENTINEL);
+}
+
+std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(const CutInfo& ci) {
+  Assert(ci.reconstructed());
+  const DenseMap<Rational>& lhs = ci.getReconstruction().lhs;
+  const Rational& rhs = ci.getReconstruction().rhs;
+  Kind k = ci.getKind();
+
+  return replayGetConstraint(lhs, k, rhs, ci.getKlass() == BranchCutKlass);
+}
+
+Node toSumNode(const ArithVariables& vars, const DenseMap<Rational>& sum){
+  Trace("arith::toSumNode") << "toSumNode() begin" << endl;
+  NodeManager* nm = NodeManager::currentNM();
+  DenseMap<Rational>::const_iterator iter, end;
+  iter = sum.begin(), end = sum.end();
+  std::vector<Node> children;
+  for(; iter != end; ++iter){
+    ArithVar x = *iter;
+    if(!vars.hasNode(x)){ return Node::null(); }
+    Node xNode = vars.asNode(x);
+    const Rational& q = sum[x];
+    Node mult = nm->mkNode(kind::MULT, mkRationalNode(q), xNode);
+    Trace("arith::toSumNode") << "toSumNode() " << x << " " << mult << endl;
+    children.push_back(mult);
+  }
+  Trace("arith::toSumNode") << "toSumNode() end" << endl;
+  if (children.empty())
+  {
+    // NOTE: real type assumed here
+    return nm->mkConstReal(Rational(0));
+  }
+  else if (children.size() == 1)
+  {
+    return children[0];
+  }
+  return nm->mkNode(kind::ADD, children);
+}
+
+ConstraintCP TheoryArithPrivate::vectorToIntHoleConflict(const ConstraintCPVec& conflict){
+  Assert(conflict.size() >= 2);
+  ConstraintCPVec exp(conflict.begin(), conflict.end()-1);
+  ConstraintCP back = conflict.back();
+  Assert(back->hasProof());
+  ConstraintP negBack = back->getNegation();
+  // This can select negBack multiple times so we need to test if negBack has a proof.
+  if(negBack->hasProof()){
+    // back is in conflict already
+  } else {
+    negBack->impliedByIntHole(exp, true);
+  }
+
+  return back;
+}
+
+void TheoryArithPrivate::intHoleConflictToVector(ConstraintCP conflicting, ConstraintCPVec& conflict){
+  ConstraintCP negConflicting = conflicting->getNegation();
+  Assert(conflicting->hasProof());
+  Assert(negConflicting->hasProof());
+
+  conflict.push_back(conflicting);
+  conflict.push_back(negConflicting);
+
+  Constraint::assertionFringe(conflict);
+}
+
+void TheoryArithPrivate::tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bci){
+  Assert(conflictQueueEmpty());
+  std::vector< ConstraintCPVec > conflicts;
+
+  approx->tryCut(nid, bci);
+  Trace("approx::branch") << "tryBranchCut" << bci << endl;
+  Assert(bci.reconstructed());
+  Assert(!bci.proven());
+  pair<ConstraintP, ArithVar> p = replayGetConstraint(bci);
+  Assert(p.second == ARITHVAR_SENTINEL);
+  ConstraintP bc = p.first;
+  Assert(bc != NullConstraint);
+  if(bc->hasProof()){
+    return;
+  }
+
+  ConstraintP bcneg = bc->getNegation();
+  {
+    context::Context::ScopedPush speculativePush(context());
+    replayAssert(bcneg);
+    if(conflictQueueEmpty()){
+      TimerStat::CodeTimer codeTimer(d_statistics.d_replaySimplexTimer);
+
+      //test for linear feasibility
+      d_partialModel.stopQueueingBoundCounts();
+      UpdateTrackingCallback utcb(&d_linEq);
+      d_partialModel.processBoundsQueue(utcb);
+      d_linEq.startTrackingBoundCounts();
+
+      SimplexDecisionProcedure& simplex = selectSimplex(true);
+      simplex.findModel(false);
+      // Can change d_qflraStatus
+
+      d_linEq.stopTrackingBoundCounts();
+      d_partialModel.startQueueingBoundCounts();
+    }
+    for(size_t i = 0, N = d_conflicts.size(); i < N; ++i){
+
+      conflicts.push_back(ConstraintCPVec());
+      intHoleConflictToVector(d_conflicts[i].first, conflicts.back());
+      Constraint::assertionFringe(conflicts.back());
+
+      // ConstraintCP conflicting = d_conflicts[i];
+      // ConstraintCP negConflicting = conflicting->getNegation();
+      // Assert(conflicting->hasProof());
+      // Assert(negConflicting->hasProof());
+
+      // conflicts.push_back(ConstraintCPVec());
+      // ConstraintCPVec& back = conflicts.back();
+      // back.push_back(conflicting);
+      // back.push_back(negConflicting);
+
+      // // remove the floor/ceiling contraint implied by bcneg
+      // Constraint::assertionFringe(back);
+    }
+
+    if(TraceIsOn("approx::branch")){
+      if(d_conflicts.empty()){
+        entireStateIsConsistent("branchfailure");
+      }
+    }
+  }
+
+  Trace("approx::branch") << "branch constraint " << bc << endl;
+  for(size_t i = 0, N = conflicts.size(); i < N; ++i){
+    ConstraintCPVec& conf = conflicts[i];
+
+    // make sure to be working on the assertion fringe!
+    if(!contains(conf, bcneg)){
+      Trace("approx::branch") << "reraise " << conf  << endl;
+      ConstraintCP conflicting = vectorToIntHoleConflict(conf);
+      raiseConflict(conflicting, InferenceId::ARITH_CONF_BRANCH_CUT);
+    }else if(!bci.proven()){
+      drop(conf, bcneg);
+      bci.setExplanation(conf);
+      Trace("approx::branch") << "dropped " << bci  << endl;
+    }
+  }
+}
+
+void TheoryArithPrivate::replayAssert(ConstraintP c) {
+  if(!c->assertedToTheTheory()){
+    bool inConflict = c->negationHasProof();
+    if(!c->hasProof()){
+      c->setInternalAssumption(inConflict);
+      Trace("approx::replayAssert") << "replayAssert " << c << " set internal" << endl;
+    }else{
+      Trace("approx::replayAssert") << "replayAssert " << c << " has explanation" << endl;
+    }
+    Trace("approx::replayAssert") << "replayAssertion " << c << endl;
+    if(inConflict){
+      raiseConflict(c, InferenceId::ARITH_CONF_REPLAY_ASSERT);
+    }else{
+      assertionCases(c);
+    }
+  }else{
+    Trace("approx::replayAssert")
+        << "replayAssert " << c << " already asserted" << endl;
+  }
+}
+
+
+void TheoryArithPrivate::resolveOutPropagated(std::vector<ConstraintCPVec>& confs, const std::set<ConstraintCP>& propagated) const {
+  Trace("arith::resolveOutPropagated")
+    << "starting resolveOutPropagated() " << confs.size() << endl;
+  for(size_t i =0, N = confs.size(); i < N; ++i){
+    ConstraintCPVec& conf = confs[i];
+    size_t orig = conf.size();
+    Constraint::assertionFringe(conf);
+    Trace("arith::resolveOutPropagated")
+      << "  conf["<<i<<"] " << orig << " to " << conf.size() << endl;
+  }
+  Trace("arith::resolveOutPropagated")
+    << "ending resolveOutPropagated() " << confs.size() << endl;
+}
+
+struct SizeOrd {
+  bool operator()(const ConstraintCPVec& a, const ConstraintCPVec& b) const{
+    return a.size() < b.size();
+  }
+};
+
+void TheoryArithPrivate::subsumption(
+    std::vector<ConstraintCPVec> &confs) const {
+  int checks CVC5_UNUSED = 0;
+  int subsumed CVC5_UNUSED = 0;
+
+  for (size_t i = 0, N = confs.size(); i < N; ++i) {
+    ConstraintCPVec &conf = confs[i];
+    std::sort(conf.begin(), conf.end());
+  }
+
+  std::sort(confs.begin(), confs.end(), SizeOrd());
+  for (size_t i = 0; i < confs.size(); i++) {
+    // i is not subsumed
+    for (size_t j = i + 1; j < confs.size();) {
+      ConstraintCPVec& a = confs[i];
+      ConstraintCPVec& b = confs[j];
+      checks++;
+      bool subsumes = std::includes(a.begin(), a.end(), b.begin(), b.end());
+      if (subsumes) {
+        ConstraintCPVec& back = confs.back();
+        b.swap(back);
+        confs.pop_back();
+        subsumed++;
+      } else {
+        j++;
+      }
+    }
+  }
+  Trace("arith::subsumption") << "subsumed " << subsumed << "/" << checks
+                              << endl;
+}
+
+std::vector<ConstraintCPVec> TheoryArithPrivate::replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth){
+  ++(d_statistics.d_replayLogRecCount);
+  Trace("approx::replayLogRec") << "replayLogRec()" << std::endl;
+
+  size_t rpvars_size = d_replayVariables.size();
+  size_t rpcons_size = d_replayConstraints.size();
+  std::vector<ConstraintCPVec> res;
+
+  { /* create a block for the purpose of pushing the sat context */
+    context::Context::ScopedPush speculativePush(context());
+    Assert(!anyConflict());
+    Assert(conflictQueueEmpty());
+    set<ConstraintCP> propagated;
+
+    TreeLog& tl = getTreeLog();
+
+    if(bc != NullConstraint){
+      replayAssert(bc);
+    }
+
+    const NodeLog& nl = tl.getNode(nid);
+    NodeLog::const_iterator iter = nl.begin(), end = nl.end();
+    for(; conflictQueueEmpty() && iter != end; ++iter){
+      CutInfo* ci = *iter;
+      bool reject = false;
+      //cout << "  trying " << *ci << endl;
+      if(ci->getKlass() == RowsDeletedKlass){
+        RowsDeleted* rd = dynamic_cast<RowsDeleted*>(ci);
+
+        tl.applyRowsDeleted(nid, *rd);
+        // The previous line modifies nl
+
+        ++d_statistics.d_applyRowsDeleted;
+      }else if(ci->getKlass() == BranchCutKlass){
+        BranchCutInfo* bci = dynamic_cast<BranchCutInfo*>(ci);
+        Assert(bci != NULL);
+        tryBranchCut(approx, nid, *bci);
+
+        ++d_statistics.d_branchCutsAttempted;
+        if(!(conflictQueueEmpty() || ci->reconstructed())){
+          ++d_statistics.d_numBranchesFailed;
+        }
+      }else{
+        approx->tryCut(nid, *ci);
+        if(ci->getKlass() == GmiCutKlass){
+          ++d_statistics.d_gmiCutsAttempted;
+        }else if(ci->getKlass() == MirCutKlass){
+          ++d_statistics.d_mirCutsAttempted;
+        }
+
+        if(ci->reconstructed() && ci->proven()){
+          const DenseMap<Rational>& row = ci->getReconstruction().lhs;
+          reject = !complexityBelow(row, options().arith.replayRejectCutSize);
+        }
+      }
+      if(conflictQueueEmpty()){
+        if(reject){
+          ++d_statistics.d_cutsRejectedDuringReplay;
+        }else if(ci->reconstructed()){
+          // success
+          ++d_statistics.d_cutsReconstructed;
+
+          pair<ConstraintP, ArithVar> p = replayGetConstraint(*ci);
+          if(p.second != ARITHVAR_SENTINEL){
+            Assert(ci->getRowId() >= 1);
+            tl.mapRowId(nl.getNodeId(), ci->getRowId(), p.second);
+          }
+          ConstraintP con = p.first;
+          if(TraceIsOn("approx::replayLogRec")){
+            Trace("approx::replayLogRec") << "cut was remade " << con << " " << *ci << endl;
+          }
+
+          if(ci->proven()){
+            ++d_statistics.d_cutsProven;
+
+            const ConstraintCPVec& exp = ci->getExplanation();
+            // success
+            if(con->isTrue()){
+              Trace("approx::replayLogRec") << "not asserted?" << endl;
+            }else if(!con->negationHasProof()){
+              con->impliedByIntHole(exp, false);
+              replayAssert(con);
+              Trace("approx::replayLogRec") << "cut prop" << endl;
+            }else {
+              con->impliedByIntHole(exp, true);
+              Trace("approx::replayLogRec") << "cut into conflict " << con << endl;
+              raiseConflict(con, InferenceId::ARITH_CONF_REPLAY_LOG_REC);
+            }
+          }else{
+            ++d_statistics.d_cutsProofFailed;
+            Trace("approx::replayLogRec") << "failed to get proof " << *ci << endl;
+          }
+        }else if(ci->getKlass() != RowsDeletedKlass){
+          ++d_statistics.d_cutsReconstructionFailed;
+        }
+      }
+    }
+
+    /* check if the system is feasible under with the cuts */
+    if(conflictQueueEmpty()){
+      Assert(options().arith.replayEarlyCloseDepths >= 1);
+      if (!nl.isBranch() || depth % options().arith.replayEarlyCloseDepths == 0)
+      {
+        TimerStat::CodeTimer codeTimer(d_statistics.d_replaySimplexTimer);
+        //test for linear feasibility
+        d_partialModel.stopQueueingBoundCounts();
+        UpdateTrackingCallback utcb(&d_linEq);
+        d_partialModel.processBoundsQueue(utcb);
+        d_linEq.startTrackingBoundCounts();
+
+        SimplexDecisionProcedure& simplex = selectSimplex(true);
+        simplex.findModel(false);
+        // can change d_qflraStatus
+
+        d_linEq.stopTrackingBoundCounts();
+        d_partialModel.startQueueingBoundCounts();
+      }
+    }else{
+      ++d_statistics.d_replayLogRecConflictEscalation;
+    }
+
+    if(!conflictQueueEmpty()){
+      /* if a conflict has been found stop */
+      for(size_t i = 0, N = d_conflicts.size(); i < N; ++i){
+        res.push_back(ConstraintCPVec());
+        intHoleConflictToVector(d_conflicts[i].first, res.back());
+      }
+      ++d_statistics.d_replayLogRecEarlyExit;
+    }else if(nl.isBranch()){
+      /* if it is a branch try the branch */
+      pair<ConstraintP, ArithVar> p = replayGetConstraint(approx, nl);
+      Assert(p.second == ARITHVAR_SENTINEL);
+      ConstraintP dnc = p.first;
+      if(dnc != NullConstraint){
+        ConstraintP upc = dnc->getNegation();
+
+        int dnid = nl.getDownId();
+        int upid = nl.getUpId();
+
+        NodeLog& dnlog = tl.getNode(dnid);
+        NodeLog& uplog = tl.getNode(upid);
+        dnlog.copyParentRowIds();
+        uplog.copyParentRowIds();
+
+        std::vector<ConstraintCPVec> dnres;
+        std::vector<ConstraintCPVec> upres;
+        std::vector<size_t> containsdn;
+        std::vector<size_t> containsup;
+        if(res.empty()){
+          dnres = replayLogRec(approx, dnid, dnc, depth+1);
+          for(size_t i = 0, N = dnres.size(); i < N; ++i){
+            ConstraintCPVec& conf = dnres[i];
+            if(contains(conf, dnc)){
+              containsdn.push_back(i);
+            }else{
+              res.push_back(conf);
+            }
+          }
+        }else{
+          Trace("approx::replayLogRec") << "replayLogRec() skipping" << dnlog << std::endl;
+          ++d_statistics.d_replayBranchSkips;
+        }
+
+        if(res.empty()){
+          upres = replayLogRec(approx, upid, upc, depth+1);
+
+          for(size_t i = 0, N = upres.size(); i < N; ++i){
+            ConstraintCPVec& conf = upres[i];
+            if(contains(conf, upc)){
+              containsup.push_back(i);
+            }else{
+              res.push_back(conf);
+            }
+          }
+        }else{
+          Trace("approx::replayLogRec") << "replayLogRec() skipping" << uplog << std::endl;
+          ++d_statistics.d_replayBranchSkips;
+        }
+
+        if(res.empty()){
+          for(size_t i = 0, N = containsdn.size(); i < N; ++i){
+            ConstraintCPVec& dnconf = dnres[containsdn[i]];
+            for(size_t j = 0, M = containsup.size(); j < M; ++j){
+              ConstraintCPVec& upconf = upres[containsup[j]];
+
+              res.push_back(ConstraintCPVec());
+              ConstraintCPVec& back = res.back();
+              resolve(back, dnc, dnconf, upconf);
+            }
+          }
+          if(res.size() >= 2u){
+            subsumption(res);
+
+            if(res.size() > 100u){
+              res.resize(100u);
+            }
+          }
+        }else{
+          Trace("approx::replayLogRec") << "replayLogRec() skipping resolving" << nl << std::endl;
+        }
+        Trace("approx::replayLogRec") << "found #"<<res.size()<<" conflicts on branch " << nid << endl;
+        if(res.empty()){
+          ++d_statistics.d_replayBranchCloseFailures;
+        }
+
+      }else{
+        Trace("approx::replayLogRec") << "failed to make a branch " << nid << endl;
+      }
+    }else{
+      ++d_statistics.d_replayLeafCloseFailures;
+      Trace("approx::replayLogRec") << "failed on node " << nid << endl;
+      Assert(res.empty());
+    }
+    resolveOutPropagated(res, propagated);
+    Trace("approx::replayLogRec") << "replayLogRec() ending" << std::endl;
+
+    if (options().arith.replayFailureLemma)
+    {
+      // must be done inside the sat context to get things
+      // propagated at this level
+      if(res.empty() && nid == getTreeLog().getRootId()){
+        Assert(!d_replayedLemmas);
+        d_replayedLemmas = replayLemmas(approx);
+        Assert(d_acTmp.empty());
+        while(!d_approxCuts.empty()){
+          TrustNode lem = d_approxCuts.front();
+          d_approxCuts.pop();
+          d_acTmp.push_back(lem);
+        }
+      }
+    }
+  } /* pop the sat context */
+
+  /* move into the current context. */
+  while(!d_acTmp.empty()){
+    TrustNode lem = d_acTmp.back();
+    d_acTmp.pop_back();
+    d_approxCuts.push_back(lem);
+  }
+  Assert(d_acTmp.empty());
+
+  /* Garbage collect the constraints from this call */
+  while(d_replayConstraints.size() > rpcons_size){
+    ConstraintP c = d_replayConstraints.back();
+    d_replayConstraints.pop_back();
+    d_constraintDatabase.deleteConstraintAndNegation(c);
+  }
+
+  /* Garbage collect the ArithVars made by this call */
+  if(d_replayVariables.size() > rpvars_size){
+    d_partialModel.stopQueueingBoundCounts();
+    UpdateTrackingCallback utcb(&d_linEq);
+    d_partialModel.processBoundsQueue(utcb);
+    d_linEq.startTrackingBoundCounts();
+    while(d_replayVariables.size() > rpvars_size){
+      ArithVar v = d_replayVariables.back();
+      d_replayVariables.pop_back();
+      Assert(d_partialModel.canBeReleased(v));
+      if(!d_tableau.isBasic(v)){
+        /* if it is not basic make it basic. */
+        auto ci = d_tableau.colIterator(v);
+        Assert(!ci.atEnd());
+        ArithVar b = d_tableau.rowIndexToBasic((*ci).getRowIndex());
+        Assert(b != ARITHVAR_SENTINEL);
+        DeltaRational cp = d_partialModel.getAssignment(b);
+        if(d_partialModel.cmpAssignmentLowerBound(b) < 0){
+          cp = d_partialModel.getLowerBound(b);
+        }else if(d_partialModel.cmpAssignmentUpperBound(b) > 0){
+          cp = d_partialModel.getUpperBound(b);
+        }
+        d_linEq.pivotAndUpdate(b, v, cp);
+      }
+      Assert(d_tableau.isBasic(v));
+      d_linEq.stopTrackingRowIndex(d_tableau.basicToRowIndex(v));
+      d_tableau.removeBasicRow(v);
+
+      releaseArithVar(v);
+      Trace("approx::vars") << "releasing " << v << endl;
+    }
+    d_linEq.stopTrackingBoundCounts();
+    d_partialModel.startQueueingBoundCounts();
+    d_partialModel.attemptToReclaimReleased();
+  }
+  return res;
+}
+
+TreeLog& TheoryArithPrivate::getTreeLog(){
+  if(d_treeLog == NULL){
+    d_treeLog = new TreeLog();
+  }
+  return *d_treeLog;
+}
+
+ApproximateStatistics& TheoryArithPrivate::getApproxStats(){
+  if(d_approxStats == NULL){
+    d_approxStats = new ApproximateStatistics();
+  }
+  return *d_approxStats;
+}
+
+Node TheoryArithPrivate::branchToNode(ApproximateSimplex* approx,
+                                      const NodeLog& bn) const
+{
+  Assert(bn.isBranch());
+  ArithVar v = approx->getBranchVar(bn);
+  if(v != ARITHVAR_SENTINEL && d_partialModel.isIntegerInput(v)){
+    if(d_partialModel.hasNode(v)){
+      Node n = d_partialModel.asNode(v);
+      double dval = bn.branchValue();
+      std::optional<Rational> maybe_value =
+          ApproximateSimplex::estimateWithCFE(dval);
+      if (!maybe_value)
+      {
+        return Node::null();
+      }
+      Rational fl(maybe_value.value().floor());
+      NodeManager* nm = NodeManager::currentNM();
+      Node leq =
+          nm->mkNode(kind::LEQ, n, nm->mkConstRealOrInt(n.getType(), fl));
+      Node norm = rewrite(leq);
+      return norm;
+    }
+  }
+  return Node::null();
+}
+
+Node TheoryArithPrivate::cutToLiteral(ApproximateSimplex* approx, const CutInfo& ci) const{
+  Assert(ci.reconstructed());
+
+  const DenseMap<Rational>& lhs = ci.getReconstruction().lhs;
+  Node sum = toSumNode(d_partialModel, lhs);
+  if(!sum.isNull()){
+    NodeManager* nm = NodeManager::currentNM();
+    Kind k = ci.getKind();
+    Assert(k == kind::LEQ || k == kind::GEQ);
+    Node rhs = nm->mkConstRealOrInt(sum.getType(), ci.getReconstruction().rhs);
+    Node ineq = nm->mkNode(k, sum, rhs);
+    return rewrite(ineq);
+  }
+  return Node::null();
+}
+
+bool TheoryArithPrivate::replayLemmas(ApproximateSimplex* approx){
+    ++(d_statistics.d_mipReplayLemmaCalls);
+    bool anythingnew = false;
+
+    TreeLog& tl = getTreeLog();
+    NodeLog& root = tl.getRootNode();
+    root.applySelected(); /* set row ids */
+
+    vector<const CutInfo*> cuts = approx->getValidCuts(root);
+    for(size_t i =0, N =cuts.size(); i < N; ++i){
+      const CutInfo* cut = cuts[i];
+      Assert(cut->reconstructed());
+      Assert(cut->proven());
+
+      const DenseMap<Rational>& row =  cut->getReconstruction().lhs;
+      if (!complexityBelow(row, options().arith.lemmaRejectCutSize))
+      {
+        ++(d_statistics.d_cutsRejectedDuringLemmas);
+        continue;
+      }
+
+      Node cutConstraint = cutToLiteral(approx, *cut);
+      if(!cutConstraint.isNull()){
+        const ConstraintCPVec& exp = cut->getExplanation();
+        Node asLemma = Constraint::externalExplainByAssertions(exp);
+
+        Node implied = rewrite(cutConstraint);
+        anythingnew = anythingnew || !isSatLiteral(implied);
+
+        Node implication = asLemma.impNode(implied);
+        // DO NOT CALL OUTPUT LEMMA!
+        // TODO (project #37): justify
+        d_approxCuts.push_back(TrustNode::mkTrustLemma(implication, nullptr));
+        Trace("approx::lemmas") << "cut["<<i<<"] " << implication << endl;
+        ++(d_statistics.d_mipExternalCuts);
+      }
+    }
+    if(root.isBranch()){
+      Node lit = branchToNode(approx, root);
+      if(!lit.isNull()){
+        anythingnew = anythingnew || !isSatLiteral(lit);
+        Node branch = lit.orNode(lit.notNode());
+        if (proofsEnabled())
+        {
+          d_pfGen->mkTrustNode(branch, PfRule::SPLIT, {}, {lit});
+        }
+        else
+        {
+          d_approxCuts.push_back(TrustNode::mkTrustLemma(branch, nullptr));
+        }
+        ++(d_statistics.d_mipExternalBranch);
+        Trace("approx::lemmas") << "branching "<< root <<" as " << branch << endl;
+      }
+    }
+    return anythingnew;
+}
+
+void TheoryArithPrivate::turnOffApproxFor(int32_t rounds){
+  d_attemptSolveIntTurnedOff = d_attemptSolveIntTurnedOff + rounds;
+  ++(d_statistics.d_approxDisabled);
+}
+
+bool TheoryArithPrivate::safeToCallApprox() const{
+  unsigned numRows = 0;
+  unsigned numCols = 0;
+  var_iterator vi = var_begin(), vi_end = var_end();
+  // Assign each variable to a row and column variable as it appears in the input
+  for(; vi != vi_end && !(numRows > 0 && numCols > 0); ++vi){
+    ArithVar v = *vi;
+
+    if(d_partialModel.isAuxiliary(v)){
+      ++numRows;
+    }else{
+      ++numCols;
+    }
+  }
+  return (numRows > 0 && numCols > 0);
+}
+
+// solve()
+//   res = solveRealRelaxation(effortLevel);
+//   switch(res){
+//   case LinFeas:
+//   case LinInfeas:
+//     return replay()
+//   case Unknown:
+//   case Error
+//     if()
+void TheoryArithPrivate::solveInteger(Theory::Effort effortLevel){
+  if(!safeToCallApprox()) { return; }
+
+  Assert(safeToCallApprox());
+  TimerStat::CodeTimer codeTimer0(d_statistics.d_solveIntTimer);
+
+  ++(d_statistics.d_solveIntCalls);
+  d_statistics.d_inSolveInteger = 1;
+
+  if(!Theory::fullEffort(effortLevel)){
+    d_solveIntAttempts++;
+    ++(d_statistics.d_solveStandardEffort);
+  }
+
+  // if integers are attempted,
+  Assert(options().arith.useApprox);
+  Assert(ApproximateSimplex::enabled());
+
+  int level = context()->getLevel();
+  d_lastContextIntegerAttempted = level;
+
+  static constexpr int32_t mipLimit = 200000;
+
+  TreeLog& tl = getTreeLog();
+  ApproximateStatistics& stats = getApproxStats();
+  ApproximateSimplex* approx =
+    ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, tl, stats);
+
+    approx->setPivotLimit(mipLimit);
+    if(!d_guessedCoeffSet){
+      d_guessedCoeffs = approx->heuristicOptCoeffs();
+      d_guessedCoeffSet = true;
+    }
+    if(!d_guessedCoeffs.empty()){
+      approx->setOptCoeffs(d_guessedCoeffs);
+    }
+    static constexpr int32_t depthForLikelyInfeasible = 10;
+    int maxDepthPass1 = d_likelyIntegerInfeasible
+                            ? depthForLikelyInfeasible
+                            : options().arith.maxApproxDepth;
+    approx->setBranchingDepth(maxDepthPass1);
+    approx->setBranchOnVariableLimit(100);
+    LinResult relaxRes = approx->solveRelaxation();
+    if( relaxRes == LinFeasible ){
+      MipResult mipRes = MipUnknown;
+      {
+        TimerStat::CodeTimer codeTimer1(d_statistics.d_mipTimer);
+        mipRes = approx->solveMIP(false);
+      }
+
+      Trace("arith::solveInteger") << "mipRes " << mipRes << endl;
+      switch(mipRes) {
+      case MipBingo:
+        // attempt the solution
+        {
+          ++(d_statistics.d_solveIntModelsAttempts);
+
+          d_partialModel.stopQueueingBoundCounts();
+          UpdateTrackingCallback utcb(&d_linEq);
+          d_partialModel.processBoundsQueue(utcb);
+          d_linEq.startTrackingBoundCounts();
+
+          ApproximateSimplex::Solution mipSolution;
+          mipSolution = approx->extractMIP();
+          importSolution(mipSolution);
+          solveRelaxationOrPanic(effortLevel);
+
+          if (d_qflraStatus == Result::SAT)
+          {
+            if (!anyConflict())
+            {
+              if (ARITHVAR_SENTINEL == nextIntegerViolation(false))
+              {
+                ++(d_statistics.d_solveIntModelsSuccessful);
+              }
+            }
+          }
+
+          // shutdown simplex
+          d_linEq.stopTrackingBoundCounts();
+          d_partialModel.startQueueingBoundCounts();
+        }
+        break;
+      case MipClosed:
+        /* All integer branches closed */
+        approx->setPivotLimit(2*mipLimit);
+        {
+          TimerStat::CodeTimer codeTimer2(d_statistics.d_mipTimer);
+          mipRes = approx->solveMIP(true);
+        }
+
+        if(mipRes == MipClosed){
+          d_likelyIntegerInfeasible = true;
+          replayLog(approx);
+          AlwaysAssert(anyConflict() || d_qflraStatus != Result::SAT);
+
+          if (!anyConflict())
+          {
+            solveRealRelaxation(effortLevel);
+          }
+        }
+        if(!(anyConflict() || !d_approxCuts.empty())){
+          turnOffApproxFor(options().arith.replayNumericFailurePenalty);
+        }
+        break;
+      case BranchesExhausted:
+      case ExecExhausted:
+      case PivotsExhauasted:
+        if(mipRes == BranchesExhausted){
+          ++d_statistics.d_branchesExhausted;
+        }else if(mipRes == ExecExhausted){
+          ++d_statistics.d_execExhausted;
+        }else if(mipRes == PivotsExhauasted){
+          ++d_statistics.d_pivotsExhausted;
+        }
+
+        approx->setPivotLimit(2*mipLimit);
+        approx->setBranchingDepth(2);
+        {
+          TimerStat::CodeTimer codeTimer3(d_statistics.d_mipTimer);
+          mipRes = approx->solveMIP(true);
+        }
+        replayLemmas(approx);
+        break;
+      case MipUnknown:
+        break;
+      }
+    }
+  delete approx;
+
+  if(!Theory::fullEffort(effortLevel)){
+    if(anyConflict() || !d_approxCuts.empty()){
+      d_solveIntMaybeHelp++;
+    }
+  }
+
+  d_statistics.d_inSolveInteger = 0;
+}
+
+SimplexDecisionProcedure& TheoryArithPrivate::selectSimplex(bool pass1){
+  if(pass1){
+    if(d_pass1SDP == NULL){
+      if (options().arith.useFC)
+      {
+        d_pass1SDP = (SimplexDecisionProcedure*)(&d_fcSimplex);
+      }
+      else if (options().arith.useSOI)
+      {
+        d_pass1SDP = (SimplexDecisionProcedure*)(&d_soiSimplex);
+      }
+      else
+      {
+        d_pass1SDP = (SimplexDecisionProcedure*)(&d_dualSimplex);
+      }
+    }
+    Assert(d_pass1SDP != NULL);
+    return *d_pass1SDP;
+  }else{
+     if(d_otherSDP == NULL){
+       if (options().arith.useFC)
+       {
+         d_otherSDP = (SimplexDecisionProcedure*)(&d_fcSimplex);
+       }
+       else if (options().arith.useSOI)
+       {
+         d_otherSDP = (SimplexDecisionProcedure*)(&d_soiSimplex);
+       }
+       else
+       {
+         d_otherSDP = (SimplexDecisionProcedure*)(&d_soiSimplex);
+       }
+    }
+    Assert(d_otherSDP != NULL);
+    return *d_otherSDP;
+  }
+}
+
+void TheoryArithPrivate::importSolution(const ApproximateSimplex::Solution& solution){
+  if(TraceIsOn("arith::importSolution")){
+    Trace("arith::importSolution") << "importSolution before " << d_qflraStatus << endl;
+    d_partialModel.printEntireModel(Trace("arith::importSolution"));
+  }
+
+  d_qflraStatus = d_attemptSolSimplex.attempt(solution);
+
+  if(TraceIsOn("arith::importSolution")){
+    Trace("arith::importSolution") << "importSolution intermediate " << d_qflraStatus << endl;
+    d_partialModel.printEntireModel(Trace("arith::importSolution"));
+  }
+
+  if(d_qflraStatus != Result::UNSAT){
+    static constexpr int64_t pass2Limit = 20;
+    SimplexDecisionProcedure& simplex = selectSimplex(false);
+    simplex.setVarOrderPivotLimit(pass2Limit);
+    d_qflraStatus = simplex.findModel(false);
+  }
+
+  if(TraceIsOn("arith::importSolution")){
+    Trace("arith::importSolution") << "importSolution after " << d_qflraStatus << endl;
+    d_partialModel.printEntireModel(Trace("arith::importSolution"));
+  }
+}
+
+bool TheoryArithPrivate::solveRelaxationOrPanic(Theory::Effort effortLevel)
+{
+  // if at this point the linear relaxation is still unknown,
+  //  attempt to branch an integer variable as a last ditch effort on full check
+  if (d_qflraStatus == Result::UNKNOWN)
+  {
+    d_qflraStatus = selectSimplex(true).findModel(false);
+  }
+
+  if (Theory::fullEffort(effortLevel) && d_qflraStatus == Result::UNKNOWN)
+  {
+    ArithVar canBranch = nextIntegerViolation(false);
+    if (canBranch != ARITHVAR_SENTINEL)
+    {
+      ++d_statistics.d_panicBranches;
+      TrustNode branch = branchIntegerVariable(canBranch);
+      Assert(branch.getNode().getKind() == kind::OR);
+      Node rwbranch = rewrite(branch.getNode()[0]);
+      if (!isSatLiteral(rwbranch))
+      {
+        d_approxCuts.push_back(branch);
+        return true;
+      }
+    }
+    d_qflraStatus = selectSimplex(false).findModel(true);
+  }
+  return false;
+}
+
+bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){
+  TimerStat::CodeTimer codeTimer0(d_statistics.d_solveRealRelaxTimer);
+  Assert(d_qflraStatus != Result::SAT);
+
+  d_partialModel.stopQueueingBoundCounts();
+  UpdateTrackingCallback utcb(&d_linEq);
+  d_partialModel.processBoundsQueue(utcb);
+  d_linEq.startTrackingBoundCounts();
+
+  bool noPivotLimit =
+      Theory::fullEffort(effortLevel) || !options().arith.restrictedPivots;
+
+  SimplexDecisionProcedure& simplex = selectSimplex(true);
+
+  bool useApprox = options().arith.useApprox && ApproximateSimplex::enabled()
+                   && getSolveIntegerResource();
+
+  Trace("TheoryArithPrivate::solveRealRelaxation")
+      << "solveRealRelaxation() approx"
+      << " " << options().arith.useApprox << " "
+      << ApproximateSimplex::enabled() << " " << useApprox << " "
+      << safeToCallApprox() << endl;
+
+  bool noPivotLimitPass1 = noPivotLimit && !useApprox;
+  d_qflraStatus = simplex.findModel(noPivotLimitPass1);
+
+  Trace("TheoryArithPrivate::solveRealRelaxation")
+    << "solveRealRelaxation()" << " pass1 " << d_qflraStatus << endl;
+
+  if (d_qflraStatus == Result::UNKNOWN && useApprox && safeToCallApprox())
+  {
+    // pass2: fancy-final
+    static constexpr int32_t relaxationLimit = 10000;
+    Assert(ApproximateSimplex::enabled());
+
+    TreeLog& tl = getTreeLog();
+    ApproximateStatistics& stats = getApproxStats();
+    ApproximateSimplex* approxSolver =
+      ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, tl, stats);
+
+    approxSolver->setPivotLimit(relaxationLimit);
+
+    if(!d_guessedCoeffSet){
+      d_guessedCoeffs = approxSolver->heuristicOptCoeffs();
+      d_guessedCoeffSet = true;
+    }
+    if(!d_guessedCoeffs.empty()){
+      approxSolver->setOptCoeffs(d_guessedCoeffs);
+    }
+
+    ++d_statistics.d_relaxCalls;
+
+    ApproximateSimplex::Solution relaxSolution;
+    LinResult relaxRes = LinUnknown;
+    {
+      TimerStat::CodeTimer codeTimer1(d_statistics.d_lpTimer);
+      relaxRes = approxSolver->solveRelaxation();
+    }
+      Trace("solveRealRelaxation") << "solve relaxation? " << endl;
+      switch(relaxRes){
+      case LinFeasible:
+        Trace("solveRealRelaxation") << "lin feasible? " << endl;
+        ++d_statistics.d_relaxLinFeas;
+        relaxSolution = approxSolver->extractRelaxation();
+        importSolution(relaxSolution);
+        if(d_qflraStatus != Result::SAT){
+          ++d_statistics.d_relaxLinFeasFailures;
+        }
+        break;
+      case LinInfeasible:
+        // todo attempt to recreate approximate conflict
+        ++d_statistics.d_relaxLinInfeas;
+        Trace("solveRealRelaxation") << "lin infeasible " << endl;
+        relaxSolution = approxSolver->extractRelaxation();
+        importSolution(relaxSolution);
+        if(d_qflraStatus != Result::UNSAT){
+          ++d_statistics.d_relaxLinInfeasFailures;
+        }
+        break;
+      case LinExhausted:
+        ++d_statistics.d_relaxLinExhausted;
+        Trace("solveRealRelaxation") << "exhuasted " << endl;
+        break;
+      case LinUnknown:
+      default:
+        ++d_statistics.d_relaxOthers;
+        break;
+      }
+    delete approxSolver;
+
+  }
+
+  bool emmittedConflictOrSplit = solveRelaxationOrPanic(effortLevel);
+
+  // TODO Save zeroes with no conflicts
+  d_linEq.stopTrackingBoundCounts();
+  d_partialModel.startQueueingBoundCounts();
+
+  return emmittedConflictOrSplit;
+}
+
+bool TheoryArithPrivate::hasFreshArithLiteral(Node n) const{
+  switch(n.getKind()){
+  case kind::LEQ:
+  case kind::GEQ:
+  case kind::GT:
+  case kind::LT:
+    return !isSatLiteral(n);
+  case kind::EQUAL:
+    if (n[0].getType().isRealOrInt())
+    {
+      return !isSatLiteral(n);
+    }
+    else if (n[0].getType().isBoolean())
+    {
+      return hasFreshArithLiteral(n[0]) ||
+        hasFreshArithLiteral(n[1]);
+    }
+    else
+    {
+      return false;
+    }
+  case kind::IMPLIES:
+    // try the rhs first
+    return hasFreshArithLiteral(n[1]) ||
+      hasFreshArithLiteral(n[0]);
+  default:
+    if(n.getType().isBoolean()){
+      for(Node::iterator ni=n.begin(), nend=n.end(); ni!=nend; ++ni){
+        Node child = *ni;
+        if(hasFreshArithLiteral(child)){
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+}
+
+bool TheoryArithPrivate::preCheck(Theory::Effort level)
+{
+  Assert(d_currentPropagationList.empty());
+  if(TraceIsOn("arith::consistency")){
+    Assert(unenqueuedVariablesAreConsistent());
+  }
+
+  d_newFacts = !done();
+  // If d_previousStatus == SAT, then reverts on conflicts are safe
+  // Otherwise, they are not and must be committed.
+  d_previousStatus = d_qflraStatus;
+  if (d_newFacts)
+  {
+    d_qflraStatus = Result::UNKNOWN;
+    d_hasDoneWorkSinceCut = true;
+  }
+  return false;
+}
+
+void TheoryArithPrivate::preNotifyFact(TNode atom, bool pol, TNode fact)
+{
+  ConstraintP curr = constraintFromFactQueue(fact);
+  if (curr != NullConstraint)
+  {
+    bool res CVC5_UNUSED = assertionCases(curr);
+    Assert(!res || anyConflict());
+  }
+}
+
+bool TheoryArithPrivate::postCheck(Theory::Effort effortLevel)
+{
+  if(!anyConflict()){
+    while(!d_learnedBounds.empty()){
+      // we may attempt some constraints twice.  this is okay!
+      ConstraintP curr = d_learnedBounds.front();
+      d_learnedBounds.pop();
+      Trace("arith::learned") << curr << endl;
+
+      bool res CVC5_UNUSED = assertionCases(curr);
+      Assert(!res || anyConflict());
+
+      if(anyConflict()){ break; }
+    }
+  }
+
+  if(anyConflict()){
+    d_qflraStatus = Result::UNSAT;
+    if (options().arith.revertArithModels && d_previousStatus == Result::SAT)
+    {
+      ++d_statistics.d_revertsOnConflicts;
+      Trace("arith::bt") << "clearing here "
+                         << " " << d_newFacts << " " << d_previousStatus << " "
+                         << d_qflraStatus << endl;
+      revertOutOfConflict();
+      d_errorSet.clear();
+    }else{
+      ++d_statistics.d_commitsOnConflicts;
+      Trace("arith::bt") << "committing here "
+                         << " " << d_newFacts << " " << d_previousStatus << " "
+                         << d_qflraStatus << endl;
+      d_partialModel.commitAssignmentChanges();
+      revertOutOfConflict();
+    }
+    outputConflicts();
+    //cout << "unate conflict 1 " << effortLevel << std::endl;
+    return true;
+  }
+
+
+  if(TraceIsOn("arith::print_assertions")) {
+    debugPrintAssertions(Trace("arith::print_assertions"));
+  }
+
+  bool emmittedConflictOrSplit = false;
+  Assert(d_conflicts.empty());
+
+  bool useSimplex = d_qflraStatus != Result::SAT;
+  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+                      << "pre realRelax" << endl;
+
+  if(useSimplex){
+    emmittedConflictOrSplit = solveRealRelaxation(effortLevel);
+  }
+  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+                      << "post realRelax" << endl;
+
+
+  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+                      << "pre solveInteger" << endl;
+
+  if(attemptSolveInteger(effortLevel, emmittedConflictOrSplit)){
+    solveInteger(effortLevel);
+    if(anyConflict()){
+      ++d_statistics.d_commitsOnConflicts;
+      Trace("arith::bt") << "committing here "
+                         << " " << d_newFacts << " " << d_previousStatus << " "
+                         << d_qflraStatus << endl;
+      revertOutOfConflict();
+      d_errorSet.clear();
+      outputConflicts();
+      return true;
+    }
+  }
+
+  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+                      << "post solveInteger" << endl;
+
+  switch(d_qflraStatus){
+  case Result::SAT:
+    if (d_newFacts)
+    {
+      ++d_statistics.d_nontrivialSatChecks;
+    }
+
+    Trace("arith::bt") << "committing sap inConflit"
+                       << " " << d_newFacts << " " << d_previousStatus << " "
+                       << d_qflraStatus << endl;
+    d_partialModel.commitAssignmentChanges();
+    d_unknownsInARow = 0;
+    if(TraceIsOn("arith::consistency")){
+      Assert(entireStateIsConsistent("sat comit"));
+    }
+    if (useSimplex && options().arith.collectPivots)
+    {
+      if (options().arith.useFC)
+      {
+        d_statistics.d_satPivots << d_fcSimplex.getPivots();
+      }
+      else
+      {
+        d_statistics.d_satPivots << d_dualSimplex.getPivots();
+      }
+    }
+    break;
+  case Result::UNKNOWN:
+    ++d_unknownsInARow;
+    ++(d_statistics.d_unknownChecks);
+    Assert(!Theory::fullEffort(effortLevel));
+    Trace("arith::bt") << "committing unknown"
+                       << " " << d_newFacts << " " << d_previousStatus << " "
+                       << d_qflraStatus << endl;
+    d_partialModel.commitAssignmentChanges();
+    d_statistics.d_maxUnknownsInARow.maxAssign(d_unknownsInARow);
+
+    if (useSimplex && options().arith.collectPivots)
+    {
+      if (options().arith.useFC)
+      {
+        d_statistics.d_unknownPivots << d_fcSimplex.getPivots();
+      }
+      else
+      {
+        d_statistics.d_unknownPivots << d_dualSimplex.getPivots();
+      }
+    }
+    break;
+  case Result::UNSAT:
+    d_unknownsInARow = 0;
+
+    ++d_statistics.d_commitsOnConflicts;
+
+    Trace("arith::bt") << "committing on conflict"
+                       << " " << d_newFacts << " " << d_previousStatus << " "
+                       << d_qflraStatus << endl;
+    d_partialModel.commitAssignmentChanges();
+    revertOutOfConflict();
+
+    if(TraceIsOn("arith::consistency::comitonconflict")){
+      entireStateIsConsistent("commit on conflict");
+    }
+    outputConflicts();
+    emmittedConflictOrSplit = true;
+    Trace("arith::conflict") << "simplex conflict" << endl;
+
+    if (useSimplex && options().arith.collectPivots)
+    {
+      if (options().arith.useFC)
+      {
+        d_statistics.d_unsatPivots << d_fcSimplex.getPivots();
+      }
+      else
+      {
+        d_statistics.d_unsatPivots << d_dualSimplex.getPivots();
+      }
+    }
+    break;
+  default:
+    Unimplemented();
+  }
+  d_statistics.d_avgUnknownsInARow << d_unknownsInARow;
+
+  size_t nPivots = options().arith.useFC ? d_fcSimplex.getPivots()
+                                         : d_dualSimplex.getPivots();
+  for (std::size_t i = 0; i < nPivots; ++i)
+  {
+    d_containing.d_out->spendResource(
+        Resource::ArithPivotStep);
+  }
+
+  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+                      << "pre approx cuts" << endl;
+  if(!d_approxCuts.empty()){
+    bool anyFresh = false;
+    while(!d_approxCuts.empty()){
+      TrustNode lem = d_approxCuts.front();
+      d_approxCuts.pop();
+      Trace("arith::approx::cuts") << "approximate cut:" << lem << endl;
+      anyFresh = anyFresh || hasFreshArithLiteral(lem.getNode());
+      Trace("arith::lemma") << "approximate cut:" << lem << endl;
+      outputTrustedLemma(lem, InferenceId::ARITH_APPROX_CUT);
+    }
+    if(anyFresh){
+      emmittedConflictOrSplit = true;
+    }
+  }
+
+  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+                      << "post approx cuts" << endl;
+
+  // This should be fine if sat or unknown
+  if (!emmittedConflictOrSplit
+      && (options().arith.arithPropagationMode
+              == options::ArithPropagationMode::UNATE_PROP
+          || options().arith.arithPropagationMode
+                 == options::ArithPropagationMode::BOTH_PROP))
+  {
+    TimerStat::CodeTimer codeTimer0(d_statistics.d_newPropTime);
+    Assert(d_qflraStatus != Result::UNSAT);
+
+    while(!d_currentPropagationList.empty()  && !anyConflict()){
+      ConstraintP curr = d_currentPropagationList.front();
+      d_currentPropagationList.pop_front();
+
+      ConstraintType t = curr->getType();
+      Assert(t != Disequality)
+          << "Disequalities are not allowed in d_currentPropagation";
+
+      switch(t){
+      case LowerBound:
+        {
+          ConstraintP prev = d_currentPropagationList.front();
+          d_currentPropagationList.pop_front();
+          d_constraintDatabase.unatePropLowerBound(curr, prev);
+          break;
+        }
+      case UpperBound:
+        {
+          ConstraintP prev = d_currentPropagationList.front();
+          d_currentPropagationList.pop_front();
+          d_constraintDatabase.unatePropUpperBound(curr, prev);
+          break;
+        }
+      case Equality:
+        {
+          ConstraintP prevLB = d_currentPropagationList.front();
+          d_currentPropagationList.pop_front();
+          ConstraintP prevUB = d_currentPropagationList.front();
+          d_currentPropagationList.pop_front();
+          d_constraintDatabase.unatePropEquality(curr, prevLB, prevUB);
+          break;
+        }
+        default: Unhandled() << curr->getType();
+      }
+    }
+
+    if(anyConflict()){
+      Trace("arith::unate") << "unate conflict" << endl;
+      revertOutOfConflict();
+      d_qflraStatus = Result::UNSAT;
+      outputConflicts();
+      emmittedConflictOrSplit = true;
+      //cout << "unate conflict " << endl;
+      Trace("arith::bt") << "committing on unate conflict"
+                         << " " << d_newFacts << " " << d_previousStatus << " "
+                         << d_qflraStatus << endl;
+
+      Trace("arith::conflict") << "unate arith conflict" << endl;
+    }
+  }
+  else
+  {
+    TimerStat::CodeTimer codeTimer1(d_statistics.d_newPropTime);
+    d_currentPropagationList.clear();
+  }
+  Assert(d_currentPropagationList.empty());
+
+  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+                      << "post unate" << endl;
+
+  if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
+    ++d_fullCheckCounter;
+  }
+  if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
+    emmittedConflictOrSplit = splitDisequalities();
+  }
+  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+                      << "pos splitting" << endl;
+
+
+  Trace("arith") << "integer? "
+       << " conf/split " << emmittedConflictOrSplit
+       << " fulleffort " << Theory::fullEffort(effortLevel) << endl;
+
+  if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel) && !hasIntegerModel()){
+    Node possibleConflict = Node::null();
+    if (!emmittedConflictOrSplit && options().arith.arithDioSolver)
+    {
+      possibleConflict = callDioSolver();
+      if(possibleConflict != Node::null()){
+        revertOutOfConflict();
+        Trace("arith::conflict") << "dio conflict   " << possibleConflict << endl;
+        // TODO (project #37): justify (proofs in the DIO solver)
+        raiseBlackBoxConflict(possibleConflict);
+        outputConflicts();
+        emmittedConflictOrSplit = true;
+      }
+    }
+
+    if (!emmittedConflictOrSplit && d_hasDoneWorkSinceCut
+        && options().arith.arithDioSolver)
+    {
+      if(getDioCuttingResource()){
+        TrustNode possibleLemma = dioCutting();
+        if(!possibleLemma.isNull()){
+          d_hasDoneWorkSinceCut = false;
+          d_cutCount = d_cutCount + 1;
+          Trace("arith::lemma") << "dio cut   " << possibleLemma << endl;
+          if (outputTrustedLemma(possibleLemma, InferenceId::ARITH_DIO_CUT))
+          {
+            emmittedConflictOrSplit = true;
+          }
+        }
+      }
+    }
+
+    if(!emmittedConflictOrSplit) {
+      TrustNode possibleLemma = roundRobinBranch();
+      if (!possibleLemma.getNode().isNull())
+      {
+        ++(d_statistics.d_externalBranchAndBounds);
+        d_cutCount = d_cutCount + 1;
+        Trace("arith::lemma") << "rrbranch lemma"
+                              << possibleLemma << endl;
+        if (outputTrustedLemma(possibleLemma, InferenceId::ARITH_BB_LEMMA))
+        {
+          emmittedConflictOrSplit = true;
+        }
+      }
+    }
+
+    if (options().arith.maxCutsInContext <= d_cutCount)
+    {
+      if(d_diosolver.hasMoreDecompositionLemmas()){
+        while(d_diosolver.hasMoreDecompositionLemmas()){
+          Node decompositionLemma = d_diosolver.nextDecompositionLemma();
+          Trace("arith::lemma") << "dio decomposition lemma "
+                                << decompositionLemma << endl;
+          outputLemma(decompositionLemma, InferenceId::ARITH_DIO_DECOMPOSITION);
+        }
+      }else{
+        Trace("arith::restart") << "arith restart!" << endl;
+        outputRestart();
+      }
+    }
+  }//if !emmittedConflictOrSplit && fullEffort(effortLevel) && !hasIntegerModel()
+
+  if(Theory::fullEffort(effortLevel)){
+    if(TraceIsOn("arith::consistency::final")){
+      entireStateIsConsistent("arith::consistency::final");
+    }
+  }
+
+  if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+  if(TraceIsOn("arith::print_model")) {
+    debugPrintModel(Trace("arith::print_model"));
+  }
+  Trace("arith") << "TheoryArithPrivate::check end" << std::endl;
+  return emmittedConflictOrSplit;
+}
+
+bool TheoryArithPrivate::foundNonlinear() const { return d_foundNl; }
+
+TrustNode TheoryArithPrivate::branchIntegerVariable(ArithVar x) const
+{
+  const DeltaRational& d = d_partialModel.getAssignment(x);
+  Assert(!d.isIntegral());
+  const Rational& r = d.getNoninfinitesimalPart();
+  const Rational& i = d.getInfinitesimalPart();
+  Trace("integers") << "integers: assignment to [[" << d_partialModel.asNode(x) << "]] is " << r << "[" << i << "]" << endl;
+  Assert(!(r.getDenominator() == 1 && i.getNumerator() == 0));
+  TNode var = d_partialModel.asNode(x);
+  TrustNode lem = d_bab.branchIntegerVariable(var, r);
+  if (TraceIsOn("integers"))
+  {
+    Node l = lem.getNode();
+    if (isSatLiteral(l[0]))
+    {
+      Trace("integers") << "    " << l[0] << " == " << getSatValue(l[0])
+                        << endl;
+    }
+    else
+    {
+      Trace("integers") << "    " << l[0] << " is not assigned a SAT literal"
+                        << endl;
+    }
+    if (isSatLiteral(l[1]))
+    {
+      Trace("integers") << "    " << l[1] << " == " << getSatValue(l[1])
+                        << endl;
+    }
+    else
+    {
+      Trace("integers") << "    " << l[1] << " is not assigned a SAT literal"
+                        << endl;
+    }
+  }
+  return lem;
+}
+
+std::vector<ArithVar> TheoryArithPrivate::cutAllBounded() const{
+  vector<ArithVar> lemmas;
+  ArithVar max = d_partialModel.getNumberOfVariables();
+
+  if (options().arith.doCutAllBounded && max > 0)
+  {
+    for(ArithVar iter = 0; iter != max; ++iter){
+    //Do not include slack variables
+      const DeltaRational& d = d_partialModel.getAssignment(iter);
+      if(isIntegerInput(iter) &&
+         !d_cutInContext.contains(iter) &&
+         d_partialModel.hasUpperBound(iter) &&
+         d_partialModel.hasLowerBound(iter) &&
+         !d.isIntegral()){
+        lemmas.push_back(iter);
+      }
+    }
+  }
+  return lemmas;
+}
+
+/** Returns true if the roundRobinBranching() issues a lemma. */
+TrustNode TheoryArithPrivate::roundRobinBranch()
+{
+  if(hasIntegerModel()){
+    return TrustNode::null();
+  }else{
+    ArithVar v = d_nextIntegerCheckVar;
+
+    Assert(isInteger(v));
+    Assert(!isAuxiliaryVariable(v));
+    return branchIntegerVariable(v);
+  }
+}
+
+bool TheoryArithPrivate::splitDisequalities(){
+  bool splitSomething = false;
+
+  vector<ConstraintP> save;
+
+  while(!d_diseqQueue.empty()){
+    ConstraintP front = d_diseqQueue.front();
+    d_diseqQueue.pop();
+
+    if(front->isSplit()){
+      Trace("arith::eq") << "split already" << endl;
+    }else{
+      Trace("arith::eq") << "not split already" << endl;
+
+      ArithVar lhsVar = front->getVariable();
+
+      const DeltaRational& lhsValue = d_partialModel.getAssignment(lhsVar);
+      const DeltaRational& rhsValue = front->getValue();
+      if(lhsValue == rhsValue){
+        Trace("arith::lemma") << "Splitting on " << front << endl;
+        Trace("arith::lemma") << "LHS value = " << lhsValue << endl;
+        Trace("arith::lemma") << "RHS value = " << rhsValue << endl;
+        TrustNode lemma = front->split();
+        ++(d_statistics.d_statDisequalitySplits);
+
+        Trace("arith::lemma") << "Now " << lemma.getNode() << endl;
+        outputTrustedLemma(lemma, InferenceId::ARITH_SPLIT_DEQ);
+        splitSomething = true;
+      }else if(d_partialModel.strictlyLessThanLowerBound(lhsVar, rhsValue)){
+        Trace("arith::eq") << "can drop as less than lb" << front << endl;
+      }else if(d_partialModel.strictlyGreaterThanUpperBound(lhsVar, rhsValue)){
+        Trace("arith::eq") << "can drop as greater than ub" << front << endl;
+      }else{
+        Trace("arith::eq") << "save" << front << ": " <<lhsValue << " != " << rhsValue << endl;
+        save.push_back(front);
+      }
+    }
+  }
+  vector<ConstraintP>::const_iterator i=save.begin(), i_end = save.end();
+  for(; i != i_end; ++i){
+    d_diseqQueue.push(*i);
+  }
+  return splitSomething;
+}
+
+/**
+ * Should be guarded by at least TraceIsOn("arith::print_assertions").
+ * Prints to Trace("arith::print_assertions")
+ */
+void TheoryArithPrivate::debugPrintAssertions(std::ostream& out) const {
+  out << "Assertions:" << endl;
+  for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+    ArithVar i = *vi;
+    if (d_partialModel.hasLowerBound(i)) {
+      ConstraintP lConstr = d_partialModel.getLowerBoundConstraint(i);
+      out << lConstr << endl;
+    }
+
+    if (d_partialModel.hasUpperBound(i)) {
+      ConstraintP uConstr = d_partialModel.getUpperBoundConstraint(i);
+      out << uConstr << endl;
+    }
+  }
+  context::CDQueue<ConstraintP>::const_iterator it = d_diseqQueue.begin();
+  context::CDQueue<ConstraintP>::const_iterator it_end = d_diseqQueue.end();
+  for(; it != it_end; ++ it) {
+    out << *it << endl;
+  }
+}
+
+void TheoryArithPrivate::debugPrintModel(std::ostream& out) const{
+  out << "Model:" << endl;
+  for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+    ArithVar i = *vi;
+    if(d_partialModel.hasNode(i)){
+      out << d_partialModel.asNode(i) << " : " <<
+        d_partialModel.getAssignment(i);
+      if(d_tableau.isBasic(i)){
+        out << " (basic)";
+      }
+      out << endl;
+    }
+  }
+}
+
+TrustNode TheoryArithPrivate::explain(TNode n)
+{
+  Trace("arith::explain") << "explain @" << context()->getLevel() << ": " << n
+                          << endl;
+
+  ConstraintP c = d_constraintDatabase.lookup(n);
+  TrustNode exp;
+  if(c != NullConstraint){
+    Assert(!c->isAssumption());
+    exp = c->externalExplainForPropagation(n);
+    Trace("arith::explain") << "constraint explanation" << n << ":" << exp << endl;
+  }else if(d_assertionsThatDoNotMatchTheirLiterals.find(n) != d_assertionsThatDoNotMatchTheirLiterals.end()){
+    c = d_assertionsThatDoNotMatchTheirLiterals[n];
+    if(!c->isAssumption()){
+      exp = c->externalExplainForPropagation(n);
+      Trace("arith::explain") << "assertions explanation" << n << ":" << exp << endl;
+    }else{
+      Trace("arith::explain") << "this is a strange mismatch" << n << endl;
+      Assert(d_congruenceManager.canExplain(n));
+      exp = d_congruenceManager.explain(n);
+    }
+  }else{
+    Assert(d_congruenceManager.canExplain(n));
+    Trace("arith::explain") << "dm explanation" << n << endl;
+    exp = d_congruenceManager.explain(n);
+  }
+  return exp;
+}
+
+void TheoryArithPrivate::propagate(Theory::Effort e) {
+  // This uses model values for safety. Disable for now.
+  if (d_qflraStatus == Result::SAT
+      && (options().arith.arithPropagationMode
+              == options::ArithPropagationMode::BOUND_INFERENCE_PROP
+          || options().arith.arithPropagationMode
+                 == options::ArithPropagationMode::BOTH_PROP)
+      && hasAnyUpdates())
+  {
+    if (options().arith.newProp)
+    {
+      propagateCandidatesNew();
+    }
+    else
+    {
+      propagateCandidates();
+    }
+  }
+  else
+  {
+    clearUpdates();
+  }
+
+  while(d_constraintDatabase.hasMorePropagations()){
+    ConstraintCP c = d_constraintDatabase.nextPropagation();
+    Trace("arith::prop") << "next prop" << context()->getLevel() << ": " << c
+                         << endl;
+
+    if(c->negationHasProof()){
+      Trace("arith::prop") << "negation has proof " << c->getNegation() << endl;
+      Trace("arith::prop") << c->getNegation()->externalExplainByAssertions()
+                           << endl;
+    }
+    Assert(!c->negationHasProof())
+        << "A constraint has been propagated on the constraint propagation "
+           "queue, but the negation has been set to true.  Contact Tim now!";
+
+    if(!c->assertedToTheTheory()){
+      Node literal = c->getLiteral();
+      Trace("arith::prop") << "propagating @" << context()->getLevel() << " "
+                           << literal << endl;
+
+      outputPropagate(literal);
+    }else{
+      Trace("arith::prop") << "already asserted to the theory " <<  c->getLiteral() << endl;
+    }
+  }
+
+  NodeManager* nm = NodeManager::currentNM();
+  while(d_congruenceManager.hasMorePropagations()){
+    TNode toProp = d_congruenceManager.getNextPropagation();
+
+    //Currently if the flag is set this came from an equality detected by the
+    //equality engine in the the difference manager.
+    Node normalized = rewrite(toProp);
+
+    ConstraintP constraint = d_constraintDatabase.lookup(normalized);
+    if(constraint == NullConstraint){
+      Trace("arith::prop") << "propagating on non-constraint? "  << toProp << endl;
+
+      outputPropagate(toProp);
+    }else if(constraint->negationHasProof()){
+      // The congruence manager can prove: antecedents => toProp,
+      // ergo. antecedents ^ ~toProp is a conflict.
+      TrustNode exp = d_congruenceManager.explain(toProp);
+      Node notNormalized = normalized.negate();
+      std::vector<Node> ants(exp.getNode().begin(), exp.getNode().end());
+      ants.push_back(notNormalized);
+      Node lp = nm->mkAnd(ants);
+      Trace("arith::prop") << "propagate conflict" <<  lp << endl;
+      if (proofsEnabled())
+      {
+        // Assume all of antecedents and ~toProp (rewritten)
+        std::vector<Pf> pfAntList;
+        for (size_t i = 0; i < ants.size(); ++i)
+        {
+          pfAntList.push_back(d_pnm->mkAssume(ants[i]));
+        }
+        Pf pfAnt = pfAntList.size() > 1
+                       ? d_pnm->mkNode(PfRule::AND_INTRO, pfAntList, {})
+                       : pfAntList[0];
+        // Use modus ponens to get toProp (un rewritten)
+        Pf pfConc = d_pnm->mkNode(
+            PfRule::MODUS_PONENS,
+            {pfAnt, exp.getGenerator()->getProofFor(exp.getProven())},
+            {});
+        // prove toProp (rewritten)
+        Pf pfConcRewritten = d_pnm->mkNode(
+            PfRule::MACRO_SR_PRED_TRANSFORM, {pfConc}, {normalized});
+        Pf pfNotNormalized = d_pnm->mkAssume(notNormalized);
+        // prove bottom from toProp and ~toProp
+        Pf pfBot;
+        if (normalized.getKind() == kind::NOT)
+        {
+          pfBot = d_pnm->mkNode(
+              PfRule::CONTRA, {pfNotNormalized, pfConcRewritten}, {});
+        }
+        else
+        {
+          pfBot = d_pnm->mkNode(
+              PfRule::CONTRA, {pfConcRewritten, pfNotNormalized}, {});
+        }
+        // close scope
+        Pf pfNotAnd = d_pnm->mkScope(pfBot, ants);
+        raiseBlackBoxConflict(lp, pfNotAnd);
+      }
+      else
+      {
+        raiseBlackBoxConflict(lp);
+      }
+      outputConflicts();
+      return;
+    }else{
+      Trace("arith::prop") << "propagating still?" <<  toProp << endl;
+      outputPropagate(toProp);
+    }
+  }
+}
+
+DeltaRational TheoryArithPrivate::getDeltaValue(TNode term) const
+{
+  AlwaysAssert(d_qflraStatus != Result::UNKNOWN);
+  Trace("arith::value") << term << std::endl;
+
+  if (d_partialModel.hasArithVar(term)) {
+    ArithVar var = d_partialModel.asArithVar(term);
+    return d_partialModel.getAssignment(var);
+  }
+
+  switch (Kind kind = term.getKind()) {
+    case kind::CONST_RATIONAL:
+    case kind::CONST_INTEGER: return term.getConst<Rational>();
+
+    case kind::ADD:
+    {  // 2+ args
+      DeltaRational value(0);
+      for (TNode::iterator i = term.begin(), iend = term.end(); i != iend;
+           ++i) {
+        value = value + getDeltaValue(*i);
+      }
+      return value;
+    }
+
+    case kind::NONLINEAR_MULT:
+    case kind::MULT: {  // 2+ args
+      Assert(!isSetup(term));
+      DeltaRational value(1);
+      for (TNode::iterator i = term.begin(), iend = term.end(); i != iend;
+           ++i) {
+        value = value * getDeltaValue(*i);
+      }
+      return value;
+    }
+    case kind::SUB:
+    {  // 2 args
+      return getDeltaValue(term[0]) - getDeltaValue(term[1]);
+    }
+    case kind::NEG:
+    {  // 1 arg
+      return (-getDeltaValue(term[0]));
+    }
+
+    case kind::DIVISION: {  // 2 args
+      Assert(!isSetup(term));
+      return getDeltaValue(term[0]) / getDeltaValue(term[1]);
+    }
+    case kind::DIVISION_TOTAL:
+    case kind::INTS_DIVISION_TOTAL:
+    case kind::INTS_MODULUS_TOTAL: {  // 2 args
+      Assert(!isSetup(term));
+      DeltaRational denominator = getDeltaValue(term[1]);
+      if (denominator.isZero()) {
+        return DeltaRational(0, 0);
+      }
+      DeltaRational numerator = getDeltaValue(term[0]);
+      if (kind == kind::DIVISION_TOTAL) {
+        return numerator / denominator;
+      } else if (kind == kind::INTS_DIVISION_TOTAL) {
+        return Rational(numerator.euclidianDivideQuotient(denominator));
+      } else {
+        Assert(kind == kind::INTS_MODULUS_TOTAL);
+        return Rational(numerator.euclidianDivideRemainder(denominator));
+      }
+    }
+
+    default:
+      throw ModelException(term, "No model assignment.");
+  }
+}
+
+Rational TheoryArithPrivate::deltaValueForTotalOrder() const{
+  Rational min(2);
+  std::set<DeltaRational> relevantDeltaValues;
+  context::CDQueue<ConstraintP>::const_iterator qiter = d_diseqQueue.begin();
+  context::CDQueue<ConstraintP>::const_iterator qiter_end = d_diseqQueue.end();
+
+  for(; qiter != qiter_end; ++qiter){
+    ConstraintP curr = *qiter;
+
+    const DeltaRational& rhsValue = curr->getValue();
+    relevantDeltaValues.insert(rhsValue);
+  }
+
+  Theory::shared_terms_iterator shared_iter = d_containing.shared_terms_begin();
+  Theory::shared_terms_iterator shared_end = d_containing.shared_terms_end();
+  for(; shared_iter != shared_end; ++shared_iter){
+    Node sharedCurr = *shared_iter;
+
+    // ModelException is fatal as this point. Don't catch!
+    // DeltaRationalException is fatal as this point. Don't catch!
+    DeltaRational val = getDeltaValue(sharedCurr);
+    relevantDeltaValues.insert(val);
+  }
+
+  for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+    ArithVar v = *vi;
+    const DeltaRational& value = d_partialModel.getAssignment(v);
+    relevantDeltaValues.insert(value);
+    if( d_partialModel.hasLowerBound(v)){
+      const DeltaRational& lb = d_partialModel.getLowerBound(v);
+      relevantDeltaValues.insert(lb);
+    }
+    if( d_partialModel.hasUpperBound(v)){
+      const DeltaRational& ub = d_partialModel.getUpperBound(v);
+      relevantDeltaValues.insert(ub);
+    }
+  }
+
+  if(relevantDeltaValues.size() >= 2){
+    std::set<DeltaRational>::const_iterator iter = relevantDeltaValues.begin();
+    std::set<DeltaRational>::const_iterator iter_end = relevantDeltaValues.end();
+    DeltaRational prev = *iter;
+    ++iter;
+    for(; iter != iter_end; ++iter){
+      const DeltaRational& curr = *iter;
+
+      Assert(prev < curr);
+
+      DeltaRational::seperatingDelta(min, prev, curr);
+      prev = curr;
+    }
+  }
+
+  Assert(min.sgn() > 0);
+  Rational belowMin = min/Rational(2);
+  return belowMin;
+}
+
+void TheoryArithPrivate::collectModelValues(const std::set<Node>& termSet,
+                                            std::map<Node, Node>& arithModel)
+{
+  AlwaysAssert(d_qflraStatus == Result::SAT);
+
+  if(TraceIsOn("arith::collectModelInfo")){
+    debugPrintFacts();
+  }
+
+  Trace("arith::collectModelInfo") << "collectModelInfo() begin " << endl;
+
+  // Delta lasts at least the duration of the function call
+  const Rational& delta = d_partialModel.getDelta();
+  std::unordered_set<TNode> shared = d_containing.currentlySharedTerms();
+
+  // TODO:
+  // This is not very good for user push/pop....
+  // Revisit when implementing push/pop
+  NodeManager* nm = NodeManager::currentNM();
+  for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+    ArithVar v = *vi;
+
+    if(!isAuxiliaryVariable(v)){
+      Node term = d_partialModel.asNode(v);
+
+      if((theoryOf(term) == THEORY_ARITH || shared.find(term) != shared.end())
+         && termSet.find(term) != termSet.end()){
+
+        const DeltaRational& mod = d_partialModel.getAssignment(v);
+        Rational qmodel = mod.substituteDelta(delta);
+
+        Node qNode = nm->mkConstRealOrInt(term.getType(), qmodel);
+        Trace("arith::collectModelInfo") << "m->assertEquality(" << term << ", " << qmodel << ", true)" << endl;
+        // Add to the map
+        arithModel[term] = qNode;
+      }else{
+        Trace("arith::collectModelInfo") << "Skipping m->assertEquality(" << term << ", true)" << endl;
+
+      }
+    }
+  }
+
+  // Iterate over equivalence classes in LinearEqualityModule
+  // const eq::EqualityEngine& ee = d_congruenceManager.getEqualityEngine();
+  // m->assertEqualityEngine(&ee);
+
+  Trace("arith::collectModelInfo") << "collectModelInfo() end " << endl;
+}
+
+bool TheoryArithPrivate::safeToReset() const {
+  Assert(!d_tableauSizeHasBeenModified);
+  Assert(d_errorSet.noSignals());
+
+  ErrorSet::error_iterator error_iter = d_errorSet.errorBegin();
+  ErrorSet::error_iterator error_end = d_errorSet.errorEnd();
+  for(; error_iter != error_end; ++error_iter){
+    ArithVar basic = *error_iter;
+    if(!d_smallTableauCopy.isBasic(basic)){
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TheoryArithPrivate::notifyRestart(){
+  TimerStat::CodeTimer codeTimer(d_statistics.d_restartTimer);
+
+  if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+
+  ++d_restartsCounter;
+  d_solveIntMaybeHelp = 0;
+  d_solveIntAttempts = 0;
+}
+
+bool TheoryArithPrivate::entireStateIsConsistent(const string& s){
+  bool result = true;
+  for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+    ArithVar var = *vi;
+    //ArithVar var = d_partialModel.asArithVar(*i);
+    if(!d_partialModel.assignmentIsConsistent(var)){
+      d_partialModel.printModel(var);
+      warning() << s << ":" << "Assignment is not consistent for " << var << d_partialModel.asNode(var);
+      if(d_tableau.isBasic(var)){
+        warning() << " (basic)";
+      }
+      warning() << std::endl;
+      result = false;
+    }else if(d_partialModel.isInteger(var) && !d_partialModel.integralAssignment(var)){
+      d_partialModel.printModel(var);
+      warning() << s << ":" << "Assignment is not integer for integer variable " << var << d_partialModel.asNode(var);
+      if(d_tableau.isBasic(var)){
+        warning() << " (basic)";
+      }
+      warning() << std::endl;
+      result = false;
+    }
+  }
+  return result;
+}
+
+bool TheoryArithPrivate::unenqueuedVariablesAreConsistent(){
+  bool result = true;
+  for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+    ArithVar var = *vi;
+    if(!d_partialModel.assignmentIsConsistent(var)){
+      if(!d_errorSet.inError(var)){
+
+        d_partialModel.printModel(var);
+        warning() << "Unenqueued var is not consistent for " << var <<  d_partialModel.asNode(var);
+        if(d_tableau.isBasic(var)){
+          warning() << " (basic)";
+        }
+        warning() << std::endl;
+        result = false;
+      } else if(TraceIsOn("arith::consistency::initial")){
+        d_partialModel.printModel(var);
+        warning() << "Initial var is not consistent for " << var <<  d_partialModel.asNode(var);
+        if(d_tableau.isBasic(var)){
+          warning() << " (basic)";
+        }
+        warning() << std::endl;
+      }
+     }
+  }
+  return result;
+}
+
+void TheoryArithPrivate::presolve(){
+  TimerStat::CodeTimer codeTimer(d_statistics.d_presolveTime);
+
+  d_statistics.d_initialTableauSize = d_tableau.size();
+
+  if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+
+  if(TraceIsOn("arith::presolve")) {
+    Trace("arith::presolve") << "TheoryArithPrivate::presolve" << endl;
+  }
+
+  vector<TrustNode> lemmas;
+  if (!options().base.incrementalSolving)
+  {
+    switch (options().arith.arithUnateLemmaMode)
+    {
+      case options::ArithUnateLemmaMode::NO: break;
+      case options::ArithUnateLemmaMode::INEQUALITY:
+        d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
+        break;
+      case options::ArithUnateLemmaMode::EQUALITY:
+        d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
+        break;
+      case options::ArithUnateLemmaMode::ALL:
+        d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
+        d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
+        break;
+      default: Unhandled() << options().arith.arithUnateLemmaMode;
+    }
+  }
+
+  vector<TrustNode>::const_iterator i = lemmas.begin(), i_end = lemmas.end();
+  for(; i != i_end; ++i){
+    TrustNode lem = *i;
+    Trace("arith::oldprop") << " lemma lemma duck " <<lem << endl;
+    outputTrustedLemma(lem, InferenceId::ARITH_UNATE);
+  }
+}
+
+EqualityStatus TheoryArithPrivate::getEqualityStatus(TNode a, TNode b) {
+  if (d_qflraStatus == Result::UNKNOWN)
+  {
+    return EQUALITY_UNKNOWN;
+  }else{
+    try {
+      if (getDeltaValue(a) == getDeltaValue(b)) {
+        return EQUALITY_TRUE_IN_MODEL;
+      } else {
+        return EQUALITY_FALSE_IN_MODEL;
+      }
+    } catch (DeltaRationalException& dr) {
+      return EQUALITY_UNKNOWN;
+    } catch (ModelException& me) {
+      return EQUALITY_UNKNOWN;
+    }
+  }
+}
+
+bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound){
+  ++d_statistics.d_boundComputations;
+
+  RowIndex ridx = d_tableau.basicToRowIndex(basic);
+  DeltaRational bound = d_linEq.computeRowBound(ridx, upperBound, basic);
+
+  if((upperBound && d_partialModel.strictlyLessThanUpperBound(basic, bound)) ||
+     (!upperBound && d_partialModel.strictlyGreaterThanLowerBound(basic, bound))){
+
+    // TODO: "Policy point"
+    //We are only going to recreate the functionality for now.
+    //In the future this can be improved to generate a temporary constraint
+    //if none exists.
+    //Experiment with doing this every time or only when the new constraint
+    //implies an unknown fact.
+
+    ConstraintType t = upperBound ? UpperBound : LowerBound;
+    ConstraintP bestImplied = d_constraintDatabase.getBestImpliedBound(basic, t, bound);
+
+    // Node bestImplied = upperBound ?
+    //   d_apm.getBestImpliedUpperBound(basic, bound):
+    //   d_apm.getBestImpliedLowerBound(basic, bound);
+
+    if(bestImplied != NullConstraint){
+      //This should be stronger
+      Assert(!upperBound || bound <= bestImplied->getValue());
+      Assert(
+          !upperBound
+          || d_partialModel.lessThanUpperBound(basic, bestImplied->getValue()));
+
+      Assert(upperBound || bound >= bestImplied->getValue());
+      Assert(upperBound
+             || d_partialModel.greaterThanLowerBound(basic,
+                                                     bestImplied->getValue()));
+      //slightly changed
+
+      // ConstraintP c = d_constraintDatabase.lookup(bestImplied);
+      // Assert(c != NullConstraint);
+
+      bool assertedToTheTheory = bestImplied->assertedToTheTheory();
+      bool canBePropagated = bestImplied->canBePropagated();
+      bool hasProof = bestImplied->hasProof();
+
+      Trace("arith::prop") << "arith::prop" << basic
+                           << " " << assertedToTheTheory
+                           << " " << canBePropagated
+                           << " " << hasProof
+                           << endl;
+
+      if(bestImplied->negationHasProof()){
+        warning() << "the negation of " <<  bestImplied << " : " << std::endl
+                  << "has proof " << bestImplied->getNegation() << std::endl
+                  << bestImplied->getNegation()->externalExplainByAssertions()
+                  << std::endl;
+      }
+
+      if(!assertedToTheTheory && canBePropagated && !hasProof ){
+        d_linEq.propagateBasicFromRow(bestImplied, options().smt.produceProofs);
+        // I think this can be skipped if canBePropagated is true
+        //d_learnedBounds.push(bestImplied);
+        if(TraceIsOn("arith::prop")){
+          Trace("arith::prop") << "success " << bestImplied << endl;
+          d_partialModel.printModel(basic, Trace("arith::prop"));
+        }
+        return true;
+      }
+      if(TraceIsOn("arith::prop")){
+        Trace("arith::prop") << "failed " << basic
+                             << " " << bound
+                             << " " << assertedToTheTheory
+                             << " " << canBePropagated
+                             << " " << hasProof << endl;
+        d_partialModel.printModel(basic, Trace("arith::prop"));
+      }
+    }
+  }else if(TraceIsOn("arith::prop")){
+    Trace("arith::prop") << "false " << bound << " ";
+    d_partialModel.printModel(basic, Trace("arith::prop"));
+  }
+  return false;
+}
+
+void TheoryArithPrivate::propagateCandidate(ArithVar basic){
+  bool success = false;
+  RowIndex ridx = d_tableau.basicToRowIndex(basic);
+
+  bool tryLowerBound =
+    d_partialModel.strictlyAboveLowerBound(basic) &&
+    d_linEq.rowLacksBound(ridx, false, basic) == NULL;
+
+  bool tryUpperBound =
+    d_partialModel.strictlyBelowUpperBound(basic) &&
+    d_linEq.rowLacksBound(ridx, true, basic) == NULL;
+
+  if(tryLowerBound){
+    success |= propagateCandidateLowerBound(basic);
+  }
+  if(tryUpperBound){
+    success |= propagateCandidateUpperBound(basic);
+  }
+  if(success){
+    ++d_statistics.d_boundPropagations;
+  }
+}
+
+void TheoryArithPrivate::propagateCandidates(){
+  TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
+
+  Trace("arith::prop") << "propagateCandidates begin" << endl;
+
+  Assert(d_candidateBasics.empty());
+
+  if(d_updatedBounds.empty()){ return; }
+
+  DenseSet::const_iterator i = d_updatedBounds.begin();
+  DenseSet::const_iterator end = d_updatedBounds.end();
+  for(; i != end; ++i){
+    ArithVar var = *i;
+    if (d_tableau.isBasic(var)
+        && d_tableau.basicRowLength(var)
+               <= options().arith.arithPropagateMaxLength)
+    {
+      d_candidateBasics.softAdd(var);
+    }
+    else
+    {
+      Tableau::ColIterator basicIter = d_tableau.colIterator(var);
+      for(; !basicIter.atEnd(); ++basicIter){
+        const Tableau::Entry& entry = *basicIter;
+        RowIndex ridx = entry.getRowIndex();
+        ArithVar rowVar = d_tableau.rowIndexToBasic(ridx);
+        Assert(entry.getColVar() == var);
+        Assert(d_tableau.isBasic(rowVar));
+        if (d_tableau.getRowLength(ridx)
+            <= options().arith.arithPropagateMaxLength)
+        {
+          d_candidateBasics.softAdd(rowVar);
+        }
+      }
+    }
+  }
+  d_updatedBounds.purge();
+
+  while(!d_candidateBasics.empty()){
+    ArithVar candidate = d_candidateBasics.back();
+    d_candidateBasics.pop_back();
+    Assert(d_tableau.isBasic(candidate));
+    propagateCandidate(candidate);
+  }
+  Trace("arith::prop") << "propagateCandidates end" << endl << endl << endl;
+}
+
+void TheoryArithPrivate::propagateCandidatesNew(){
+  /* Four criteria must be met for progagation on a variable to happen using a row:
+   * 0: A new bound has to have been added to the row.
+   * 1: The hasBoundsCount for the row must be "full" or be full minus one variable
+   *    (This is O(1) to check, but requires book keeping.)
+   * 2: The current assignment must be strictly smaller/greater than the current bound.
+   *    assign(x) < upper(x)
+   *    (This is O(1) to compute.)
+   * 3: There is a bound that is strictly smaller/greater than the current assignment.
+   *    assign(x) < c for some x <= c literal
+   *    (This is O(log n) to compute.)
+   * 4: The implied bound on x is strictly smaller/greater than the current bound.
+   *    (This is O(n) to compute.)
+   */
+
+  TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
+  Trace("arith::prop") << "propagateCandidatesNew begin" << endl;
+
+  Assert(d_qflraStatus == Result::SAT);
+  if(d_updatedBounds.empty()){ return; }
+  dumpUpdatedBoundsToRows();
+  Assert(d_updatedBounds.empty());
+
+  if(!d_candidateRows.empty()){
+    UpdateTrackingCallback utcb(&d_linEq);
+    d_partialModel.processBoundsQueue(utcb);
+  }
+
+  while(!d_candidateRows.empty()){
+    RowIndex candidate = d_candidateRows.back();
+    d_candidateRows.pop_back();
+    propagateCandidateRow(candidate);
+  }
+  Trace("arith::prop") << "propagateCandidatesNew end" << endl << endl << endl;
+}
+
+bool TheoryArithPrivate::propagateMightSucceed(ArithVar v, bool ub) const{
+  int cmp = ub ? d_partialModel.cmpAssignmentUpperBound(v)
+    : d_partialModel.cmpAssignmentLowerBound(v);
+  bool hasSlack = ub ? cmp < 0 : cmp > 0;
+  if(hasSlack){
+    ConstraintType t = ub ? UpperBound : LowerBound;
+    const DeltaRational& a = d_partialModel.getAssignment(v);
+
+    if(isInteger(v) && !a.isIntegral()){
+      return true;
+    }
+
+    ConstraintP strongestPossible = d_constraintDatabase.getBestImpliedBound(v, t, a);
+    if(strongestPossible == NullConstraint){
+      return false;
+    }else{
+      bool assertedToTheTheory = strongestPossible->assertedToTheTheory();
+      bool canBePropagated = strongestPossible->canBePropagated();
+      bool hasProof = strongestPossible->hasProof();
+
+      return !assertedToTheTheory && canBePropagated && !hasProof;
+    }
+  }else{
+    return false;
+  }
+}
+
+bool TheoryArithPrivate::attemptSingleton(RowIndex ridx, bool rowUp){
+  Trace("arith::prop") << "  attemptSingleton" << ridx;
+
+  const Tableau::Entry* ep;
+  ep = d_linEq.rowLacksBound(ridx, rowUp, ARITHVAR_SENTINEL);
+  Assert(ep != NULL);
+
+  ArithVar v = ep->getColVar();
+  const Rational& coeff = ep->getCoefficient();
+
+  // 0 = c * v + \sum rest
+  // Suppose rowUp
+  // - c * v = \sum rest \leq D
+  // if c > 0, v \geq -D/c so !vUp
+  // if c < 0, v \leq -D/c so  vUp
+  // Suppose not rowUp
+  // - c * v = \sum rest \geq D
+  // if c > 0, v \leq -D/c so  vUp
+  // if c < 0, v \geq -D/c so !vUp
+  bool vUp = (rowUp == ( coeff.sgn() < 0));
+
+  Trace("arith::prop") << "  " << rowUp << " " << v << " " << coeff << " " << vUp << endl;
+  Trace("arith::prop") << "  " << propagateMightSucceed(v, vUp) << endl;
+
+  if(propagateMightSucceed(v, vUp)){
+    DeltaRational dr = d_linEq.computeRowBound(ridx, rowUp, v);
+    DeltaRational bound = dr / (- coeff);
+    return tryToPropagate(ridx, rowUp, v, vUp, bound);
+  }
+  return false;
+}
+
+bool TheoryArithPrivate::attemptFull(RowIndex ridx, bool rowUp){
+  Trace("arith::prop") << "  attemptFull" << ridx << endl;
+
+  vector<const Tableau::Entry*> candidates;
+
+  for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){
+    const Tableau::Entry& e =*i;
+    const Rational& c = e.getCoefficient();
+    ArithVar v = e.getColVar();
+    bool vUp = (rowUp == (c.sgn() < 0));
+    if(propagateMightSucceed(v, vUp)){
+      candidates.push_back(&e);
+    }
+  }
+  if(candidates.empty()){ return false; }
+
+  const DeltaRational slack =
+    d_linEq.computeRowBound(ridx, rowUp, ARITHVAR_SENTINEL);
+  bool any = false;
+  vector<const Tableau::Entry*>::const_iterator i, iend;
+  for(i = candidates.begin(), iend = candidates.end(); i != iend; ++i){
+    const Tableau::Entry* ep = *i;
+    const Rational& c = ep->getCoefficient();
+    ArithVar v = ep->getColVar();
+
+    // See the comment for attemptSingleton()
+    bool activeUp = (rowUp == (c.sgn() > 0));
+    bool vUb = (rowUp == (c.sgn() < 0));
+
+    const DeltaRational& activeBound = activeUp ?
+      d_partialModel.getUpperBound(v):
+      d_partialModel.getLowerBound(v);
+
+    DeltaRational contribution = activeBound * c;
+    DeltaRational impliedBound = (slack - contribution)/(-c);
+
+    bool success = tryToPropagate(ridx, rowUp, v, vUb, impliedBound);
+    any |= success;
+  }
+  return any;
+}
+
+bool TheoryArithPrivate::tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, bool vUb, const DeltaRational& bound){
+
+  bool weaker = vUb ? d_partialModel.strictlyLessThanUpperBound(v, bound):
+    d_partialModel.strictlyGreaterThanLowerBound(v, bound);
+  if(weaker){
+    ConstraintType t = vUb ? UpperBound : LowerBound;
+
+    ConstraintP implied = d_constraintDatabase.getBestImpliedBound(v, t, bound);
+    if(implied != NullConstraint){
+      return rowImplicationCanBeApplied(ridx, rowUp, implied);
+    }
+  }
+  return false;
+}
+
+Node flattenImplication(Node imp){
+  NodeBuilder nb(kind::OR);
+  std::unordered_set<Node> included;
+  Node left = imp[0];
+  Node right = imp[1];
+
+  if(left.getKind() == kind::AND){
+    for(Node::iterator i = left.begin(), iend = left.end(); i != iend; ++i) {
+      if (!included.count((*i).negate()))
+      {
+        nb << (*i).negate();
+        included.insert((*i).negate());
+      }
+    }
+  }else{
+    if (!included.count(left.negate()))
+    {
+      nb << left.negate();
+      included.insert(left.negate());
+    }
+  }
+
+  if(right.getKind() == kind::OR){
+    for(Node::iterator i = right.begin(), iend = right.end(); i != iend; ++i) {
+      if (!included.count(*i))
+      {
+        nb << *i;
+        included.insert(*i);
+      }
+    }
+  }else{
+    if (!included.count(right))
+    {
+      nb << right;
+      included.insert(right);
+    }
+  }
+
+  return nb;
+}
+
+bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, ConstraintP implied){
+  Assert(implied != NullConstraint);
+  ArithVar v = implied->getVariable();
+
+  bool assertedToTheTheory = implied->assertedToTheTheory();
+  bool canBePropagated = implied->canBePropagated();
+  bool hasProof = implied->hasProof();
+
+  Trace("arith::prop") << "arith::prop" << v
+                       << " " << assertedToTheTheory
+                       << " " << canBePropagated
+                       << " " << hasProof
+                       << endl;
+
+
+  if( !assertedToTheTheory && canBePropagated && !hasProof ){
+    ConstraintCPVec explain;
+    if (options().smt.produceProofs)
+    {
+      d_farkasBuffer.clear();
+    }
+    RationalVectorP coeffs =
+        options().smt.produceProofs ? &d_farkasBuffer : nullptr;
+
+    // After invoking `propegateRow`:
+    //   * coeffs[0] is for implied
+    //   * coeffs[i+1] is for explain[i]
+    d_linEq.propagateRow(explain, ridx, rowUp, implied, coeffs);
+    if (d_tableau.getRowLength(ridx) <= options().arith.arithPropAsLemmaLength)
+    {
+      if (TraceIsOn("arith::prop::pf")) {
+        for (const auto & constraint : explain) {
+          Assert(constraint->hasProof());
+          constraint->printProofTree(Trace("arith::prop::pf"));
+        }
+      }
+      Node implication = implied->externalImplication(explain);
+      Node clause = flattenImplication(implication);
+      std::shared_ptr<ProofNode> clausePf{nullptr};
+
+      if (isProofEnabled())
+      {
+        // We can prove this lemma from Farkas...
+        std::vector<std::shared_ptr<ProofNode>> conflictPfs;
+        Node pfLit = implied->getNegation()->getProofLiteral();
+        TypeNode type = pfLit[0].getType();
+        // Assume the negated getLiteral version of the implied constaint
+        // then rewrite it into proof normal form.
+        conflictPfs.push_back(
+            d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
+                          {d_pnm->mkAssume(implied->getLiteral().negate())},
+                          {pfLit}));
+        // Add the explaination proofs.
+        for (const auto constraint : explain)
+        {
+          NodeBuilder nb;
+          conflictPfs.push_back(constraint->externalExplainByAssertions(nb));
+        }
+        // Collect the farkas coefficients, as nodes.
+        std::vector<Node> farkasCoefficients;
+        farkasCoefficients.reserve(coeffs->size());
+        auto nm = NodeManager::currentNM();
+        std::transform(coeffs->begin(),
+                       coeffs->end(),
+                       std::back_inserter(farkasCoefficients),
+                       [nm, type](const Rational& r) {
+                         return nm->mkConstRealOrInt(type, r);
+                       });
+
+        // Prove bottom.
+        auto sumPf = d_pnm->mkNode(
+            PfRule::MACRO_ARITH_SCALE_SUM_UB, conflictPfs, farkasCoefficients);
+        auto botPf = d_pnm->mkNode(
+            PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
+
+        // Prove the conflict
+        std::vector<Node> assumptions;
+        assumptions.reserve(clause.getNumChildren());
+        std::transform(clause.begin(),
+                       clause.end(),
+                       std::back_inserter(assumptions),
+                       [](TNode r) { return r.negate(); });
+        auto notAndNotPf = d_pnm->mkScope(botPf, assumptions);
+
+        // Convert it to a clause
+        auto orNotNotPf = d_pnm->mkNode(PfRule::NOT_AND, {notAndNotPf}, {});
+        clausePf = d_pnm->mkNode(
+            PfRule::MACRO_SR_PRED_TRANSFORM, {orNotNotPf}, {clause});
+
+        // Output it
+        TrustNode trustedClause = d_pfGen->mkTrustNode(clause, clausePf);
+        outputTrustedLemma(trustedClause, InferenceId::ARITH_ROW_IMPL);
+      }
+      else
+      {
+        outputLemma(clause, InferenceId::ARITH_ROW_IMPL);
+      }
+    }
+    else
+    {
+      Assert(!implied->negationHasProof());
+      implied->impliedByFarkas(explain, coeffs, false);
+      implied->tryToPropagate();
+    }
+    return true;
+  }
+
+  if(TraceIsOn("arith::prop")){
+    Trace("arith::prop")
+      << "failed " << v << " " << assertedToTheTheory << " "
+      << canBePropagated << " " << hasProof << " " << implied << endl;
+    d_partialModel.printModel(v, Trace("arith::prop"));
+  }
+  return false;
+}
+
+bool TheoryArithPrivate::propagateCandidateRow(RowIndex ridx){
+  BoundCounts hasCount = d_linEq.hasBoundCount(ridx);
+  uint32_t rowLength = d_tableau.getRowLength(ridx);
+
+  bool success = false;
+
+  Trace("arith::prop") << "propagateCandidateRow attempt " << rowLength << " "
+                       << hasCount << endl;
+
+  if (rowLength >= options().arith.arithPropagateMaxLength
+      && Random::getRandom().pickWithProb(
+          1.0 - double(options().arith.arithPropagateMaxLength) / rowLength))
+  {
+    return false;
+  }
+
+  if(hasCount.lowerBoundCount() == rowLength){
+    success |= attemptFull(ridx, false);
+  }else if(hasCount.lowerBoundCount() + 1 == rowLength){
+    success |= attemptSingleton(ridx, false);
+  }
+
+  if(hasCount.upperBoundCount() == rowLength){
+    success |= attemptFull(ridx, true);
+  }else if(hasCount.upperBoundCount() + 1 == rowLength){
+    success |= attemptSingleton(ridx, true);
+  }
+  return success;
+}
+
+void TheoryArithPrivate::dumpUpdatedBoundsToRows(){
+  Assert(d_candidateRows.empty());
+  DenseSet::const_iterator i = d_updatedBounds.begin();
+  DenseSet::const_iterator end = d_updatedBounds.end();
+  for(; i != end; ++i){
+    ArithVar var = *i;
+    if(d_tableau.isBasic(var)){
+      RowIndex ridx = d_tableau.basicToRowIndex(var);
+      d_candidateRows.softAdd(ridx);
+    }else{
+      Tableau::ColIterator basicIter = d_tableau.colIterator(var);
+      for(; !basicIter.atEnd(); ++basicIter){
+        const Tableau::Entry& entry = *basicIter;
+        RowIndex ridx = entry.getRowIndex();
+        d_candidateRows.softAdd(ridx);
+      }
+    }
+  }
+  d_updatedBounds.purge();
+}
+
+const BoundsInfo& TheoryArithPrivate::boundsInfo(ArithVar basic) const{
+  RowIndex ridx = d_tableau.basicToRowIndex(basic);
+  return d_rowTracking[ridx];
+}
+
+std::pair<bool, Node> TheoryArithPrivate::entailmentCheck(TNode lit)
+{
+  ArithEntailmentCheckParameters params;
+  params.addLookupRowSumAlgorithms();
+  ArithEntailmentCheckSideEffects out;
+  
+  using namespace inferbounds;
+
+  // l k r
+  // diff : (l - r) k 0
+  Trace("arith::entailCheck") << "TheoryArithPrivate::entailmentCheck(" << lit << ")"<< endl;
+  Kind k;
+  int primDir;
+  Rational lm, rm, dm;
+  Node lp, rp, dp;
+  DeltaRational sep;
+  bool successful = decomposeLiteral(lit, k, primDir, lm, lp, rm, rp, dm, dp, sep);
+  if(!successful) { return make_pair(false, Node::null()); }
+
+  if (dp.isConst())
+  {
+    Node eval = rewrite(lit);
+    Assert(eval.getKind() == kind::CONST_BOOLEAN);
+    // if true, true is an acceptable explaination
+    // if false, the node is uninterpreted and eval can be forgotten
+    return make_pair(eval.getConst<bool>(), eval);
+  }
+  Assert(dm != Rational(0));
+  Assert(primDir == 1 || primDir == -1);
+
+  int negPrim = -primDir;
+
+  int secDir = (k == EQUAL || k == DISTINCT) ? negPrim: 0;
+  int negSecDir = (k == EQUAL || k == DISTINCT) ? primDir: 0;
+
+  // primDir*[lm*( lp )] k primDir*[ [rm*( rp )] + sep ]
+  // primDir*[lm*( lp ) - rm*( rp ) ] k primDir*sep
+  // primDir*[dm * dp] k primDir*sep
+
+  std::pair<Node, DeltaRational> bestPrimLeft, bestNegPrimRight, bestPrimDiff, tmp;
+  std::pair<Node, DeltaRational> bestSecLeft, bestNegSecRight, bestSecDiff;
+  bestPrimLeft.first = Node::null(); bestNegPrimRight.first = Node::null(); bestPrimDiff.first = Node::null();
+  bestSecLeft.first = Node::null(); bestNegSecRight.first = Node::null(); bestSecDiff.first = Node::null();
+
+
+
+  ArithEntailmentCheckParameters::const_iterator alg, alg_end;
+  for( alg = params.begin(), alg_end = params.end(); alg != alg_end; ++alg ){
+    const inferbounds::InferBoundAlgorithm& ibalg = *alg;
+
+    Trace("arith::entailCheck") << "entailmentCheck trying " << (inferbounds::Algorithms) ibalg.getAlgorithm() << endl;
+    switch(ibalg.getAlgorithm()){
+    case inferbounds::None:
+      break;
+    case inferbounds::Lookup:
+    case inferbounds::RowSum:
+      {
+        typedef void (TheoryArithPrivate::*EntailmentCheckFunc)(std::pair<Node, DeltaRational>&, int, TNode) const;
+
+        EntailmentCheckFunc ecfunc =
+          (ibalg.getAlgorithm() == inferbounds::Lookup)
+          ? (&TheoryArithPrivate::entailmentCheckBoundLookup)
+          : (&TheoryArithPrivate::entailmentCheckRowSum);
+
+        (*this.*ecfunc)(tmp, primDir * lm.sgn(), lp);
+        setToMin(primDir * lm.sgn(), bestPrimLeft, tmp);
+
+        (*this.*ecfunc)(tmp, negPrim * rm.sgn(), rp);
+        setToMin(negPrim * rm.sgn(), bestNegPrimRight, tmp);
+
+        (*this.*ecfunc)(tmp, secDir * lm.sgn(), lp);
+        setToMin(secDir * lm.sgn(), bestSecLeft, tmp);
+
+        (*this.*ecfunc)(tmp, negSecDir * rm.sgn(), rp);
+        setToMin(negSecDir * rm.sgn(), bestNegSecRight, tmp);
+
+        (*this.*ecfunc)(tmp, primDir * dm.sgn(), dp);
+        setToMin(primDir * dm.sgn(), bestPrimDiff, tmp);
+
+        (*this.*ecfunc)(tmp, secDir * dm.sgn(), dp);
+        setToMin(secDir * dm.sgn(), bestSecDiff, tmp);
+      }
+      break;
+    default:
+      Unhandled();
+    }
+
+    // turn bounds on prim * left and -prim * right into bounds on prim * diff
+    if(!bestPrimLeft.first.isNull() && !bestNegPrimRight.first.isNull()){
+      //  primDir*lm* lp <= primDir*lm*L
+      // -primDir*rm* rp <= -primDir*rm*R
+      // primDir*lm* lp -primDir*rm* rp <=  primDir*lm*L - primDir*rm*R
+      // primDir [lm* lp -rm* rp] <= primDir[lm*L - *rm*R]
+      // primDir [dm * dp] <= primDir[lm*L - *rm*R]
+      // primDir [dm * dp] <= primDir * dm * ([lm*L - *rm*R]/dm)
+      tmp.second = ((bestPrimLeft.second * lm) - (bestNegPrimRight.second * rm)) / dm;
+      tmp.first = (bestPrimLeft.first).andNode(bestNegPrimRight.first);
+      setToMin(primDir, bestPrimDiff, tmp);
+    }
+
+    // turn bounds on sec * left and sec * right into bounds on sec * diff
+    if(secDir != 0 && !bestSecLeft.first.isNull() && !bestNegSecRight.first.isNull()){
+      //  secDir*lm* lp <= secDir*lm*L
+      // -secDir*rm* rp <= -secDir*rm*R
+      // secDir*lm* lp -secDir*rm* rp <=  secDir*lm*L - secDir*rm*R
+      // secDir [lm* lp -rm* rp] <= secDir[lm*L - *rm*R]
+      // secDir [dm * dp] <= secDir[lm*L - *rm*R]
+      // secDir [dm * dp] <= secDir * dm * ([lm*L - *rm*R]/dm)
+      tmp.second = ((bestSecLeft.second * lm) - (bestNegSecRight.second * rm)) / dm;
+      tmp.first = (bestSecLeft.first).andNode(bestNegSecRight.first);
+      setToMin(secDir, bestSecDiff, tmp);
+    }
+
+    switch(k){
+    case LEQ:
+      if(!bestPrimDiff.first.isNull()){
+        DeltaRational d = (bestPrimDiff.second * dm);
+        if((primDir > 0 && d <= sep) || (primDir < 0 && d >= sep) ){
+          Trace("arith::entailCheck") << "entailmentCheck found "
+                                      << primDir << "*" << dm << "*(" << dp<<")"
+                                      << " <= " << primDir << "*" << dm << "*" << bestPrimDiff.second
+                                      << " <= " << primDir << "*" << sep << endl
+                                      << " by " << bestPrimDiff.first << endl;
+          Assert(bestPrimDiff.second * (Rational(primDir) * dm)
+                 <= (sep * Rational(primDir)));
+          return make_pair(true, bestPrimDiff.first);
+        }
+      }
+      break;
+    case EQUAL:
+      if(!bestPrimDiff.first.isNull() && !bestSecDiff.first.isNull()){
+        // Is primDir [dm * dp] == primDir * sep entailed?
+        // Iff [dm * dp] == sep entailed?
+        // Iff dp == sep / dm entailed?
+        // Iff dp <= sep / dm and dp >= sep / dm entailed?
+
+        // primDir [dm * dp] <= primDir * dm * U
+        // secDir [dm * dp] <= secDir * dm * L
+
+        // Suppose primDir * dm > 0
+        // then secDir * dm < 0
+        //   dp >= (secDir * L) / secDir * dm
+        //   dp >= (primDir * L) / primDir * dm
+        //
+        //   dp <= U / dm
+        //   dp >= L / dm
+        //   dp == sep / dm entailed iff U == L == sep
+        // Suppose primDir * dm < 0
+        // then secDir * dm > 0
+        //   dp >= U / dm
+        //   dp <= L / dm
+        //   dp == sep / dm entailed iff U == L == sep
+        if(bestPrimDiff.second == bestSecDiff.second){
+          if(bestPrimDiff.second == sep){
+            return make_pair(true, (bestPrimDiff.first).andNode(bestSecDiff.first));
+          }
+        }
+      }
+      // intentionally fall through to DISTINCT case!
+      // entailments of negations are eager exit cases for EQUAL
+      CVC5_FALLTHROUGH;
+    case DISTINCT:
+      if(!bestPrimDiff.first.isNull()){
+        // primDir [dm * dp] <= primDir * dm * U < primDir * sep
+        if((primDir > 0 && (bestPrimDiff.second * dm  < sep)) ||
+           (primDir < 0 && (bestPrimDiff.second * dm  > sep))){
+          // entailment of negation
+          if(k == DISTINCT){
+            return make_pair(true, bestPrimDiff.first);
+          }else{
+            Assert(k == EQUAL);
+            return make_pair(false, Node::null());
+          }
+        }
+      }
+      if(!bestSecDiff.first.isNull()){
+        // If primDir [dm * dp] > primDir * sep, then this is not entailed.
+        // If primDir [dm * dp] >= primDir * dm * L > primDir * sep
+        // -primDir * dm * L < -primDir * sep
+        // secDir * dm * L < secDir * sep
+        if((secDir > 0 && (bestSecDiff.second * dm < sep)) ||
+           (secDir < 0 && (bestSecDiff.second * dm > sep))){
+          if(k == DISTINCT){
+            return make_pair(true, bestSecDiff.first);
+          }else{
+            Assert(k == EQUAL);
+            return make_pair(false, Node::null());
+          }
+        }
+      }
+
+      break;
+    default:
+      Unreachable();
+      break;
+    }
+  }
+  return make_pair(false, Node::null());
+}
+
+bool TheoryArithPrivate::decomposeTerm(Node t,
+                                       Rational& m,
+                                       Node& p,
+                                       Rational& c)
+{
+  if(!Polynomial::isMember(t)){
+    return false;
+  }
+
+  // TODO Speed up
+  preprocessing::util::ContainsTermITEVisitor ctv;
+  if(ctv.containsTermITE(t)){
+    return false;
+  }
+
+  Polynomial poly = Polynomial::parsePolynomial(t);
+  if(poly.isConstant()){
+    c = poly.getHead().getConstant().getValue();
+    p = mkRationalNode(Rational(0));
+    m = Rational(1);
+    return true;
+  }else if(poly.containsConstant()){
+    c = poly.getHead().getConstant().getValue();
+    poly = poly.getTail();
+  }else{
+    c = Rational(0);
+  }
+  Assert(!poly.isConstant());
+  Assert(!poly.containsConstant());
+
+  const bool intVars = poly.allIntegralVariables();
+
+  if(intVars){
+    m = Rational(1);
+    if(!poly.isIntegral()){
+      Integer denom = poly.denominatorLCM();
+      m /= denom;
+      poly = poly * denom;
+    }
+    Integer g = poly.gcd();
+    m *= g;
+    poly = poly * Rational(1,g);
+    Assert(poly.isIntegral());
+  }else{
+    Assert(!intVars);
+    m = poly.getHead().getConstant().getValue();
+    poly = poly * m.inverse();
+    Assert(poly.leadingCoefficientIsAbsOne());
+  }
+  p = poly.getNode();
+  return true;
+}
+
+void TheoryArithPrivate::setToMin(int sgn, std::pair<Node, DeltaRational>& min, const std::pair<Node, DeltaRational>& e){
+  if(sgn != 0){
+    if(min.first.isNull() && !e.first.isNull()){
+      min = e;
+    }else if(!min.first.isNull() && !e.first.isNull()){
+      if(sgn > 0 && min.second > e.second){
+        min = e;
+      }else if(sgn < 0 &&  min.second < e.second){
+        min = e;
+      }
+    }
+  }
+}
+
+// std::pair<bool, Node> TheoryArithPrivate::entailmentUpperCheck(const Rational& lm, Node lp, const Rational& rm, Node rp, const DeltaRational& sep, const ArithEntailmentCheckParameters& params, ArithEntailmentCheckSideEffects& out){
+
+//   Rational negRM = -rm;
+//   Node diff = NodeManager::currentNM()->mkNode(MULT, mkRationalConstan(lm), lp) + (negRM * rp);
+
+//   Rational diffm;
+//   Node diffp;
+//   decompose(diff, diffm, diffNode);
+
+
+//   std::pair<Node, DeltaRational> bestUbLeft, bestLbRight, bestUbDiff, tmp;
+//   bestUbLeft = bestLbRight = bestUbDiff = make_pair(Node::Null(), DeltaRational());
+
+//   return make_pair(false, Node::null());
+// }
+
+/**
+ * Decomposes a literal into the form:
+ *   dir*[lm*( lp )] k dir*[ [rm*( rp )] + sep ]
+ *   dir*[dm* dp]  k dir *sep
+ *   dir is either 1 or -1
+ */
+bool TheoryArithPrivate::decomposeLiteral(Node lit, Kind& k, int& dir, Rational& lm,  Node& lp, Rational& rm, Node& rp, Rational& dm, Node& dp, DeltaRational& sep){
+  bool negated = (lit.getKind() == kind::NOT);
+  TNode atom = negated ? lit[0] : lit;
+
+  TNode left = atom[0];
+  TNode right = atom[1];
+
+  // left : lm*( lp ) + lc
+  // right: rm*( rp ) + rc
+  Rational lc, rc;
+  bool success = decomposeTerm(rewrite(left), lm, lp, lc);
+  if(!success){ return false; }
+  success = decomposeTerm(rewrite(right), rm, rp, rc);
+  if(!success){ return false; }
+
+  Node diff = rewrite(NodeManager::currentNM()->mkNode(kind::SUB, left, right));
+  Rational dc;
+  success = decomposeTerm(diff, dm, dp, dc);
+  Assert(success);
+
+  // reduce the kind of the to not include literals
+  // GT, NOT LEQ
+  // GEQ, NOT LT
+  // LT, NOT GEQ
+  // LEQ, NOT LT
+  Kind atomKind = atom.getKind();
+  Kind normKind = negated ? negateKind(atomKind) : atomKind;
+
+  if(normKind == GEQ || normKind == GT){
+    dir = -1;
+    normKind = (normKind == GEQ) ? LEQ : LT;
+  }else{
+    dir = 1;
+  }
+
+  Trace("arith::decomp") << "arith::decomp "
+                         << lit << "(" << normKind << "*" << dir << ")"<< endl
+                         << "  left:" << lc << " + " << lm << "*(" <<  lp << ") : " <<left << endl
+                         << "  right:" << rc << " + " << rm << "*(" <<  rp << ") : " << right << endl
+                         << "  diff: " << dc << " + " << dm << "*("<< dp <<"): " << diff << endl
+                         << "  sep: " << sep << endl;
+
+
+  // k in LT, LEQ, EQUAL, DISEQUAL
+  // [dir*lm*( lp ) + dir*lc] k [dir*rm*( rp ) + dir*rc]
+  Rational change = rc - lc;
+  Assert(change == (-dc));
+  // [dir*lm*( lp )] k [dir*rm*( rp ) + dir*(rc - lc)]
+  if(normKind == LT){
+    sep = DeltaRational(change, Rational(-1));
+    k = LEQ;
+  }else{
+    sep = DeltaRational(change);
+    k = normKind;
+  }
+  // k in LEQ, EQUAL, DISEQUAL
+  // dir*lm*( lp ) k [dir*rm*( rp )] + dir*(sep + d * delta)
+  return true;
+}
+
+/**
+ *  Precondition:
+ *   tp is a polynomial not containing an ite.
+ *   either tp is constant or contains no constants.
+ *  Post:
+ *    if tmp.first is not null, then
+ *      sgn * tp <= sgn * tmp.second
+ */
+void TheoryArithPrivate::entailmentCheckBoundLookup(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const {
+  tmp.first = Node::null();
+  if(sgn == 0){ return; }
+
+  Assert(Polynomial::isMember(tp));
+  if (tp.isConst())
+  {
+    tmp.first = mkBoolNode(true);
+    tmp.second = DeltaRational(tp.getConst<Rational>());
+  }
+  else if (d_partialModel.hasArithVar(tp))
+  {
+    Assert(!tp.isConst());
+    ArithVar v = d_partialModel.asArithVar(tp);
+    Assert(v != ARITHVAR_SENTINEL);
+    ConstraintP c = (sgn > 0)
+      ? d_partialModel.getUpperBoundConstraint(v)
+      : d_partialModel.getLowerBoundConstraint(v);
+    if(c != NullConstraint){
+      tmp.first = Constraint::externalExplainByAssertions({c});
+      tmp.second = c->getValue();
+    }
+  }
+}
+
+void TheoryArithPrivate::entailmentCheckRowSum(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const {
+  tmp.first = Node::null();
+  if(sgn == 0){ return; }
+  if (tp.getKind() != ADD)
+  {
+    return;
+  }
+  Assert(Polynomial::isMember(tp));
+
+  tmp.second = DeltaRational(0);
+  NodeBuilder nb(kind::AND);
+
+  Polynomial p = Polynomial::parsePolynomial(tp);
+  for(Polynomial::iterator i = p.begin(), iend = p.end(); i != iend; ++i) {
+    Monomial m = *i;
+    Node x = m.getVarList().getNode();
+    if(d_partialModel.hasArithVar(x)){
+      ArithVar v = d_partialModel.asArithVar(x);
+      const Rational& coeff = m.getConstant().getValue();
+      int dir = sgn * coeff.sgn();
+      ConstraintP c = (dir > 0)
+        ? d_partialModel.getUpperBoundConstraint(v)
+        : d_partialModel.getLowerBoundConstraint(v);
+      if(c != NullConstraint){
+        tmp.second += c->getValue() * coeff;
+        c->externalExplainByAssertions(nb);
+      }else{
+        //failed
+        return;
+      }
+    }else{
+      // failed
+      return;
+    }
+  }
+  // success
+  tmp.first = nb;
+}
+
+ArithProofRuleChecker* TheoryArithPrivate::getProofChecker()
+{
+  return &d_checker;
+}
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear/theory_arith_private.h b/src/theory/arith/linear/theory_arith_private.h
new file mode 100644 (file)
index 0000000..d8a3613
--- /dev/null
@@ -0,0 +1,881 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Tim King, Andrew Reynolds, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include "context/cdhashset.h"
+#include "context/cdinsert_hashmap.h"
+#include "context/cdlist.h"
+#include "context/cdqueue.h"
+#include "expr/kind.h"
+#include "expr/node.h"
+#include "expr/node_builder.h"
+#include "proof/trust_node.h"
+#include "theory/arith/linear/arith_static_learner.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/attempt_solution_simplex.h"
+#include "theory/arith/branch_and_bound.h"
+#include "theory/arith/linear/congruence_manager.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/linear/dio_solver.h"
+#include "theory/arith/linear/dual_simplex.h"
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/fc_simplex.h"
+#include "theory/arith/linear/infer_bounds.h"
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/matrix.h"
+#include "theory/arith/linear/normal_form.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/arith/proof_checker.h"
+#include "theory/arith/linear/soi_simplex.h"
+#include "theory/arith/theory_arith.h"
+#include "theory/valuation.h"
+#include "util/dense_map.h"
+#include "util/integer.h"
+#include "util/rational.h"
+#include "util/result.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+
+class EagerProofGenerator;
+
+namespace theory {
+
+class TheoryModel;
+
+namespace arith::linear {
+
+class BranchCutInfo;
+class TreeLog;
+class ApproximateStatistics;
+
+class ArithEntailmentCheckParameters;
+class ArithEntailmentCheckSideEffects;
+namespace inferbounds {
+  class InferBoundAlgorithm;
+}
+class InferBoundsResult;
+  
+/**
+ * Implementation of QF_LRA.
+ * Based upon:
+ * http://research.microsoft.com/en-us/um/people/leonardo/cav06.pdf
+ */
+class TheoryArithPrivate : protected EnvObj
+{
+ private:
+  static constexpr uint32_t RESET_START = 2;
+
+  TheoryArith& d_containing;
+
+  /**
+   * Whether we encountered non-linear arithmetic at any time during solving.
+   */
+  bool d_foundNl;
+
+  BoundInfoMap d_rowTracking;
+  /** Branch and bound utility */
+  BranchAndBound& d_bab;
+  // For proofs
+  /** Manages the proof nodes of this theory. */
+  ProofNodeManager* d_pnm;
+  /** Checks the proof rules of this theory. */
+  ArithProofRuleChecker d_checker;
+  /** Stores proposition(node)/proof pairs. */
+  std::unique_ptr<EagerProofGenerator> d_pfGen;
+
+  /**
+   * The constraint database associated with the theory.
+   * This must be declared before ArithPartialModel.
+   */
+  ConstraintDatabase d_constraintDatabase;
+
+  enum Result::Status d_qflraStatus;
+  // check()
+  //   !done() -> d_qflraStatus = Unknown
+  //   fullEffort(e) -> simplex returns either sat or unsat
+  //   !fullEffort(e) -> simplex returns either sat, unsat or unknown
+  //                     if unknown, save the assignment
+  //                     if unknown, the simplex priority queue cannot be emptied
+  int d_unknownsInARow;
+
+  bool d_replayedLemmas;
+
+  /**
+   * This counter is false if nothing has been done since the last cut.
+   * This is used to break an infinite loop.
+   */
+  bool d_hasDoneWorkSinceCut;
+
+  /** Static learner. */
+  ArithStaticLearner d_learner;
+
+  //std::vector<ArithVar> d_pool;
+public:
+  void releaseArithVar(ArithVar v);
+  void signal(ArithVar v){ d_errorSet.signalVariable(v); }
+
+
+private:
+  // t does not contain constants
+  void entailmentCheckBoundLookup(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const;
+  void entailmentCheckRowSum(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const;
+
+  /**
+   * Infers either a new upper/lower bound on term in the real relaxation.
+   * Either:
+   * - term is malformed (see below)
+   * - a maximum/minimum is found with the result being a pair
+   * -- <dr, exp> where
+   * -- term <?> dr is implies by exp
+   * -- <?> is <= if inferring an upper bound, >= otherwise
+   * -- exp is in terms of the assertions to the theory.
+   * - No upper or lower bound is inferrable in the real relaxation.
+   * -- Returns <0, Null()>
+   * - the maximum number of rounds was exhausted:
+   * -- Returns <v, term> where v is the current feasible value of term
+   * - Threshold reached:
+   * -- If theshold != NULL, and a feasible value is found to exceed threshold
+   * -- Simplex stops and returns <threshold, term>
+   */
+  //std::pair<DeltaRational, Node> inferBound(TNode term, bool lb, int maxRounds = -1, const DeltaRational* threshold = NULL);
+
+private:
+ static bool decomposeTerm(Node t, Rational& m, Node& p, Rational& c);
+ bool decomposeLiteral(Node lit,
+                       Kind& k,
+                       int& dir,
+                       Rational& lm,
+                       Node& lp,
+                       Rational& rm,
+                       Node& rp,
+                       Rational& dm,
+                       Node& dp,
+                       DeltaRational& sep);
+ static void setToMin(int sgn,
+                      std::pair<Node, DeltaRational>& min,
+                      const std::pair<Node, DeltaRational>& e);
+
+ typedef ArithVariables::var_iterator var_iterator;
+ var_iterator var_begin() const { return d_partialModel.var_begin(); }
+ var_iterator var_end() const { return d_partialModel.var_end(); }
+
+ NodeSet d_setupNodes;
+public:
+  bool isSetup(Node n) const {
+    return d_setupNodes.find(n) != d_setupNodes.end();
+  }
+  void markSetup(Node n){
+    Assert(!isSetup(n));
+    d_setupNodes.insert(n);
+  }
+private:
+  void setupVariable(const Variable& x);
+  void setupVariableList(const VarList& vl);
+  void setupPolynomial(const Polynomial& poly);
+public:
+  void setupAtom(TNode atom);
+private:
+  void cautiousSetupPolynomial(const Polynomial& p);
+
+  /**
+   * A superset of all of the assertions that currently are not the literal for
+   * their constraint do not match constraint literals. Not just the witnesses.
+   */
+  context::CDInsertHashMap<Node, ConstraintP>
+      d_assertionsThatDoNotMatchTheirLiterals;
+
+  /** Returns true if x is of type Integer. */
+  inline bool isInteger(ArithVar x) const {
+    return d_partialModel.isInteger(x);
+  }
+
+
+  /** Returns true if the variable was initially introduced as an auxiliary variable. */
+  inline bool isAuxiliaryVariable(ArithVar x) const{
+    return d_partialModel.isAuxiliary(x);
+  }
+
+  inline bool isIntegerInput(ArithVar x) const
+  {
+    return d_partialModel.isIntegerInput(x)
+           && d_preregisteredNodes.contains(d_partialModel.asNode(x));
+  }
+
+  /**
+   * On full effort checks (after determining LA(Q) satisfiability), we
+   * consider integer vars, but we make sure to do so fairly to avoid
+   * nontermination (although this isn't a guarantee).  To do it fairly,
+   * we consider variables in round-robin fashion.  This is the
+   * round-robin index.
+   */
+  ArithVar d_nextIntegerCheckVar;
+
+  /**
+   * Queue of Integer variables that are known to be equal to a constant.
+   */
+  context::CDQueue<ArithVar> d_constantIntegerVariables;
+
+  Node callDioSolver();
+  /**
+   * Produces lemmas of the form (or (>= f 0) (<= f 0)),
+   * where f is a plane that the diophantine solver is interested in.
+   *
+   * More precisely, produces lemmas of the form (or (>= lc -c) (<= lc -c))
+   * where lc is a linear combination of variables, c is a constant, and lc + c
+   * is the plane.
+   */
+  TrustNode dioCutting();
+
+  Comparison mkIntegerEqualityFromAssignment(ArithVar v);
+
+  /**
+   * List of all of the disequalities asserted in the current context that are not known
+   * to be satisfied.
+   */
+  context::CDQueue<ConstraintP> d_diseqQueue;
+
+  /**
+   * Constraints that have yet to be processed by proagation work list.
+   * All of the elements have type of LowerBound, UpperBound, or
+   * Equality.
+   *
+   * This is empty at the beginning of every check call.
+   *
+   * If head()->getType() == LowerBound or UpperBound,
+   * then d_cPL[1] is the previous constraint in d_partialModel for the
+   * corresponding bound.
+   * If head()->getType() == Equality,
+   * then d_cPL[1] is the previous lowerBound in d_partialModel,
+   * and d_cPL[2] is the previous upperBound in d_partialModel.
+   */
+  std::deque<ConstraintP> d_currentPropagationList;
+
+  context::CDQueue<ConstraintP> d_learnedBounds;
+
+  /**
+   * Contains all nodes that have been preregistered
+   */
+  context::CDHashSet<Node> d_preregisteredNodes;
+
+  /**
+   * Manages information about the assignment and upper and lower bounds on
+   * variables.
+   */
+  ArithVariables d_partialModel;
+
+  /** The set of variables in error in the partial model. */
+  ErrorSet d_errorSet;
+
+  /**
+   * The tableau for all of the constraints seen thus far in the system.
+   */
+  Tableau d_tableau;
+
+  /**
+   * Maintains the relationship between the PartialModel and the Tableau.
+   */
+  LinearEqualityModule d_linEq;
+
+  /**
+   * A Diophantine equation solver.  Accesses the tableau and partial
+   * model (each in a read-only fashion).
+   */
+  DioSolver d_diosolver;
+
+  /** Counts the number of notifyRestart() calls to the theory. */
+  uint32_t d_restartsCounter;
+
+  /**
+   * Every number of restarts equal to s_TABLEAU_RESET_PERIOD,
+   * the density of the tableau, d, is computed.
+   * If d >= s_TABLEAU_RESET_DENSITY * d_initialDensity, the tableau
+   * is set to d_initialTableau.
+   */
+  bool d_tableauSizeHasBeenModified;
+  double d_tableauResetDensity;
+  uint32_t d_tableauResetPeriod;
+  static constexpr uint32_t s_TABLEAU_RESET_INCREMENT = 5;
+
+  /** This is only used by simplex at the moment. */
+  context::CDList<std::pair<ConstraintCP, InferenceId>> d_conflicts;
+
+  /** This is only used by simplex at the moment. */
+  context::CDO<Node> d_blackBoxConflict;
+  /** For holding the proof of the above conflict node. */
+  context::CDO<std::shared_ptr<ProofNode>> d_blackBoxConflictPf;
+
+  bool isProofEnabled() const;
+
+ public:
+  /**
+   * This adds the constraint a to the queue of conflicts in d_conflicts.
+   * Both a and ~a must have a proof.
+   */
+  void raiseConflict(ConstraintCP a, InferenceId id);
+
+  // inline void raiseConflict(const ConstraintCPVec& cv){
+  //   d_conflicts.push_back(cv);
+  // }
+
+  // void raiseConflict(ConstraintCP a, ConstraintCP b);
+  // void raiseConflict(ConstraintCP a, ConstraintCP b, ConstraintCP c);
+
+  /** This is a conflict that is magically known to hold. */
+  void raiseBlackBoxConflict(Node bb, std::shared_ptr<ProofNode> pf = nullptr);
+  /**
+   * Returns true iff a conflict has been raised. This method is public since
+   * it is needed by the ArithState class to know whether we are in conflict.
+   */
+  bool anyConflict() const;
+
+ private:
+  inline bool conflictQueueEmpty() const {
+    return d_conflicts.empty();
+  }
+
+  /**
+   * Outputs the contents of d_conflicts onto d_out.
+   * The conditions of anyConflict() must hold.
+   */
+  void outputConflicts();
+
+  /**
+   * A copy of the tableau.
+   * This is equivalent  to the original tableau if d_tableauSizeHasBeenModified
+   * is false.
+   * The set of basic and non-basic variables may differ from d_tableau.
+   */
+  Tableau d_smallTableauCopy;
+
+  /**
+   * Returns true if all of the basic variables in the simplex queue of
+   * basic variables that violate their bounds in the current tableau
+   * are basic in d_smallTableauCopy.
+   *
+   * d_tableauSizeHasBeenModified must be false when calling this.
+   * Simplex's priority queue must be in collection mode.
+   */
+  bool safeToReset() const;
+
+  /** This keeps track of difference equalities. Mostly for sharing. */
+  ArithCongruenceManager d_congruenceManager;
+  context::CDO<bool> d_cmEnabled;
+
+  /** This implements the Simplex decision procedure. */
+  DualSimplexDecisionProcedure d_dualSimplex;
+  FCSimplexDecisionProcedure d_fcSimplex;
+  SumOfInfeasibilitiesSPD d_soiSimplex;
+  AttemptSolutionSDP d_attemptSolSimplex;
+
+  bool solveRealRelaxation(Theory::Effort effortLevel);
+
+  /* Returns true if this is heuristically a good time to try
+   * to solve the integers.
+   */
+  bool attemptSolveInteger(Theory::Effort effortLevel, bool emmmittedLemmaOrSplit);
+  bool replayLemmas(ApproximateSimplex* approx);
+  void solveInteger(Theory::Effort effortLevel);
+  bool safeToCallApprox() const;
+  SimplexDecisionProcedure& selectSimplex(bool pass1);
+  SimplexDecisionProcedure* d_pass1SDP;
+  SimplexDecisionProcedure* d_otherSDP;
+  /* Sets d_qflraStatus */
+  void importSolution(const ApproximateSimplex::Solution& solution);
+  bool solveRelaxationOrPanic(Theory::Effort effortLevel);
+  context::CDO<int> d_lastContextIntegerAttempted;
+  bool replayLog(ApproximateSimplex* approx);
+
+  class ModelException : public Exception {
+   public:
+    ModelException(TNode n, const char* msg);
+    ~ModelException() override;
+  };
+
+  /**
+   * Computes the delta rational value of a term from the current partial
+   * model. This returns the delta value assignment to the term if it is in the
+   * partial model. Otherwise, this is computed recursively for arithmetic terms
+   * from each subterm.
+   *
+   * This throws a DeltaRationalException if the value cannot be represented as
+   * a DeltaRational. This throws a ModelException if there is a term is not in
+   * the partial model and is not a theory of arithmetic term.
+   *
+   * precondition: The linear abstraction of the nodes must be satisfiable.
+   */
+  DeltaRational getDeltaValue(TNode term) const
+      /* throw(DeltaRationalException, ModelException) */;
+ public:
+  TheoryArithPrivate(TheoryArith& containing, Env& env, BranchAndBound& bab);
+  ~TheoryArithPrivate();
+
+  //--------------------------------- initialization
+  /**
+   * Returns true if we need an equality engine, see
+   * Theory::needsEqualityEngine.
+   */
+  bool needsEqualityEngine(EeSetupInfo& esi);
+  /** finish initialize */
+  void finishInit();
+  //--------------------------------- end initialization
+
+  /**
+   * Does non-context dependent setup for a node connected to a theory.
+   */
+  void preRegisterTerm(TNode n);
+
+  void propagate(Theory::Effort e);
+  TrustNode explain(TNode n);
+
+  Rational deltaValueForTotalOrder() const;
+
+  bool collectModelInfo(TheoryModel* m);
+  /**
+   * Collect model values. This is the main method for extracting information
+   * about how to construct the model. This method relies on the caller for
+   * processing the map, which is done so that other modules (e.g. the
+   * non-linear extension) can modify arithModel before it is sent to the model.
+   *
+   * @param termSet The set of relevant terms
+   * @param arithModel Mapping from terms (of real type) to their values. The
+   * caller should assert equalities to the model for each entry in this map.
+   */
+  void collectModelValues(const std::set<Node>& termSet,
+                          std::map<Node, Node>& arithModel);
+
+  void shutdown(){ }
+
+  void presolve();
+  void notifyRestart();
+  Theory::PPAssertStatus ppAssert(TrustNode tin,
+                                  TrustSubstitutionMap& outSubstitutions);
+  void ppStaticLearn(TNode in, NodeBuilder& learned);
+
+  std::string identify() const { return std::string("TheoryArith"); }
+
+  EqualityStatus getEqualityStatus(TNode a, TNode b);
+
+  /** Called when n is notified as being a shared term with TheoryArith. */
+  void notifySharedTerm(TNode n);
+
+  Node getModelValue(TNode var);
+
+
+  std::pair<bool, Node> entailmentCheck(TNode lit);
+
+  //--------------------------------- standard check
+  /** Pre-check, called before the fact queue of the theory is processed. */
+  bool preCheck(Theory::Effort level);
+  /** Pre-notify fact. */
+  void preNotifyFact(TNode atom, bool pol, TNode fact);
+  /**
+   * Post-check, called after the fact queue of the theory is processed. Returns
+   * true if a conflict or lemma was emitted.
+   */
+  bool postCheck(Theory::Effort level);
+  //--------------------------------- end standard check
+  /**
+   * Found non-linear? This returns true if this solver ever encountered
+   * any non-linear terms that were unhandled. Note that this class is not
+   * responsible for handling non-linear arithmetic. If the owner of this
+   * class does not handle non-linear arithmetic in another way, then
+   * setIncomplete should be called on the output channel of TheoryArith.
+   */
+  bool foundNonlinear() const;
+
+  /** get the proof checker of this theory */
+  ArithProofRuleChecker* getProofChecker();
+
+ private:
+  /** The constant zero. */
+  DeltaRational d_DELTA_ZERO;
+
+  /** propagates an arithvar */
+  void propagateArithVar(bool upperbound, ArithVar var );
+
+  /**
+   * Using the simpleKind return the ArithVar associated with the assertion.
+   */
+  ArithVar determineArithVar(const Polynomial& p) const;
+  ArithVar determineArithVar(TNode assertion) const;
+
+  /**
+   * Splits the disequalities in d_diseq that are violated using lemmas on demand.
+   * returns true if any lemmas were issued.
+   * returns false if all disequalities are satisfied in the current model.
+   */
+  bool splitDisequalities();
+
+  /** A Difference variable is known to be 0.*/
+  void zeroDifferenceDetected(ArithVar x);
+
+
+  /**
+   * Looks for the next integer variable without an integer assignment in a
+   * round-robin fashion. Changes the value of d_nextIntegerCheckVar.
+   *
+   * This returns true if all integer variables have integer assignments.
+   * If this returns false, d_nextIntegerCheckVar does not have an integer
+   * assignment.
+   */
+  bool hasIntegerModel();
+
+  /**
+   * Looks for through the variables starting at d_nextIntegerCheckVar
+   * for the first integer variable that is between its upper and lower bounds
+   * that has a non-integer assignment.
+   *
+   * If assumeBounds is true, skip the check that the variable is in bounds.
+   *
+   * If there is no such variable, returns ARITHVAR_SENTINEL;
+   */
+  ArithVar nextIntegerViolation(bool assumeBounds) const;
+
+  /**
+   * Issues branches for non-auxiliary integer variables with non-integer assignments.
+   * Returns a cut for a lemma.
+   * If there is an integer model, this returns Node::null().
+   */
+  TrustNode roundRobinBranch();
+
+  bool proofsEnabled() const { return d_pnm; }
+
+ public:
+  /**
+   * This requests a new unique ArithVar value for x.
+   * This also does initial (not context dependent) set up for a variable,
+   * except for setting up the initial.
+   *
+   * If aux is true, this is an auxiliary variable.
+   * If internal is true, x might not be unique up to a constant multiple.
+   */
+  ArithVar requestArithVar(TNode x, bool aux, bool internal);
+
+public:
+  const BoundsInfo& boundsInfo(ArithVar basic) const;
+
+
+private:
+  /** Initial (not context dependent) sets up for a variable.*/
+  void setupBasicValue(ArithVar x);
+
+  /** Initial (not context dependent) sets up for a new auxiliary variable.*/
+  void setupAuxiliary(TNode left);
+
+
+  /**
+   * Assert*(n, orig) takes an bound n that is implied by orig.
+   * and asserts that as a new bound if it is tighter than the current bound
+   * and updates the value of a basic variable if needed.
+   *
+   * orig must be a literal in the SAT solver so that it can be used for
+   * conflict analysis.
+   *
+   * x is the variable getting the new bound,
+   * c is the value of the new bound.
+   *
+   * If this new bound is in conflict with the other bound,
+   * a node describing this conflict is returned.
+   * If this new bound is not in conflict, Node::null() is returned.
+   */
+  bool AssertLower(ConstraintP constraint);
+  bool AssertUpper(ConstraintP constraint);
+  bool AssertEquality(ConstraintP constraint);
+  bool AssertDisequality(ConstraintP constraint);
+
+  /** Tracks the bounds that were updated in the current round. */
+  DenseSet d_updatedBounds;
+
+  /** Tracks the basic variables where propagation might be possible. */
+  DenseSet d_candidateBasics;
+  DenseSet d_candidateRows;
+
+  bool hasAnyUpdates() { return !d_updatedBounds.empty(); }
+  void clearUpdates();
+
+  void revertOutOfConflict();
+
+  void propagateCandidatesNew();
+  void dumpUpdatedBoundsToRows();
+  bool propagateCandidateRow(RowIndex rid);
+  bool propagateMightSucceed(ArithVar v, bool ub) const;
+  /** Attempt to perform a row propagation where there is at most 1 possible variable.*/
+  bool attemptSingleton(RowIndex ridx, bool rowUp);
+  /** Attempt to perform a row propagation where every variable is a potential candidate.*/
+  bool attemptFull(RowIndex ridx, bool rowUp);
+  bool tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, bool vUp, const DeltaRational& bound);
+  bool rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, ConstraintP bestImplied);
+  //void enqueueConstraints(std::vector<ConstraintCP>& out, Node n) const;
+  //ConstraintCPVec resolveOutPropagated(const ConstraintCPVec& v, const std::set<ConstraintCP>& propagated) const;
+  void resolveOutPropagated(std::vector<ConstraintCPVec>& confs, const std::set<ConstraintCP>& propagated) const;
+  void subsumption(std::vector<ConstraintCPVec>& confs) const;
+
+  Node cutToLiteral(ApproximateSimplex*  approx, const CutInfo& cut) const;
+  Node branchToNode(ApproximateSimplex* approx, const NodeLog& cut) const;
+
+  void propagateCandidates();
+  void propagateCandidate(ArithVar basic);
+  bool propagateCandidateBound(ArithVar basic, bool upperBound);
+
+  inline bool propagateCandidateLowerBound(ArithVar basic){
+    return propagateCandidateBound(basic, false);
+  }
+  inline bool propagateCandidateUpperBound(ArithVar basic){
+    return propagateCandidateBound(basic, true);
+  }
+
+  /**
+   * Performs a check to see if it is definitely true that setup can be avoided.
+   */
+  bool canSafelyAvoidEqualitySetup(TNode equality);
+
+  /**
+   * Handles the case splitting for check() for a new assertion.
+   * Returns a conflict if one was found.
+   * Returns Node::null if no conflict was found.
+   *
+   * @param assertion The assertion that was just popped from the fact queue
+   * of TheoryArith and given to this class via preNotifyFact.
+   */
+  ConstraintP constraintFromFactQueue(TNode assertion);
+  bool assertionCases(ConstraintP c);
+
+  /**
+   * Returns the basic variable with the shorted row containing a non-basic variable.
+   * If no such row exists, return ARITHVAR_SENTINEL.
+   */
+  ArithVar findShortestBasicRow(ArithVar variable);
+
+  /**
+   * Debugging only routine!
+   * Returns true iff every variable is consistent in the partial model.
+   */
+  bool entireStateIsConsistent(const std::string& locationHint);
+  bool unenqueuedVariablesAreConsistent();
+
+  bool isImpliedUpperBound(ArithVar var, Node exp);
+  bool isImpliedLowerBound(ArithVar var, Node exp);
+
+  void internalExplain(TNode n, NodeBuilder& explainBuilder);
+
+  void asVectors(const Polynomial& p,
+                 std::vector<Rational>& coeffs,
+                 std::vector<ArithVar>& variables);
+
+  /** Routine for debugging. Print the assertions the theory is aware of. */
+  void debugPrintAssertions(std::ostream& out) const;
+  /** Debugging only routine. Prints the model. */
+  void debugPrintModel(std::ostream& out) const;
+
+  bool done() const { return d_containing.done(); }
+  bool isLeaf(TNode x) const { return d_containing.isLeaf(x); }
+  TheoryId theoryOf(TNode x) const { return d_containing.theoryOf(x); }
+  void debugPrintFacts() const { d_containing.debugPrintFacts(); }
+  bool outputTrustedLemma(TrustNode lem, InferenceId id);
+  bool outputLemma(TNode lem, InferenceId id);
+  void outputTrustedConflict(TrustNode conf, InferenceId id);
+  void outputConflict(TNode lit, InferenceId id);
+  void outputPropagate(TNode lit);
+  void outputRestart();
+
+  inline bool isSatLiteral(TNode l) const {
+    return (d_containing.d_valuation).isSatLiteral(l);
+  }
+  inline Node getSatValue(TNode n) const {
+    return (d_containing.d_valuation).getSatValue(n);
+  }
+
+  /** Used for replaying approximate simplex */
+  context::CDQueue<TrustNode> d_approxCuts;
+  /** Also used for replaying approximate simplex. "approximate cuts temporary storage" */
+  std::vector<TrustNode> d_acTmp;
+
+  /** Counts the number of fullCheck calls to arithmetic. */
+  uint32_t d_fullCheckCounter;
+  std::vector<ArithVar> cutAllBounded() const;
+  TrustNode branchIntegerVariable(ArithVar x) const;
+  void branchVector(const std::vector<ArithVar>& lemmas);
+
+  context::CDO<unsigned> d_cutCount;
+  context::CDHashSet<ArithVar, std::hash<ArithVar>> d_cutInContext;
+
+  context::CDO<bool> d_likelyIntegerInfeasible;
+
+  context::CDO<bool> d_guessedCoeffSet;
+  ArithRatPairVec d_guessedCoeffs;
+
+
+  TreeLog* d_treeLog;
+  TreeLog& getTreeLog();
+
+
+  ArithVarVec d_replayVariables;
+  std::vector<ConstraintP> d_replayConstraints;
+  DenseMap<Rational> d_lhsTmp;
+
+  /* Approximate simpplex solvers are given a copy of their stats */
+  ApproximateStatistics* d_approxStats;
+  ApproximateStatistics& getApproxStats();
+  context::CDO<int32_t> d_attemptSolveIntTurnedOff;
+  void turnOffApproxFor(int32_t rounds);
+  bool getSolveIntegerResource();
+
+  void tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bl);
+  std::vector<ConstraintCPVec> replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth);
+
+  std::pair<ConstraintP, ArithVar> replayGetConstraint(const CutInfo& info);
+  std::pair<ConstraintP, ArithVar> replayGetConstraint(
+      ApproximateSimplex* approx, const NodeLog& nl);
+  std::pair<ConstraintP, ArithVar> replayGetConstraint(const DenseMap<Rational>& lhs, Kind k, const Rational& rhs, bool branch);
+
+  void replayAssert(ConstraintP c);
+
+  static ConstraintCP vectorToIntHoleConflict(const ConstraintCPVec& conflict);
+  static void intHoleConflictToVector(ConstraintCP conflicting, ConstraintCPVec& conflict);
+
+  // Returns true if the node contains a literal
+  // that is an arithmetic literal and is not a sat literal
+  // No caching is done so this should likely only
+  // be called carefully!
+  bool hasFreshArithLiteral(Node n) const;
+
+  int32_t d_dioSolveResources;
+  bool getDioCuttingResource();
+
+  uint32_t d_solveIntMaybeHelp, d_solveIntAttempts;
+
+  RationalVector d_farkasBuffer;
+
+  //---------------- during check
+  /** Whether there were new facts during preCheck */
+  bool d_newFacts;
+  /** The previous status, computed during preCheck */
+  Result::Status d_previousStatus;
+  //---------------- end during check
+
+  /** These fields are designed to be accessible to TheoryArith methods. */
+  class Statistics {
+  public:
+    IntStat d_statAssertUpperConflicts, d_statAssertLowerConflicts;
+
+    IntStat d_statUserVariables, d_statAuxiliaryVariables;
+    IntStat d_statDisequalitySplits;
+    IntStat d_statDisequalityConflicts;
+    TimerStat d_simplifyTimer;
+    TimerStat d_staticLearningTimer;
+
+    TimerStat d_presolveTime;
+
+    TimerStat d_newPropTime;
+
+    IntStat d_externalBranchAndBounds;
+
+    IntStat d_initialTableauSize;
+    IntStat d_currSetToSmaller;
+    IntStat d_smallerSetToCurr;
+    TimerStat d_restartTimer;
+
+    TimerStat d_boundComputationTime;
+    IntStat d_boundComputations, d_boundPropagations;
+
+    IntStat d_unknownChecks;
+    IntStat d_maxUnknownsInARow;
+    AverageStat d_avgUnknownsInARow;
+
+    IntStat d_revertsOnConflicts;
+    IntStat d_commitsOnConflicts;
+    IntStat d_nontrivialSatChecks;
+
+    IntStat d_replayLogRecCount,
+      d_replayLogRecConflictEscalation,
+      d_replayLogRecEarlyExit,
+      d_replayBranchCloseFailures,
+      d_replayLeafCloseFailures,
+      d_replayBranchSkips,
+      d_mirCutsAttempted,
+      d_gmiCutsAttempted,
+      d_branchCutsAttempted,
+      d_cutsReconstructed,
+      d_cutsReconstructionFailed,
+      d_cutsProven,
+      d_cutsProofFailed,
+      d_mipReplayLemmaCalls,
+      d_mipExternalCuts,
+      d_mipExternalBranch;
+
+    IntStat d_inSolveInteger,
+      d_branchesExhausted,
+      d_execExhausted,
+      d_pivotsExhausted,
+      d_panicBranches,
+      d_relaxCalls,
+      d_relaxLinFeas,
+      d_relaxLinFeasFailures,
+      d_relaxLinInfeas,
+      d_relaxLinInfeasFailures,
+      d_relaxLinExhausted,
+      d_relaxOthers;
+
+    IntStat d_applyRowsDeleted;
+    TimerStat d_replaySimplexTimer;
+
+    TimerStat d_replayLogTimer,
+      d_solveIntTimer,
+      d_solveRealRelaxTimer;
+
+    IntStat d_solveIntCalls,
+      d_solveStandardEffort;
+
+    IntStat d_approxDisabled;
+    IntStat d_replayAttemptFailed;
+
+    IntStat d_cutsRejectedDuringReplay;
+    IntStat d_cutsRejectedDuringLemmas;
+
+    HistogramStat<uint32_t> d_satPivots;
+    HistogramStat<uint32_t> d_unsatPivots;
+    HistogramStat<uint32_t> d_unknownPivots;
+
+    IntStat d_solveIntModelsAttempts;
+    IntStat d_solveIntModelsSuccessful;
+    TimerStat d_mipTimer;
+    TimerStat d_lpTimer;
+
+    IntStat d_mipProofsAttempted;
+    IntStat d_mipProofsSuccessful;
+
+    IntStat d_numBranchesFailed;
+
+    Statistics(StatisticsRegistry& reg, const std::string& name);
+  };
+
+
+  Statistics d_statistics;
+}; /* class TheoryArithPrivate */
+
+
+}  // namespace arith
+}  // namespace theory
+}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear_equality.cpp b/src/theory/arith/linear_equality.cpp
deleted file mode 100644 (file)
index 337f244..0000000
+++ /dev/null
@@ -1,1375 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Andres Noetzli
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This implements the LinearEqualityModule.
- */
-#include "theory/arith/linear_equality.h"
-
-#include "base/output.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/* Explicitly instatiate these functions. */
-
-template ArithVar LinearEqualityModule::selectSlack<true>(ArithVar x_i, VarPreferenceFunction pf) const;
-template ArithVar LinearEqualityModule::selectSlack<false>(ArithVar x_i, VarPreferenceFunction pf) const;
-
-template bool LinearEqualityModule::preferWitness<true>(const UpdateInfo& a, const UpdateInfo& b) const;
-template bool LinearEqualityModule::preferWitness<false>(const UpdateInfo& a, const UpdateInfo& b) const;
-
-
-void Border::output(std::ostream& out) const{
-  out << "{Border"
-      << ", " << d_bound->getVariable()
-      << ", " << d_bound->getValue()
-      << ", " << d_diff
-      << ", " << d_areFixing
-      << ", " << d_upperbound;
-  if(ownBorder()){
-    out << ", ownBorder";
-  }else{
-    out << ", " << d_entry->getCoefficient();
-  }
-  out << ", " << d_bound
-      << "}";
-}
-
-LinearEqualityModule::LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundInfoMap& boundsTracking, BasicVarModelUpdateCallBack f):
-  d_variables(vars),
-  d_tableau(t),
-  d_basicVariableUpdates(f),
-  d_increasing(1),
-  d_decreasing(-1),
-  d_upperBoundDifference(),
-  d_lowerBoundDifference(),
-  d_one(1),
-  d_negOne(-1),
-  d_btracking(boundsTracking),
-  d_areTracking(false),
-  d_trackCallback(this)
-{}
-
-LinearEqualityModule::Statistics::Statistics()
-    : d_statPivots(
-        smtStatisticsRegistry().registerInt("theory::arith::pivots")),
-      d_statUpdates(
-          smtStatisticsRegistry().registerInt("theory::arith::updates")),
-      d_pivotTime(
-          smtStatisticsRegistry().registerTimer("theory::arith::pivotTime")),
-      d_adjTime(
-          smtStatisticsRegistry().registerTimer("theory::arith::adjTime")),
-      d_weakeningAttempts(smtStatisticsRegistry().registerInt(
-          "theory::arith::weakening::attempts")),
-      d_weakeningSuccesses(smtStatisticsRegistry().registerInt(
-          "theory::arith::weakening::success")),
-      d_weakenings(smtStatisticsRegistry().registerInt(
-          "theory::arith::weakening::total")),
-      d_weakenTime(smtStatisticsRegistry().registerTimer(
-          "theory::arith::weakening::time")),
-      d_forceTime(
-          smtStatisticsRegistry().registerTimer("theory::arith::forcing::time"))
-{
-}
-
-void LinearEqualityModule::includeBoundUpdate(ArithVar v, const BoundsInfo& prev){
-  Assert(!d_areTracking);
-
-  BoundsInfo curr = d_variables.boundsInfo(v);
-
-  Assert(prev != curr);
-  Tableau::ColIterator basicIter = d_tableau.colIterator(v);
-  for(; !basicIter.atEnd(); ++basicIter){
-    const Tableau::Entry& entry = *basicIter;
-    Assert(entry.getColVar() == v);
-    int a_ijSgn = entry.getCoefficient().sgn();
-
-    RowIndex ridx = entry.getRowIndex();
-    BoundsInfo& counts = d_btracking.get(ridx);
-    Trace("includeBoundUpdate") << d_tableau.rowIndexToBasic(ridx) << " " << counts << " to " ;
-    counts.addInChange(a_ijSgn, prev, curr);
-    Trace("includeBoundUpdate") << counts << " " << a_ijSgn << std::endl;
-  }
-}
-
-void LinearEqualityModule::updateMany(const DenseMap<DeltaRational>& many){
-  for(DenseMap<DeltaRational>::const_iterator i = many.begin(), i_end = many.end(); i != i_end; ++i){
-    ArithVar nb = *i;
-    if(!d_tableau.isBasic(nb)){
-      Assert(!d_tableau.isBasic(nb));
-      const DeltaRational& newValue = many[nb];
-      if(newValue != d_variables.getAssignment(nb)){
-        Trace("arith::updateMany")
-          << "updateMany:" << nb << " "
-          << d_variables.getAssignment(nb) << " to "<< newValue << endl;
-        update(nb, newValue);
-      }
-    }
-  }
-}
-
-
-
-
-void LinearEqualityModule::applySolution(const DenseSet& newBasis, const DenseMap<DeltaRational>& newValues){
-  forceNewBasis(newBasis);
-  updateMany(newValues);
-}
-
-void LinearEqualityModule::forceNewBasis(const DenseSet& newBasis){
-  TimerStat::CodeTimer codeTimer(d_statistics.d_forceTime);
-  cout << "force begin" << endl;
-  DenseSet needsToBeAdded;
-  for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){
-    ArithVar b = *i;
-    if(!d_tableau.isBasic(b)){
-      needsToBeAdded.add(b);
-    }
-  }
-
-  while(!needsToBeAdded.empty()){
-    ArithVar toRemove = ARITHVAR_SENTINEL;
-    ArithVar toAdd = ARITHVAR_SENTINEL;
-    DenseSet::const_iterator i = needsToBeAdded.begin(), i_end = needsToBeAdded.end();
-    for(; toAdd == ARITHVAR_SENTINEL && i != i_end; ++i){
-      ArithVar v = *i;
-
-      Tableau::ColIterator colIter = d_tableau.colIterator(v);
-      for(; !colIter.atEnd(); ++colIter){
-        const Tableau::Entry& entry = *colIter;
-        Assert(entry.getColVar() == v);
-        ArithVar b = d_tableau.rowIndexToBasic(entry.getRowIndex());
-        if(!newBasis.isMember(b)){
-          toAdd = v;
-          if(toRemove == ARITHVAR_SENTINEL ||
-             d_tableau.basicRowLength(toRemove) > d_tableau.basicRowLength(b)){
-            toRemove = b;
-          }
-        }
-      }
-    }
-    Assert(toRemove != ARITHVAR_SENTINEL);
-    Assert(toAdd != ARITHVAR_SENTINEL);
-
-    Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl;
-    d_tableau.pivot(toRemove, toAdd, d_trackCallback);
-    d_basicVariableUpdates(toAdd);
-
-    Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
-    needsToBeAdded.remove(toAdd);
-  }
-}
-
-void LinearEqualityModule::updateUntracked(ArithVar x_i, const DeltaRational& v){
-  Assert(!d_tableau.isBasic(x_i));
-  Assert(!d_areTracking);
-  const DeltaRational& assignment_x_i = d_variables.getAssignment(x_i);
-  ++(d_statistics.d_statUpdates);
-
-
-  Trace("arith") <<"update " << x_i << ": "
-                 << assignment_x_i << "|-> " << v << endl;
-  DeltaRational diff = v - assignment_x_i;
-
-  Tableau::ColIterator colIter = d_tableau.colIterator(x_i);
-  for(; !colIter.atEnd(); ++colIter){
-    const Tableau::Entry& entry = *colIter;
-    Assert(entry.getColVar() == x_i);
-
-    ArithVar x_j = d_tableau.rowIndexToBasic(entry.getRowIndex());
-    const Rational& a_ji = entry.getCoefficient();
-
-    const DeltaRational& assignment = d_variables.getAssignment(x_j);
-    DeltaRational  nAssignment = assignment+(diff * a_ji);
-    d_variables.setAssignment(x_j, nAssignment);
-
-    d_basicVariableUpdates(x_j);
-  }
-
-  d_variables.setAssignment(x_i, v);
-
-  if(TraceIsOn("paranoid:check_tableau")){  debugCheckTableau(); }
-}
-
-void LinearEqualityModule::updateTracked(ArithVar x_i, const DeltaRational& v){
-  TimerStat::CodeTimer codeTimer(d_statistics.d_adjTime);
-
-  Assert(!d_tableau.isBasic(x_i));
-  Assert(d_areTracking);
-
-  ++(d_statistics.d_statUpdates);
-
-  DeltaRational diff =  v - d_variables.getAssignment(x_i);
-  Trace("arith") <<"update " << x_i << ": "
-                 << d_variables.getAssignment(x_i) << "|-> " << v << endl;
-
-
-  BoundCounts before = d_variables.atBoundCounts(x_i);
-  d_variables.setAssignment(x_i, v);
-  BoundCounts after = d_variables.atBoundCounts(x_i);
-
-  bool anyChange = before != after;
-
-  Tableau::ColIterator colIter = d_tableau.colIterator(x_i);
-  for(; !colIter.atEnd(); ++colIter){
-    const Tableau::Entry& entry = *colIter;
-    Assert(entry.getColVar() == x_i);
-
-    RowIndex ridx = entry.getRowIndex();
-    ArithVar x_j = d_tableau.rowIndexToBasic(ridx);
-    const Rational& a_ji = entry.getCoefficient();
-
-    const DeltaRational& assignment = d_variables.getAssignment(x_j);
-    DeltaRational  nAssignment = assignment+(diff * a_ji);
-    Trace("update") << x_j << " " << a_ji << assignment << " -> " << nAssignment << endl;
-    BoundCounts xjBefore = d_variables.atBoundCounts(x_j);
-    d_variables.setAssignment(x_j, nAssignment);
-    BoundCounts xjAfter = d_variables.atBoundCounts(x_j);
-
-    Assert(rowIndexIsTracked(ridx));
-    BoundsInfo& next_bc_k = d_btracking.get(ridx);
-    if(anyChange){
-      next_bc_k.addInAtBoundChange(a_ji.sgn(), before, after);
-    }
-    if(xjBefore != xjAfter){
-      next_bc_k.addInAtBoundChange(-1, xjBefore, xjAfter);
-    }
-
-    d_basicVariableUpdates(x_j);
-  }
-
-  if(TraceIsOn("paranoid:check_tableau")){  debugCheckTableau(); }
-}
-
-void LinearEqualityModule::pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& x_i_value){
-  Assert(x_i != x_j);
-
-  TimerStat::CodeTimer codeTimer(d_statistics.d_pivotTime);
-
-  if(TraceIsOn("arith::tracking::pre")){
-    Trace("arith::tracking") << "pre update" << endl;
-    debugCheckTracking();
-  }
-
-  if(TraceIsOn("arith::simplex:row")){ debugPivot(x_i, x_j); }
-
-  RowIndex ridx = d_tableau.basicToRowIndex(x_i);
-  const Tableau::Entry& entry_ij =  d_tableau.findEntry(ridx, x_j);
-  Assert(!entry_ij.blank());
-
-  const Rational& a_ij = entry_ij.getCoefficient();
-  const DeltaRational& betaX_i = d_variables.getAssignment(x_i);
-  DeltaRational theta = (x_i_value - betaX_i)/a_ij;
-  DeltaRational x_j_value = d_variables.getAssignment(x_j) + theta;
-
-  updateTracked(x_j, x_j_value);
-
-  if(TraceIsOn("arith::tracking::mid")){
-    Trace("arith::tracking") << "postupdate prepivot" << endl;
-    debugCheckTracking();
-  }
-
-  // Pivots
-  ++(d_statistics.d_statPivots);
-
-  d_tableau.pivot(x_i, x_j, d_trackCallback);
-
-  if(TraceIsOn("arith::tracking::post")){
-    Trace("arith::tracking") << "postpivot" << endl;
-    debugCheckTracking();
-  }
-
-  d_basicVariableUpdates(x_j);
-
-  if(TraceIsOn("matrix")){
-    d_tableau.printMatrix();
-  }
-}
-
-uint32_t LinearEqualityModule::updateProduct(const UpdateInfo& inf) const {
-  uint32_t colLen = d_tableau.getColLength(inf.nonbasic());
-  if(inf.describesPivot()){
-    Assert(inf.leaving() != inf.nonbasic());
-    return colLen + d_tableau.basicRowLength(inf.leaving());
-  }else{
-    return colLen;
-  }
-}
-
-void LinearEqualityModule::debugCheckTracking(){
-  Tableau::BasicIterator basicIter = d_tableau.beginBasic(),
-    endIter = d_tableau.endBasic();
-  for(; basicIter != endIter; ++basicIter){
-    ArithVar basic = *basicIter;
-    Trace("arith::tracking") << "arith::tracking row basic: " << basic << endl;
-
-    for(Tableau::RowIterator iter = d_tableau.basicRowIterator(basic); !iter.atEnd() && TraceIsOn("arith::tracking"); ++iter){
-      const Tableau::Entry& entry = *iter;
-
-      ArithVar var = entry.getColVar();
-      const Rational& coeff = entry.getCoefficient();
-      DeltaRational beta = d_variables.getAssignment(var);
-      Trace("arith::tracking") << var << " " << d_variables.boundsInfo(var)
-                               << " " << beta << coeff;
-      if(d_variables.hasLowerBound(var)){
-        Trace("arith::tracking") << "(lb " << d_variables.getLowerBound(var) << ")";
-      }
-      if(d_variables.hasUpperBound(var)){
-        Trace("arith::tracking") << "(up " << d_variables.getUpperBound(var) << ")";
-      }
-      Trace("arith::tracking") << endl;
-    }
-    Trace("arith::tracking") << "end row"<< endl;
-
-    if(basicIsTracked(basic)){
-      RowIndex ridx = d_tableau.basicToRowIndex(basic);
-      BoundsInfo computed = computeRowBoundInfo(ridx, false);
-      Trace("arith::tracking")
-        << "computed " << computed
-        << " tracking " << d_btracking[ridx] << endl;
-      Assert(computed == d_btracking[ridx]);
-    }
-  }
-}
-
-void LinearEqualityModule::debugPivot(ArithVar x_i, ArithVar x_j){
-  Trace("arith::pivot") << "debugPivot("<< x_i  <<"|->"<< x_j << ")" << endl;
-
-  for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
-    const Tableau::Entry& entry = *iter;
-
-    ArithVar var = entry.getColVar();
-    const Rational& coeff = entry.getCoefficient();
-    DeltaRational beta = d_variables.getAssignment(var);
-    Trace("arith::pivot") << var << beta << coeff;
-    if(d_variables.hasLowerBound(var)){
-      Trace("arith::pivot") << "(lb " << d_variables.getLowerBound(var) << ")";
-    }
-    if(d_variables.hasUpperBound(var)){
-      Trace("arith::pivot") << "(up " << d_variables.getUpperBound(var) << ")";
-    }
-    Trace("arith::pivot") << endl;
-  }
-  Trace("arith::pivot") << "end row"<< endl;
-}
-
-/**
- * This check is quite expensive.
- * It should be wrapped in a TraceIsOn() guard.
- *   if(TraceIsOn("paranoid:check_tableau")){
- *      checkTableau();
- *   }
- */
-void LinearEqualityModule::debugCheckTableau(){
-  Tableau::BasicIterator basicIter = d_tableau.beginBasic(),
-    endIter = d_tableau.endBasic();
-  for(; basicIter != endIter; ++basicIter){
-    ArithVar basic = *basicIter;
-    DeltaRational sum;
-    Trace("paranoid:check_tableau") << "starting row" << basic << endl;
-    Tableau::RowIterator nonbasicIter = d_tableau.basicRowIterator(basic);
-    for(; !nonbasicIter.atEnd(); ++nonbasicIter){
-      const Tableau::Entry& entry = *nonbasicIter;
-      ArithVar nonbasic = entry.getColVar();
-      if(basic == nonbasic) continue;
-
-      const Rational& coeff = entry.getCoefficient();
-      DeltaRational beta = d_variables.getAssignment(nonbasic);
-      Trace("paranoid:check_tableau") << nonbasic << beta << coeff<<endl;
-      sum = sum + (beta*coeff);
-    }
-    DeltaRational shouldBe = d_variables.getAssignment(basic);
-    Trace("paranoid:check_tableau") << "ending row" << sum
-                                    << "," << shouldBe << endl;
-
-    Assert(sum == shouldBe);
-  }
-}
-
-DeltaRational LinearEqualityModule::computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const {
-  DeltaRational sum(0,0);
-  for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){
-    const Tableau::Entry& entry = (*i);
-    ArithVar v = entry.getColVar();
-    if(v == skip){ continue; }
-
-    const Rational& coeff =  entry.getCoefficient();
-    bool vUb = (rowUb == (coeff.sgn() > 0));
-
-    const DeltaRational& bound = vUb ?
-      d_variables.getUpperBound(v):
-      d_variables.getLowerBound(v);
-
-    DeltaRational diff = bound * coeff;
-    sum = sum + diff;
-  }
-  return sum;
-}
-
-/**
- * Computes the value of a basic variable using the current assignment.
- */
-DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe) const{
-  Assert(d_tableau.isBasic(x));
-  DeltaRational sum(0);
-
-  for(Tableau::RowIterator i = d_tableau.basicRowIterator(x); !i.atEnd(); ++i){
-    const Tableau::Entry& entry = (*i);
-    ArithVar nonbasic = entry.getColVar();
-    if(nonbasic == x) continue;
-    const Rational& coeff = entry.getCoefficient();
-
-    const DeltaRational& assignment = d_variables.getAssignment(nonbasic, useSafe);
-    sum = sum + (assignment * coeff);
-  }
-  return sum;
-}
-
-const Tableau::Entry* LinearEqualityModule::rowLacksBound(RowIndex ridx, bool rowUb, ArithVar skip){
-  Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
-  for(; !iter.atEnd(); ++iter){
-    const Tableau::Entry& entry = *iter;
-
-    ArithVar var = entry.getColVar();
-    if(var == skip) { continue; }
-
-    int sgn = entry.getCoefficient().sgn();
-    bool selectUb = (rowUb == (sgn > 0));
-    bool hasBound = selectUb ?
-      d_variables.hasUpperBound(var):
-      d_variables.hasLowerBound(var);
-    if(!hasBound){
-      return &entry;
-    }
-  }
-  return NULL;
-}
-
-void LinearEqualityModule::propagateBasicFromRow(ConstraintP c,
-                                                 bool produceProofs)
-{
-  Assert(c != NullConstraint);
-  Assert(c->isUpperBound() || c->isLowerBound());
-  Assert(!c->assertedToTheTheory());
-  Assert(!c->hasProof());
-
-  bool upperBound = c->isUpperBound();
-  ArithVar basic = c->getVariable();
-  RowIndex ridx = d_tableau.basicToRowIndex(basic);
-
-  ConstraintCPVec bounds;
-  RationalVectorP coeffs = produceProofs ? new RationalVector() : nullptr;
-  propagateRow(bounds, ridx, upperBound, c, coeffs);
-  c->impliedByFarkas(bounds, coeffs, false);
-  c->tryToPropagate();
-
-  if(coeffs != RationalVectorPSentinel) { delete coeffs; }
-}
-
-/* An explanation of the farkas coefficients.
- *
- * We are proving c using the other variables on the row.
- * The proof is in terms of the other constraints and the negation of c, ~c.
- *
- * A row has the form:
- *   sum a_i * x_i  = 0
- * or
- *   sx + sum r y + sum q z = 0
- * where r > 0 and q < 0.
- *
- * If rowUp, we are proving c
- *   g = sum r u_y + sum q l_z
- *   and c is entailed by -sx <= g
- * If !rowUp, we are proving c
- *   g = sum r l_y + sum q u_z
- *   and c is entailed by -sx >= g
- *
- *             | s     | c         | ~c       | u_i     | l_i
- *   if  rowUp | s > 0 | x >= -g/s | x < -g/s | a_i > 0 | a_i < 0
- *   if  rowUp | s < 0 | x <= -g/s | x > -g/s | a_i > 0 | a_i < 0
- *   if !rowUp | s > 0 | x <= -g/s | x > -g/s | a_i < 0 | a_i > 0
- *   if !rowUp | s < 0 | x >= -g/s | x < -g/s | a_i < 0 | a_i > 0
- *
- *
- * Thus we treat !rowUp as multiplying the row by -1 and rowUp as 1
- * for the entire row.
- */
-void LinearEqualityModule::propagateRow(ConstraintCPVec& into, RowIndex ridx, bool rowUp, ConstraintP c, RationalVectorP farkas){
-  Assert(!c->assertedToTheTheory());
-  Assert(c->canBePropagated());
-  Assert(!c->hasProof());
-
-  if(farkas != RationalVectorPSentinel){
-    Assert(farkas->empty());
-    farkas->push_back(Rational(0));
-  }
-
-  ArithVar v = c->getVariable();
-  Trace("arith::propagateRow") << "LinearEqualityModule::propagateRow("
-                                   << ridx << ", " << rowUp << ", " << v << ") start" << endl;
-
-  const Rational& multiple = rowUp ? d_one : d_negOne;
-
-  Trace("arith::propagateRow") << "multiple: " << multiple << endl;
-
-  Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
-  for(; !iter.atEnd(); ++iter){
-    const Tableau::Entry& entry = *iter;
-    ArithVar nonbasic = entry.getColVar();
-    const Rational& a_ij = entry.getCoefficient();
-    int sgn = a_ij.sgn();
-    Assert(sgn != 0);
-    bool selectUb = rowUp ? (sgn > 0) : (sgn < 0);
-
-    Assert(nonbasic != v || (rowUp && a_ij.sgn() > 0 && c->isLowerBound())
-           || (rowUp && a_ij.sgn() < 0 && c->isUpperBound())
-           || (!rowUp && a_ij.sgn() > 0 && c->isUpperBound())
-           || (!rowUp && a_ij.sgn() < 0 && c->isLowerBound()));
-
-    if(TraceIsOn("arith::propagateRow")){
-      if(nonbasic == v){
-        Trace("arith::propagateRow") << "(target) "
-                                     << rowUp << " "
-                                     << a_ij.sgn() << " "
-                                     << c->isLowerBound() << " "
-                                     << c->isUpperBound() << endl;
-
-        Trace("arith::propagateRow") << "(target) ";
-      }
-      Trace("arith::propagateRow") << "propagateRow " << a_ij << " * " << nonbasic ;
-    }
-
-    if(nonbasic == v){
-      if(farkas != RationalVectorPSentinel){
-        Assert(farkas->front().isZero());
-        Rational multAij = multiple * a_ij;
-        Trace("arith::propagateRow") << "(" << multAij << ") ";
-        farkas->front() = multAij;
-      }
-
-      Trace("arith::propagateRow") << c << endl;
-    }else{
-
-      ConstraintCP bound = selectUb
-        ? d_variables.getUpperBoundConstraint(nonbasic)
-        : d_variables.getLowerBoundConstraint(nonbasic);
-
-      if(farkas != RationalVectorPSentinel){
-        Rational multAij = multiple * a_ij;
-        Trace("arith::propagateRow") << "(" << multAij << ") ";
-        farkas->push_back(multAij);
-      }
-      Assert(bound != NullConstraint);
-      Trace("arith::propagateRow") << bound << endl;
-      into.push_back(bound);
-    }
-  }
-  Trace("arith::propagateRow") << "LinearEqualityModule::propagateRow("
-                                   << ridx << ", " << rowUp << ", " << v << ") done" << endl;
-
-}
-
-ConstraintP LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic) const {
-
-  int sgn = coeff.sgn();
-  bool ub = aboveUpper?(sgn < 0) : (sgn > 0);
-
-  ConstraintP c = ub ?
-    d_variables.getUpperBoundConstraint(v) :
-    d_variables.getLowerBoundConstraint(v);
-
-  bool weakened;
-  do{
-    const DeltaRational& bound = c->getValue();
-
-    weakened = false;
-
-    ConstraintP weaker = ub?
-      c->getStrictlyWeakerUpperBound(true, true):
-      c->getStrictlyWeakerLowerBound(true, true);
-
-    if(weaker != NullConstraint){
-      const DeltaRational& weakerBound = weaker->getValue();
-
-      DeltaRational diff = aboveUpper ? bound - weakerBound : weakerBound - bound;
-      //if var == basic,
-      //  if aboveUpper, weakerBound > bound, multiply by -1
-      //  if !aboveUpper, weakerBound < bound, multiply by -1
-      diff = diff * coeff;
-      if(surplus > diff){
-        ++d_statistics.d_weakenings;
-        weakened = true;
-        anyWeakening = true;
-        surplus = surplus - diff;
-
-        Trace("arith::weak") << "found:" << endl;
-        if(v == basic){
-          Trace("arith::weak") << "  basic: ";
-        }
-        Trace("arith::weak") << "  " << surplus << " "<< diff  << endl
-                             << "  " << bound << c << endl
-                             << "  " << weakerBound << weaker << endl;
-
-        Assert(diff.sgn() > 0);
-        c = weaker;
-      }
-    }
-  }while(weakened);
-
-  return c;
-}
-
-/* An explanation of the farkas coefficients.
- *
- * We are proving a conflict on the basic variable x_b.
- * If aboveUpper, then the conflict is with the constraint c : x_b <= u_b.
- * If !aboveUpper, then the conflict is with the constraint c : x_b >= l_b.
- *
- * A row has the form:
- *   -x_b sum a_i * x_i  = 0
- * or
- *   -x_b + sum r y + sum q z = 0,
- *    x_b = sum r y + sum q z
- * where r > 0 and q < 0.
- *
- *
- * If !aboveUp, we are proving ~c: x_b < l_b
- *   g = sum r u_y + sum q l_z
- *   x_b <= g < l_b
- *   and ~c is entailed by x_b <= g
- *
- * If aboveUp, we are proving ~c : x_b > u_b
- *   g = sum r l_y + sum q u_z
- *   x_b >= g > u_b
- *   and ~c is entailed by x_b >= g
- *
- *
- *               | s     | c         | ~c       | u_i     | l_i
- *   if !aboveUp | s > 0 | x >= -g/s | x < -g/s | a_i > 0 | a_i < 0
- *   if !aboveUp | s < 0 | x <= -g/s | x > -g/s | a_i > 0 | a_i < 0
- *   if  aboveUp | s > 0 | x <= -g/s | x > -g/s | a_i < 0 | a_i > 0
- *   if  aboveUp | s < 0 | x >= -g/s | x < -g/s | a_i < 0 | a_i > 0
- *
- * Thus we treat aboveUp as multiplying the row by -1 and !aboveUp as 1
- * for the entire row.
- */
-ConstraintCP LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, FarkasConflictBuilder& fcs) const {
-  Assert(!fcs.underConstruction());
-  TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime);
-
-  Trace("arith::weak") << "LinearEqualityModule::minimallyWeakConflict("
-                       << aboveUpper <<", "<< basicVar << ", ...) start" << endl;
-
-  const Rational& adjustSgn = aboveUpper ? d_negOne : d_one;
-  const DeltaRational& assignment = d_variables.getAssignment(basicVar);
-  DeltaRational surplus;
-  if(aboveUpper){
-    Assert(d_variables.hasUpperBound(basicVar));
-    Assert(assignment > d_variables.getUpperBound(basicVar));
-    surplus = assignment - d_variables.getUpperBound(basicVar);
-  }else{
-    Assert(d_variables.hasLowerBound(basicVar));
-    Assert(assignment < d_variables.getLowerBound(basicVar));
-    surplus = d_variables.getLowerBound(basicVar) - assignment;
-  }
-
-  bool anyWeakenings = false;
-  for(Tableau::RowIterator i = d_tableau.basicRowIterator(basicVar); !i.atEnd(); ++i){
-    const Tableau::Entry& entry = *i;
-    ArithVar v = entry.getColVar();
-    const Rational& coeff = entry.getCoefficient();
-    bool weakening = false;
-    ConstraintP c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar);
-    Trace("arith::weak") << "weak : " << weakening << " "
-                         << c->assertedToTheTheory() << " "
-                         << d_variables.getAssignment(v) << " "
-                         << c << endl;
-    anyWeakenings = anyWeakenings || weakening;
-
-    fcs.addConstraint(c, coeff, adjustSgn);
-    if(basicVar == v){
-      Assert(!c->negationHasProof());
-      fcs.makeLastConsequent();
-    }
-  }
-  Assert(fcs.consequentIsSet());
-
-  ConstraintCP conflicted = fcs.commitConflict();
-
-  ++d_statistics.d_weakeningAttempts;
-  if(anyWeakenings){
-    ++d_statistics.d_weakeningSuccesses;
-  }
-  Trace("arith::weak") << "LinearEqualityModule::minimallyWeakConflict("
-                       << aboveUpper <<", "<< basicVar << ", ...) done" << endl;
-  return conflicted;
-}
-
-ArithVar LinearEqualityModule::minVarOrder(ArithVar x, ArithVar y) const {
-  Assert(x != ARITHVAR_SENTINEL);
-  Assert(y != ARITHVAR_SENTINEL);
-  if(x <= y){
-    return x;
-  } else {
-    return y;
-  }
-}
-
-ArithVar LinearEqualityModule::minColLength(ArithVar x, ArithVar y) const {
-  Assert(x != ARITHVAR_SENTINEL);
-  Assert(y != ARITHVAR_SENTINEL);
-  Assert(!d_tableau.isBasic(x));
-  Assert(!d_tableau.isBasic(y));
-  uint32_t xLen = d_tableau.getColLength(x);
-  uint32_t yLen = d_tableau.getColLength(y);
-  if( xLen > yLen){
-     return y;
-  } else if( xLen== yLen ){
-    return minVarOrder(x,y);
-  }else{
-    return x;
-  }
-}
-
-ArithVar LinearEqualityModule::minRowLength(ArithVar x, ArithVar y) const {
-  Assert(x != ARITHVAR_SENTINEL);
-  Assert(y != ARITHVAR_SENTINEL);
-  Assert(d_tableau.isBasic(x));
-  Assert(d_tableau.isBasic(y));
-  uint32_t xLen = d_tableau.basicRowLength(x);
-  uint32_t yLen = d_tableau.basicRowLength(y);
-  if( xLen > yLen){
-     return y;
-  } else if( xLen== yLen ){
-    return minVarOrder(x,y);
-  }else{
-    return x;
-  }
-}
-
-ArithVar LinearEqualityModule::minBoundAndColLength(ArithVar x, ArithVar y) const{
-  Assert(x != ARITHVAR_SENTINEL);
-  Assert(y != ARITHVAR_SENTINEL);
-  Assert(!d_tableau.isBasic(x));
-  Assert(!d_tableau.isBasic(y));
-  if(d_variables.hasEitherBound(x) && !d_variables.hasEitherBound(y)){
-    return y;
-  }else if(!d_variables.hasEitherBound(x) && d_variables.hasEitherBound(y)){
-    return x;
-  }else {
-    return minColLength(x, y);
-  }
-}
-
-template <bool above>
-ArithVar LinearEqualityModule::selectSlack(ArithVar x_i, VarPreferenceFunction pref) const{
-  ArithVar slack = ARITHVAR_SENTINEL;
-
-  for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd();  ++iter){
-    const Tableau::Entry& entry = *iter;
-    ArithVar nonbasic = entry.getColVar();
-    if(nonbasic == x_i) continue;
-
-    const Rational& a_ij = entry.getCoefficient();
-    int sgn = a_ij.sgn();
-    if(isAcceptableSlack<above>(sgn, nonbasic)){
-      //If one of the above conditions is met, we have found an acceptable
-      //nonbasic variable to pivot x_i with.  We can now choose which one we
-      //prefer the most.
-      slack = (slack == ARITHVAR_SENTINEL) ? nonbasic : (this->*pref)(slack, nonbasic);
-    }
-  }
-
-  return slack;
-}
-
-const Tableau::Entry* LinearEqualityModule::selectSlackEntry(ArithVar x_i, bool above) const{
-  for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd();  ++iter){
-    const Tableau::Entry& entry = *iter;
-    ArithVar nonbasic = entry.getColVar();
-    if(nonbasic == x_i) continue;
-
-    const Rational& a_ij = entry.getCoefficient();
-    int sgn = a_ij.sgn();
-    if(above && isAcceptableSlack<true>(sgn, nonbasic)){
-      //If one of the above conditions is met, we have found an acceptable
-      //nonbasic variable to pivot x_i with.  We can now choose which one we
-      //prefer the most.
-      return &entry;
-    }else if(!above && isAcceptableSlack<false>(sgn, nonbasic)){
-      return &entry;
-    }
-  }
-
-  return NULL;
-}
-
-void LinearEqualityModule::startTrackingBoundCounts(){
-  Assert(!d_areTracking);
-  d_areTracking = true;
-  if(TraceIsOn("arith::tracking")){
-    debugCheckTracking();
-  }
-  Assert(d_areTracking);
-}
-
-void LinearEqualityModule::stopTrackingBoundCounts(){
-  Assert(d_areTracking);
-  d_areTracking = false;
-  if(TraceIsOn("arith::tracking")){
-    debugCheckTracking();
-  }
-  Assert(!d_areTracking);
-}
-
-
-void LinearEqualityModule::trackRowIndex(RowIndex ridx){
-  Assert(!rowIndexIsTracked(ridx));
-  BoundsInfo bi = computeRowBoundInfo(ridx, true);
-  d_btracking.set(ridx, bi);
-}
-
-BoundsInfo LinearEqualityModule::computeRowBoundInfo(RowIndex ridx, bool inQueue) const{
-  BoundsInfo bi;
-
-  Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
-  for(; !iter.atEnd();  ++iter){
-    const Tableau::Entry& entry = *iter;
-    ArithVar v = entry.getColVar();
-    const Rational& a_ij = entry.getCoefficient();
-    bi += (d_variables.selectBoundsInfo(v, inQueue)).multiplyBySgn(a_ij.sgn());
-  }
-  return bi;
-}
-
-BoundCounts LinearEqualityModule::debugBasicAtBoundCount(ArithVar x_i) const {
-  return d_btracking[d_tableau.basicToRowIndex(x_i)].atBounds();
-}
-
-/**
- * If the pivot described in u were performed,
- * then the row would qualify as being either at the minimum/maximum
- * to the non-basics being at their bounds.
- * The minimum/maximum is determined by the direction the non-basic is changing.
- */
-bool LinearEqualityModule::basicsAtBounds(const UpdateInfo& u) const {
-  Assert(u.describesPivot());
-
-  ArithVar nonbasic = u.nonbasic();
-  ArithVar basic = u.leaving();
-  Assert(basicIsTracked(basic));
-  int coeffSgn = u.getCoefficient().sgn();
-  int nbdir = u.nonbasicDirection();
-
-  ConstraintP c = u.limiting();
-  int toUB = (c->getType() == UpperBound ||
-              c->getType() == Equality) ? 1 : 0;
-  int toLB = (c->getType() == LowerBound ||
-              c->getType() == Equality) ? 1 : 0;
-
-  RowIndex ridx = d_tableau.basicToRowIndex(basic);
-
-  BoundCounts bcs = d_btracking[ridx].atBounds();
-  // x = c*n + \sum d*m
-  // 0 = -x + c*n + \sum d*m
-  // n = 1/c * x + -1/c * (\sum d*m)
-  BoundCounts nonb = bcs - d_variables.atBoundCounts(nonbasic).multiplyBySgn(coeffSgn);
-  nonb.addInChange(-1, d_variables.atBoundCounts(basic), BoundCounts(toLB, toUB));
-  nonb = nonb.multiplyBySgn(-coeffSgn);
-
-  uint32_t length = d_tableau.basicRowLength(basic);
-  Trace("basicsAtBounds")
-    << "bcs " << bcs
-    << "nonb " << nonb
-    << "length " << length << endl;
-  // nonb has nb excluded.
-  if(nbdir < 0){
-    return nonb.lowerBoundCount() + 1 == length;
-  }else{
-    Assert(nbdir > 0);
-    return nonb.upperBoundCount() + 1 == length;
-  }
-}
-
-bool LinearEqualityModule::nonbasicsAtLowerBounds(ArithVar basic) const {
-  Assert(basicIsTracked(basic));
-  RowIndex ridx = d_tableau.basicToRowIndex(basic);
-
-  BoundCounts bcs = d_btracking[ridx].atBounds();
-  uint32_t length = d_tableau.basicRowLength(basic);
-
-  // return true if excluding the basic is every element is at its "lowerbound"
-  // The psuedo code is:
-  //   bcs -= basic.count(basic, basic's sgn)
-  //   return bcs.lowerBoundCount() + 1 == length
-  // As basic's sign is always -1, we can pull out the pieces of the count:
-  //   bcs.lowerBoundCount() - basic.atUpperBoundInd() + 1 == length
-  // basic.atUpperBoundInd() is either 0 or 1
-  uint32_t lbc = bcs.lowerBoundCount();
-  return  (lbc == length) ||
-    (lbc + 1 == length && d_variables.cmpAssignmentUpperBound(basic) != 0);
-}
-
-bool LinearEqualityModule::nonbasicsAtUpperBounds(ArithVar basic) const {
-  Assert(basicIsTracked(basic));
-  RowIndex ridx = d_tableau.basicToRowIndex(basic);
-  BoundCounts bcs = d_btracking[ridx].atBounds();
-  uint32_t length = d_tableau.basicRowLength(basic);
-  uint32_t ubc = bcs.upperBoundCount();
-  // See the comment for nonbasicsAtLowerBounds()
-
-  return (ubc == length) ||
-    (ubc + 1 == length && d_variables.cmpAssignmentLowerBound(basic) != 0);
-}
-
-void LinearEqualityModule::trackingMultiplyRow(RowIndex ridx, int sgn) {
-  Assert(rowIndexIsTracked(ridx));
-  Assert(sgn != 0);
-  if(sgn < 0){
-    BoundsInfo& bi = d_btracking.get(ridx);
-    bi = bi.multiplyBySgn(sgn);
-  }
-}
-
-void LinearEqualityModule::trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){
-  Assert(oldSgn != currSgn);
-  BoundsInfo nb_inf = d_variables.boundsInfo(nb);
-
-  Assert(rowIndexIsTracked(ridx));
-
-  BoundsInfo& row_bi = d_btracking.get(ridx);
-  row_bi.addInSgn(nb_inf, oldSgn, currSgn);
-}
-
-ArithVar LinearEqualityModule::minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const{
-  if(vec.empty()) {
-    return ARITHVAR_SENTINEL;
-  }else {
-    ArithVar sel = vec.front();
-    ArithVarVec::const_iterator i = vec.begin() + 1;
-    ArithVarVec::const_iterator i_end = vec.end();
-    for(; i != i_end; ++i){
-      sel = (this->*pf)(sel, *i);
-    }
-    return sel;
-  }
-}
-
-bool LinearEqualityModule::accumulateBorder(const Tableau::Entry& entry, bool ub){
-  ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex());
-
-  Assert(basicIsTracked(currBasic));
-
-  ConstraintP bound = ub ?
-    d_variables.getUpperBoundConstraint(currBasic):
-    d_variables.getLowerBoundConstraint(currBasic);
-
-  if(bound == NullConstraint){ return false; }
-  Assert(bound != NullConstraint);
-
-  const Rational& coeff = entry.getCoefficient();
-
-  const DeltaRational& assignment = d_variables.getAssignment(currBasic);
-  DeltaRational toBound = bound->getValue() - assignment;
-  DeltaRational nbDiff = toBound/coeff;
-
-  // if ub
-  // if toUB >= 0
-  // then ub >= currBasic
-  //   if sgn > 0,
-  //   then diff >= 0, so nb must increase for G
-  //   else diff <= 0, so nb must decrease for G
-  // else ub < currBasic
-  //   if sgn > 0,
-  //   then diff < 0, so nb must decrease for G
-  //   else diff > 0, so nb must increase for G
-
-  int diffSgn = nbDiff.sgn();
-
-  if(diffSgn != 0 && willBeInConflictAfterPivot(entry, nbDiff, ub)){
-    return true;
-  }else{
-    bool areFixing = ub ? (toBound.sgn() < 0 ) : (toBound.sgn() > 0);
-    Border border(bound, nbDiff, areFixing, &entry, ub);
-    bool increasing =
-      (diffSgn > 0) ||
-      (diffSgn == 0 && ((coeff.sgn() > 0) == ub));
-
-    // assume diffSgn == 0
-    // if coeff > 0,
-    //   if ub, inc
-    //   else, dec
-    // else coeff < 0
-    //   if ub, dec
-    //   else, inc
-
-    if(increasing){
-      Trace("handleBorders") << "push back increasing " << border << endl;
-      d_increasing.push_back(border);
-    }else{
-      Trace("handleBorders") << "push back decreasing " << border << endl;
-      d_decreasing.push_back(border);
-    }
-    return false;
-  }
-}
-
-bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entry, const DeltaRational& nbDiff, bool bToUB) const{
-  int nbSgn = nbDiff.sgn();
-  Assert(nbSgn != 0);
-
-  if(nbSgn > 0){
-    if (!d_upperBoundDifference || nbDiff <= *d_upperBoundDifference)
-    {
-      return false;
-    }
-  }else{
-    if (!d_lowerBoundDifference || nbDiff >= *d_lowerBoundDifference)
-    {
-      return false;
-    }
-  }
-
-  // Assume past this point, nb will be in error if this pivot is done
-  ArithVar nb = entry.getColVar();
-  RowIndex ridx = entry.getRowIndex();
-  ArithVar basic = d_tableau.rowIndexToBasic(ridx);
-  Assert(rowIndexIsTracked(ridx));
-  int coeffSgn = entry.getCoefficient().sgn();
-
-
-  // if bToUB, then basic is going to be set to its upperbound
-  // if not bToUB, then basic is going to be set to its lowerbound
-
-  // Different steps of solving for this:
-  // 1) y = a * x + \sum b * z
-  // 2) -a * x = -y + \sum b * z
-  // 3) x = (-1/a) * ( -y + \sum b * z)
-
-  BoundCounts bc = d_btracking[ridx].atBounds();
-
-  // 1) y = a * x + \sum b * z
-  // Get bc(\sum b * z)
-  BoundCounts sumOnly = bc - d_variables.atBoundCounts(nb).multiplyBySgn(coeffSgn);
-
-  // y's bounds in the proposed model
-  int yWillBeAtUb = (bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0;
-  int yWillBeAtLb = (!bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0;
-  BoundCounts ysBounds(yWillBeAtLb, yWillBeAtUb);
-
-  // 2) -a * x = -y + \sum b * z
-  // Get bc(-y + \sum b * z)
-  sumOnly.addInChange(-1, d_variables.atBoundCounts(basic), ysBounds);
-
-  // 3) x = (-1/a) * ( -y + \sum b * z)
-  // Get bc((-1/a) * ( -y + \sum b * z))
-  BoundCounts xsBoundsAfterPivot = sumOnly.multiplyBySgn(-coeffSgn);
-
-  uint32_t length = d_tableau.basicRowLength(basic);
-  if(nbSgn > 0){
-    // Only check for the upper bound being violated
-    return xsBoundsAfterPivot.lowerBoundCount() + 1 == length;
-  }else{
-    // Only check for the lower bound being violated
-    return xsBoundsAfterPivot.upperBoundCount() + 1 == length;
-  }
-}
-
-UpdateInfo LinearEqualityModule::mkConflictUpdate(const Tableau::Entry& entry, bool ub) const{
-  ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex());
-  ArithVar nb = entry.getColVar();
-
-  ConstraintP bound = ub ?
-    d_variables.getUpperBoundConstraint(currBasic):
-    d_variables.getLowerBoundConstraint(currBasic);
-
-
-  const Rational& coeff = entry.getCoefficient();
-  const DeltaRational& assignment = d_variables.getAssignment(currBasic);
-  DeltaRational toBound = bound->getValue() - assignment;
-  DeltaRational nbDiff = toBound/coeff;
-
-  return UpdateInfo::conflict(nb, nbDiff, coeff, bound);
-}
-
-UpdateInfo LinearEqualityModule::speculativeUpdate(ArithVar nb, const Rational& focusCoeff, UpdatePreferenceFunction pref){
-  Assert(d_increasing.empty());
-  Assert(d_decreasing.empty());
-  Assert(!d_lowerBoundDifference);
-  Assert(!d_upperBoundDifference);
-
-  int focusCoeffSgn = focusCoeff.sgn();
-
-  Trace("speculativeUpdate") << "speculativeUpdate" << endl;
-  Trace("speculativeUpdate") << "nb " << nb << endl;
-  Trace("speculativeUpdate") << "focusCoeff " << focusCoeff << endl;
-
-  if(d_variables.hasUpperBound(nb)){
-    ConstraintP ub = d_variables.getUpperBoundConstraint(nb);
-    d_upperBoundDifference = ub->getValue() - d_variables.getAssignment(nb);
-    Border border(ub, *d_upperBoundDifference, false, NULL, true);
-    Trace("handleBorders") << "push back increasing " << border << endl;
-    d_increasing.push_back(border);
-  }
-  if(d_variables.hasLowerBound(nb)){
-    ConstraintP lb = d_variables.getLowerBoundConstraint(nb);
-    d_lowerBoundDifference = lb->getValue() - d_variables.getAssignment(nb);
-    Border border(lb, *d_lowerBoundDifference, false, NULL, false);
-    Trace("handleBorders") << "push back decreasing " << border << endl;
-    d_decreasing.push_back(border);
-  }
-
-  Tableau::ColIterator colIter = d_tableau.colIterator(nb);
-  for(; !colIter.atEnd(); ++colIter){
-    const Tableau::Entry& entry = *colIter;
-    Assert(entry.getColVar() == nb);
-
-    if(accumulateBorder(entry, true)){
-      clearSpeculative();
-      return mkConflictUpdate(entry, true);
-    }
-    if(accumulateBorder(entry, false)){
-      clearSpeculative();
-      return mkConflictUpdate(entry, false);
-    }
-  }
-
-  UpdateInfo selected;
-  BorderHeap& withSgn = focusCoeffSgn > 0 ? d_increasing : d_decreasing;
-  BorderHeap& againstSgn = focusCoeffSgn > 0 ? d_decreasing : d_increasing;
-
-  handleBorders(selected, nb, focusCoeff, withSgn, 0, pref);
-  int m = 1 - selected.errorsChangeSafe(0);
-  handleBorders(selected, nb, focusCoeff, againstSgn, m, pref);
-
-  clearSpeculative();
-  return selected;
-}
-
-void LinearEqualityModule::clearSpeculative(){
-  // clear everything away
-  d_increasing.clear();
-  d_decreasing.clear();
-  d_lowerBoundDifference.reset();
-  d_upperBoundDifference.reset();
-}
-
-void LinearEqualityModule::handleBorders(UpdateInfo& selected, ArithVar nb, const Rational& focusCoeff, BorderHeap& heap, int minimumFixes, UpdatePreferenceFunction pref){
-  Assert(minimumFixes >= 0);
-
-  // The values popped off of the heap
-  // should be popped with the values closest to 0
-  // being first and larger in absolute value last
-
-
-  int fixesRemaining = heap.possibleFixes();
-
-  Trace("handleBorders")
-    << "handleBorders "
-    << "nb " << nb
-    << "fc " << focusCoeff
-    << "h.e " << heap.empty()
-    << "h.dir " << heap.direction()
-    << "h.rem " << fixesRemaining
-    << "h.0s " << heap.numZeroes()
-    << "min " << minimumFixes
-    << endl;
-
-  if(heap.empty()){
-    // if the heap is empty, return
-    return;
-  }
-
-  bool zeroesWillDominate = fixesRemaining - heap.numZeroes() < minimumFixes;
-
-  // can the number of fixes ever exceed the minimum?
-  // no more than the number of possible fixes can be fixed in total
-  // nothing can be fixed before the zeroes are taken care of
-  if(minimumFixes > 0 && zeroesWillDominate){
-    return;
-  }
-
-
-  int negErrorChange = 0;
-  int nbDir = heap.direction();
-
-  // points at the beginning of the heap
-  if(zeroesWillDominate){
-    heap.dropNonZeroes();
-  }
-  heap.make_heap();
-
-
-  // pretend like the previous block had a value of zero.
-  // The block that actually has a value of 0 must handle this.
-  const DeltaRational zero(0);
-  const DeltaRational* prevBlockValue = &zero;
-
-  /** The coefficient changes as the value crosses border. */
-  Rational effectiveCoefficient = focusCoeff;
-
-  /* Keeps track of the change to the value of the focus function.*/
-  DeltaRational totalFocusChange(0);
-
-  const int focusCoeffSgn = focusCoeff.sgn();
-
-  while(heap.more()  &&
-        (fixesRemaining + negErrorChange > minimumFixes ||
-         (fixesRemaining + negErrorChange == minimumFixes &&
-          effectiveCoefficient.sgn() == focusCoeffSgn))){
-    // There are more elements &&
-    // we can either fix at least 1 more variable in the error function
-    // or we can improve the error function
-
-
-    int brokenInBlock = 0;
-    BorderVec::const_iterator endBlock = heap.end();
-
-    pop_block(heap, brokenInBlock, fixesRemaining, negErrorChange);
-
-    // if endVec == beginVec, block starts there
-    // other wise, block starts at endVec
-    BorderVec::const_iterator startBlock
-      = heap.more() ? heap.end() : heap.begin();
-
-    const DeltaRational& blockValue = (*startBlock).d_diff;
-
-    // if decreasing
-    // blockValue < prevBlockValue
-    // diff.sgn() = -1
-    DeltaRational diff = blockValue - (*prevBlockValue);
-    DeltaRational blockChangeToFocus =  diff * effectiveCoefficient;
-    totalFocusChange += blockChangeToFocus;
-
-    Trace("handleBorders")
-      << "blockValue " << (blockValue)
-      << "diff " << diff
-      << "blockChangeToFocus " << totalFocusChange
-      << "blockChangeToFocus " << totalFocusChange
-      << "negErrorChange " << negErrorChange
-      << "brokenInBlock " << brokenInBlock
-      << "fixesRemaining " << fixesRemaining
-      << endl;
-
-    int currFocusChangeSgn = totalFocusChange.sgn();
-    for(BorderVec::const_iterator i = startBlock; i != endBlock; ++i){
-      const Border& b = *i;
-
-      Trace("handleBorders") << b << endl;
-
-      bool makesImprovement = negErrorChange > 0 ||
-        (negErrorChange == 0  && currFocusChangeSgn > 0);
-
-      if(!makesImprovement){
-        if(b.ownBorder() || minimumFixes > 0){
-          continue;
-        }
-      }
-
-      UpdateInfo proposal(nb, nbDir);
-      if(b.ownBorder()){
-        proposal.witnessedUpdate(b.d_diff, b.d_bound, -negErrorChange, currFocusChangeSgn);
-      }else{
-        proposal.update(b.d_diff, b.getCoefficient(), b.d_bound, -negErrorChange, currFocusChangeSgn);
-      }
-
-      if(selected.unbounded() || (this->*pref)(selected, proposal)){
-        selected = proposal;
-      }
-    }
-
-    effectiveCoefficient += updateCoefficient(startBlock, endBlock);
-    prevBlockValue = &blockValue;
-    negErrorChange -= brokenInBlock;
-  }
-}
-
-Rational LinearEqualityModule::updateCoefficient(BorderVec::const_iterator startBlock, BorderVec::const_iterator endBlock){
-  //update coefficient
-  Rational changeToCoefficient(0);
-  for(BorderVec::const_iterator i = startBlock; i != endBlock; ++i){
-    const Border& curr = *i;
-    if(curr.ownBorder()){// breaking its own bound
-      if(curr.d_upperbound){
-        changeToCoefficient -= 1;
-      }else{
-        changeToCoefficient += 1;
-      }
-    }else{
-      const Rational& coeff = curr.d_entry->getCoefficient();
-      if(curr.d_areFixing){
-        if(curr.d_upperbound){// fixing an upper bound
-          changeToCoefficient += coeff;
-        }else{// fixing a lower bound
-          changeToCoefficient -= coeff;
-        }
-      }else{
-        if(curr.d_upperbound){// breaking an upper bound
-          changeToCoefficient -= coeff;
-        }else{
-          // breaking a lower bound
-          changeToCoefficient += coeff;
-        }
-      }
-    }
-  }
-  return changeToCoefficient;
-}
-
-void LinearEqualityModule::pop_block(BorderHeap& heap, int& brokenInBlock, int& fixesRemaining, int& negErrorChange){
-  Assert(heap.more());
-
-  if(heap.top().d_areFixing){
-    fixesRemaining--;
-    negErrorChange++;
-  }else{
-    brokenInBlock++;
-  }
-  heap.pop_heap();
-  const DeltaRational& blockValue = (*heap.end()).d_diff;
-
-  while(heap.more()){
-    const Border& top = heap.top();
-    if(blockValue == top.d_diff){
-      // belongs to the block
-      if(top.d_areFixing){
-        fixesRemaining--;
-        negErrorChange++;
-      }else{
-        brokenInBlock++;
-      }
-      heap.pop_heap();
-    }else{
-      // does not belong to the block
-      Assert((heap.direction() > 0) ? (blockValue < top.d_diff)
-                                    : (blockValue > top.d_diff));
-      break;
-    }
-  }
-}
-
-void LinearEqualityModule::substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult){
-  d_tableau.substitutePlusTimesConstant(to, from, mult, d_trackCallback);
-}
-void LinearEqualityModule::directlyAddToCoefficient(ArithVar row, ArithVar col, const Rational& mult){
-  d_tableau.directlyAddToCoefficient(row, col, mult, d_trackCallback);
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/linear_equality.h b/src/theory/arith/linear_equality.h
deleted file mode 100644 (file)
index ac6b8de..0000000
+++ /dev/null
@@ -1,762 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This module maintains the relationship between a Tableau and
- * PartialModel.
- *
- * This shares with the theory a Tableau, and a PartialModel that:
- *  - satisfies the equalities in the Tableau, and
- *  - the assignment for the non-basic variables satisfies their bounds.
- * This maintains the relationship needed by the SimplexDecisionProcedure.
- *
- * In the language of Simplex for DPLL(T), this provides:
- * - update()
- * - pivotAndUpdate()
- *
- * This class also provides utility functions that require
- * using both the Tableau and PartialModel.
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "options/arith_options.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/simplex_update.h"
-#include "theory/arith/tableau.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-struct Border{
-  // The constraint for the border
-  ConstraintP d_bound;
-
-  // The change to the nonbasic to reach the border
-  DeltaRational d_diff;
-
-  // Is reach this value fixing the constraint
-  // or is going past this value hurting the constraint
-  bool d_areFixing;
-
-  // Entry into the tableau
-  const Tableau::Entry* d_entry;
-
-  // Was this an upper bound or a lower bound?
-  bool d_upperbound;
-
-  Border():
-    d_bound(NullConstraint) // ignore the other values
-  {}
-
-  Border(ConstraintP l, const DeltaRational& diff, bool areFixing, const Tableau::Entry* en, bool ub):
-    d_bound(l), d_diff(diff), d_areFixing(areFixing), d_entry(en),  d_upperbound(ub)
-  {}
-
-  Border(ConstraintP l, const DeltaRational& diff, bool areFixing, bool ub):
-    d_bound(l), d_diff(diff), d_areFixing(areFixing), d_entry(NULL),  d_upperbound(ub)
-  {}
-  bool operator<(const Border& other) const{
-    return d_diff < other.d_diff;
-  }
-
-  /** d_lim is the nonbasic variable's own bound. */
-  bool ownBorder() const { return d_entry == NULL; }
-
-  bool isZero() const { return d_diff.sgn() == 0; }
-  static bool nonZero(const Border& b) { return !b.isZero(); }
-
-  const Rational& getCoefficient() const {
-    Assert(!ownBorder());
-    return d_entry->getCoefficient();
-  }
-  void output(std::ostream& out) const;
-};
-
-inline std::ostream& operator<<(std::ostream& out, const Border& b){
-  b.output(out);
-  return out;
-}
-
-typedef std::vector<Border> BorderVec;
-
-class BorderHeap {
-  const int d_dir;
-
-  class BorderHeapCmp {
-  private:
-    int d_nbDirection;
-  public:
-    BorderHeapCmp(int dir): d_nbDirection(dir){}
-    bool operator()(const Border& a, const Border& b) const{
-      if(d_nbDirection > 0){
-        // if nb is increasing,
-        //  this needs to act like a max
-        //  in order to have a min heap
-        return b < a;
-      }else{
-        // if nb is decreasing,
-        //  this needs to act like a min
-        //  in order to have a max heap
-        return a < b;
-      }
-    }
-  };
-  const BorderHeapCmp d_cmp;
-
-  BorderVec d_vec;
-
-  BorderVec::iterator d_begin;
-
-  /**
-   * Once this is initialized the top of the heap will always
-   * be at d_end - 1
-   */
-  BorderVec::iterator d_end;
-
-  int d_possibleFixes;
-  int d_numZeroes;
-
-public:
-  BorderHeap(int dir)
-  : d_dir(dir), d_cmp(dir), d_possibleFixes(0), d_numZeroes(0)
-  {}
-
-  void push_back(const Border& b){
-    d_vec.push_back(b);
-    if(b.d_areFixing){
-      d_possibleFixes++;
-    }
-    if(b.d_diff.sgn() == 0){
-      d_numZeroes++;
-    }
-  }
-
-  int numZeroes() const { return d_numZeroes; }
-  int possibleFixes() const { return d_possibleFixes; }
-  int direction() const { return d_dir; }
-
-  void make_heap(){
-    d_begin = d_vec.begin();
-    d_end = d_vec.end();
-    std::make_heap(d_begin, d_end, d_cmp);
-  }
-
-  void dropNonZeroes(){
-    d_vec.erase(std::remove_if(d_vec.begin(), d_vec.end(), &Border::nonZero),
-                d_vec.end());
-  }
-
-  const Border& top() const {
-    Assert(more());
-    return *d_begin;
-  }
-  void pop_heap(){
-    Assert(more());
-
-    std::pop_heap(d_begin, d_end, d_cmp);
-    --d_end;
-  }
-
-  BorderVec::const_iterator end() const{
-    return BorderVec::const_iterator(d_end);
-  }
-  BorderVec::const_iterator begin() const{
-    return BorderVec::const_iterator(d_begin);
-  }
-
-  inline bool more() const{ return d_begin != d_end; }
-
-  inline bool empty() const{ return d_vec.empty(); }
-
-  void clear(){
-    d_possibleFixes = 0;
-    d_numZeroes = 0;
-    d_vec.clear();
-  }
-};
-
-
-class LinearEqualityModule {
-public:
-  typedef ArithVar (LinearEqualityModule::*VarPreferenceFunction)(ArithVar, ArithVar) const;
-
-
-  typedef bool (LinearEqualityModule::*UpdatePreferenceFunction)(const UpdateInfo&, const UpdateInfo&) const;
-
-  
-private:
-  /**
-   * Manages information about the assignment and upper and lower bounds on the
-   * variables.
-   */
-  ArithVariables& d_variables;
-
-  /** Reference to the Tableau to operate upon. */
-  Tableau& d_tableau;
-
-  /** Called whenever the value of a basic variable is updated. */
-  BasicVarModelUpdateCallBack d_basicVariableUpdates;
-
-  BorderHeap d_increasing;
-  BorderHeap d_decreasing;
-  std::optional<DeltaRational> d_upperBoundDifference;
-  std::optional<DeltaRational> d_lowerBoundDifference;
-
-  Rational d_one;
-  Rational d_negOne;
-public:
-
-  /**
-   * Initializes a LinearEqualityModule with a partial model, a tableau,
-   * and a callback function for when basic variables update their values.
-   */
-  LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundInfoMap& boundTracking, BasicVarModelUpdateCallBack f);
-
-  /**
-   * Updates the assignment of a nonbasic variable x_i to v.
-   * Also updates the assignment of basic variables accordingly.
-   */
-  void update(ArithVar x_i, const DeltaRational& v){
-    if(d_areTracking){
-      updateTracked(x_i,v);
-    }else{
-      updateUntracked(x_i,v);
-    }
-  }
-
-  /** Specialization of update if the module is not tracking yet (for Assert*). */
-  void updateUntracked(ArithVar x_i, const DeltaRational& v);
-
-  /** Specialization of update if the module is not tracking yet (for Simplex). */
-  void updateTracked(ArithVar x_i, const DeltaRational& v);
-
-
-  /**
-   * Updates the value of a basic variable x_i to v,
-   * and then pivots x_i with the nonbasic variable in its row x_j.
-   * Updates the assignment of the other basic variables accordingly.
-   */
-  void pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& v);
-
-  ArithVariables& getVariables() const{ return d_variables; }
-  Tableau& getTableau() const{ return d_tableau; }
-
-  /**
-   * Updates every non-basic to reflect the assignment in many.
-   * For use with ApproximateSimplex.
-   */
-  void updateMany(const DenseMap<DeltaRational>& many);
-  void forceNewBasis(const DenseSet& newBasis);
-  void applySolution(const DenseSet& newBasis, const DenseMap<DeltaRational>& newValues);
-
-
-  /**
-   * Returns a pointer to the first Tableau entry on the row ridx that does not
-   * have an either a lower bound/upper bound for proving a bound on skip.
-   * The variable skip is always excluded. Returns NULL if there is no such element.
-   *
-   * If skip == ARITHVAR_SENTINEL, this is equivalent to considering the whole row.
-   */
-  const Tableau::Entry* rowLacksBound(RowIndex ridx, bool upperBound, ArithVar skip);
-
-
-  void startTrackingBoundCounts();
-  void stopTrackingBoundCounts();
-
-
-  void includeBoundUpdate(ArithVar nb, const BoundsInfo& prev);
-
-
-  uint32_t updateProduct(const UpdateInfo& inf) const;
-
-  inline bool minNonBasicVarOrder(const UpdateInfo& a, const UpdateInfo& b) const{
-    return a.nonbasic() >= b.nonbasic();
-  }
-
-  /**
-   * Prefer the update that touch the fewest entries in the matrix.
-   *
-   * The intuition is that this operation will be cheaper.
-   * This strongly biases the system towards updates instead of pivots.
-   */
-  inline bool minProduct(const UpdateInfo& a, const UpdateInfo& b) const{
-    uint32_t aprod = updateProduct(a);
-    uint32_t bprod = updateProduct(b);
-
-    if(aprod == bprod){
-      return minNonBasicVarOrder(a,b);
-    }else{
-      return aprod > bprod;
-    }
-  }
-  inline bool constrainedMin(const UpdateInfo& a, const UpdateInfo& b) const{
-    if(a.describesPivot() && b.describesPivot()){
-      bool aAtBounds = basicsAtBounds(a);
-      bool bAtBounds = basicsAtBounds(b);
-      if(aAtBounds != bAtBounds){
-        return bAtBounds;
-      }
-    }
-    return minProduct(a,b);
-  }
-
-  /**
-   * If both a and b are pivots, prefer the pivot with the leaving variables that has equal bounds.
-   * The intuition is that such variables will be less likely to lead to future problems.
-   */
-  inline bool preferFrozen(const UpdateInfo& a, const UpdateInfo& b) const {
-    if(a.describesPivot() && b.describesPivot()){
-      bool aFrozen = d_variables.boundsAreEqual(a.leaving());
-      bool bFrozen = d_variables.boundsAreEqual(b.leaving());
-
-      if(aFrozen != bFrozen){
-        return bFrozen;
-      }
-    }
-    return constrainedMin(a,b);
-  }
-
-  /**
-   * Prefer pivots with entering variables that do not have bounds.
-   * The intuition is that such variables will be less likely to lead to future problems.
-   */
-  bool preferNeitherBound(const UpdateInfo& a, const UpdateInfo& b) const {
-    if(d_variables.hasEitherBound(a.nonbasic()) == d_variables.hasEitherBound(b.nonbasic())){
-      return preferFrozen(a,b);
-    }else{
-      return d_variables.hasEitherBound(a.nonbasic());
-    }
-  }
-
-  bool modifiedBlands(const UpdateInfo& a, const UpdateInfo& b) const {
-    Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
-    Assert(a.describesPivot());
-    Assert(b.describesPivot());
-    if(a.nonbasic() == b.nonbasic()){
-      bool aIsZero = a.nonbasicDelta().sgn() == 0;
-      bool bIsZero = b.nonbasicDelta().sgn() == 0;
-
-      if((aIsZero || bIsZero) && (!aIsZero || !bIsZero)){
-        return bIsZero;
-      }else{
-        return a.leaving() >= b.leaving();
-      }
-    }else{
-      return a.nonbasic() > b.nonbasic();
-    }
-  }
-
-  template <bool heuristic>
-  bool preferWitness(const UpdateInfo& a, const UpdateInfo& b) const{
-    WitnessImprovement aImp = a.getWitness(!heuristic);
-    WitnessImprovement bImp = b.getWitness(!heuristic);
-
-    if(aImp == bImp){
-      switch(aImp){
-      case ConflictFound:
-        return preferNeitherBound(a,b);
-      case ErrorDropped:
-        if(a.errorsChange() == b.errorsChange()){
-          return preferNeitherBound(a,b);
-        }else{
-          return a.errorsChange() > b.errorsChange();
-        }
-      case FocusImproved:
-        return preferNeitherBound(a,b);
-      case BlandsDegenerate:
-        Assert(a.describesPivot());
-        Assert(b.describesPivot());
-        Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
-        return modifiedBlands(a,b);
-      case HeuristicDegenerate:
-        Assert(a.describesPivot());
-        Assert(b.describesPivot());
-        Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
-        return preferNeitherBound(a,b);
-      case AntiProductive:
-        return minNonBasicVarOrder(a, b);
-      // Not valid responses
-      case Degenerate:
-      case FocusShrank:
-        Unreachable();
-      }
-      Unreachable();
-    }else{
-      return aImp > bImp;
-    }
-  }
-
-private:
-
-  /**
-   * This maps each row index to its relevant bounds info.
-   * This tracks the count for how many variables on a row have bounds
-   * and how many are assigned at their bounds.
-   */
-  BoundInfoMap& d_btracking;
-  bool d_areTracking;
-
-public:
-  /**
-   * The constraint on a basic variable b is implied by the constraints
-   * on its row.  This is a wrapper for propagateRow().
-   */
- void propagateBasicFromRow(ConstraintP c, bool produceProofs);
-
- /**
-  * Let v be the variable for the constraint c.
-  * Exports either the explanation of an upperbound or a lower bound
-  * of v using the other variables in the row.
-  *
-  * If farkas != RationalVectorPSentinel, this function additionally
-  * stores the farkas coefficients of the constraints stored in into.
-  * Position 0 is the coefficient of v.
-  * Position i > 0, corresponds to the order of the other constraints.
-  */
- void propagateRow(ConstraintCPVec& into,
-                   RowIndex ridx,
-                   bool rowUp,
-                   ConstraintP c,
-                   RationalVectorP farkas);
-
- /**
-  * Computes the value of a basic variable using the assignments
-  * of the values of the variables in the basic variable's row tableau.
-  * This can compute the value using either:
-  * - the the current assignment (useSafe=false) or
-  * - the safe assignment (useSafe = true).
-  */
- DeltaRational computeRowValue(ArithVar x, bool useSafe) const;
-
- /**
-  * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
-  * and 2 ArithVar variables and returns one of the ArithVar variables
-  * potentially using the internals of the SimplexDecisionProcedure.
-  */
-
- ArithVar noPreference(ArithVar x, ArithVar y) const { return x; }
-
- /**
-  * minVarOrder is a PreferenceFunction for selecting the smaller of the 2
-  * ArithVars. This PreferenceFunction is used during the VarOrder stage of
-  * findModel.
-  */
- ArithVar minVarOrder(ArithVar x, ArithVar y) const;
-
- /**
-  * minColLength is a PreferenceFunction for selecting the variable with the
-  * smaller row count in the tableau.
-  *
-  * This is a heuristic rule and should not be used during the VarOrder
-  * stage of findModel.
-  */
- ArithVar minColLength(ArithVar x, ArithVar y) const;
-
- /**
-  * minRowLength is a PreferenceFunction for selecting the variable with the
-  * smaller row count in the tableau.
-  *
-  * This is a heuristic rule and should not be used during the VarOrder
-  * stage of findModel.
-  */
- ArithVar minRowLength(ArithVar x, ArithVar y) const;
-
- /**
-  * minBoundAndRowCount is a PreferenceFunction for preferring a variable
-  * without an asserted bound over variables with an asserted bound.
-  * If both have bounds or both do not have bounds,
-  * the rule falls back to minRowCount(...).
-  *
-  * This is a heuristic rule and should not be used during the VarOrder
-  * stage of findModel.
-  */
- ArithVar minBoundAndColLength(ArithVar x, ArithVar y) const;
-
- template <bool above>
- inline bool isAcceptableSlack(int sgn, ArithVar nonbasic) const
- {
-   return (above && sgn < 0 && d_variables.strictlyBelowUpperBound(nonbasic))
-          || (above && sgn > 0 && d_variables.strictlyAboveLowerBound(nonbasic))
-          || (!above && sgn > 0
-              && d_variables.strictlyBelowUpperBound(nonbasic))
-          || (!above && sgn < 0
-              && d_variables.strictlyAboveLowerBound(nonbasic));
-  }
-
-  /**
-   * Given the basic variable x_i,
-   * this function finds the smallest nonbasic variable x_j in the row of x_i
-   * in the tableau that can "take up the slack" to let x_i satisfy its bounds.
-   * This returns ARITHVAR_SENTINEL if none exists.
-   *
-   * More formally one of the following conditions must be satisfied:
-   * -  lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j)
-   * -  lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j)
-   * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j)
-   * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j)
-   *
-   */
-  template <bool lowerBound>  ArithVar selectSlack(ArithVar x_i, VarPreferenceFunction pf) const;
-  ArithVar selectSlackLowerBound(ArithVar x_i, VarPreferenceFunction pf) const {
-    return selectSlack<true>(x_i, pf);
-  }
-  ArithVar selectSlackUpperBound(ArithVar x_i, VarPreferenceFunction pf) const {
-    return selectSlack<false>(x_i, pf);
-  }
-
-  const Tableau::Entry* selectSlackEntry(ArithVar x_i, bool above) const;
-
-  inline bool rowIndexIsTracked(RowIndex ridx) const {
-    return d_btracking.isKey(ridx);
-  }
-  inline bool basicIsTracked(ArithVar v) const {
-    return rowIndexIsTracked(d_tableau.basicToRowIndex(v));
-  }
-  void trackRowIndex(RowIndex ridx);
-  void stopTrackingRowIndex(RowIndex ridx){
-    Assert(rowIndexIsTracked(ridx));
-    d_btracking.remove(ridx);
-  }
-
-  /**
-   * If the pivot described in u were performed,
-   * then the row would qualify as being either at the minimum/maximum
-   * to the non-basics being at their bounds.
-   * The minimum/maximum is determined by the direction the non-basic is changing.
-   */
-  bool basicsAtBounds(const UpdateInfo& u) const;
-
-private:
-
-  /**
-   * Recomputes the bound info for a row using either the information
-   * in the bounds queue or the current information.
-   * O(row length of ridx)
-   */
-  BoundsInfo computeRowBoundInfo(RowIndex ridx, bool inQueue) const;
-
-public:
-  /** Debug only routine. */
-  BoundCounts debugBasicAtBoundCount(ArithVar x_i) const;
-
-  /** Track the effect of the change of coefficient for bound counting. */
-  void trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn);
-
-  /** Track the effect of multiplying a row by a sign for bound counting. */
-  void trackingMultiplyRow(RowIndex ridx, int sgn);
-
-  /** Count for how many on a row have *an* upper/lower bounds. */
-  BoundCounts hasBoundCount(RowIndex ri) const {
-    Assert(d_variables.boundsQueueEmpty());
-    return d_btracking[ri].hasBounds();
-  }
-
-  /**
-   * Are there any non-basics on x_i's row that are not at
-   * their respective lower bounds (mod sgns).
-   * O(1) time due to the atBound() count.
-   */
-  bool nonbasicsAtLowerBounds(ArithVar x_i) const;
-
-  /**
-   * Are there any non-basics on x_i's row that are not at
-   * their respective upper bounds (mod sgns).
-   * O(1) time due to the atBound() count.
-   */
-  bool nonbasicsAtUpperBounds(ArithVar x_i) const;
-
-private:
-  class TrackingCallback : public CoefficientChangeCallback {
-  private:
-    LinearEqualityModule* d_linEq;
-  public:
-    TrackingCallback(LinearEqualityModule* le) : d_linEq(le) {}
-    void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) override
-    {
-      d_linEq->trackingCoefficientChange(ridx, nb, oldSgn, currSgn);
-    }
-    void multiplyRow(RowIndex ridx, int sgn) override
-    {
-      d_linEq->trackingMultiplyRow(ridx, sgn);
-    }
-    bool canUseRow(RowIndex ridx) const override
-    {
-      ArithVar basic = d_linEq->getTableau().rowIndexToBasic(ridx);
-      return d_linEq->basicIsTracked(basic);
-    }
- } d_trackCallback;
-
-  /**
-   * Selects the constraint for the variable v on the row for basic
-   * with the weakest possible constraint that is consistent with the surplus
-   * surplus.
-   */
-  ConstraintP weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v,
-                                const Rational& coeff, bool& anyWeakening, ArithVar basic) const;
-
-public:
-  /**
-   * Constructs a minimally weak conflict for the basic variable basicVar.
-   *
-   * Returns a constraint that is now in conflict.
-   */
-  ConstraintCP minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, FarkasConflictBuilder& rc) const;
-
-  /**
-   * Given a basic variable that is know to have a conflict on it,
-   * construct and return a conflict.
-   * Follows section 4.2 in the CAV06 paper.
-   */
-  inline ConstraintCP generateConflictAboveUpperBound(ArithVar conflictVar, FarkasConflictBuilder& rc) const {
-    return minimallyWeakConflict(true, conflictVar, rc);
-  }
-
-  inline ConstraintCP generateConflictBelowLowerBound(ArithVar conflictVar, FarkasConflictBuilder& rc) const {
-    return minimallyWeakConflict(false, conflictVar, rc);
-  }
-
-  /**
-   * Computes the sum of the upper/lower bound of row.
-   * The variable skip is not included in the sum.
-   */
-  DeltaRational computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const;
-
-public:
-  void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult);
-  void directlyAddToCoefficient(ArithVar row, ArithVar col, const Rational& mult);
-
-
-  /**
-   * Checks to make sure the assignment is consistent with the tableau.
-   * This code is for debugging.
-   */
-  void debugCheckTableau();
-
-  void debugCheckTracking();
-
-  /** Debugging information for a pivot. */
-  void debugPivot(ArithVar x_i, ArithVar x_j);
-
-  ArithVar minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const;
-
-  /**
-   * Returns true if there would be a conflict on this row after a pivot
-   * and update using its basic variable and one of the non-basic variables on
-   * the row.
-   */
-  bool willBeInConflictAfterPivot(const Tableau::Entry& entry, const DeltaRational& nbDiff, bool bToUB) const;
-  UpdateInfo mkConflictUpdate(const Tableau::Entry& entry, bool ub) const;
-
-  /**
-   * Looks more an update for fcSimplex on the nonbasic variable nb with the focus coefficient.
-   */
-  UpdateInfo speculativeUpdate(ArithVar nb, const Rational& focusCoeff, UpdatePreferenceFunction pref);
-
-private:
-
-  /**
-   * Examines the effects of pivoting the entries column variable
-   * with the row's basic variable and setting the variable s.t.
-   * the basic variable is equal to one of its bounds.
-   *
-   * If ub, then the basic variable will be equal its upper bound.
-   * If not ub,then the basic variable will be equal its lower bound.
-   *
-   * Returns iff this row will be in conflict after the pivot.
-   *
-   * If this is false, add the bound to the relevant heap.
-   * If the bound is +/-infinity, this is ignored.
-
-   *
-   * Returns true if this would be a conflict.
-   * If it returns false, this
-   */
-  bool accumulateBorder(const Tableau::Entry& entry, bool ub);
-
-  void handleBorders(UpdateInfo& selected, ArithVar nb, const Rational& focusCoeff, BorderHeap& heap, int minimumFixes, UpdatePreferenceFunction pref);
-  void pop_block(BorderHeap& heap, int& brokenInBlock, int& fixesRemaining, int& negErrorChange);
-  void clearSpeculative();
-  Rational updateCoefficient(BorderVec::const_iterator startBlock, BorderVec::const_iterator endBlock);
-
-private:
-  /** These fields are designed to be accessible to TheoryArith methods. */
-  class Statistics {
-  public:
-    IntStat d_statPivots, d_statUpdates;
-    TimerStat d_pivotTime;
-    TimerStat d_adjTime;
-
-    IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
-    TimerStat d_weakenTime;
-    TimerStat d_forceTime;
-
-    Statistics();
-  };
-  mutable Statistics d_statistics;
-
-};/* class LinearEqualityModule */
-
-struct Cand {
-  ArithVar d_nb;
-  uint32_t d_penalty;
-  int d_sgn;
-  const Rational* d_coeff;
-
-  Cand(ArithVar nb, uint32_t penalty, int s, const Rational* c) :
-    d_nb(nb), d_penalty(penalty), d_sgn(s), d_coeff(c){}
-};
-
-
-class CompPenaltyColLength {
-private:
-  LinearEqualityModule* d_mod;
-  const bool d_havePenalties;
-
- public:
-  CompPenaltyColLength(LinearEqualityModule* mod, bool havePenalties)
-      : d_mod(mod), d_havePenalties(havePenalties)
-  {
-  }
-
-  bool operator()(const Cand& x, const Cand& y) const {
-    if (x.d_penalty == y.d_penalty || !d_havePenalties)
-    {
-      return x.d_nb == d_mod->minBoundAndColLength(x.d_nb,y.d_nb);
-    }
-    else
-    {
-      return x.d_penalty < y.d_penalty;
-    }
-  }
-};
-
-class UpdateTrackingCallback : public BoundUpdateCallback {
-private:
-  LinearEqualityModule* d_mod;
-public:
-  UpdateTrackingCallback(LinearEqualityModule* mod): d_mod(mod){}
-  void operator()(ArithVar v, const BoundsInfo& bi) override
-  {
-    d_mod->includeBoundUpdate(v, bi);
-  }
-};
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/matrix.cpp b/src/theory/arith/matrix.cpp
deleted file mode 100644 (file)
index bcd4eb8..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Sparse matrix implementations for different types.
- */
-
-#include "theory/arith/matrix.h"
-
-using namespace std;
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-void NoEffectCCCB::update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) {}
-void NoEffectCCCB::multiplyRow(RowIndex ridx, int sgn){}
-bool NoEffectCCCB::canUseRow(RowIndex ridx) const { return false; }
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/matrix.h b/src/theory/arith/matrix.h
deleted file mode 100644 (file)
index 95ce022..0000000
+++ /dev/null
@@ -1,1002 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- * Sparse matrix implementations for different types.
- *
- * Sparse matrix implementations for different types.
- * This defines Matrix<T>, IntegerEqualityTables and Tableau.
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <queue>
-#include <utility>
-#include <vector>
-
-#include "base/output.h"
-#include "theory/arith/arithvar.h"
-#include "util/dense_map.h"
-#include "util/index.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-typedef Index EntryID;
-const EntryID ENTRYID_SENTINEL = std::numeric_limits<EntryID>::max();
-
-typedef Index RowIndex;
-const RowIndex ROW_INDEX_SENTINEL  = std::numeric_limits<RowIndex>::max();
-
-class CoefficientChangeCallback {
-public:
-  virtual ~CoefficientChangeCallback() {}
-  virtual void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) = 0;
-  virtual void multiplyRow(RowIndex ridx, int Sgn) = 0;
-  virtual bool canUseRow(RowIndex ridx) const = 0;
-};
-
-class NoEffectCCCB : public CoefficientChangeCallback {
-public:
- void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) override;
- void multiplyRow(RowIndex ridx, int Sgn) override;
- bool canUseRow(RowIndex ridx) const override;
-};
-
-template<class T>
-class MatrixEntry {
-private:
-  RowIndex d_rowIndex;
-  ArithVar d_colVar;
-
-  EntryID d_nextRow;
-  EntryID d_nextCol;
-
-  EntryID d_prevRow;
-  EntryID d_prevCol;
-
-  T d_coefficient;
-
-public:
-  MatrixEntry():
-    d_rowIndex(ROW_INDEX_SENTINEL),
-    d_colVar(ARITHVAR_SENTINEL),
-    d_nextRow(ENTRYID_SENTINEL),
-    d_nextCol(ENTRYID_SENTINEL),
-    d_prevRow(ENTRYID_SENTINEL),
-    d_prevCol(ENTRYID_SENTINEL),
-    d_coefficient()
-  {}
-
-  MatrixEntry(RowIndex row, ArithVar col, const T& coeff):
-     d_rowIndex(row),
-     d_colVar(col),
-     d_nextRow(ENTRYID_SENTINEL),
-     d_nextCol(ENTRYID_SENTINEL),
-     d_prevRow(ENTRYID_SENTINEL),
-     d_prevCol(ENTRYID_SENTINEL),
-     d_coefficient(coeff)
-  {}
-
-private:
-  bool unusedConsistent() const {
-    return
-      (d_rowIndex == ROW_INDEX_SENTINEL && d_colVar == ARITHVAR_SENTINEL) ||
-      (d_rowIndex != ROW_INDEX_SENTINEL && d_colVar != ARITHVAR_SENTINEL);
-  }
-
-public:
-
-  EntryID getNextRowEntryID() const {
-    return d_nextRow;
-  }
-
-  EntryID getNextColEntryID() const {
-    return d_nextCol;
-  }
-  EntryID getPrevRowEntryID() const {
-    return d_prevRow;
-  }
-
-  EntryID getPrevColEntryID() const {
-    return d_prevCol;
-  }
-
-  void setNextRowEntryID(EntryID id) {
-    d_nextRow = id;
-  }
-  void setNextColEntryID(EntryID id) {
-    d_nextCol = id;
-  }
-  void setPrevRowEntryID(EntryID id) {
-    d_prevRow = id;
-  }
-  void setPrevColEntryID(EntryID id) {
-    d_prevCol = id;
-  }
-
-  RowIndex getRowIndex() const{
-    return d_rowIndex;
-  }
-
-  ArithVar getColVar() const{
-    return d_colVar;
-  }
-
-  const T& getCoefficient() const {
-    return d_coefficient;
-  }
-
-  T& getCoefficient(){
-    return d_coefficient;
-  }
-
-  void setCoefficient(const T& t){
-    d_coefficient = t;
-  }
-
-  void markBlank() {
-    d_rowIndex = ROW_INDEX_SENTINEL;
-    d_colVar = ARITHVAR_SENTINEL;
-  }
-
-  bool blank() const{
-    Assert(unusedConsistent());
-
-    return d_rowIndex == ROW_INDEX_SENTINEL;
-  }
-}; /* class MatrixEntry<T> */
-
-template<class T>
-class MatrixEntryVector {
-private:
-  typedef MatrixEntry<T> EntryType;
-  typedef std::vector<EntryType> EntryArray;
-
-  EntryArray d_entries;
-  std::queue<EntryID> d_freedEntries;
-
-  uint32_t d_size;
-
-public:
-  MatrixEntryVector():
-    d_entries(), d_freedEntries(), d_size(0)
-  {}
-
-  const EntryType& operator[](EntryID id) const{
-    Assert(inBounds(id));
-    return d_entries[id];
-  }
-
-  EntryType& get(EntryID id){
-    Assert(inBounds(id));
-    return d_entries[id];
-  }
-
-  void freeEntry(EntryID id){
-    Assert(get(id).blank());
-    Assert(d_size > 0);
-
-    d_freedEntries.push(id);
-    --d_size;
-  }
-
-  EntryID newEntry(){
-    EntryID newId;
-    if(d_freedEntries.empty()){
-      newId = d_entries.size();
-      d_entries.push_back(MatrixEntry<T>());
-    }else{
-      newId = d_freedEntries.front();
-      d_freedEntries.pop();
-    }
-    ++d_size;
-    return newId;
-  }
-
-  uint32_t size() const{ return d_size; }
-  uint32_t capacity() const{ return d_entries.capacity(); }
-
-
-private:
-  bool inBounds(EntryID id) const{
-    return id <  d_entries.size();
-  }
-}; /* class MatrixEntryVector<T> */
-
-template <class T, bool isRow>
-class MatrixVector {
-private:
-  EntryID d_head;
-  uint32_t d_size;
-
-  MatrixEntryVector<T>* d_entries;
-
-  class Iterator {
-  private:
-    EntryID d_curr;
-    const MatrixEntryVector<T>* d_entries;
-
-  public:
-    Iterator(EntryID start, const MatrixEntryVector<T>* entries) :
-      d_curr(start), d_entries(entries)
-    {}
-
-  public:
-
-    EntryID getID() const {
-      return d_curr;
-    }
-
-    const MatrixEntry<T>& operator*() const{
-      Assert(!atEnd());
-      return (*d_entries)[d_curr];
-    }
-
-    Iterator& operator++(){
-      Assert(!atEnd());
-      const MatrixEntry<T>& entry = (*d_entries)[d_curr];
-      d_curr = isRow ? entry.getNextRowEntryID() : entry.getNextColEntryID();
-      return *this;
-    }
-
-    bool atEnd() const {
-      return d_curr == ENTRYID_SENTINEL;
-    }
-
-    bool operator==(const Iterator& i) const{
-      return d_curr == i.d_curr && d_entries == i.d_entries;
-    }
-
-    bool operator!=(const Iterator& i) const{
-      return !(d_curr == i.d_curr && d_entries == i.d_entries);
-    }
-  }; /* class MatrixVector<T, isRow>::Iterator */
-
-public:
-  MatrixVector(MatrixEntryVector<T>* mev)
-    : d_head(ENTRYID_SENTINEL), d_size(0), d_entries(mev)
-  {}
-
-  MatrixVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
-    : d_head(head), d_size(size), d_entries(mev)
-  {}
-
-  typedef Iterator const_iterator;
-  const_iterator begin() const {
-    return Iterator(d_head, d_entries);
-  }
-  const_iterator end() const {
-    return Iterator(ENTRYID_SENTINEL, d_entries);
-  }
-
-  EntryID getHead() const { return d_head; }
-
-  uint32_t getSize() const { return d_size; }
-
-  void insert(EntryID newId){
-    if(isRow){
-      d_entries->get(newId).setNextRowEntryID(d_head);
-
-      if(d_head != ENTRYID_SENTINEL){
-        d_entries->get(d_head).setPrevRowEntryID(newId);
-      }
-    }else{
-      d_entries->get(newId).setNextColEntryID(d_head);
-
-      if(d_head != ENTRYID_SENTINEL){
-        d_entries->get(d_head).setPrevColEntryID(newId);
-      }
-    }
-
-    d_head = newId;
-    ++d_size;
-  }
-  void remove(EntryID id){
-    Assert(d_size > 0);
-    --d_size;
-    if(isRow){
-      EntryID prevRow = d_entries->get(id).getPrevRowEntryID();
-      EntryID nextRow = d_entries->get(id).getNextRowEntryID();
-
-      if(d_head == id){
-        d_head = nextRow;
-      }
-      if(prevRow != ENTRYID_SENTINEL){
-        d_entries->get(prevRow).setNextRowEntryID(nextRow);
-      }
-      if(nextRow != ENTRYID_SENTINEL){
-        d_entries->get(nextRow).setPrevRowEntryID(prevRow);
-      }
-    }else{
-      EntryID prevCol = d_entries->get(id).getPrevColEntryID();
-      EntryID nextCol = d_entries->get(id).getNextColEntryID();
-
-      if(d_head == id){
-        d_head = nextCol;
-      }
-
-      if(prevCol != ENTRYID_SENTINEL){
-        d_entries->get(prevCol).setNextColEntryID(nextCol);
-      }
-      if(nextCol != ENTRYID_SENTINEL){
-        d_entries->get(nextCol).setPrevColEntryID(prevCol);
-      }
-    }
-  }
-}; /* class MatrixVector<T, isRow> */
-
-template <class T>
-  class RowVector : public MatrixVector<T, true>
-{
-private:
-  typedef MatrixVector<T, true> SuperT;
-public:
-  typedef typename SuperT::const_iterator const_iterator;
-
-  RowVector(MatrixEntryVector<T>* mev) : SuperT(mev){}
-  RowVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
-    : SuperT(head, size, mev){}
-};/* class RowVector<T> */
-
-template <class T>
-  class ColumnVector : public MatrixVector<T, false>
-{
-private:
-  typedef MatrixVector<T, false> SuperT;
-public:
-  typedef typename SuperT::const_iterator const_iterator;
-
-  ColumnVector(MatrixEntryVector<T>* mev) : SuperT(mev){}
-  ColumnVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
-    : SuperT(head, size, mev){}
-};/* class ColumnVector<T> */
-
-template <class T>
-class Matrix {
-public:
-  typedef MatrixEntry<T> Entry;
-
-protected:
- typedef cvc5::internal::theory::arith::RowVector<T> RowVectorT;
- typedef cvc5::internal::theory::arith::ColumnVector<T> ColumnVectorT;
-
-public:
-  typedef typename RowVectorT::const_iterator RowIterator;
-  typedef typename ColumnVectorT::const_iterator ColIterator;
-
-protected:
-  // RowTable : RowID |-> RowVector
-  typedef std::vector< RowVectorT > RowTable;
-  RowTable d_rows;
-
-  // ColumnTable : ArithVar |-> ColumnVector
-  typedef std::vector< ColumnVectorT > ColumnTable;
-  ColumnTable d_columns;
-
-  /* The merge buffer is used to store a row in order to optimize row addition. */
-  typedef std::pair<EntryID, bool> PosUsedPair;
-  typedef DenseMap< PosUsedPair > RowToPosUsedPairMap;
-  RowToPosUsedPairMap d_mergeBuffer;
-
-  /* The row that is in the merge buffer. */
-  RowIndex d_rowInMergeBuffer;
-
-  uint32_t d_entriesInUse;
-  MatrixEntryVector<T> d_entries;
-
-  std::vector<RowIndex> d_pool;
-
-  T d_zero;
-
-public:
-  /**
-   * Constructs an empty Matrix.
-   */
-  Matrix()
-  : d_rows(),
-    d_columns(),
-    d_mergeBuffer(),
-    d_rowInMergeBuffer(ROW_INDEX_SENTINEL),
-    d_entriesInUse(0),
-    d_entries(),
-    d_zero(0)
-  {}
-
-  Matrix(const T& zero)
-  : d_rows(),
-    d_columns(),
-    d_mergeBuffer(),
-    d_rowInMergeBuffer(ROW_INDEX_SENTINEL),
-    d_entriesInUse(0),
-    d_entries(),
-    d_zero(zero)
-  {}
-
-  Matrix(const Matrix& m)
-  : d_rows(),
-    d_columns(),
-    d_mergeBuffer(m.d_mergeBuffer),
-    d_rowInMergeBuffer(m.d_rowInMergeBuffer),
-    d_entriesInUse(m.d_entriesInUse),
-    d_entries(m.d_entries),
-    d_zero(m.d_zero)
-  {
-    d_columns.clear();
-    for(typename ColumnTable::const_iterator c=m.d_columns.begin(), cend = m.d_columns.end(); c!=cend; ++c){
-      const ColumnVectorT& col = *c;
-      d_columns.push_back(ColumnVectorT(col.getHead(),col.getSize(),&d_entries));
-    }
-    d_rows.clear();
-    for(typename RowTable::const_iterator r=m.d_rows.begin(), rend = m.d_rows.end(); r!=rend; ++r){
-      const RowVectorT& row = *r;
-      d_rows.push_back(RowVectorT(row.getHead(),row.getSize(),&d_entries));
-    }
-  }
-
-  Matrix& operator=(const Matrix& m){
-    d_mergeBuffer = (m.d_mergeBuffer);
-    d_rowInMergeBuffer = (m.d_rowInMergeBuffer);
-    d_entriesInUse = (m.d_entriesInUse);
-    d_entries = (m.d_entries);
-    d_zero = (m.d_zero);
-    d_columns.clear();
-    for(typename ColumnTable::const_iterator c=m.d_columns.begin(), cend = m.d_columns.end(); c!=cend; ++c){
-      const ColumnVector<T>& col = *c;
-      d_columns.push_back(ColumnVector<T>(col.getHead(), col.getSize(), &d_entries));
-    }
-    d_rows.clear();
-    for(typename RowTable::const_iterator r=m.d_rows.begin(), rend = m.d_rows.end(); r!=rend; ++r){
-      const RowVector<T>& row = *r;
-      d_rows.push_back(RowVector<T>(row.getHead(), row.getSize(), &d_entries));
-    }
-    return *this;
-  }
-
-protected:
-
-  void addEntry(RowIndex row, ArithVar col, const T& coeff){
-    Trace("tableau") << "addEntry(" << row << "," << col <<"," << coeff << ")" << std::endl;
-
-    Assert(coeff != 0);
-    Assert(row < d_rows.size());
-    Assert(col < d_columns.size());
-
-    EntryID newId = d_entries.newEntry();
-    Entry& newEntry = d_entries.get(newId);
-    newEntry = Entry(row, col, coeff);
-
-    Assert(newEntry.getCoefficient() != 0);
-
-    ++d_entriesInUse;
-
-    d_rows[row].insert(newId);
-    d_columns[col].insert(newId);
-  }
-
-  void removeEntry(EntryID id){
-    Assert(d_entriesInUse > 0);
-    --d_entriesInUse;
-
-    Entry& entry = d_entries.get(id);
-
-    RowIndex ridx = entry.getRowIndex();
-    ArithVar col = entry.getColVar();
-
-    Assert(d_rows[ridx].getSize() > 0);
-    Assert(d_columns[col].getSize() > 0);
-
-    d_rows[ridx].remove(id);
-    d_columns[col].remove(id);
-
-    entry.markBlank();
-
-    d_entries.freeEntry(id);
-  }
-
- private:
-  RowIndex requestRowIndex(){
-    if(d_pool.empty()){
-      RowIndex ridx = d_rows.size();
-      d_rows.push_back(RowVectorT(&d_entries));
-      return ridx;
-    }else{
-      RowIndex rid = d_pool.back();
-      d_pool.pop_back();
-      return rid;
-    }
-  }
-
-  void releaseRowIndex(RowIndex rid){
-    d_pool.push_back(rid);
-  }
-
-public:
-
-  size_t getNumRows() const {
-    return d_rows.size();
-  }
-
-  size_t getNumColumns() const {
-    return d_columns.size();
-  }
-
-  void increaseSize(){
-    d_columns.push_back(ColumnVector<T>(&d_entries));
-  }
-
-  void increaseSizeTo(size_t s){
-    while(getNumColumns() < s){
-      increaseSize();
-    }
-  }
-
-  const RowVector<T>& getRow(RowIndex r) const {
-    Assert(r < d_rows.size());
-    return d_rows[r];
-  }
-
-  const ColumnVector<T>& getColumn(ArithVar v) const {
-    Assert(v < d_columns.size());
-    return d_columns[v];
-  }
-
-  uint32_t getRowLength(RowIndex r) const{
-    return getRow(r).getSize();
-  }
-
-  uint32_t getColLength(ArithVar x) const{
-    return getColumn(x).getSize();
-  }
-
-  /**
-   * Adds a row to the matrix.
-   * The new row is equivalent to:
-   *   \f$\sum_i\f$ coeffs[i] * variables[i]
-   */
-  RowIndex addRow(const std::vector<T>& coeffs,
-                  const std::vector<ArithVar>& variables){
-
-    RowIndex ridx = requestRowIndex();
-
-    //RowIndex ridx = d_rows.size();
-    //d_rows.push_back(RowVectorT(&d_entries));
-
-    typename std::vector<T>::const_iterator coeffIter = coeffs.begin();
-    std::vector<ArithVar>::const_iterator varsIter = variables.begin();
-    std::vector<ArithVar>::const_iterator varsEnd = variables.end();
-
-    for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
-      const T& coeff = *coeffIter;
-      ArithVar var_i = *varsIter;
-      Assert(var_i < getNumColumns());
-      addEntry(ridx, var_i, coeff);
-    }
-
-    return ridx;
-  }
-
-
-  void loadRowIntoBuffer(RowIndex rid){
-    Assert(d_mergeBuffer.empty());
-    Assert(d_rowInMergeBuffer == ROW_INDEX_SENTINEL);
-
-    RowIterator i = getRow(rid).begin(), i_end = getRow(rid).end();
-    for(; i != i_end; ++i){
-      EntryID id = i.getID();
-      const MatrixEntry<T>& entry = *i;
-      ArithVar colVar = entry.getColVar();
-      d_mergeBuffer.set(colVar, std::make_pair(id, false));
-    }
-
-    d_rowInMergeBuffer = rid;
-  }
-
-  void clearBuffer() {
-    Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
-
-    d_rowInMergeBuffer = ROW_INDEX_SENTINEL;
-    d_mergeBuffer.purge();
-  }
-
-  /* to *= mult */
-  void multiplyRowByConstant(RowIndex to, const T& mult){
-    RowIterator i = getRow(to).begin();
-    RowIterator i_end = getRow(to).end();
-    for( ; i != i_end; ++i){
-      EntryID id = i.getID();
-      Entry& entry = d_entries.get(id);
-      T& coeff = entry.getCoefficient();
-      coeff *= mult;
-    }
-  }
-
-  /**  to += mult * from.
-   * Use the more efficient rowPlusBufferTimesConstant() for
-   * repeated use.
-   */
-  void rowPlusRowTimesConstant(RowIndex to, RowIndex from, const T& mult){
-    Assert(to != from);
-    loadRowIntoBuffer(from);
-    rowPlusBufferTimesConstant(to, mult);
-    clearBuffer();
-  }
-
-  /**  to += mult * buffer.
-   * Invalidates coefficients on the row.
-   * (mult should never be a direct copy of a coefficient!)
-   */
-  void rowPlusBufferTimesConstant(RowIndex to, const T& mult){
-    Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
-    Assert(to != ROW_INDEX_SENTINEL);
-
-    Trace("tableau") << "rowPlusRowTimesConstant("
-                     << to << "," << mult << "," << d_rowInMergeBuffer << ")"
-                     << std::endl;
-
-    Assert(debugNoZeroCoefficients(to));
-    Assert(debugNoZeroCoefficients(d_rowInMergeBuffer));
-
-    Assert(mult != 0);
-
-    RowIterator i = getRow(to).begin();
-    RowIterator i_end = getRow(to).end();
-    while(i != i_end){
-      EntryID id = i.getID();
-      Entry& entry = d_entries.get(id);
-      ArithVar colVar = entry.getColVar();
-
-      ++i;
-
-      if(d_mergeBuffer.isKey(colVar)){
-        EntryID bufferEntry = d_mergeBuffer[colVar].first;
-        Assert(!d_mergeBuffer[colVar].second);
-        d_mergeBuffer.get(colVar).second = true;
-
-        const Entry& other = d_entries.get(bufferEntry);
-        T& coeff = entry.getCoefficient();
-        coeff += mult * other.getCoefficient();
-
-        if(coeff.sgn() == 0){
-          removeEntry(id);
-        }
-      }
-    }
-
-    i = getRow(d_rowInMergeBuffer).begin();
-    i_end = getRow(d_rowInMergeBuffer).end();
-
-    for(; i != i_end; ++i){
-      const Entry& entry = *i;
-      ArithVar colVar = entry.getColVar();
-
-      if(d_mergeBuffer[colVar].second){
-        d_mergeBuffer.get(colVar).second = false;
-      }else{
-        Assert(!(d_mergeBuffer[colVar]).second);
-        T newCoeff =  mult * entry.getCoefficient();
-        addEntry(to, colVar, newCoeff);
-      }
-    }
-
-    Assert(mergeBufferIsClear());
-
-    if(TraceIsOn("matrix")) { printMatrix(); }
-  }
-
-  /**  to += mult * buffer. */
-  void rowPlusBufferTimesConstant(RowIndex to, const T& mult, CoefficientChangeCallback& cb){
-    Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
-    Assert(to != ROW_INDEX_SENTINEL);
-
-    Trace("tableau") << "rowPlusRowTimesConstant("
-                     << to << "," << mult << "," << d_rowInMergeBuffer << ")"
-                     << std::endl;
-
-    Assert(debugNoZeroCoefficients(to));
-    Assert(debugNoZeroCoefficients(d_rowInMergeBuffer));
-
-    Assert(mult != 0);
-
-    RowIterator i = getRow(to).begin();
-    RowIterator i_end = getRow(to).end();
-    while(i != i_end){
-      EntryID id = i.getID();
-      Entry& entry = d_entries.get(id);
-      ArithVar colVar = entry.getColVar();
-
-      ++i;
-
-      if(d_mergeBuffer.isKey(colVar)){
-        EntryID bufferEntry = d_mergeBuffer[colVar].first;
-        Assert(!d_mergeBuffer[colVar].second);
-        d_mergeBuffer.get(colVar).second = true;
-
-        const Entry& other = d_entries.get(bufferEntry);
-        T& coeff = entry.getCoefficient();
-        int coeffOldSgn = coeff.sgn();
-        coeff += mult * other.getCoefficient();
-        int coeffNewSgn = coeff.sgn();
-
-        if(coeffOldSgn != coeffNewSgn){
-          cb.update(to, colVar, coeffOldSgn,  coeffNewSgn);
-
-          if(coeffNewSgn == 0){
-            removeEntry(id);
-          }
-        }
-      }
-    }
-
-    i = getRow(d_rowInMergeBuffer).begin();
-    i_end = getRow(d_rowInMergeBuffer).end();
-
-    for(; i != i_end; ++i){
-      const Entry& entry = *i;
-      ArithVar colVar = entry.getColVar();
-
-      if(d_mergeBuffer[colVar].second){
-        d_mergeBuffer.get(colVar).second = false;
-      }else{
-        Assert(!(d_mergeBuffer[colVar]).second);
-        T newCoeff =  mult * entry.getCoefficient();
-        addEntry(to, colVar, newCoeff);
-
-        cb.update(to, colVar, 0,  newCoeff.sgn());
-      }
-    }
-
-    Assert(mergeBufferIsClear());
-
-    if(TraceIsOn("matrix")) { printMatrix(); }
-  }
-
-  bool mergeBufferIsClear() const{
-    RowToPosUsedPairMap::const_iterator i = d_mergeBuffer.begin();
-    RowToPosUsedPairMap::const_iterator i_end = d_mergeBuffer.end();
-    for(; i != i_end; ++i){
-      RowIndex rid = *i;
-      if(d_mergeBuffer[rid].second){
-        return false;
-      }
-    }
-    return true;
-  }
-
-protected:
-
-  EntryID findOnRow(RowIndex rid, ArithVar column) const {
-    RowIterator i = d_rows[rid].begin(), i_end = d_rows[rid].end();
-    for(; i != i_end; ++i){
-      EntryID id = i.getID();
-      const MatrixEntry<T>& entry = *i;
-      ArithVar colVar = entry.getColVar();
-
-      if(colVar == column){
-        return id;
-      }
-    }
-    return ENTRYID_SENTINEL;
-  }
-
-  EntryID findOnCol(RowIndex rid, ArithVar column) const{
-    ColIterator i = d_columns[column].begin(), i_end = d_columns[column].end();
-    for(; i != i_end; ++i){
-      EntryID id = i.getID();
-      const MatrixEntry<T>& entry = *i;
-      RowIndex currRow = entry.getRowIndex();
-
-      if(currRow == rid){
-        return id;
-      }
-    }
-    return ENTRYID_SENTINEL;
-  }
-
-  EntryID findEntryID(RowIndex rid, ArithVar col) const{
-    bool colIsShorter = getColLength(col) < getRowLength(rid);
-    EntryID id = colIsShorter ? findOnCol(rid, col) : findOnRow(rid,col);
-    return id;
-  }
-  MatrixEntry<T> d_failedFind;
-public:
-
-  /** If the find fails, isUnused is true on the entry. */
-  const MatrixEntry<T>& findEntry(RowIndex rid, ArithVar col) const{
-    EntryID id = findEntryID(rid, col);
-    if(id == ENTRYID_SENTINEL){
-      return d_failedFind;
-    }else{
-      return d_entries[id];
-    }
-  }
-
-  /**
-   * Prints the contents of the Matrix to Trace("matrix")
-   */
-  void printMatrix(std::ostream& out) const {
-    out << "Matrix::printMatrix"  << std::endl;
-
-    for(RowIndex i = 0, N = d_rows.size(); i < N; ++i){
-      printRow(i, out);
-    }
-  }
-  void printMatrix() const {
-    printMatrix(Trace("matrix"));
-  }
-
-  void printRow(RowIndex rid, std::ostream& out) const {
-    out << "{" << rid << ":";
-    const RowVector<T>& row = getRow(rid);
-    RowIterator i = row.begin();
-    RowIterator i_end = row.end();
-    for(; i != i_end; ++i){
-      printEntry(*i, out);
-      out << ",";
-    }
-    out << "}" << std::endl;
-  }
-  void printRow(RowIndex rid) const {
-    printRow(rid, Trace("matrix"));
-  }
-
-  void printEntry(const MatrixEntry<T>& entry, std::ostream& out) const {
-    out << entry.getColVar() << "*" << entry.getCoefficient();
-  }
-  void printEntry(const MatrixEntry<T>& entry) const {
-    printEntry(entry, Trace("matrix"));
-  }
-public:
-  uint32_t size() const {
-    return d_entriesInUse;
-  }
-  uint32_t getNumEntriesInTableau() const {
-    return d_entries.size();
-  }
-  uint32_t getEntryCapacity() const {
-    return d_entries.capacity();
-  }
-
-  void manipulateRowEntry(RowIndex row, ArithVar col, const T& c, CoefficientChangeCallback& cb){
-    int coeffOldSgn;
-    int coeffNewSgn;
-
-    EntryID id = findEntryID(row, col);
-    if(id == ENTRYID_SENTINEL){
-      coeffOldSgn = 0;
-      addEntry(row, col, c);
-      coeffNewSgn = c.sgn();
-    }else{
-      Entry& e = d_entries.get(id);
-      T& t = e.getCoefficient();
-      coeffOldSgn = t.sgn();
-      t += c;
-      coeffNewSgn = t.sgn();
-    }
-
-    if(coeffOldSgn != coeffNewSgn){
-      cb.update(row, col, coeffOldSgn,  coeffNewSgn);
-    }
-    if(coeffNewSgn == 0){
-      removeEntry(id);
-    }
-  }
-
-  void removeRow(RowIndex rid){
-    RowIterator i = getRow(rid).begin();
-    RowIterator i_end = getRow(rid).end();
-    for(; i != i_end; ++i){
-      EntryID id = i.getID();
-      removeEntry(id);
-    }
-    releaseRowIndex(rid);
-  }
-
-  double densityMeasure() const{
-    Assert(numNonZeroEntriesByRow() == numNonZeroEntries());
-    Assert(numNonZeroEntriesByCol() == numNonZeroEntries());
-
-    uint32_t n = getNumRows();
-    if(n == 0){
-      return 1.0;
-    }else {
-      uint32_t s = numNonZeroEntries();
-      uint32_t m = d_columns.size();
-      uint32_t divisor = (n *(m - n + 1));
-
-      Assert(n >= 1);
-      Assert(m >= n);
-      Assert(divisor > 0);
-      Assert(divisor >= s);
-
-      return (double(s)) / divisor;
-    }
-  }
-
-  void loadSignQueries(RowIndex rid, DenseMap<int>& target) const{
-
-    RowIterator i = getRow(rid).begin(), i_end = getRow(rid).end();
-    for(; i != i_end; ++i){
-      const MatrixEntry<T>& entry = *i;
-      target.set(entry.getColVar(), entry.getCoefficient().sgn());
-    }
-  }
-
-protected:
-  uint32_t numNonZeroEntries() const { return size(); }
-
-  uint32_t numNonZeroEntriesByRow() const {
-    uint32_t rowSum = 0;
-    for(RowIndex rid = 0, N = d_rows.size(); rid < N; ++rid){
-      rowSum += getRowLength(rid);
-    }
-    return rowSum;
-  }
-
-  uint32_t numNonZeroEntriesByCol() const {
-    uint32_t colSum = 0;
-    for(ArithVar v = 0, N = d_columns.size(); v < N; ++v){
-      colSum += getColLength(v);
-    }
-    return colSum;
-  }
-
-
-  bool debugNoZeroCoefficients(RowIndex ridx){
-    for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
-      const Entry& entry = *i;
-      if(entry.getCoefficient() == 0){
-        return false;
-      }
-    }
-    return true;
-  }
-  bool debugMatchingCountsForRow(RowIndex ridx){
-    for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
-      const Entry& entry = *i;
-      ArithVar colVar = entry.getColVar();
-      uint32_t count = debugCountColLength(colVar);
-      Trace("tableau") << "debugMatchingCountsForRow "
-                       << ridx << ":" << colVar << " " << count
-                       <<" "<< getColLength(colVar) << std::endl;
-      if( count != getColLength(colVar) ){
-        return false;
-      }
-    }
-    return true;
-  }
-
-  uint32_t debugCountColLength(ArithVar var){
-    Trace("tableau") << var << " ";
-    uint32_t count = 0;
-    for(ColIterator i=getColumn(var).begin(); !i.atEnd(); ++i){
-      const Entry& entry = *i;
-      Trace("tableau") << "(" << entry.getRowIndex() << ", " << i.getID() << ") ";
-      ++count;
-    }
-    Trace("tableau") << std::endl;
-    return count;
-  }
-  uint32_t debugCountRowLength(RowIndex ridx){
-    uint32_t count = 0;
-    for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
-      ++count;
-    }
-    return count;
-  }
-
-};/* class Matrix<T> */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
index 8b6e0c303aa183c7df6b2c21fa0454763a43de5a..1a19216b514d73d5f7b5e24d0bea1736f71db988 100644 (file)
@@ -23,7 +23,7 @@
 #include "theory/arith/arith_msum.h"
 #include "theory/arith/inference_manager.h"
 #include "theory/arith/nl/poly_conversion.h"
-#include "theory/arith/normal_form.h"
+#include "theory/arith/linear/normal_form.h"
 #include "theory/rewriter.h"
 #include "util/poly_util.h"
 
@@ -89,7 +89,7 @@ std::vector<Candidate> ICPSolver::constructCandidates(const Node& n)
   {
     return {};
   }
-  auto comp = Comparison::parseNormalForm(tmp).decompose(false);
+  auto comp = linear::Comparison::parseNormalForm(tmp).decompose(false);
   Kind k = std::get<1>(comp);
   if (k == Kind::DISTINCT)
   {
diff --git a/src/theory/arith/normal_form.cpp b/src/theory/arith/normal_form.cpp
deleted file mode 100644 (file)
index b7b1003..0000000
+++ /dev/null
@@ -1,1427 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-#include "theory/arith/normal_form.h"
-
-#include <list>
-
-#include "base/output.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/theory.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-Constant Constant::mkConstant(const Rational& rat) {
-  return Constant(mkRationalNode(rat));
-}
-
-size_t Variable::getComplexity() const{
-  return 1u;
-}
-
-size_t VarList::getComplexity() const{
-  if(empty()){
-    return 1;
-  }else if(singleton()){
-    return 1;
-  }else{
-    return size() + 1;
-  }
-}
-
-size_t Monomial::getComplexity() const{
-  return getConstant().getComplexity() + getVarList().getComplexity();
-}
-
-size_t Polynomial::getComplexity() const{
-  size_t cmp = 0;
-  iterator i = begin(), e = end();
-  for(; i != e; ++i){
-    Monomial m = *i;
-    cmp += m.getComplexity();
-  }
-  return cmp;
-}
-
-size_t Constant::getComplexity() const{
-  return getValue().complexity();
-}
-
-bool Variable::isLeafMember(Node n){
-  return (!isRelationOperator(n.getKind())) &&
-    (Theory::isLeafOf(n, theory::THEORY_ARITH));
-}
-
-VarList::VarList(Node n) : NodeWrapper(n) { Assert(isSorted(begin(), end())); }
-
-bool Variable::isIAndMember(Node n)
-{
-  return n.getKind() == kind::IAND && Polynomial::isMember(n[0])
-         && Polynomial::isMember(n[1]);
-}
-
-bool Variable::isPow2Member(Node n)
-{
-  return n.getKind() == kind::POW2 && Polynomial::isMember(n[0]);
-}
-
-bool Variable::isDivMember(Node n){
-  switch(n.getKind()){
-  case kind::DIVISION:
-  case kind::INTS_DIVISION:
-  case kind::INTS_MODULUS:
-  case kind::DIVISION_TOTAL:
-  case kind::INTS_DIVISION_TOTAL:
-  case kind::INTS_MODULUS_TOTAL:
-    return Polynomial::isMember(n[0]) && Polynomial::isMember(n[1]);
-  default:
-    return false;
-  }
-}
-
-bool Variable::isTranscendentalMember(Node n) {
-  switch(n.getKind()){
-  case kind::EXPONENTIAL:
-  case kind::SINE:
-  case kind::COSINE:
-  case kind::TANGENT:
-  case kind::COSECANT:
-  case kind::SECANT:
-  case kind::COTANGENT:
-  case kind::ARCSINE:
-  case kind::ARCCOSINE:
-  case kind::ARCTANGENT:
-  case kind::ARCCOSECANT:
-  case kind::ARCSECANT:
-  case kind::ARCCOTANGENT:
-  case kind::SQRT: return Polynomial::isMember(n[0]);
-  case kind::PI:
-    return true;
-  default:
-    return false;
-  }
-}
-
-
-bool VarList::isSorted(iterator start, iterator end) {
-  return std::is_sorted(start, end);
-}
-
-bool VarList::isMember(Node n) {
-  if(Variable::isMember(n)) {
-    return true;
-  }
-  if(n.getKind() == kind::NONLINEAR_MULT) {
-    Node::iterator curr = n.begin(), end = n.end();
-    Node prev = *curr;
-    if(!Variable::isMember(prev)) return false;
-
-    Variable::VariableNodeCmp cmp;
-
-    while( (++curr) != end) {
-      if(!Variable::isMember(*curr)) return false;
-      // prev <= curr : accept
-      // !(prev <= curr) : reject
-      // !(!(prev > curr)) : reject
-      // curr < prev : reject
-      if((cmp(*curr, prev))) return false;
-      prev = *curr;
-    }
-    return true;
-  } else {
-    return false;
-  }
-}
-
-int VarList::cmp(const VarList& vl) const {
-  int dif = this->size() - vl.size();
-  if (dif == 0) {
-    if(this->getNode() == vl.getNode()) {
-      return 0;
-    }
-
-    Assert(!empty());
-    Assert(!vl.empty());
-    if(this->size() == 1){
-      return Variable::VariableNodeCmp::cmp(this->getNode(), vl.getNode());
-    }
-
-
-    internal_iterator ii=this->internalBegin(), ie=this->internalEnd();
-    internal_iterator ci=vl.internalBegin(), ce=vl.internalEnd();
-    for(; ii != ie; ++ii, ++ci){
-      Node vi = *ii;
-      Node vc = *ci;
-      int tmp = Variable::VariableNodeCmp::cmp(vi, vc);
-      if(tmp != 0){
-        return tmp;
-      }
-    }
-    Unreachable();
-  } else if(dif < 0) {
-    return -1;
-  } else {
-    return 1;
-  }
-}
-
-VarList VarList::parseVarList(Node n) {
-  return VarList(n);
-  // if(Variable::isMember(n)) {
-  //   return VarList(Variable(n));
-  // } else {
-  //   Assert(n.getKind() == kind::MULT);
-  //   for(Node::iterator i=n.begin(), end = n.end(); i!=end; ++i) {
-  //     Assert(Variable::isMember(*i));
-  //   }
-  //   return VarList(n);
-  // }
-}
-
-VarList VarList::operator*(const VarList& other) const {
-  if(this->empty()) {
-    return other;
-  } else if(other.empty()) {
-    return *this;
-  } else {
-    vector<Node> result;
-
-    internal_iterator
-      thisBegin = this->internalBegin(),
-      thisEnd = this->internalEnd(),
-      otherBegin = other.internalBegin(),
-      otherEnd = other.internalEnd();
-
-    Variable::VariableNodeCmp cmp;
-    std::merge(thisBegin, thisEnd, otherBegin, otherEnd, std::back_inserter(result), cmp);
-
-    Assert(result.size() >= 2);
-    Node mult = NodeManager::currentNM()->mkNode(kind::NONLINEAR_MULT, result);
-    return VarList::parseVarList(mult);
-  }
-}
-
-bool Monomial::isMember(TNode n){
-  if(n.getKind() == kind::CONST_RATIONAL) {
-    return true;
-  } else if(multStructured(n)) {
-    return VarList::isMember(n[1]);
-  } else {
-    return VarList::isMember(n);
-  }
-}
-
-Monomial Monomial::mkMonomial(const Constant& c, const VarList& vl) {
-  if(c.isZero() || vl.empty() ) {
-    return Monomial(c);
-  } else if(c.isOne()) {
-    return Monomial(vl);
-  } else {
-    return Monomial(c, vl);
-  }
-}
-
-Monomial Monomial::mkMonomial(const VarList& vl) {
-  // acts like Monomial::mkMonomial( 1, vl)
-  if( vl.empty() ) {
-    return Monomial::mkOne();
-  } else if(true){
-    return Monomial(vl);
-  }
-}
-
-Monomial Monomial::parseMonomial(Node n) {
-  if(n.getKind() == kind::CONST_RATIONAL) {
-    return Monomial(Constant(n));
-  } else if(multStructured(n)) {
-    return Monomial::mkMonomial(Constant(n[0]),VarList::parseVarList(n[1]));
-  } else {
-    return Monomial(VarList::parseVarList(n));
-  }
-}
-Monomial Monomial::operator*(const Rational& q) const {
-  if(q.isZero()){
-    return mkZero();
-  }else{
-    Constant newConstant = this->getConstant() * q;
-    return Monomial::mkMonomial(newConstant, getVarList());
-  }
-}
-
-Monomial Monomial::operator*(const Constant& c) const {
-  return (*this) * c.getValue();
-  // if(c.isZero()){
-  //   return mkZero();
-  // }else{
-  //   Constant newConstant = this->getConstant() * c;
-  //   return Monomial::mkMonomial(newConstant, getVarList());
-  // }
-}
-
-Monomial Monomial::operator*(const Monomial& mono) const {
-  Constant newConstant = this->getConstant() * mono.getConstant();
-  VarList newVL = this->getVarList() * mono.getVarList();
-
-  return Monomial::mkMonomial(newConstant, newVL);
-}
-
-// vector<Monomial> Monomial::sumLikeTerms(const std::vector<Monomial> & monos)
-// {
-//   Assert(isSorted(monos));
-//   vector<Monomial> outMonomials;
-//   typedef vector<Monomial>::const_iterator iterator;
-//   for(iterator rangeIter = monos.begin(), end=monos.end(); rangeIter != end;)
-//   {
-//     Rational constant = (*rangeIter).getConstant().getValue();
-//     VarList varList  = (*rangeIter).getVarList();
-//     ++rangeIter;
-//     while(rangeIter != end && varList == (*rangeIter).getVarList()) {
-//       constant += (*rangeIter).getConstant().getValue();
-//       ++rangeIter;
-//     }
-//     if(constant != 0) {
-//       Constant asConstant = Constant::mkConstant(constant);
-//       Monomial nonZero = Monomial::mkMonomial(asConstant, varList);
-//       outMonomials.push_back(nonZero);
-//     }
-//   }
-
-//   Assert(isStrictlySorted(outMonomials));
-//   return outMonomials;
-// }
-
-void Monomial::sort(std::vector<Monomial>& m){
-  if(!isSorted(m)){
-    std::sort(m.begin(), m.end());
-  }
-}
-
-void Monomial::combineAdjacentMonomials(std::vector<Monomial>& monos) {
-  Assert(isSorted(monos));
-  size_t writePos, readPos, N;
-  for(writePos = 0, readPos = 0, N = monos.size(); readPos < N;){
-    Monomial& atRead = monos[readPos];
-    const VarList& varList  = atRead.getVarList();
-
-    size_t rangeEnd = readPos+1;
-    for(; rangeEnd < N; rangeEnd++){
-      if(!(varList == monos[rangeEnd].getVarList())){ break; }
-    }
-    // monos[i] for i in [readPos, rangeEnd) has the same var list
-    if(readPos+1 == rangeEnd){ // no addition needed
-      if(!atRead.getConstant().isZero()){
-        Monomial cpy = atRead; // being paranoid here
-        monos[writePos] = cpy;
-        writePos++;
-      }
-    }else{
-      Rational constant(monos[readPos].getConstant().getValue());
-      for(size_t i=readPos+1; i < rangeEnd; ++i){
-        constant += monos[i].getConstant().getValue();
-      }
-      if(!constant.isZero()){
-        Constant asConstant = Constant::mkConstant(constant);
-        Monomial nonZero = Monomial::mkMonomial(asConstant, varList);
-        monos[writePos] = nonZero;
-        writePos++;
-      }
-    }
-    Assert(rangeEnd > readPos);
-    readPos = rangeEnd;
-  }
-  if(writePos > 0 ){
-    Monomial cp = monos[0];
-    Assert(writePos <= N);
-    monos.resize(writePos, cp);
-  }else{
-    monos.clear();
-  }
-  Assert(isStrictlySorted(monos));
-}
-
-void Monomial::print() const {
-  Trace("normal-form") <<  getNode() << std::endl;
-}
-
-void Monomial::printList(const std::vector<Monomial>& list) {
-  for(vector<Monomial>::const_iterator i = list.begin(), end = list.end(); i != end; ++i) {
-    const Monomial& m =*i;
-    m.print();
-  }
-}
-Polynomial Polynomial::operator+(const Polynomial& vl) const {
-
-  std::vector<Monomial> sortedMonos;
-  std::merge(begin(), end(), vl.begin(), vl.end(), std::back_inserter(sortedMonos));
-
-  Monomial::combineAdjacentMonomials(sortedMonos);
-  //std::vector<Monomial> combined = Monomial::sumLikeTerms(sortedMonos);
-
-  Polynomial result = mkPolynomial(sortedMonos);
-  return result;
-}
-
-Polynomial Polynomial::exactDivide(const Integer& z) const {
-  Assert(isIntegral());
-  if(z.isOne()){
-    return (*this);
-  }else {
-    Constant invz = Constant::mkConstant(Rational(1,z));
-    Polynomial prod = (*this) * Monomial::mkMonomial(invz);
-    Assert(prod.isIntegral());
-    return prod;
-  }
-}
-
-Polynomial Polynomial::sumPolynomials(const std::vector<Polynomial>& ps){
-  if(ps.empty()){
-    return mkZero();
-  }else if(ps.size() <= 4){
-    // if there are few enough polynomials just add them
-    Polynomial p = ps[0];
-    for(size_t i = 1; i < ps.size(); ++i){
-      p = p + ps[i];
-    }
-    return p;
-  }else{
-    // general case
-    std::map<Node, Rational> coeffs;
-    for(size_t i = 0, N = ps.size(); i<N; ++i){
-      const Polynomial& p = ps[i];
-      for(iterator pi = p.begin(), pend = p.end(); pi != pend; ++pi) {
-        Monomial m = *pi;
-        coeffs[m.getVarList().getNode()] += m.getConstant().getValue();
-      }
-    }
-    std::vector<Monomial> monos;
-    std::map<Node, Rational>::const_iterator ci = coeffs.begin(), cend = coeffs.end();
-    for(; ci != cend; ++ci){
-      if(!(*ci).second.isZero()){
-        Constant c = Constant::mkConstant((*ci).second);
-        Node n = (*ci).first;
-        VarList vl = VarList::parseVarList(n);
-        monos.push_back(Monomial::mkMonomial(c, vl));
-      }
-    }
-    Monomial::sort(monos);
-    Monomial::combineAdjacentMonomials(monos);
-
-    Polynomial result = mkPolynomial(monos);
-    return result;
-  }
-}
-
-Polynomial Polynomial::operator-(const Polynomial& vl) const {
-  Constant negOne = Constant::mkConstant(Rational(-1));
-
-  return *this + (vl*negOne);
-}
-
-Polynomial Polynomial::operator*(const Rational& q) const{
-  if(q.isZero()){
-    return Polynomial::mkZero();
-  }else if(q.isOne()){
-    return *this;
-  }else{
-    std::vector<Monomial> newMonos;
-    for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
-      newMonos.push_back((*i)*q);
-    }
-
-    Assert(Monomial::isStrictlySorted(newMonos));
-    return Polynomial::mkPolynomial(newMonos);
-  }
-}
-
-Polynomial Polynomial::operator*(const Constant& c) const{
-  return (*this) * c.getValue();
-  // if(c.isZero()){
-  //   return Polynomial::mkZero();
-  // }else if(c.isOne()){
-  //   return *this;
-  // }else{
-  //   std::vector<Monomial> newMonos;
-  //   for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
-  //     newMonos.push_back((*i)*c);
-  //   }
-
-  //   Assert(Monomial::isStrictlySorted(newMonos));
-  //   return Polynomial::mkPolynomial(newMonos);
-  // }
-}
-
-Polynomial Polynomial::operator*(const Monomial& mono) const {
-  if(mono.isZero()) {
-    return Polynomial(mono); //Don't multiply by zero
-  } else {
-    std::vector<Monomial> newMonos;
-    for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
-      newMonos.push_back(mono * (*i));
-    }
-
-    // We may need to sort newMonos.
-    // Suppose this = (+ x y), mono = x, (* x y).getId() < (* x x).getId()
-    // newMonos = <(* x x), (* x y)> after this loop.
-    // This is not sorted according to the current VarList order.
-    Monomial::sort(newMonos);
-    return Polynomial::mkPolynomial(newMonos);
-  }
-}
-
-Polynomial Polynomial::operator*(const Polynomial& poly) const {
-  Polynomial res = Polynomial::mkZero();
-  for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
-    Monomial curr = *i;
-    Polynomial prod = poly * curr;
-    Polynomial sum  = res + prod;
-    res = sum;
-  }
-  return res;
-}
-
-Monomial Polynomial::selectAbsMinimum() const {
-  iterator iter = begin(), myend = end();
-  Assert(iter != myend);
-
-  Monomial min = *iter;
-  ++iter;
-  for(; iter != end(); ++iter){
-    Monomial curr = *iter;
-    if(curr.absCmp(min) < 0){
-      min = curr;
-    }
-  }
-  return min;
-}
-
-bool Polynomial::leadingCoefficientIsAbsOne() const {
-  return getHead().absCoefficientIsOne();
-}
-bool Polynomial::leadingCoefficientIsPositive() const {
-  return getHead().getConstant().isPositive();
-}
-
-bool Polynomial::denominatorLCMIsOne() const {
-  return denominatorLCM().isOne();
-}
-
-bool Polynomial::numeratorGCDIsOne() const {
-  return gcd().isOne();
-}
-
-Integer Polynomial::gcd() const {
-  Assert(isIntegral());
-  return numeratorGCD();
-}
-
-Integer Polynomial::numeratorGCD() const {
-  //We'll use the standardization that gcd(0, 0) = 0
-  //So that the gcd of the zero polynomial is gcd{0} = 0
-  iterator i=begin(), e=end();
-  Assert(i != e);
-
-  Integer d = (*i).getConstant().getValue().getNumerator().abs();
-  if(d.isOne()){
-    return d;
-  }
-  ++i;
-  for(; i!=e; ++i){
-    Integer c = (*i).getConstant().getValue().getNumerator();
-    d = d.gcd(c);
-    if(d.isOne()){
-      return d;
-    }
-  }
-  return d;
-}
-
-Integer Polynomial::denominatorLCM() const {
-  Integer tmp(1);
-  for (iterator i = begin(), e = end(); i != e; ++i) {
-    const Integer denominator = (*i).getConstant().getValue().getDenominator();
-    tmp = tmp.lcm(denominator);
-  }
-  return tmp;
-}
-
-Constant Polynomial::getCoefficient(const VarList& vl) const{
-  //TODO improve to binary search...
-  for(iterator iter=begin(), myend=end(); iter != myend; ++iter){
-    Monomial m = *iter;
-    VarList curr = m.getVarList();
-    if(curr == vl){
-      return m.getConstant();
-    }
-  }
-  return Constant::mkConstant(0);
-}
-
-Node Polynomial::computeQR(const Polynomial& p, const Integer& div){
-  Assert(p.isIntegral());
-  std::vector<Monomial> q_vec, r_vec;
-  Integer tmp_q, tmp_r;
-  for(iterator iter = p.begin(), pend = p.end(); iter != pend; ++iter){
-    Monomial curr = *iter;
-    VarList vl = curr.getVarList();
-    Constant c = curr.getConstant();
-
-    const Integer& a = c.getValue().getNumerator();
-    Integer::floorQR(tmp_q, tmp_r, a, div);
-    Constant q=Constant::mkConstant(tmp_q);
-    Constant r=Constant::mkConstant(tmp_r);
-    if(!q.isZero()){
-      q_vec.push_back(Monomial::mkMonomial(q, vl));
-    }
-    if(!r.isZero()){
-      r_vec.push_back(Monomial::mkMonomial(r, vl));
-    }
-  }
-
-  Polynomial p_q = Polynomial::mkPolynomial(q_vec);
-  Polynomial p_r = Polynomial::mkPolynomial(r_vec);
-
-  return NodeManager::currentNM()->mkNode(
-      kind::ADD, p_q.getNode(), p_r.getNode());
-}
-
-
-Monomial Polynomial::minimumVariableMonomial() const{
-  Assert(!isConstant());
-  if(singleton()){
-    return getHead();
-  }else{
-    iterator i = begin();
-    Monomial first = *i;
-    if( first.isConstant() ){
-      ++i;
-      Assert(i != end());
-      return *i;
-    }else{
-      return first;
-    }
-  }
-}
-
-bool Polynomial::variableMonomialAreStrictlyGreater(const Monomial& m) const{
-  if(isConstant()){
-    return true;
-  }else{
-    Monomial minimum = minimumVariableMonomial();
-    Trace("nf::tmp") << "minimum " << minimum.getNode() << endl;
-    Trace("nf::tmp") << "m " << m.getNode() << endl;
-    return m < minimum;
-  }
-}
-
-bool Polynomial::isMember(TNode n) {
-  if(Monomial::isMember(n)){
-    return true;
-  }
-  else if (n.getKind() == kind::ADD)
-  {
-    Assert(n.getNumChildren() >= 2);
-    Node::iterator currIter = n.begin(), end = n.end();
-    Node prev = *currIter;
-    if(!Monomial::isMember(prev)){
-      return false;
-    }
-
-    Monomial mprev = Monomial::parseMonomial(prev);
-    ++currIter;
-    for(; currIter != end; ++currIter){
-      Node curr = *currIter;
-      if(!Monomial::isMember(curr)){
-        return false;
-      }
-      Monomial mcurr = Monomial::parseMonomial(curr);
-      if(!(mprev < mcurr)){
-        return false;
-      }
-      mprev = mcurr;
-    }
-    return true;
-  }
-  else
-  {
-    return false;
-  }
-}
-
-Node SumPair::computeQR(const SumPair& sp, const Integer& div){
-  Assert(sp.isIntegral());
-
-  const Integer& constant = sp.getConstant().getValue().getNumerator();
-
-  Integer constant_q, constant_r;
-  Integer::floorQR(constant_q, constant_r, constant, div);
-
-  Node p_qr = Polynomial::computeQR(sp.getPolynomial(), div);
-  Assert(p_qr.getKind() == kind::ADD);
-  Assert(p_qr.getNumChildren() == 2);
-
-  Polynomial p_q = Polynomial::parsePolynomial(p_qr[0]);
-  Polynomial p_r = Polynomial::parsePolynomial(p_qr[1]);
-
-  SumPair sp_q(p_q, Constant::mkConstant(constant_q));
-  SumPair sp_r(p_r, Constant::mkConstant(constant_r));
-
-  return NodeManager::currentNM()->mkNode(
-      kind::ADD, sp_q.getNode(), sp_r.getNode());
-}
-
-SumPair SumPair::mkSumPair(const Polynomial& p){
-  if(p.isConstant()){
-    Constant leadingConstant = p.getHead().getConstant();
-    return SumPair(Polynomial::mkZero(), leadingConstant);
-  }else if(p.containsConstant()){
-    Assert(!p.singleton());
-    return SumPair(p.getTail(), p.getHead().getConstant());
-  }else{
-    return SumPair(p, Constant::mkZero());
-  }
-}
-
-Comparison::Comparison(TNode n) : NodeWrapper(n) { Assert(isNormalForm()); }
-
-SumPair Comparison::toSumPair() const {
-  Kind cmpKind = comparisonKind();
-  switch(cmpKind){
-  case kind::LT:
-  case kind::LEQ:
-  case kind::GT:
-  case kind::GEQ:
-    {
-      TNode lit = getNode();
-      TNode atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
-      Polynomial p = Polynomial::parsePolynomial(atom[0]);
-      Constant c = Constant::mkConstant(atom[1]);
-      if(p.leadingCoefficientIsPositive()){
-        return SumPair(p, -c);
-      }else{
-        return SumPair(-p, c);
-      }
-    }
-  case kind::EQUAL:
-  case kind::DISTINCT:
-    {
-      Polynomial left = getLeft();
-      Polynomial right = getRight();
-      Trace("nf::tmp") << "left: " << left.getNode() << endl;
-      Trace("nf::tmp") << "right: " << right.getNode() << endl;
-      if(right.isConstant()){
-        return SumPair(left, -right.getHead().getConstant());
-      }else if(right.containsConstant()){
-        Assert(!right.singleton());
-
-        Polynomial noConstant = right.getTail();
-        return SumPair(left - noConstant, -right.getHead().getConstant());
-      }else{
-        return SumPair(left - right, Constant::mkZero());
-      }
-    }
-    default: Unhandled() << cmpKind;
-  }
-}
-
-Polynomial Comparison::normalizedVariablePart() const {
-  Kind cmpKind = comparisonKind();
-  switch(cmpKind){
-  case kind::LT:
-  case kind::LEQ:
-  case kind::GT:
-  case kind::GEQ:
-    {
-      TNode lit = getNode();
-      TNode atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
-      Polynomial p = Polynomial::parsePolynomial(atom[0]);
-      if(p.leadingCoefficientIsPositive()){
-        return p;
-      }else{
-        return -p;
-      }
-    }
-  case kind::EQUAL:
-  case kind::DISTINCT:
-    {
-      Polynomial left = getLeft();
-      Polynomial right = getRight();
-      if(right.isConstant()){
-        return left;
-      }else{
-        Polynomial noConstant = right.containsConstant() ? right.getTail() : right;
-        Polynomial diff = left - noConstant;
-        if(diff.leadingCoefficientIsPositive()){
-          return diff;
-        }else{
-          return -diff;
-        }
-      }
-    }
-    default: Unhandled() << cmpKind;
-  }
-}
-
-DeltaRational Comparison::normalizedDeltaRational() const {
-  Kind cmpKind = comparisonKind();
-  int delta = deltaCoeff(cmpKind);
-  switch(cmpKind){
-  case kind::LT:
-  case kind::LEQ:
-  case kind::GT:
-  case kind::GEQ:
-    {
-      Node lit = getNode();
-      Node atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
-      Polynomial left = Polynomial::parsePolynomial(atom[0]);
-      const Rational& q = atom[1].getConst<Rational>();
-      if(left.leadingCoefficientIsPositive()){
-        return DeltaRational(q, delta);
-      }else{
-        return DeltaRational(-q, -delta);
-      }
-    }
-  case kind::EQUAL:
-  case kind::DISTINCT:
-    {
-      Polynomial right = getRight();
-      Monomial firstRight = right.getHead();
-      if(firstRight.isConstant()){
-        DeltaRational c = DeltaRational(firstRight.getConstant().getValue(), 0);
-        Polynomial left = getLeft();
-        if(!left.allIntegralVariables()){
-          return c;
-          //this is a qpolynomial and the sign of the leading
-          //coefficient will not change after the diff below
-        } else{
-          // the polynomial may be a z polynomial in which case
-          // taking the diff is the simplest and obviously correct means
-          Polynomial diff = right.singleton() ? left : left - right.getTail();
-          if(diff.leadingCoefficientIsPositive()){
-            return c;
-          }else{
-            return -c;
-          }
-        }
-      }else{ // The constant is 0 sign cannot change
-        return DeltaRational(0, 0);
-      }
-    }
-    default: Unhandled() << cmpKind;
-  }
-}
-
-std::tuple<Polynomial, Kind, Constant> Comparison::decompose(
-    bool split_constant) const
-{
-  Kind rel = getNode().getKind();
-  if (rel == Kind::NOT)
-  {
-    switch (getNode()[0].getKind())
-    {
-      case kind::LEQ: rel = Kind::GT; break;
-      case kind::LT: rel = Kind::GEQ; break;
-      case kind::EQUAL: rel = Kind::DISTINCT; break;
-      case kind::DISTINCT: rel = Kind::EQUAL; break;
-      case kind::GEQ: rel = Kind::LT; break;
-      case kind::GT: rel = Kind::LEQ; break;
-      default:
-        Assert(false) << "Unsupported relation: " << getNode()[0].getKind();
-    }
-  }
-
-  Polynomial poly = getLeft() - getRight();
-
-  if (!split_constant)
-  {
-    return std::tuple<Polynomial, Kind, Constant>{
-        poly, rel, Constant::mkZero()};
-  }
-
-  Constant right = Constant::mkZero();
-  if (poly.containsConstant())
-  {
-    right = -poly.getHead().getConstant();
-    poly = poly + Polynomial::mkPolynomial(right);
-  }
-
-  Constant lcoeff = poly.getHead().getConstant();
-  if (!lcoeff.isOne())
-  {
-    Constant invlcoeff = lcoeff.inverse();
-    if (lcoeff.isNegative())
-    {
-      switch (rel)
-      {
-        case kind::LEQ: rel = Kind::GEQ; break;
-        case kind::LT: rel = Kind::GT; break;
-        case kind::EQUAL: break;
-        case kind::DISTINCT: break;
-        case kind::GEQ: rel = Kind::LEQ; break;
-        case kind::GT: rel = Kind::LT; break;
-        default: Assert(false) << "Unsupported relation: " << rel;
-      }
-    }
-    poly = poly * invlcoeff;
-    right = right * invlcoeff;
-  }
-
-  return std::tuple<Polynomial, Kind, Constant>{poly, rel, right};
-}
-
-Comparison Comparison::parseNormalForm(TNode n) {
-  Trace("polynomial") << "Comparison::parseNormalForm(" << n << ")";
-  Comparison result(n);
-  Assert(result.isNormalForm());
-  return result;
-}
-
-Node Comparison::toNode(Kind k, const Polynomial& l, const Constant& r) {
-  Assert(isRelationOperator(k));
-  switch(k) {
-  case kind::GEQ:
-  case kind::GT:
-    return NodeManager::currentNM()->mkNode(k, l.getNode(), r.getNode());
-  default: Unhandled() << k;
-  }
-}
-
-Node Comparison::toNode(Kind k, const Polynomial& l, const Polynomial& r) {
-  Assert(isRelationOperator(k));
-  switch(k) {
-  case kind::GEQ:
-  case kind::EQUAL:
-  case kind::GT:
-    return NodeManager::currentNM()->mkNode(k, l.getNode(), r.getNode());
-  case kind::LEQ:
-    return toNode(kind::GEQ, r, l).notNode();
-  case kind::LT:
-    return toNode(kind::GT, r, l).notNode();
-  case kind::DISTINCT:
-    return toNode(kind::EQUAL, r, l).notNode();
-  default:
-    Unreachable();
-  }
-}
-
-bool Comparison::rightIsConstant() const {
-  if(getNode().getKind() == kind::NOT){
-    return getNode()[0][1].getKind() == kind::CONST_RATIONAL;
-  }else{
-    return getNode()[1].getKind() == kind::CONST_RATIONAL;
-  }
-}
-
-size_t Comparison::getComplexity() const{
-  switch(comparisonKind()){
-  case kind::CONST_BOOLEAN: return 1;
-  case kind::LT:
-  case kind::LEQ:
-  case kind::DISTINCT:
-  case kind::EQUAL:
-  case kind::GT:
-  case kind::GEQ:
-    return getLeft().getComplexity() +  getRight().getComplexity();
-  default: Unhandled() << comparisonKind(); return -1;
-  }
-}
-
-Polynomial Comparison::getLeft() const {
-  TNode left;
-  Kind k = comparisonKind();
-  switch(k){
-  case kind::LT:
-  case kind::LEQ:
-  case kind::DISTINCT:
-    left = getNode()[0][0];
-    break;
-  case kind::EQUAL:
-  case kind::GT:
-  case kind::GEQ:
-    left = getNode()[0];
-    break;
-  default: Unhandled() << k;
-  }
-  return Polynomial::parsePolynomial(left);
-}
-
-Polynomial Comparison::getRight() const {
-  TNode right;
-  Kind k = comparisonKind();
-  switch(k){
-  case kind::LT:
-  case kind::LEQ:
-  case kind::DISTINCT:
-    right = getNode()[0][1];
-    break;
-  case kind::EQUAL:
-  case kind::GT:
-  case kind::GEQ:
-    right = getNode()[1];
-    break;
-  default: Unhandled() << k;
-  }
-  return Polynomial::parsePolynomial(right);
-}
-
-// Polynomial Comparison::getLeft() const {
-//   Node n = getNode();
-//   Node left = (n.getKind() == kind::NOT ? n[0]: n)[0];
-//   return Polynomial::parsePolynomial(left);
-// }
-
-// Polynomial Comparison::getRight() const {
-//   Node n = getNode();
-//   Node right = (n.getKind() == kind::NOT ? n[0]: n)[1];
-//   return Polynomial::parsePolynomial(right);
-// }
-
-bool Comparison::isNormalForm() const {
-  Node n = getNode();
-  Kind cmpKind = comparisonKind(n);
-  Trace("nf::tmp") << "isNormalForm " << n << " " << cmpKind << endl;
-  switch(cmpKind){
-  case kind::CONST_BOOLEAN:
-    return true;
-  case kind::GT:
-    return isNormalGT();
-  case kind::GEQ:
-    return isNormalGEQ();
-  case kind::EQUAL:
-    return isNormalEquality();
-  case kind::LT:
-    return isNormalLT();
-  case kind::LEQ:
-    return isNormalLEQ();
-  case kind::DISTINCT:
-    return isNormalDistinct();
-  default:
-    return false;
-  }
-}
-
-/** This must be (> qpolynomial constant) */
-bool Comparison::isNormalGT() const {
-  Node n = getNode();
-  Assert(n.getKind() == kind::GT);
-  if(!rightIsConstant()){
-    return false;
-  }else{
-    Polynomial left = getLeft();
-    if(left.containsConstant()){
-      return false;
-    }else if(!left.leadingCoefficientIsAbsOne()){
-      return false;
-    }else{
-      return !left.isIntegral();
-    }
-  }
-}
-
-/** This must be (not (> qpolynomial constant)) */
-bool Comparison::isNormalLEQ() const {
-  Node n = getNode();
-  Trace("nf::tmp") << "isNormalLEQ " << n << endl;
-  Assert(n.getKind() == kind::NOT);
-  Assert(n[0].getKind() == kind::GT);
-  if(!rightIsConstant()){
-    return false;
-  }else{
-    Polynomial left = getLeft();
-    if(left.containsConstant()){
-      return false;
-    }else if(!left.leadingCoefficientIsAbsOne()){
-      return false;
-    }else{
-      return !left.isIntegral();
-    }
-  }
-}
-
-
-/** This must be (>= qpolynomial constant) or  (>= zpolynomial constant) */
-bool Comparison::isNormalGEQ() const {
-  Node n = getNode();
-  Assert(n.getKind() == kind::GEQ);
-
-  Trace("nf::tmp") << "isNormalGEQ " << n << " " << rightIsConstant() << endl;
-
-  if(!rightIsConstant()){
-    return false;
-  }else{
-    Polynomial left = getLeft();
-    if(left.containsConstant()){
-      return false;
-    }else{
-      if(left.isIntegral()){
-        return left.signNormalizedReducedSum();
-      }else{
-        return left.leadingCoefficientIsAbsOne();
-      }
-    }
-  }
-}
-
-/** This must be (not (>= qpolynomial constant)) or (not (>= zpolynomial constant)) */
-bool Comparison::isNormalLT() const {
-  Node n = getNode();
-  Assert(n.getKind() == kind::NOT);
-  Assert(n[0].getKind() == kind::GEQ);
-
-  if(!rightIsConstant()){
-    return false;
-  }else{
-    Polynomial left = getLeft();
-    if(left.containsConstant()){
-      return false;
-    }else{
-      if(left.isIntegral()){
-        return left.signNormalizedReducedSum();
-      }else{
-        return left.leadingCoefficientIsAbsOne();
-      }
-    }
-  }
-}
-
-
-bool Comparison::isNormalEqualityOrDisequality() const {
-  Polynomial pleft = getLeft();
-
-  if(pleft.numMonomials() == 1){
-    Monomial mleft = pleft.getHead();
-    if(mleft.isConstant()){
-      return false;
-    }else{
-      Polynomial pright = getRight();
-      if(allIntegralVariables()){
-        const Rational& lcoeff = mleft.getConstant().getValue();
-        if(pright.isConstant()){
-          return pright.isIntegral() && lcoeff.isOne();
-        }
-        Polynomial varRight = pright.containsConstant() ? pright.getTail() : pright;
-        if(lcoeff.sgn() <= 0){
-          return false;
-        }else{
-          Integer lcm = lcoeff.getDenominator().lcm(varRight.denominatorLCM());
-          Integer g = lcoeff.getNumerator().gcd(varRight.numeratorGCD());
-          Trace("nf::tmp") << lcm << " " << g << endl;
-          if(!lcm.isOne()){
-            return false;
-          }else if(!g.isOne()){
-            return false;
-          }else{
-            Monomial absMinRight = varRight.selectAbsMinimum();
-            Trace("nf::tmp") << mleft.getNode() << " " << absMinRight.getNode() << endl;
-            if( mleft.absCmp(absMinRight) < 0){
-              return true;
-            }else{
-              return (!(absMinRight.absCmp(mleft)< 0)) && mleft < absMinRight;
-            }
-          }
-        }
-      }else{
-        if(mleft.coefficientIsOne()){
-          Trace("nf::tmp")
-            << "dfklj " << mleft.getNode() << endl
-            << pright.getNode() << endl
-            << pright.variableMonomialAreStrictlyGreater(mleft)
-            << endl;
-          return pright.variableMonomialAreStrictlyGreater(mleft);
-        }else{
-          return false;
-        }
-      }
-    }
-  }else{
-    return false;
-  }
-}
-
-/** This must be (= qvarlist qpolynomial) or (= zmonomial zpolynomial)*/
-bool Comparison::isNormalEquality() const {
-  Assert(getNode().getKind() == kind::EQUAL);
-  return Theory::theoryOf(getNode()[0].getType()) == THEORY_ARITH &&
-         isNormalEqualityOrDisequality();
-}
-
-/**
- * This must be (not (= qvarlist qpolynomial)) or
- * (not (= zmonomial zpolynomial)).
- */
-bool Comparison::isNormalDistinct() const {
-  Assert(getNode().getKind() == kind::NOT);
-  Assert(getNode()[0].getKind() == kind::EQUAL);
-
-  return Theory::theoryOf(getNode()[0][0].getType()) == THEORY_ARITH &&
-         isNormalEqualityOrDisequality();
-}
-
-Node Comparison::mkRatEquality(const Polynomial& p){
-  Assert(!p.isConstant());
-  Assert(!p.allIntegralVariables());
-
-  Monomial minimalVList = p.minimumVariableMonomial();
-  Constant coeffInv = -(minimalVList.getConstant().inverse());
-
-  Polynomial newRight = (p - minimalVList) * coeffInv;
-  Polynomial newLeft(Monomial::mkMonomial(minimalVList.getVarList()));
-
-  return toNode(kind::EQUAL, newLeft, newRight);
-}
-
-Node Comparison::mkRatInequality(Kind k, const Polynomial& p){
-  Assert(k == kind::GEQ || k == kind::GT);
-  Assert(!p.isConstant());
-  Assert(!p.allIntegralVariables());
-
-  SumPair sp = SumPair::mkSumPair(p);
-  Polynomial left = sp.getPolynomial();
-  Constant right = - sp.getConstant();
-
-  Monomial minimalVList = left.getHead();
-  Assert(!minimalVList.isConstant());
-
-  Constant coeffInv = minimalVList.getConstant().inverse().abs();
-  Polynomial newLeft = left * coeffInv;
-  Constant newRight = right * (coeffInv);
-
-  return toNode(k, newLeft, newRight);
-}
-
-Node Comparison::mkIntInequality(Kind k, const Polynomial& p){
-  Assert(kind::GT == k || kind::GEQ == k);
-  Assert(!p.isConstant());
-  Assert(p.allIntegralVariables());
-
-  SumPair sp = SumPair::mkSumPair(p);
-  Polynomial left = sp.getPolynomial();
-  Rational right = - (sp.getConstant().getValue());
-
-
-  Monomial m = left.getHead();
-  Assert(!m.isConstant());
-
-  Integer lcm = left.denominatorLCM();
-  Integer g = left.numeratorGCD();
-  Rational mult(lcm,g);
-
-  Polynomial newLeft = left * mult;
-  Rational rightMult = right * mult;
-
-  bool negateResult = false;
-  if(!newLeft.leadingCoefficientIsPositive()){
-    // multiply by -1
-    // a: left >= right or b: left > right
-    // becomes
-    // a: -left <= -right or b: -left < -right
-    // a: not (-left > -right) or b: (not -left >= -right)
-    newLeft = -newLeft;
-    rightMult = -rightMult;
-    k = (kind::GT == k) ? kind::GEQ : kind::GT;
-    negateResult = true;
-    // the later stages handle:
-    // a: not (-left >= -right + 1) or b: (not -left >= -right)
-  }
-
-  Node result = Node::null();
-  if(rightMult.isIntegral()){
-    if(k == kind::GT){
-      // (> p z)
-      // (>= p (+ z 1))
-      Constant rightMultPlusOne = Constant::mkConstant(rightMult + 1);
-      result = toNode(kind::GEQ, newLeft, rightMultPlusOne);
-    }else{
-      Constant newRight = Constant::mkConstant(rightMult);
-      result = toNode(kind::GEQ, newLeft, newRight);
-    }
-  }else{
-    //(>= l (/ n d))
-    //(>= l (ceil (/ n d)))
-    //This also hold for GT as (ceil (/ n d)) > (/ n d)
-    Integer ceilr = rightMult.ceiling();
-    Constant ceilRight = Constant::mkConstant(ceilr);
-    result = toNode(kind::GEQ, newLeft, ceilRight);
-  }
-  Assert(!result.isNull());
-  if(negateResult){
-    return result.notNode();
-  }else{
-    return result;
-  }
-}
-
-Node Comparison::mkIntEquality(const Polynomial& p){
-  Assert(!p.isConstant());
-  Assert(p.allIntegralVariables());
-
-  SumPair sp = SumPair::mkSumPair(p);
-  Polynomial varPart = sp.getPolynomial();
-  Constant constPart = sp.getConstant();
-
-  Integer lcm = varPart.denominatorLCM();
-  Integer g = varPart.numeratorGCD();
-  Constant mult = Constant::mkConstant(Rational(lcm,g));
-
-  Constant constMult = constPart * mult;
-
-  if(constMult.isIntegral()){
-    Polynomial varPartMult = varPart * mult;
-
-    Monomial m = varPartMult.selectAbsMinimum();
-    bool mIsPositive =  m.getConstant().isPositive();
-
-    Polynomial noM = (varPartMult + (- m)) + Polynomial::mkPolynomial(constMult);
-
-    // m + noM = 0
-    Polynomial newRight = mIsPositive ? -noM : noM;
-    Polynomial newLeft  = mIsPositive ? m  : -m;
-
-    Assert(newRight.isIntegral());
-    return toNode(kind::EQUAL, newLeft, newRight);
-  }else{
-    return mkBoolNode(false);
-  }
-}
-
-Comparison Comparison::mkComparison(Kind k, const Polynomial& l, const Polynomial& r){
-
-  //Make this special case fast for sharing!
-  if((k == kind::EQUAL || k == kind::DISTINCT) && l.isVarList() && r.isVarList()){
-    VarList vLeft = l.asVarList();
-    VarList vRight = r.asVarList();
-
-    if(vLeft == vRight){
-      // return true for equalities and false for disequalities
-      return Comparison(k == kind::EQUAL);
-    }else{
-      Node eqNode = vLeft < vRight ? toNode( kind::EQUAL, l, r) : toNode( kind::EQUAL, r, l);
-      Node forK = (k == kind::DISTINCT) ? eqNode.notNode() : eqNode;
-      return Comparison(forK);
-    }
-  }
-
-  //General case
-  Polynomial diff = l - r;
-  if(diff.isConstant()){
-    bool res = evaluateConstantPredicate(k, diff.asConstant(), Rational(0));
-    return Comparison(res);
-  }else{
-    Node result = Node::null();
-    bool isInteger = diff.allIntegralVariables();
-    switch(k){
-    case kind::EQUAL:
-      result = isInteger ? mkIntEquality(diff) : mkRatEquality(diff);
-      break;
-    case kind::DISTINCT:
-      {
-        Node eq = isInteger ? mkIntEquality(diff) : mkRatEquality(diff);
-        result = eq.notNode();
-      }
-      break;
-    case kind::LEQ:
-    case kind::LT:
-      {
-        Polynomial neg = - diff;
-        Kind negKind = (k == kind::LEQ ? kind::GEQ : kind::GT);
-        result = isInteger ?
-          mkIntInequality(negKind, neg) : mkRatInequality(negKind, neg);
-      }
-      break;
-    case kind::GEQ:
-    case kind::GT:
-      result = isInteger ?
-        mkIntInequality(k, diff) : mkRatInequality(k, diff);
-      break;
-    default: Unhandled() << k;
-    }
-    Assert(!result.isNull());
-    if(result.getKind() == kind::NOT && result[0].getKind() == kind::CONST_BOOLEAN){
-      return Comparison(!(result[0].getConst<bool>()));
-    }else{
-      Comparison cmp(result);
-      Assert(cmp.isNormalForm());
-      return cmp;
-    }
-  }
-}
-
-bool Comparison::isBoolean() const {
-  return getNode().getKind() == kind::CONST_BOOLEAN;
-}
-
-
-bool Comparison::debugIsIntegral() const{
-  return getLeft().isIntegral() && getRight().isIntegral();
-}
-
-Kind Comparison::comparisonKind(TNode literal){
-  switch(literal.getKind()){
-  case kind::CONST_BOOLEAN:
-  case kind::GT:
-  case kind::GEQ:
-  case kind::EQUAL:
-    return literal.getKind();
-  case  kind::NOT:
-    {
-      TNode negatedAtom = literal[0];
-      switch(negatedAtom.getKind()){
-      case kind::GT: //(not (GT x c)) <=> (LEQ x c)
-        return kind::LEQ;
-      case kind::GEQ: //(not (GEQ x c)) <=> (LT x c)
-        return kind::LT;
-      case kind::EQUAL:
-        return kind::DISTINCT;
-      default:
-        return  kind::UNDEFINED_KIND;
-      }
-    }
-  default:
-    return kind::UNDEFINED_KIND;
-  }
-}
-
-
-Node Polynomial::makeAbsCondition(Variable v, Polynomial p){
-  Polynomial zerop = Polynomial::mkZero();
-
-  Polynomial varp = Polynomial::mkPolynomial(v);
-  Comparison pLeq0 = Comparison::mkComparison(kind::LEQ, p, zerop);
-  Comparison negP = Comparison::mkComparison(kind::EQUAL, varp, -p);
-  Comparison posP = Comparison::mkComparison(kind::EQUAL, varp, p);
-
-  Node absCnd = (pLeq0.getNode()).iteNode(negP.getNode(), posP.getNode());
-  return absCnd;
-}
-
-bool Polynomial::isNonlinear() const {
-
-  for(iterator i=begin(), iend =end(); i != iend; ++i){
-    Monomial m = *i;
-    if(m.isNonlinear()){
-      return true;
-    }
-  }
-  return false;
-}
-
-} //namespace arith
-} //namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/normal_form.h b/src/theory/arith/normal_form.h
deleted file mode 100644 (file)
index 5e79d8a..0000000
+++ /dev/null
@@ -1,1466 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__NORMAL_FORM_H
-#define CVC5__THEORY__ARITH__NORMAL_FORM_H
-
-#include <algorithm>
-
-#include "base/output.h"
-#include "expr/node.h"
-#include "expr/node_self_iterator.h"
-#include "theory/arith/delta_rational.h"
-#include "util/rational.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/***********************************************/
-/***************** Normal Form *****************/
-/***********************************************/
-/***********************************************/
-
-/**
- * Section 1: Languages
- * The normal form for arithmetic nodes is defined by the language
- * accepted by the following BNFs with some guard conditions.
- * (The guard conditions are in Section 3 for completeness.)
- *
- * variable := n
- *   where
- *     n.isVar() or is foreign
- *     n.getType() \in {Integer, Real}
- *
- * constant := n
- *   where
- *     n.getKind() == kind::CONST_RATIONAL
- *
- * var_list := variable | (* [variable])
- *   where
- *     len [variable] >= 2
- *     isSorted varOrder [variable]
- *
- * monomial := constant | var_list | (* constant' var_list')
- *   where
- *     \f$ constant' \not\in {0,1} \f$
- *
- * polynomial := monomial' | (+ [monomial])
- *   where
- *     len [monomial] >= 2
- *     isStrictlySorted monoOrder [monomial]
- *     forall (\x -> x != 0) [monomial]
- *
- * rational_cmp := (|><| qpolynomial constant)
- *   where
- *     |><| is GEQ, or GT
- *     not (exists constantMonomial (monomialList qpolynomial))
- *     (exists realMonomial (monomialList qpolynomial))
- *     abs(monomialCoefficient (head (monomialList qpolynomial))) == 1
- *
- * integer_cmp := (>= zpolynomial constant)
- *   where
- *     not (exists constantMonomial (monomialList zpolynomial))
- *     (forall integerMonomial (monomialList zpolynomial))
- *     the gcd of all numerators of coefficients is 1
- *     the denominator of all coefficients and the constant is 1
- *     the leading coefficient is positive
- *
- * rational_eq := (= qvarlist qpolynomial)
- *   where
- *     let allMonomials = (cons qvarlist (monomialList zpolynomial))
- *     let variableMonomials = (drop constantMonomial allMonomials)
- *     isStrictlySorted variableMonomials
- *     exists realMonomial variableMonomials
- *     is not empty qvarlist
- *
- * integer_eq := (= zmonomial zpolynomial)
- *   where
- *     let allMonomials = (cons zmonomial (monomialList zpolynomial))
- *     let variableMonomials = (drop constantMonomial allMonomials)
- *     not (constantMonomial zmonomial)
- *     (forall integerMonomial allMonomials)
- *     isStrictlySorted variableMonomials
- *     the gcd of all numerators of coefficients is 1
- *     the denominator of all coefficients and the constant is 1
- *     the coefficient of monomial is positive
- *     the value of the coefficient of monomial is minimal in variableMonomials
- *
- * comparison := TRUE | FALSE
- *   | rational_cmp | (not rational_cmp)
- *   | rational_eq | (not rational_eq)
- *   | integer_cmp | (not integer_cmp)
- *   | integer_eq | (not integer_eq)
- *
- * Normal Form for terms := polynomial
- * Normal Form for atoms := comparison
- */
-
-/**
- * Section 2: Helper Classes
- * The langauges accepted by each of these defintions
- * roughly corresponds to one of the following helper classes:
- *  Variable
- *  Constant
- *  VarList
- *  Monomial
- *  Polynomial
- *  Comparison
- *
- * Each of the classes obeys the following contracts/design decisions:
- * -Calling isMember(Node node) on a node returns true iff that node is a
- *  a member of the language. Note: isMember is O(n).
- * -Calling isNormalForm() on a helper class object returns true iff that
- *  helper class currently represents a normal form object.
- * -If isNormalForm() is false, then this object must have been made
- *  using a mk*() factory function.
- * -If isNormalForm() is true, calling getNode() on all of these classes
- *  returns a node that would be accepted by the corresponding language.
- *  And if isNormalForm() is false, returns Node::null().
- * -Each of the classes is immutable.
- * -Public facing constuctors have a 1-to-1 correspondence with one of
- *  production rules in the above grammar.
- * -Public facing constuctors are required to fail in debug mode when the
- *  guards of the production rule are not strictly met.
- *  For example: Monomial(Constant(1),VarList(Variable(x))) must fail.
- * -When a class has a Class parseClass(Node node) function,
- *  if isMember(node) is true, the function is required to return an instance
- *  of the helper class, instance, s.t. instance.getNode() == node.
- *  And if isMember(node) is false, this throws an assertion failure in debug
- *  mode and has undefined behaviour if not in debug mode.
- * -Only public facing constructors, parseClass(node), and mk*() functions are
- *  considered privileged functions for the helper class.
- * -Only privileged functions may use private constructors, and access
- *  private data members.
- * -All non-privileged functions are considered utility functions and
- *  must use a privileged function in order to create an instance of the class.
- */
-
-/**
- * Section 3: Guard Conditions Misc.
- *
- *
- *  variable_order x y =
- *    if (meta_kind_variable x) and (meta_kind_variable y)
- *    then node_order x y
- *    else if (meta_kind_variable x)
- *    then false
- *    else if (meta_kind_variable y)
- *    then true
- *    else node_order x y
- *
- *  var_list_len vl =
- *    match vl with
- *       variable -> 1
- *     | (* [variable]) -> len [variable]
- *
- *  order res =
- *    match res with
- *       Empty -> (0,Node::null())
- *     | NonEmpty(vl) -> (var_list_len vl, vl)
- *
- *  var_listOrder a b = tuple_cmp (order a) (order b)
- *
- *  monomialVarList monomial =
- *    match monomial with
- *        constant -> Empty
- *      | var_list -> NonEmpty(var_list)
- *      | (* constant' var_list') -> NonEmpty(var_list')
- *
- *  monoOrder m0 m1 = var_listOrder (monomialVarList m0) (monomialVarList m1)
- *
- *  integerMonomial mono =
- *    forall varHasTypeInteger (monomialVarList mono)
- *
- *  realMonomial mono = not (integerMonomial mono)
- *
- *  constantMonomial monomial =
- *    match monomial with
- *        constant -> true
- *      | var_list -> false
- *      | (* constant' var_list') -> false
- *
- *  monomialCoefficient monomial =
- *    match monomial with
- *        constant -> constant
- *      | var_list -> Constant(1)
- *      | (* constant' var_list') -> constant'
- *
- *  monomialList polynomial =
- *    match polynomial with
- *        monomial -> monomial::[]
- *      | (+ [monomial]) -> [monomial]
- */
-
-/**
- * A NodeWrapper is a class that is a thinly veiled container of a Node object.
- */
-class NodeWrapper {
-private:
-  Node node;
-public:
-  NodeWrapper(Node n) : node(n) {}
-  const Node& getNode() const { return node; }
-};/* class NodeWrapper */
-
-
-class Variable : public NodeWrapper {
-public:
- Variable(Node n) : NodeWrapper(n) { Assert(isMember(getNode())); }
-
- // TODO: check if it's a theory leaf also
- static bool isMember(Node n)
- {
-   Kind k = n.getKind();
-   switch (k)
-   {
-     case kind::CONST_RATIONAL: return false;
-     case kind::INTS_DIVISION:
-     case kind::INTS_MODULUS:
-     case kind::DIVISION:
-     case kind::INTS_DIVISION_TOTAL:
-     case kind::INTS_MODULUS_TOTAL:
-     case kind::DIVISION_TOTAL: return isDivMember(n);
-     case kind::IAND: return isIAndMember(n);
-     case kind::POW2: return isPow2Member(n);
-     case kind::EXPONENTIAL:
-     case kind::SINE:
-     case kind::COSINE:
-     case kind::TANGENT:
-     case kind::COSECANT:
-     case kind::SECANT:
-     case kind::COTANGENT:
-     case kind::ARCSINE:
-     case kind::ARCCOSINE:
-     case kind::ARCTANGENT:
-     case kind::ARCCOSECANT:
-     case kind::ARCSECANT:
-     case kind::ARCCOTANGENT:
-     case kind::SQRT:
-     case kind::PI: return isTranscendentalMember(n);
-     case kind::ABS:
-     case kind::TO_INTEGER:
-       // Treat to_int as a variable; it is replaced in early preprocessing
-       // by a variable.
-       return true;
-     default: return isLeafMember(n);
-   }
- }
-
-  static bool isLeafMember(Node n);
-  static bool isIAndMember(Node n);
-  static bool isPow2Member(Node n);
-  static bool isDivMember(Node n);
-  bool isDivLike() const{
-    return isDivMember(getNode());
-  }
-  static bool isTranscendentalMember(Node n);
-
-  bool isNormalForm() { return isMember(getNode()); }
-
-  bool isIntegral() const {
-    return getNode().getType().isInteger();
-  }
-
-  bool isMetaKindVariable() const {
-    return getNode().isVar();
-  }
-
-  bool operator<(const Variable& v) const {
-    VariableNodeCmp cmp;
-    return cmp(this->getNode(), v.getNode());
-  }
-
-  struct VariableNodeCmp {
-    static inline int cmp(const Node& n, const Node& m) {
-      if ( n == m ) { return 0; }
-
-      // RAN < real var < int var < non-variable
-
-      bool nIsRAN = n.getKind() == Kind::REAL_ALGEBRAIC_NUMBER;
-      bool mIsRAN = m.getKind() == Kind::REAL_ALGEBRAIC_NUMBER;
-
-      if (mIsRAN != nIsRAN)
-      {
-        return nIsRAN ? -1 : 1;
-      }
-
-      bool nIsInteger = n.getType().isInteger();
-      bool mIsInteger = m.getType().isInteger();
-
-      if(nIsInteger == mIsInteger){
-        bool nIsVariable = n.isVar();
-        bool mIsVariable = m.isVar();
-
-        if(nIsVariable == mIsVariable){
-          if(n < m){
-            return -1;
-          }else{
-            Assert(n != m);
-            return 1;
-          }
-        }else{
-          if(nIsVariable){
-            return -1; // nIsVariable => !mIsVariable
-          }else{
-            return 1; // !nIsVariable => mIsVariable
-          }
-        }
-      }else{
-        Assert(nIsInteger != mIsInteger);
-        if(nIsInteger){
-          return 1; // nIsInteger => !mIsInteger
-        }else{
-          return -1; // !nIsInteger => mIsInteger
-        }
-      }
-    }
-
-    bool operator()(const Node& n, const Node& m) const {
-      return VariableNodeCmp::cmp(n,m) < 0;
-    }
-  };
-
-  bool operator==(const Variable& v) const { return getNode() == v.getNode();}
-
-  size_t getComplexity() const;
-};/* class Variable */
-
-class Constant : public NodeWrapper {
-public:
- Constant(Node n) : NodeWrapper(n) { Assert(isMember(getNode())); }
-
- static bool isMember(Node n) { return n.getKind() == kind::CONST_RATIONAL; }
-
- bool isNormalForm() { return isMember(getNode()); }
-
- static Constant mkConstant(Node n)
- {
-   Assert(n.getKind() == kind::CONST_RATIONAL);
-   return Constant(n);
- }
-
-  static Constant mkConstant(const Rational& rat);
-
-  static Constant mkZero() {
-    return mkConstant(Rational(0));
-  }
-
-  static Constant mkOne() {
-    return mkConstant(Rational(1));
-  }
-
-  const Rational& getValue() const {
-    return getNode().getConst<Rational>();
-  }
-
-  static int absCmp(const Constant& a, const Constant& b);
-  bool isIntegral() const { return getValue().isIntegral(); }
-
-  int sgn() const { return getValue().sgn(); }
-
-  bool isZero() const { return sgn() == 0; }
-  bool isNegative() const { return sgn() < 0; }
-  bool isPositive() const { return sgn() > 0; }
-
-  bool isOne() const { return getValue() == 1; }
-
-  Constant operator*(const Rational& other) const {
-    return mkConstant(getValue() * other);
-  }
-
-  Constant operator*(const Constant& other) const {
-    return mkConstant(getValue() * other.getValue());
-  }
-  Constant operator+(const Constant& other) const {
-    return mkConstant(getValue() + other.getValue());
-  }
-  Constant operator-() const {
-    return mkConstant(-getValue());
-  }
-
-  Constant inverse() const{
-    Assert(!isZero());
-    return mkConstant(getValue().inverse());
-  }
-
-  bool operator<(const Constant& other) const {
-    return getValue() < other.getValue();
-  }
-
-  bool operator==(const Constant& other) const {
-    //Rely on node uniqueness.
-    return getNode() == other.getNode();
-  }
-
-  Constant abs() const {
-    if(isNegative()){
-      return -(*this);
-    }else{
-      return (*this);
-    }
-  }
-
-  uint32_t length() const{
-    Assert(isIntegral());
-    return getValue().getNumerator().length();
-  }
-
-  size_t getComplexity() const;
-
-};/* class Constant */
-
-
-template <class GetNodeIterator>
-inline Node makeNode(Kind k, GetNodeIterator start, GetNodeIterator end) {
-  NodeBuilder nb(k);
-
-  while(start != end) {
-    nb << (*start).getNode();
-    ++start;
-  }
-
-  return Node(nb);
-}/* makeNode<GetNodeIterator>(Kind, iterator, iterator) */
-
-/**
- * A VarList is a sorted list of variables representing a product.
- * If the VarList is empty, it represents an empty product or 1.
- * If the VarList has size 1, it represents a single variable.
- *
- * A non-sorted VarList can never be successfully made in debug mode.
- */
-class VarList : public NodeWrapper {
-private:
-
-  static Node multList(const std::vector<Variable>& list) {
-    Assert(list.size() >= 2);
-
-    return makeNode(kind::NONLINEAR_MULT, list.begin(), list.end());
-  }
-
-  VarList() : NodeWrapper(Node::null()) {}
-
-  VarList(Node n);
-
-  typedef expr::NodeSelfIterator internal_iterator;
-
-  internal_iterator internalBegin() const {
-    if(singleton()){
-      return expr::NodeSelfIterator::self(getNode());
-    }else{
-      return getNode().begin();
-    }
-  }
-
-  internal_iterator internalEnd() const {
-    if(singleton()){
-      return expr::NodeSelfIterator::selfEnd(getNode());
-    }else{
-      return getNode().end();
-    }
-  }
-
-public:
-
-  class iterator {
-  private:
-    internal_iterator d_iter;
-
-  public:
-    /* The following types are required by trait std::iterator_traits */
-
-    /** Iterator tag */
-    using iterator_category = std::forward_iterator_tag;
-
-    /** The type of the item */
-    using value_type = Variable;
-
-    /** The pointer type of the item */
-    using pointer = Variable*;
-
-    /** The reference type of the item */
-    using reference = Variable&;
-
-    /** The type returned when two iterators are subtracted */
-    using difference_type = std::ptrdiff_t;
-
-    /* End of std::iterator_traits required types */
-
-    explicit iterator(internal_iterator i) : d_iter(i) {}
-
-    inline Variable operator*() {
-      return Variable(*d_iter);
-    }
-
-    bool operator==(const iterator& i) {
-      return d_iter == i.d_iter;
-    }
-
-    bool operator!=(const iterator& i) {
-      return d_iter != i.d_iter;
-    }
-
-    iterator operator++() {
-      ++d_iter;
-      return *this;
-    }
-
-    iterator operator++(int) {
-      return iterator(d_iter++);
-    }
-  };
-
-  iterator begin() const {
-    return iterator(internalBegin());
-  }
-
-  iterator end() const {
-    return iterator(internalEnd());
-  }
-
-  Variable getHead() const {
-    Assert(!empty());
-    return *(begin());
-  }
-
-  VarList(Variable v) : NodeWrapper(v.getNode()) {
-    Assert(isSorted(begin(), end()));
-  }
-
-  VarList(const std::vector<Variable>& l) : NodeWrapper(multList(l)) {
-    Assert(l.size() >= 2);
-    Assert(isSorted(begin(), end()));
-  }
-
-  static bool isMember(Node n);
-
-  bool isNormalForm() const {
-    return !empty();
-  }
-
-  static VarList mkEmptyVarList() {
-    return VarList();
-  }
-
-
-  /** There are no restrictions on the size of l */
-  static VarList mkVarList(const std::vector<Variable>& l) {
-    if(l.size() == 0) {
-      return mkEmptyVarList();
-    } else if(l.size() == 1) {
-      return VarList((*l.begin()).getNode());
-    } else {
-      return VarList(l);
-    }
-  }
-
-  bool empty() const { return getNode().isNull(); }
-  bool singleton() const {
-    return !empty() && getNode().getKind() != kind::NONLINEAR_MULT;
-  }
-
-  int size() const {
-    if(singleton())
-      return 1;
-    else
-      return getNode().getNumChildren();
-  }
-
-  static VarList parseVarList(Node n);
-
-  VarList operator*(const VarList& vl) const;
-
-  int cmp(const VarList& vl) const;
-
-  bool operator<(const VarList& vl) const { return cmp(vl) < 0; }
-
-  bool operator==(const VarList& vl) const { return cmp(vl) == 0; }
-
-  bool isIntegral() const {
-    for(iterator i = begin(), e=end(); i != e; ++i ){
-      Variable var = *i;
-      if(!var.isIntegral()){
-        return false;
-      }
-    }
-    return true;
-  }
-  size_t getComplexity() const;
-
-private:
-  bool isSorted(iterator start, iterator end);
-
-};/* class VarList */
-
-
-/** Constructors have side conditions. Use the static mkMonomial functions instead. */ 
-class Monomial : public NodeWrapper {
-private:
-  Constant constant;
-  VarList varList;
-  Monomial(Node n, const Constant& c, const VarList& vl):
-    NodeWrapper(n), constant(c), varList(vl)
-  {
-    Assert(!c.isZero() || vl.empty());
-    Assert(c.isZero() || !vl.empty());
-
-    Assert(!c.isOne() || !multStructured(n));
-  }
-
-  static Node makeMultNode(const Constant& c, const VarList& vl) {
-    Assert(!c.isZero());
-    Assert(!c.isOne());
-    Assert(!vl.empty());
-    return NodeManager::currentNM()->mkNode(kind::MULT, c.getNode(), vl.getNode());
-  }
-
-  static bool multStructured(Node n) {
-    return n.getKind() ==  kind::MULT &&
-      n[0].getKind() == kind::CONST_RATIONAL &&
-      n.getNumChildren() == 2;
-  }
-
-  Monomial(const Constant& c):
-    NodeWrapper(c.getNode()), constant(c), varList(VarList::mkEmptyVarList())
-  { }
-  
-  Monomial(const VarList& vl):
-    NodeWrapper(vl.getNode()), constant(Constant::mkConstant(1)), varList(vl)
-  {
-    Assert(!varList.empty());
-  }
-
-  Monomial(const Constant& c, const VarList& vl):
-    NodeWrapper(makeMultNode(c,vl)), constant(c), varList(vl)
-  {
-    Assert(!c.isZero());
-    Assert(!c.isOne());
-    Assert(!varList.empty());
-
-    Assert(multStructured(getNode()));
-  }
-public:
-  static bool isMember(TNode n);
-
-  /** Makes a monomial with no restrictions on c and vl. */
-  static Monomial mkMonomial(const Constant& c, const VarList& vl);
-
-  /** If vl is empty, this make one. */
-  static Monomial mkMonomial(const VarList& vl);
-
-  static Monomial mkMonomial(const Constant& c){
-    return Monomial(c);
-  }
-  
-  static Monomial mkMonomial(const Variable& v){
-    return Monomial(VarList(v));
-  }
-
-  static Monomial parseMonomial(Node n);
-
-  static Monomial mkZero() {
-    return Monomial(Constant::mkConstant(0));
-  }
-  static Monomial mkOne() {
-    return Monomial(Constant::mkConstant(1));
-  }
-  const Constant& getConstant() const { return constant; }
-  const VarList& getVarList() const { return varList; }
-  
-  bool isConstant() const {
-    return varList.empty();
-  }
-
-  bool isZero() const {
-    return constant.isZero();
-  }
-
-  bool coefficientIsOne() const {
-    return constant.isOne();
-  }
-
-  bool absCoefficientIsOne() const {
-    return coefficientIsOne() || constant.getValue() == -1;
-  }
-
-  bool constantIsPositive() const {
-    return getConstant().isPositive();
-  }
-
-  Monomial operator*(const Rational& q) const;
-  Monomial operator*(const Constant& c) const;
-  Monomial operator*(const Monomial& mono) const;
-
-  Monomial operator-() const{
-    return (*this) * Rational(-1);
-  }
-
-
-  int cmp(const Monomial& mono) const {
-    return getVarList().cmp(mono.getVarList());
-  }
-
-  bool operator<(const Monomial& vl) const {
-    return cmp(vl) < 0;
-  }
-
-  bool operator==(const Monomial& vl) const {
-    return cmp(vl) == 0;
-  }
-
-  static bool isSorted(const std::vector<Monomial>& m) {
-    return std::is_sorted(m.begin(), m.end());
-  }
-
-  static bool isStrictlySorted(const std::vector<Monomial>& m) {
-    return isSorted(m) && std::adjacent_find(m.begin(),m.end()) == m.end();
-  }
-
-  static void sort(std::vector<Monomial>& m);
-  static void combineAdjacentMonomials(std::vector<Monomial>& m);
-
-  /**
-   * The variable product
-   */
-  bool integralVariables() const {
-    return getVarList().isIntegral();
-  }
-
-  /**
-   * The coefficient of the monomial is integral.
-   */
-  bool integralCoefficient() const {
-    return getConstant().isIntegral();
-  }
-
-  /**
-   * A Monomial is an "integral" monomial if the constant is integral.
-   */
-  bool isIntegral() const {
-    return integralCoefficient() && integralVariables();
-  }
-
-  /** Returns true if the VarList is a product of at least 2 Variables.*/
-  bool isNonlinear() const {
-    return getVarList().size() >= 2;
-  }
-
-  /**
-   * Given a sorted list of monomials, this function transforms this
-   * into a strictly sorted list of monomials that does not contain zero.
-   */
-  //static std::vector<Monomial> sumLikeTerms(const std::vector<Monomial>& monos);
-
-  int absCmp(const Monomial& other) const{
-    return getConstant().getValue().absCmp(other.getConstant().getValue());
-  }
-  // bool absLessThan(const Monomial& other) const{
-  //   return getConstant().abs() < other.getConstant().abs();
-  // }
-
-  uint32_t coefficientLength() const{
-    return getConstant().length();
-  }
-
-  void print() const;
-  static void printList(const std::vector<Monomial>& list);
-
-  size_t getComplexity() const;
-};/* class Monomial */
-
-class SumPair;
-class Comparison;;
-
-class Polynomial : public NodeWrapper {
-private:
-  bool d_singleton;
-
-  Polynomial(TNode n) : NodeWrapper(n), d_singleton(Monomial::isMember(n)) {
-    Assert(isMember(getNode()));
-  }
-
-  static Node makePlusNode(const std::vector<Monomial>& m) {
-    Assert(m.size() >= 2);
-
-    return makeNode(kind::ADD, m.begin(), m.end());
-  }
-
-  typedef expr::NodeSelfIterator internal_iterator;
-
-  internal_iterator internalBegin() const {
-    if(singleton()){
-      return expr::NodeSelfIterator::self(getNode());
-    }else{
-      return getNode().begin();
-    }
-  }
-
-  internal_iterator internalEnd() const {
-    if(singleton()){
-      return expr::NodeSelfIterator::selfEnd(getNode());
-    }else{
-      return getNode().end();
-    }
-  }
-
-  bool singleton() const { return d_singleton; }
-
-public:
-  static bool isMember(TNode n);
-
-  class iterator {
-  private:
-    internal_iterator d_iter;
-
-  public:
-    /* The following types are required by trait std::iterator_traits */
-
-    /** Iterator tag */
-    using iterator_category = std::forward_iterator_tag;
-
-    /** The type of the item */
-    using value_type = Monomial;
-
-    /** The pointer type of the item */
-    using pointer = Monomial*;
-
-    /** The reference type of the item */
-    using reference = Monomial&;
-
-    /** The type returned when two iterators are subtracted */
-    using difference_type = std::ptrdiff_t;
-
-    /* End of std::iterator_traits required types */
-
-    explicit iterator(internal_iterator i) : d_iter(i) {}
-
-    inline Monomial operator*() {
-      return Monomial::parseMonomial(*d_iter);
-    }
-
-    bool operator==(const iterator& i) {
-      return d_iter == i.d_iter;
-    }
-
-    bool operator!=(const iterator& i) {
-      return d_iter != i.d_iter;
-    }
-
-    iterator operator++() {
-      ++d_iter;
-      return *this;
-    }
-
-    iterator operator++(int) {
-      return iterator(d_iter++);
-    }
-  };
-
-  iterator begin() const { return iterator(internalBegin()); }
-  iterator end() const {  return iterator(internalEnd()); }
-
-  Polynomial(const Monomial& m):
-    NodeWrapper(m.getNode()), d_singleton(true)
-  {}
-
-  Polynomial(const std::vector<Monomial>& m):
-    NodeWrapper(makePlusNode(m)), d_singleton(false)
-  {
-    Assert(m.size() >= 2);
-    Assert(Monomial::isStrictlySorted(m));
-  }
-
-  static Polynomial mkPolynomial(const Constant& c){
-    return Polynomial(Monomial::mkMonomial(c));
-  }
-
-  static Polynomial mkPolynomial(const Variable& v){
-    return Polynomial(Monomial::mkMonomial(v));
-  }
-
-  static Polynomial mkPolynomial(const std::vector<Monomial>& m) {
-    if(m.size() == 0) {
-      return Polynomial(Monomial::mkZero());
-    } else if(m.size() == 1) {
-      return Polynomial((*m.begin()));
-    } else {
-      return Polynomial(m);
-    }
-  }
-
-  static Polynomial parsePolynomial(Node n) {
-    return Polynomial(n);
-  }
-
-  static Polynomial mkZero() {
-    return Polynomial(Monomial::mkZero());
-  }
-  static Polynomial mkOne() {
-    return Polynomial(Monomial::mkOne());
-  }
-  bool isZero() const {
-    return singleton() && (getHead().isZero());
-  }
-
-  bool isConstant() const {
-    return singleton() && (getHead().isConstant());
-  }
-
-  bool containsConstant() const {
-    return getHead().isConstant();
-  }
-
-  uint32_t size() const{
-    if(singleton()){
-      return 1;
-    }else{
-      Assert(getNode().getKind() == kind::ADD);
-      return getNode().getNumChildren();
-    }
-  }
-
-  Monomial getHead() const {
-    return *(begin());
-  }
-
-  Polynomial getTail() const {
-    Assert(!singleton());
-
-    iterator tailStart = begin();
-    ++tailStart;
-    std::vector<Monomial> subrange;
-    std::copy(tailStart, end(), std::back_inserter(subrange));
-    return mkPolynomial(subrange);
-  }
-
-  Monomial minimumVariableMonomial() const;
-  bool variableMonomialAreStrictlyGreater(const Monomial& m) const;
-
-  void printList() const {
-    if(TraceIsOn("normal-form")){
-      Trace("normal-form") << "start list" << std::endl;
-      for(iterator i = begin(), oend = end(); i != oend; ++i) {
-        const Monomial& m =*i;
-        m.print();
-      }
-      Trace("normal-form") << "end list" << std::endl;
-    }
-  }
-
-  /** A Polynomial is an "integral" polynomial if all of the monomials are integral. */
-  bool allIntegralVariables() const {
-    for(iterator i = begin(), e=end(); i!=e; ++i){
-      if(!(*i).integralVariables()){
-        return false;
-      }
-    }
-    return true;
-  }
-
-  /**
-   * A Polynomial is an "integral" polynomial if all of the monomials are integral
-   * and all of the coefficients are Integral. */
-  bool isIntegral() const {
-    for(iterator i = begin(), e=end(); i!=e; ++i){
-      if(!(*i).isIntegral()){
-        return false;
-      }
-    }
-    return true;
-  }
-
-  static Polynomial sumPolynomials(const std::vector<Polynomial>& polynomials);
-
-  /** Returns true if the polynomial contains a non-linear monomial.*/
-  bool isNonlinear() const;
-
-  /** Check whether this polynomial is only a single variable. */
-  bool isVariable() const
-  {
-    return singleton() && getHead().getVarList().singleton()
-           && getHead().coefficientIsOne();
-  }
-  /** Return the variable, given that isVariable() holds. */
-  Variable getVariable() const
-  {
-    Assert(isVariable());
-    return getHead().getVarList().getHead();
-  }
-
-  /**
-   * Selects a minimal monomial in the polynomial by the absolute value of
-   * the coefficient.
-   */
-  Monomial selectAbsMinimum() const;
-
-  /** Returns true if the absolute value of the head coefficient is one. */
-  bool leadingCoefficientIsAbsOne() const;
-  bool leadingCoefficientIsPositive() const;
-  bool denominatorLCMIsOne() const;
-  bool numeratorGCDIsOne() const;
-
-  bool signNormalizedReducedSum() const {
-    return leadingCoefficientIsPositive() && denominatorLCMIsOne() && numeratorGCDIsOne();
-  }
-
-  /**
-   * Returns the Least Common Multiple of the denominators of the coefficients
-   * of the monomials.
-   */
-  Integer denominatorLCM() const;
-
-  /**
-   * Returns the GCD of the numerators of the monomials.
-   * Requires this to be an isIntegral() polynomial.
-   */
-  Integer numeratorGCD() const;
-
-  /**
-   * Returns the GCD of the coefficients of the monomials.
-   * Requires this to be an isIntegral() polynomial.
-   */
-  Integer gcd() const;
-
-  /** z must divide all of the coefficients of the polynomial. */
-  Polynomial exactDivide(const Integer& z) const;
-
-  Polynomial operator+(const Polynomial& vl) const;
-  Polynomial operator-(const Polynomial& vl) const;
-  Polynomial operator-() const{
-    return (*this) * Rational(-1);
-  }
-
-  Polynomial operator*(const Rational& q) const;
-  Polynomial operator*(const Constant& c) const;
-  Polynomial operator*(const Monomial& mono) const;
-
-  Polynomial operator*(const Polynomial& poly) const;
-
-  /**
-   * Viewing the integer polynomial as a list [(* coeff_i mono_i)]
-   * The quotient and remainder of p divided by the non-zero integer z is:
-   *   q := [(* floor(coeff_i/z) mono_i )]
-   *   r := [(* rem(coeff_i/z) mono_i)]
-   * computeQR(p,z) returns the node (+ q r).
-   *
-   * q and r are members of the Polynomial class.
-   * For example:
-   * computeQR( p = (+ 5 (* 3 x) (* 8 y)) , z = 2) returns
-   *   (+ (+ 2 x (* 4 y)) (+ 1 x))
-   */
-  static Node computeQR(const Polynomial& p, const Integer& z);
-
-  /** Returns the coefficient associated with the VarList in the polynomial. */
-  Constant getCoefficient(const VarList& vl) const;
-
-  uint32_t maxLength() const{
-    iterator i = begin(), e=end();
-    if( i == e){
-      return 1;
-    }else{
-      uint32_t max = (*i).coefficientLength();
-      ++i;
-      for(; i!=e; ++i){
-        uint32_t curr = (*i).coefficientLength();
-        if(curr > max){
-          max = curr;
-        }
-      }
-      return max;
-    }
-  }
-
-  uint32_t numMonomials() const {
-    if (getNode().getKind() == kind::ADD)
-    {
-      return getNode().getNumChildren();
-    }
-    else if (isZero())
-    {
-      return 0;
-    }
-    else
-    {
-      return 1;
-    }
-  }
-
-  const Rational& asConstant() const{
-    Assert(isConstant());
-    return getNode().getConst<Rational>();
-    //return getHead().getConstant().getValue();
-  }
-
-  bool isVarList() const {
-    if(singleton()){
-      return VarList::isMember(getNode());
-    }else{
-      return false;
-    }
-  }
-
-  VarList asVarList() const {
-    Assert(isVarList());
-    return getHead().getVarList();
-  }
-
-  size_t getComplexity() const;
-
-  friend class SumPair;
-  friend class Comparison;
-
-  /** Returns a node that if asserted ensures v is the abs of this polynomial.*/
-  Node makeAbsCondition(Variable v){
-    return makeAbsCondition(v, *this);
-  }
-
-  /** Returns a node that if asserted ensures v is the abs of p.*/
-  static Node makeAbsCondition(Variable v, Polynomial p);
-
-};/* class Polynomial */
-
-
-/**
- * SumPair is a utility class that extends polynomials for use in computations.
- * A SumPair is always a combination of (+ p c) where
- *  c is a constant and p is a polynomial such that p = 0 or !p.containsConstant().
- *
- * These are a useful utility for representing the equation p = c as (+ p -c) where the pair
- * is known to implicitly be equal to 0.
- *
- * SumPairs do not have unique representations due to the potential for p = 0.
- * This makes them inappropriate for normal forms.
- */
-class SumPair : public NodeWrapper {
-private:
-  static Node toNode(const Polynomial& p, const Constant& c){
-    return NodeManager::currentNM()->mkNode(
-        kind::ADD, p.getNode(), c.getNode());
-  }
-
-  SumPair(TNode n) : NodeWrapper(n) { Assert(isNormalForm()); }
-
- public:
-  SumPair(const Polynomial& p):
-    NodeWrapper(toNode(p, Constant::mkConstant(0)))
-  {
-    Assert(isNormalForm());
-  }
-
-  SumPair(const Polynomial& p, const Constant& c):
-    NodeWrapper(toNode(p, c))
-  {
-    Assert(isNormalForm());
-  }
-
-  static bool isMember(TNode n) {
-    if (n.getKind() == kind::ADD && n.getNumChildren() == 2)
-    {
-      if(Constant::isMember(n[1])){
-        if(Polynomial::isMember(n[0])){
-          Polynomial p = Polynomial::parsePolynomial(n[0]);
-          return p.isZero() || (!p.containsConstant());
-        }else{
-          return false;
-        }
-      }else{
-        return false;
-      }
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-  bool isNormalForm() const {
-    return isMember(getNode());
-  }
-
-  Polynomial getPolynomial() const {
-    return Polynomial::parsePolynomial(getNode()[0]);
-  }
-
-  Constant getConstant() const {
-    return Constant::mkConstant((getNode())[1]);
-  }
-
-  SumPair operator+(const SumPair& other) const {
-    return SumPair(getPolynomial() + other.getPolynomial(),
-                   getConstant() + other.getConstant());
-  }
-
-  SumPair operator*(const Constant& c) const {
-    return SumPair(getPolynomial() * c, getConstant() * c);
-  }
-
-  SumPair operator-(const SumPair& other) const {
-    return (*this) + (other * Constant::mkConstant(-1));
-  }
-
-  static SumPair mkSumPair(const Polynomial& p);
-
-  static SumPair mkSumPair(const Variable& var){
-    return SumPair(Polynomial::mkPolynomial(var));
-  }
-
-  static SumPair parseSumPair(TNode n){
-    return SumPair(n);
-  }
-
-  bool isIntegral() const{
-    return getConstant().isIntegral() && getPolynomial().isIntegral();
-  }
-
-  bool isConstant() const {
-    return getPolynomial().isZero();
-  }
-
-  bool isZero() const {
-    return getConstant().isZero() && isConstant();
-  }
-
-  uint32_t size() const{
-    return getPolynomial().size();
-  }
-
-  bool isNonlinear() const{
-    return getPolynomial().isNonlinear();
-  }
-
-  /**
-   * Returns the greatest common divisor of gcd(getPolynomial()) and getConstant().
-   * The SumPair must be integral.
-   */
-  Integer gcd() const {
-    Assert(isIntegral());
-    return (getPolynomial().gcd()).gcd(getConstant().getValue().getNumerator());
-  }
-
-  uint32_t maxLength() const {
-    Assert(isIntegral());
-    return std::max(getPolynomial().maxLength(), getConstant().length());
-  }
-
-  static SumPair mkZero() {
-    return SumPair(Polynomial::mkZero(), Constant::mkConstant(0));
-  }
-
-  static Node computeQR(const SumPair& sp, const Integer& div);
-
-};/* class SumPair */
-
-/* class OrderedPolynomialPair { */
-/* private: */
-/*   Polynomial d_first; */
-/*   Polynomial d_second; */
-/* public: */
-/*   OrderedPolynomialPair(const Polynomial& f, const Polynomial& s) */
-/*     : d_first(f), */
-/*       d_second(s) */
-/*   {} */
-
-/*   /\** Returns the first part of the pair. *\/ */
-/*   const Polynomial& getFirst() const { */
-/*     return d_first; */
-/*   } */
-
-/*   /\** Returns the second part of the pair. *\/ */
-/*   const Polynomial& getSecond() const { */
-/*     return d_second; */
-/*   } */
-
-/*   OrderedPolynomialPair operator*(const Constant& c) const; */
-/*   OrderedPolynomialPair operator+(const Polynomial& p) const; */
-
-/*   /\** Returns true if both of the polynomials are constant. *\/ */
-/*   bool isConstant() const; */
-
-/*   /\** */
-/*    * Evaluates an isConstant() ordered pair as if */
-/*    *   (k getFirst() getRight()) */
-/*    *\/ */
-/*   bool evaluateConstant(Kind k) const; */
-
-/*   /\** */
-/*    * Returns the Least Common Multiple of the monomials */
-/*    * on the lefthand side and the constant on the right. */
-/*    *\/ */
-/*   Integer denominatorLCM() const; */
-
-/*   /\** Constructs a SumPair. *\/ */
-/*   SumPair toSumPair() const; */
-
-
-/*   OrderedPolynomialPair divideByGCD() const; */
-/*   OrderedPolynomialPair multiplyConstant(const Constant& c) const; */
-
-/*   /\** */
-/*    * Returns true if all of the variables are integers, */
-/*    * and the coefficients are integers. */
-/*    *\/ */
-/*   bool isIntegral() const; */
-
-/*   /\** Returns true if all of the variables are integers. *\/ */
-/*   bool allIntegralVariables() const { */
-/*     return getFirst().allIntegralVariables() && getSecond().allIntegralVariables(); */
-/*   } */
-/* }; */
-
-class Comparison : public NodeWrapper {
-private:
-
-  static Node toNode(Kind k, const Polynomial& l, const Constant& c);
-  static Node toNode(Kind k, const Polynomial& l, const Polynomial& r);
-
-  Comparison(TNode n);
-
-  /**
-   * Creates a node in normal form equivalent to (= l 0).
-   * All variables in l are integral.
-   */
-  static Node mkIntEquality(const Polynomial& l);
-
-  /**
-   * Creates a comparison equivalent to (k l 0).
-   * k is either GT or GEQ.
-   * All variables in l are integral.
-   */
-  static Node mkIntInequality(Kind k, const Polynomial& l);
-
-  /**
-   * Creates a node equivalent to (= l 0).
-   * It is not the case that all variables in l are integral.
-   */
-  static Node mkRatEquality(const Polynomial& l);
-
-  /**
-   * Creates a comparison equivalent to (k l 0).
-   * k is either GT or GEQ.
-   * It is not the case that all variables in l are integral.
-   */
-  static Node mkRatInequality(Kind k, const Polynomial& l);
-
-public:
-
-  Comparison(bool val) :
-    NodeWrapper(NodeManager::currentNM()->mkConst(val))
-  { }
-
-  /**
-   * Given a literal to TheoryArith return a single kind to
-   * to indicate its underlying structure.
-   * The function returns the following in each case:
-   * - (K left right)           -> K where is either EQUAL, GT, or GEQ
-   * - (CONST_BOOLEAN b)        -> CONST_BOOLEAN
-   * - (NOT (EQUAL left right)) -> DISTINCT
-   * - (NOT (GT left right))    -> LEQ
-   * - (NOT (GEQ left right))   -> LT
-   * If none of these match, it returns UNDEFINED_KIND.
-   */
-  static Kind comparisonKind(TNode literal);
-
-  Kind comparisonKind() const { return comparisonKind(getNode()); }
-
-  static Comparison mkComparison(Kind k, const Polynomial& l, const Polynomial& r);
-
-  /** Returns true if the comparison is a boolean constant. */
-  bool isBoolean() const;
-
-  /**
-   * Returns true if the comparison is either a boolean term,
-   * in integer normal form or mixed normal form.
-   */
-  bool isNormalForm() const;
-
-private:
-  bool isNormalGT() const;
-  bool isNormalGEQ() const;
-
-  bool isNormalLT() const;
-  bool isNormalLEQ() const;
-
-  bool isNormalEquality() const;
-  bool isNormalDistinct() const;
-  bool isNormalEqualityOrDisequality() const;
-
-  bool allIntegralVariables() const {
-    return getLeft().allIntegralVariables() && getRight().allIntegralVariables();
-  }
-  bool rightIsConstant() const;
-
-public:
-  Polynomial getLeft() const;
-  Polynomial getRight() const;
-
-  /* /\** Normal form check if at least one variable is real. *\/ */
-  /* bool isMixedCompareNormalForm() const; */
-
-  /* /\** Normal form check if at least one variable is real. *\/ */
-  /* bool isMixedEqualsNormalForm() const; */
-
-  /* /\** Normal form check is all variables are integer.*\/ */
-  /* bool isIntegerCompareNormalForm() const; */
-
-  /* /\** Normal form check is all variables are integer.*\/ */
-  /* bool isIntegerEqualsNormalForm() const; */
-
-
-  /**
-   * Returns true if all of the variables are integers, the coefficients are integers,
-   * and the right hand coefficient is an integer.
-   */
-  bool debugIsIntegral() const;
-
-  static Comparison parseNormalForm(TNode n);
-
-  inline static bool isNormalAtom(TNode n){
-    Comparison parse = Comparison::parseNormalForm(n);
-    return parse.isNormalForm();
-  }
-
-  size_t getComplexity() const;
-
-  SumPair toSumPair() const;
-
-  Polynomial normalizedVariablePart() const;
-  DeltaRational normalizedDeltaRational() const;
-
-  /**
-   * Transforms a Comparison object into a stronger normal form:
-   *    Polynomial ~Kind~ Constant
-   * 
-   * From the comparison, this method resolved a negation (if present) and
-   * moves everything to the left side.
-   * If split_constant is false, the constant is always zero.
-   * If split_constant is true, the polynomial has no constant term and is
-   * normalized to have leading coefficient one.
-   */
-  std::tuple<Polynomial, Kind, Constant> decompose(
-      bool split_constant = false) const;
-
-};/* class Comparison */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__NORMAL_FORM_H */
diff --git a/src/theory/arith/partial_model.cpp b/src/theory/arith/partial_model.cpp
deleted file mode 100644 (file)
index 6533038..0000000
+++ /dev/null
@@ -1,691 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "base/output.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/normal_form.h"
-#include "theory/arith/partial_model.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-ArithVariables::ArithVariables(context::Context* c,
-                               DeltaComputeCallback deltaComputingFunc)
-    : d_vars(),
-      d_safeAssignment(),
-      d_numberOfVariables(0),
-      d_pool(),
-      d_released(),
-      d_nodeToArithVarMap(),
-      d_boundsQueue(),
-      d_enqueueingBoundCounts(true),
-      d_lbRevertHistory(c, true, LowerBoundCleanUp(this)),
-      d_ubRevertHistory(c, true, UpperBoundCleanUp(this)),
-      d_deltaIsSafe(false),
-      d_delta(-1, 1),
-      d_deltaComputingFunc(deltaComputingFunc)
-{ }
-
-ArithVar ArithVariables::getNumberOfVariables() const {
-  return d_numberOfVariables;
-}
-
-
-bool ArithVariables::hasArithVar(TNode x) const {
-  return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end();
-}
-
-bool ArithVariables::hasNode(ArithVar a) const {
-  return d_vars.isKey(a);
-}
-
-ArithVar ArithVariables::asArithVar(TNode x) const{
-  Assert(hasArithVar(x));
-  Assert((d_nodeToArithVarMap.find(x))->second <= ARITHVAR_SENTINEL);
-  return (d_nodeToArithVarMap.find(x))->second;
-}
-
-Node ArithVariables::asNode(ArithVar a) const{
-  Assert(hasNode(a));
-  return d_vars[a].d_node;
-}
-
-ArithVariables::var_iterator::var_iterator()
-  : d_vars(NULL)
-  , d_wrapped()
-{}
-
-ArithVariables::var_iterator::var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci)
-  : d_vars(vars), d_wrapped(ci)
-{
-  nextInitialized();
-}
-
-ArithVariables::var_iterator& ArithVariables::var_iterator::operator++(){
-  ++d_wrapped;
-  nextInitialized();
-  return *this;
-}
-bool ArithVariables::var_iterator::operator==(const ArithVariables::var_iterator& other) const{
-  return d_wrapped == other.d_wrapped;
-}
-bool ArithVariables::var_iterator::operator!=(const ArithVariables::var_iterator& other) const{
-  return d_wrapped != other.d_wrapped;
-}
-ArithVar ArithVariables::var_iterator::operator*() const{
-  return *d_wrapped;
-}
-
-void ArithVariables::var_iterator::nextInitialized(){
-  VarInfoVec::const_iterator end = d_vars->end();
-  while(d_wrapped != end &&
-        !((*d_vars)[*d_wrapped].initialized())){
-    ++d_wrapped;
-  }
-}
-
-ArithVariables::var_iterator ArithVariables::var_begin() const {
-  return var_iterator(&d_vars, d_vars.begin());
-}
-
-ArithVariables::var_iterator ArithVariables::var_end() const {
-  return var_iterator(&d_vars, d_vars.end());
-}
-bool ArithVariables::isInteger(ArithVar x) const {
-  return d_vars[x].d_type >= ArithType::Integer;
-}
-
-/** Is the assignment to x integral? */
-bool ArithVariables::integralAssignment(ArithVar x) const {
-  return getAssignment(x).isIntegral();
-}
-bool ArithVariables::isAuxiliary(ArithVar x) const {
-  return d_vars[x].d_auxiliary;
-}
-
-bool ArithVariables::isIntegerInput(ArithVar x) const {
-  return isInteger(x) && !isAuxiliary(x);
-}
-
-ArithVariables::VarInfo::VarInfo()
-    : d_var(ARITHVAR_SENTINEL),
-      d_assignment(0),
-      d_lb(NullConstraint),
-      d_ub(NullConstraint),
-      d_cmpAssignmentLB(1),
-      d_cmpAssignmentUB(-1),
-      d_pushCount(0),
-      d_type(ArithType::Unset),
-      d_node(Node::null()),
-      d_auxiliary(false) {}
-
-bool ArithVariables::VarInfo::initialized() const {
-  return d_var != ARITHVAR_SENTINEL;
-}
-
-void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool aux){
-  Assert(!initialized());
-  Assert(d_lb == NullConstraint);
-  Assert(d_ub == NullConstraint);
-  Assert(d_cmpAssignmentLB > 0);
-  Assert(d_cmpAssignmentUB < 0);
-  d_var = v;
-  d_node = n;
-  d_auxiliary = aux;
-
-  if(d_auxiliary){
-    //The type computation is not quite accurate for Rationals that are
-    //integral.
-    //We'll use the isIntegral check from the polynomial package instead.
-    Polynomial p = Polynomial::parsePolynomial(n);
-    d_type = p.isIntegral() ? ArithType::Integer : ArithType::Real;
-  }else{
-    d_type = n.getType().isInteger() ? ArithType::Integer : ArithType::Real;
-  }
-
-  Assert(initialized());
-}
-
-void ArithVariables::VarInfo::uninitialize(){
-  d_var = ARITHVAR_SENTINEL;
-  d_node = Node::null();
-}
-
-bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundsInfo& prev){
-  Assert(initialized());
-  d_assignment = a;
-  int cmpUB = (d_ub == NullConstraint) ? -1 :
-    d_assignment.cmp(d_ub->getValue());
-
-  int cmpLB = (d_lb == NullConstraint) ? 1 :
-    d_assignment.cmp(d_lb->getValue());
-
-  bool lbChanged = cmpLB != d_cmpAssignmentLB &&
-    (cmpLB == 0 || d_cmpAssignmentLB == 0);
-  bool ubChanged = cmpUB != d_cmpAssignmentUB &&
-    (cmpUB == 0 || d_cmpAssignmentUB == 0);
-
-  if(lbChanged || ubChanged){
-    prev = boundsInfo();
-  }
-
-  d_cmpAssignmentUB = cmpUB;
-  d_cmpAssignmentLB = cmpLB;
-  return lbChanged || ubChanged;
-}
-
-void ArithVariables::releaseArithVar(ArithVar v){
-  VarInfo& vi = d_vars.get(v);
-
-  size_t removed CVC5_UNUSED = d_nodeToArithVarMap.erase(vi.d_node);
-  Assert(removed == 1);
-
-  vi.uninitialize();
-
-  if(d_safeAssignment.isKey(v)){
-    d_safeAssignment.remove(v);
-  }
-  if(vi.canBeReclaimed()){
-    d_pool.push_back(v);
-  }else{
-    d_released.push_back(v);
-  }
-}
-
-bool ArithVariables::VarInfo::setUpperBound(ConstraintP ub, BoundsInfo& prev){
-  Assert(initialized());
-  bool wasNull = d_ub == NullConstraint;
-  bool isNull = ub == NullConstraint;
-
-  int cmpUB = isNull ? -1 : d_assignment.cmp(ub->getValue());
-  bool ubChanged = (wasNull != isNull) ||
-    (cmpUB != d_cmpAssignmentUB && (cmpUB == 0 || d_cmpAssignmentUB == 0));
-  if(ubChanged){
-    prev = boundsInfo();
-  }
-  d_ub = ub;
-  d_cmpAssignmentUB = cmpUB;
-  return ubChanged;
-}
-
-bool ArithVariables::VarInfo::setLowerBound(ConstraintP lb, BoundsInfo& prev){
-  Assert(initialized());
-  bool wasNull = d_lb == NullConstraint;
-  bool isNull = lb == NullConstraint;
-
-  int cmpLB = isNull ? 1 : d_assignment.cmp(lb->getValue());
-
-  bool lbChanged = (wasNull != isNull) ||
-    (cmpLB != d_cmpAssignmentLB && (cmpLB == 0 || d_cmpAssignmentLB == 0));
-  if(lbChanged){
-    prev = boundsInfo();
-  }
-  d_lb = lb;
-  d_cmpAssignmentLB = cmpLB;
-  return lbChanged;
-}
-
-BoundCounts ArithVariables::VarInfo::atBoundCounts() const {
-  uint32_t lbIndc = (d_cmpAssignmentLB == 0) ? 1 : 0;
-  uint32_t ubIndc = (d_cmpAssignmentUB == 0) ? 1 : 0;
-  return BoundCounts(lbIndc, ubIndc);
-}
-
-BoundCounts ArithVariables::VarInfo::hasBoundCounts() const {
-  uint32_t lbIndc = (d_lb != NullConstraint) ? 1 : 0;
-  uint32_t ubIndc = (d_ub != NullConstraint) ? 1 : 0;
-  return BoundCounts(lbIndc, ubIndc);
-}
-
-BoundsInfo ArithVariables::VarInfo::boundsInfo() const{
-  return BoundsInfo(atBoundCounts(), hasBoundCounts());
-}
-
-bool ArithVariables::VarInfo::canBeReclaimed() const{
-  return d_pushCount == 0;
-}
-
-bool ArithVariables::canBeReleased(ArithVar v) const{
-  return d_vars[v].canBeReclaimed();
-}
-
-void ArithVariables::attemptToReclaimReleased(){
-  size_t readPos = 0, writePos = 0, N = d_released.size();
-  for(; readPos < N; ++readPos){
-    ArithVar v = d_released[readPos];
-    if(canBeReleased(v)){
-      d_pool.push_back(v);
-    }else{
-      d_released[writePos] = v;
-      writePos++;
-    }
-  }
-  d_released.resize(writePos);
-}
-
-ArithVar ArithVariables::allocateVariable(){
-  if(d_pool.empty()){
-    attemptToReclaimReleased();
-  }
-  bool reclaim = !d_pool.empty();
-
-  ArithVar varX;
-  if(reclaim){
-    varX = d_pool.back();
-    d_pool.pop_back();
-  }else{
-    varX = d_numberOfVariables;
-    ++d_numberOfVariables;
-  }
-  d_vars.set(varX, VarInfo());
-  return varX;
-}
-
-
-const Rational& ArithVariables::getDelta(){
-  if(!d_deltaIsSafe){
-    Rational nextDelta = d_deltaComputingFunc();
-    setDelta(nextDelta);
-  }
-  Assert(d_deltaIsSafe);
-  return d_delta;
-}
-
-bool ArithVariables::boundsAreEqual(ArithVar x) const{
-  if(hasLowerBound(x) && hasUpperBound(x)){
-    return getUpperBound(x) == getLowerBound(x);
-  }else{
-    return false;
-  }
-}
-
-
-std::pair<ConstraintP, ConstraintP> ArithVariables::explainEqualBounds(ArithVar x) const{
-  Assert(boundsAreEqual(x));
-
-  ConstraintP lb = getLowerBoundConstraint(x);
-  ConstraintP ub = getUpperBoundConstraint(x);
-  if(lb->isEquality()){
-    return make_pair(lb, NullConstraint);
-  }else if(ub->isEquality()){
-    return make_pair(ub, NullConstraint);
-  }else{
-    return make_pair(lb, ub);
-  }
-}
-
-void ArithVariables::setAssignment(ArithVar x, const DeltaRational& r){
-  Trace("partial_model") << "pm: updating the assignment to" << x
-                         << " now " << r <<endl;
-  VarInfo& vi = d_vars.get(x);
-  if(!d_safeAssignment.isKey(x)){
-    d_safeAssignment.set(x, vi.d_assignment);
-  }
-  invalidateDelta();
-
-  BoundsInfo prev;
-  if(vi.setAssignment(r, prev)){
-    addToBoundQueue(x, prev);
-  }
-}
-
-void ArithVariables::setAssignment(ArithVar x, const DeltaRational& safe, const DeltaRational& r){
-  Trace("partial_model") << "pm: updating the assignment to" << x
-                         << " now " << r <<endl;
-  if(safe == r){
-    if(d_safeAssignment.isKey(x)){
-      d_safeAssignment.remove(x);
-    }
-  }else{
-    d_safeAssignment.set(x, safe);
-  }
-
-  invalidateDelta();
-  VarInfo& vi = d_vars.get(x);
-  BoundsInfo prev;
-  if(vi.setAssignment(r, prev)){
-    addToBoundQueue(x, prev);
-  }
-}
-
-void ArithVariables::initialize(ArithVar x, Node n, bool aux){
-  VarInfo& vi = d_vars.get(x);
-  vi.initialize(x, n, aux);
-  d_nodeToArithVarMap[n] = x;
-}
-
-ArithVar ArithVariables::allocate(Node n, bool aux){
-  ArithVar v = allocateVariable();
-  initialize(v, n, aux);
-  return v;
-}
-
-// void ArithVariables::initialize(ArithVar x, const DeltaRational& r){
-//   Assert(x == d_mapSize);
-//   Assert(equalSizes());
-//   ++d_mapSize;
-
-//   // Is worth mentioning that this is not strictly necessary, but this maintains the internal invariant
-//   // that when d_assignment is set this gets set.
-//   invalidateDelta();
-//   d_assignment.push_back( r );
-
-//   d_boundRel.push_back(BetweenBounds);
-
-//   d_ubc.push_back(NullConstraint);
-//   d_lbc.push_back(NullConstraint);
-// }
-
-/** Must know that the bound exists both calling this! */
-const DeltaRational& ArithVariables::getUpperBound(ArithVar x) const {
-  Assert(inMaps(x));
-  Assert(hasUpperBound(x));
-
-  return getUpperBoundConstraint(x)->getValue();
-}
-
-const DeltaRational& ArithVariables::getLowerBound(ArithVar x) const {
-  Assert(inMaps(x));
-  Assert(hasLowerBound(x));
-
-  return getLowerBoundConstraint(x)->getValue();
-}
-
-const DeltaRational& ArithVariables::getSafeAssignment(ArithVar x) const{
-  Assert(inMaps(x));
-  if(d_safeAssignment.isKey(x)){
-    return d_safeAssignment[x];
-  }else{
-    return d_vars[x].d_assignment;
-  }
-}
-
-const DeltaRational& ArithVariables::getAssignment(ArithVar x, bool safe) const{
-  Assert(inMaps(x));
-  if(safe && d_safeAssignment.isKey(x)){
-    return d_safeAssignment[x];
-  }else{
-    return d_vars[x].d_assignment;
-  }
-}
-
-const DeltaRational& ArithVariables::getAssignment(ArithVar x) const{
-  Assert(inMaps(x));
-  return d_vars[x].d_assignment;
-}
-
-
-void ArithVariables::setLowerBoundConstraint(ConstraintP c){
-  AssertArgument(c != NullConstraint, "Cannot set a lower bound to NullConstraint.");
-  AssertArgument(c->isEquality() || c->isLowerBound(),
-                 "Constraint type must be set to an equality or UpperBound.");
-  ArithVar x = c->getVariable();
-  Trace("partial_model") << "setLowerBoundConstraint(" << x << ":" << c << ")" << endl;
-  Assert(inMaps(x));
-  Assert(greaterThanLowerBound(x, c->getValue()));
-
-  invalidateDelta();
-  VarInfo& vi = d_vars.get(x);
-  pushLowerBound(vi);
-  BoundsInfo prev;
-  if(vi.setLowerBound(c, prev)){
-    addToBoundQueue(x, prev);
-  }
-}
-
-void ArithVariables::setUpperBoundConstraint(ConstraintP c){
-  AssertArgument(c != NullConstraint, "Cannot set a upper bound to NullConstraint.");
-  AssertArgument(c->isEquality() || c->isUpperBound(),
-                 "Constraint type must be set to an equality or UpperBound.");
-
-  ArithVar x = c->getVariable();
-  Trace("partial_model") << "setUpperBoundConstraint(" << x << ":" << c << ")" << endl;
-  Assert(inMaps(x));
-  Assert(lessThanUpperBound(x, c->getValue()));
-
-  invalidateDelta();
-  VarInfo& vi = d_vars.get(x);
-  pushUpperBound(vi);
-  BoundsInfo prev;
-  if(vi.setUpperBound(c, prev)){
-    addToBoundQueue(x, prev);
-  }
-}
-
-int ArithVariables::cmpToLowerBound(ArithVar x, const DeltaRational& c) const{
-  if(!hasLowerBound(x)){
-    // l = -\intfy
-    // ? c < -\infty |-  _|_
-    return 1;
-  }else{
-    return c.cmp(getLowerBound(x));
-  }
-}
-
-int ArithVariables::cmpToUpperBound(ArithVar x, const DeltaRational& c) const{
-  if(!hasUpperBound(x)){
-    //u = \intfy
-    // ? c > \infty |-  _|_
-    return -1;
-  }else{
-    return c.cmp(getUpperBound(x));
-  }
-}
-
-bool ArithVariables::equalsLowerBound(ArithVar x, const DeltaRational& c){
-  if(!hasLowerBound(x)){
-    return false;
-  }else{
-    return c == getLowerBound(x);
-  }
-}
-bool ArithVariables::equalsUpperBound(ArithVar x, const DeltaRational& c){
-  if(!hasUpperBound(x)){
-    return false;
-  }else{
-    return c == getUpperBound(x);
-  }
-}
-
-bool ArithVariables::hasEitherBound(ArithVar x) const{
-  return hasLowerBound(x) || hasUpperBound(x);
-}
-
-bool ArithVariables::strictlyBelowUpperBound(ArithVar x) const{
-  return d_vars[x].d_cmpAssignmentUB < 0;
-}
-
-bool ArithVariables::strictlyAboveLowerBound(ArithVar x) const{
-  return d_vars[x].d_cmpAssignmentLB > 0;
-}
-
-bool ArithVariables::assignmentIsConsistent(ArithVar x) const{
-  return
-    d_vars[x].d_cmpAssignmentLB >= 0 &&
-    d_vars[x].d_cmpAssignmentUB <= 0;
-}
-
-
-void ArithVariables::clearSafeAssignments(bool revert){
-
-  if(revert && !d_safeAssignment.empty()){
-    invalidateDelta();
-  }
-
-  while(!d_safeAssignment.empty()){
-    ArithVar atBack = d_safeAssignment.back();
-    if(revert){
-      VarInfo& vi = d_vars.get(atBack);
-      BoundsInfo prev;
-      if(vi.setAssignment(d_safeAssignment[atBack], prev)){
-        addToBoundQueue(atBack, prev);
-      }
-    }
-    d_safeAssignment.pop_back();
-  }
-}
-
-void ArithVariables::revertAssignmentChanges(){
-  clearSafeAssignments(true);
-}
-void ArithVariables::commitAssignmentChanges(){
-  clearSafeAssignments(false);
-}
-
-bool ArithVariables::lowerBoundIsZero(ArithVar x){
-  return hasLowerBound(x) && getLowerBound(x).sgn() == 0;
-}
-
-bool ArithVariables::upperBoundIsZero(ArithVar x){
-  return hasUpperBound(x) && getUpperBound(x).sgn() == 0;
-}
-
-void ArithVariables::printEntireModel(std::ostream& out) const{
-  out << "---Printing Model ---" << std::endl;
-  for(var_iterator i = var_begin(), iend = var_end(); i != iend; ++i){
-    printModel(*i, out);
-  }
-  out << "---Done Model ---" << std::endl;
-}
-
-void ArithVariables::printModel(ArithVar x, std::ostream& out) const{
-  out << "model" << x << ": "
-      << asNode(x) << " "
-      << getAssignment(x) << " ";
-  if(!hasLowerBound(x)){
-    out << "no lb ";
-  }else{
-    out << getLowerBound(x) << " ";
-    out << getLowerBoundConstraint(x) << " ";
-  }
-  if(!hasUpperBound(x)){
-    out << "no ub ";
-  }else{
-    out << getUpperBound(x) << " ";
-    out << getUpperBoundConstraint(x) << " ";
-  }
-
-  if(isInteger(x) && !integralAssignment(x)){
-    out << "(not an integer)" << endl;
-  }
-  out << endl;
-}
-
-void ArithVariables::printModel(ArithVar x) const{
-  printModel(x,  Trace("model"));
-}
-
-void ArithVariables::pushUpperBound(VarInfo& vi){
-  ++vi.d_pushCount;
-  d_ubRevertHistory.push_back(make_pair(vi.d_var, vi.d_ub));
-}
-void ArithVariables::pushLowerBound(VarInfo& vi){
-  ++vi.d_pushCount;
-  d_lbRevertHistory.push_back(make_pair(vi.d_var, vi.d_lb));
-}
-
-void ArithVariables::popUpperBound(AVCPair* c){
-  ArithVar x = c->first;
-  VarInfo& vi = d_vars.get(x);
-  BoundsInfo prev;
-  if(vi.setUpperBound(c->second, prev)){
-    addToBoundQueue(x, prev);
-  }
-  --vi.d_pushCount;
-}
-
-void ArithVariables::popLowerBound(AVCPair* c){
-  ArithVar x = c->first;
-  VarInfo& vi = d_vars.get(x);
-  BoundsInfo prev;
-  if(vi.setLowerBound(c->second, prev)){
-    addToBoundQueue(x, prev);
-  }
-  --vi.d_pushCount;
-}
-
-void ArithVariables::addToBoundQueue(ArithVar v, const BoundsInfo& prev){
-  if(d_enqueueingBoundCounts && !d_boundsQueue.isKey(v)){
-    d_boundsQueue.set(v, prev);
-  }
-}
-
-BoundsInfo ArithVariables::selectBoundsInfo(ArithVar v, bool old) const {
-  if(old && d_boundsQueue.isKey(v)){
-    return d_boundsQueue[v];
-  }else{
-    return boundsInfo(v);
-  }
-}
-
-bool ArithVariables::boundsQueueEmpty() const {
-  return d_boundsQueue.empty();
-}
-
-void ArithVariables::processBoundsQueue(BoundUpdateCallback& changed){
-  while(!boundsQueueEmpty()){
-    ArithVar v = d_boundsQueue.back();
-    BoundsInfo prev = d_boundsQueue[v];
-    d_boundsQueue.pop_back();
-    BoundsInfo curr = boundsInfo(v);
-    if(prev != curr){
-      changed(v, prev);
-    }
-  }
-}
-
-void ArithVariables::invalidateDelta() {
-  d_deltaIsSafe = false;
-}
-
-void ArithVariables::setDelta(const Rational& d){
-  d_delta = d;
-  d_deltaIsSafe = true;
-}
-
-void ArithVariables::startQueueingBoundCounts(){
-  d_enqueueingBoundCounts = true;
-}
-void ArithVariables::stopQueueingBoundCounts(){
-  d_enqueueingBoundCounts = false;
-}
-
-bool ArithVariables::inMaps(ArithVar x) const{
-  return x < getNumberOfVariables();
-}
-
-ArithVariables::LowerBoundCleanUp::LowerBoundCleanUp(ArithVariables* pm)
-  : d_pm(pm)
-{}
-void ArithVariables::LowerBoundCleanUp::operator()(AVCPair* p){
-  d_pm->popLowerBound(p);
-}
-
-ArithVariables::UpperBoundCleanUp::UpperBoundCleanUp(ArithVariables* pm)
-  : d_pm(pm)
-{}
-void ArithVariables::UpperBoundCleanUp::operator()(AVCPair* p){
-  d_pm->popUpperBound(p);
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/partial_model.h b/src/theory/arith/partial_model.h
deleted file mode 100644 (file)
index 3f027e6..0000000
+++ /dev/null
@@ -1,420 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Morgan Deters, Aina Niemetz
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Datastructures that track variable by variable information.
- *
- * This is a datastructure that tracks variable specific information.
- * This is partially context dependent to back track upper/lower bounds
- * and information derived from these.
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__PARTIAL_MODEL_H
-#define CVC5__THEORY__ARITH__PARTIAL_MODEL_H
-
-#include <vector>
-
-#include "context/cdlist.h"
-#include "expr/node.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/bound_counts.h"
-#include "theory/arith/callbacks.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/arith/delta_rational.h"
-
-namespace cvc5::context {
-class Context;
-}
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/**
- * (For the moment) the type hierarchy goes as:
- * Integer <: Real
- * The type number of a variable is an integer representing the most specific
- * type of the variable. The possible values of type number are:
- */
-enum class ArithType {
-  Unset,
-  Real,
-  Integer,
-};
-
-class ArithVariables {
-private:
-
-  class VarInfo {
-    friend class ArithVariables;
-    ArithVar d_var;
-
-    DeltaRational d_assignment;
-    ConstraintP d_lb;
-    ConstraintP d_ub;
-    int d_cmpAssignmentLB;
-    int d_cmpAssignmentUB;
-
-    unsigned d_pushCount;
-    ArithType d_type;
-    Node d_node;
-    bool d_auxiliary;
-
-  public:
-    VarInfo();
-
-    bool setAssignment(const DeltaRational& r, BoundsInfo& prev);
-    bool setLowerBound(ConstraintP c, BoundsInfo& prev);
-    bool setUpperBound(ConstraintP c, BoundsInfo& prev);
-
-    /** Returns true if this VarInfo has been initialized. */
-    bool initialized() const;
-
-    /**
-     * Initializes the VarInfo with the ArithVar index it is associated with,
-     * the node that the variable represents, and whether it is an auxillary
-     * variable.
-     */
-    void initialize(ArithVar v, Node n, bool aux);
-
-    /** Uninitializes the VarInfo. */
-    void uninitialize();
-
-    bool canBeReclaimed() const;
-
-    /** Indicator variables for if the assignment is equal to the upper
-     * and lower bounds. */
-    BoundCounts atBoundCounts() const;
-
-    /** Combination of indicator variables for whether it has upper and
-     * lower bounds.  */
-    BoundCounts hasBoundCounts() const;
-
-    /** Stores both atBoundCounts() and hasBoundCounts().  */
-    BoundsInfo boundsInfo() const;
-  };
-
-  /**Maps from ArithVar -> VarInfo */
-  typedef DenseMap<VarInfo> VarInfoVec;
-
-  /** This maps an ArithVar to its Variable information.*/
-  VarInfoVec d_vars;
-
-  /** Partial Map from Arithvar -> PreviousAssignment */
-  DenseMap<DeltaRational> d_safeAssignment;
-
-  /** if d_vars.isKey(x), then x < d_numberOfVariables */
-  ArithVar d_numberOfVariables;
-
-  /** [0, d_numberOfVariables) \intersect d_vars.keys == d_pool */
-  // Everything in the pool is fair game.
-  // There must be NO outstanding assertions
-  std::vector<ArithVar> d_pool;
-  std::vector<ArithVar> d_released;
-  //std::list<ArithVar>::iterator d_releasedIterator;
-
-  // Reverse Map from Node to ArithVar
-  // Inverse of d_vars[x].d_node
-  NodeToArithVarMap d_nodeToArithVarMap;
-
-
-  /** The queue of constraints where the assignment is at the bound.*/
-  DenseMap<BoundsInfo> d_boundsQueue;
-
-  /**
-   * If this is true, record the incoming changes to the bound information.
-   * If this is false, the responsibility of recording the changes is
-   * LinearEqualities's.
-   */
-  bool d_enqueueingBoundCounts;
-
- public:
-
-  /** Returns the number of variables. */
-  ArithVar getNumberOfVariables() const;
-
-  /** Returns true if the node has an associated variables. */
-  bool hasArithVar(TNode x) const;
-
-  /** Returns true if the variable has a defining node. */
-  bool hasNode(ArithVar a) const;
-
-  /** Returns the ArithVar associated with a node. */
-  ArithVar asArithVar(TNode x) const;
-
-  /** Returns the node associated with an ArithVar. */
-  Node asNode(ArithVar a) const;
-
-  /** Allocates a freshly allocated variables. */
-  ArithVar allocateVariable();
-
-  class var_iterator {
-  private:
-    const VarInfoVec* d_vars;
-    VarInfoVec::const_iterator d_wrapped;
-  public:
-    var_iterator();
-    var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci);
-    var_iterator& operator++();
-
-    bool operator==(const var_iterator& other) const;
-    bool operator!=(const var_iterator& other) const;
-    ArithVar operator*() const;
-
-  private:
-    void nextInitialized();
-  };
-
-  var_iterator var_begin() const;
-  var_iterator var_end() const;
-
-
-  bool canBeReleased(ArithVar v) const;
-  void releaseArithVar(ArithVar v);
-  void attemptToReclaimReleased();
-
-  /** Is this variable guaranteed to have an integer assignment?
-   * (Should agree with the type system.) */
-  bool isInteger(ArithVar x) const;
-
-  /** Is the assignment to x integral? */
-  bool integralAssignment(ArithVar x) const;
-
-  /* Is this variable defined as a linear sum of other variables? */
-  bool isAuxiliary(ArithVar x) const;
-
-  /* Is the variable both input and not auxiliary? */
-  bool isIntegerInput(ArithVar x) const;
-
- private:
-
-  typedef std::pair<ArithVar, ConstraintP> AVCPair;
-  class LowerBoundCleanUp {
-  private:
-    ArithVariables* d_pm;
-  public:
-    LowerBoundCleanUp(ArithVariables* pm);
-    void operator()(AVCPair* restore);
-  };
-
-  class UpperBoundCleanUp {
-  private:
-    ArithVariables* d_pm;
-  public:
-    UpperBoundCleanUp(ArithVariables* pm);
-    void operator()(AVCPair* restore);
-  };
-
-  typedef context::CDList<AVCPair, LowerBoundCleanUp> LBReverts;
-  LBReverts d_lbRevertHistory;
-
-  typedef context::CDList<AVCPair, UpperBoundCleanUp> UBReverts;
-  UBReverts d_ubRevertHistory;
-
-  void pushUpperBound(VarInfo&);
-  void popUpperBound(AVCPair*);
-  void pushLowerBound(VarInfo&);
-  void popLowerBound(AVCPair*);
-
-  // This is true when setDelta() is called, until invalidateDelta is called
-  bool d_deltaIsSafe;
-  // Cache of a value of delta to ensure a total order.
-  Rational d_delta;
-  // Function to call if the value of delta needs to be recomputed.
-  DeltaComputeCallback d_deltaComputingFunc;
-
-
-public:
- ArithVariables(context::Context* c, DeltaComputeCallback deltaComputation);
-
- /**
-  * This sets the lower bound for a variable in the current context.
-  * This must be stronger the previous constraint.
-  */
- void setLowerBoundConstraint(ConstraintP lb);
-
- /**
-  * This sets the upper bound for a variable in the current context.
-  * This must be stronger the previous constraint.
-  */
- void setUpperBoundConstraint(ConstraintP ub);
-
- /** Returns the constraint for the upper bound of a variable. */
- inline ConstraintP getUpperBoundConstraint(ArithVar x) const
- {
-   return d_vars[x].d_ub;
- }
-  /** Returns the constraint for the lower bound of a variable. */
-  inline ConstraintP getLowerBoundConstraint(ArithVar x) const{
-    return d_vars[x].d_lb;
-  }
-
-  /* Initializes a variable to a safe value.*/
-  void initialize(ArithVar x, Node n, bool aux);
-
-  ArithVar allocate(Node n, bool aux = false);
-
-  /* Gets the last assignment to a variable that is known to be consistent. */
-  const DeltaRational& getSafeAssignment(ArithVar x) const;
-  const DeltaRational& getAssignment(ArithVar x, bool safe) const;
-
-  /* Reverts all variable assignments to their safe values. */
-  void revertAssignmentChanges();
-
-  /* Commits all variables assignments as safe.*/
-  void commitAssignmentChanges();
-
-
-  bool lowerBoundIsZero(ArithVar x);
-  bool upperBoundIsZero(ArithVar x);
-
-  bool boundsAreEqual(ArithVar x) const;
-
-  /* Sets an unsafe variable assignment */
-  void setAssignment(ArithVar x, const DeltaRational& r);
-  void setAssignment(ArithVar x, const DeltaRational& safe, const DeltaRational& r);
-
-
-  /** Must know that the bound exists before calling this! */
-  const DeltaRational& getUpperBound(ArithVar x) const;
-  const DeltaRational& getLowerBound(ArithVar x) const;
-  const DeltaRational& getAssignment(ArithVar x) const;
-
-
-  bool equalsLowerBound(ArithVar x, const DeltaRational& c);
-  bool equalsUpperBound(ArithVar x, const DeltaRational& c);
-
-  /**
-   * If lowerbound > - \infty:
-   *   return getAssignment(x).cmp(getLowerBound(x))
-   * If lowerbound = - \infty:
-   *   return 1
-   */
-  int cmpToLowerBound(ArithVar x, const DeltaRational& c) const;
-
-  inline bool strictlyLessThanLowerBound(ArithVar x, const DeltaRational& c) const{
-    return cmpToLowerBound(x, c) < 0;
-  }
-  inline bool lessThanLowerBound(ArithVar x, const DeltaRational& c) const{
-    return cmpToLowerBound(x, c) <= 0;
-  }
-
-  inline bool strictlyGreaterThanLowerBound(ArithVar x, const DeltaRational& c) const{
-    return cmpToLowerBound(x, c) > 0;
-  }
-
-  inline bool greaterThanLowerBound(ArithVar x, const DeltaRational& c) const{
-    return cmpToLowerBound(x, c) >= 0;
-  }
-  /**
-   * If upperbound < \infty:
-   *   return getAssignment(x).cmp(getUpperBound(x))
-   * If upperbound = \infty:
-   *   return -1
-   */
-  int cmpToUpperBound(ArithVar x, const DeltaRational& c) const;
-
-  inline bool strictlyLessThanUpperBound(ArithVar x, const DeltaRational& c) const{
-    return cmpToUpperBound(x, c) < 0;
-  }
-
-  inline bool lessThanUpperBound(ArithVar x, const DeltaRational& c) const{
-    return cmpToUpperBound(x, c) <= 0;
-  }
-
-  inline bool strictlyGreaterThanUpperBound(ArithVar x, const DeltaRational& c) const{
-    return cmpToUpperBound(x, c) > 0;
-  }
-
-  inline bool greaterThanUpperBound(ArithVar x, const DeltaRational& c) const{
-    return cmpToUpperBound(x, c) >= 0;
-  }
-
-  inline int cmpAssignmentLowerBound(ArithVar x) const{
-    return d_vars[x].d_cmpAssignmentLB;
-  }
-  inline int cmpAssignmentUpperBound(ArithVar x) const{
-    return d_vars[x].d_cmpAssignmentUB;
-  }
-
-  inline BoundCounts atBoundCounts(ArithVar x) const {
-    return d_vars[x].atBoundCounts();
-  }
-  inline BoundCounts hasBoundCounts(ArithVar x) const {
-    return d_vars[x].hasBoundCounts();
-  }
-  inline BoundsInfo boundsInfo(ArithVar x) const{
-    return d_vars[x].boundsInfo();
-  }
-
-  bool strictlyBelowUpperBound(ArithVar x) const;
-  bool strictlyAboveLowerBound(ArithVar x) const;
-  bool assignmentIsConsistent(ArithVar x) const;
-
-  void printModel(ArithVar x, std::ostream& out) const;
-  void printModel(ArithVar x) const;
-
-  /** returns true iff x has both a lower and upper bound. */
-  bool hasEitherBound(ArithVar x) const;
-  inline bool hasLowerBound(ArithVar x) const{
-    return d_vars[x].d_lb != NullConstraint;
-  }
-  inline bool hasUpperBound(ArithVar x) const{
-    return d_vars[x].d_ub != NullConstraint;
-  }
-
-  const Rational& getDelta();
-
-  void invalidateDelta();
-
-  void setDelta(const Rational& d);
-
-  void startQueueingBoundCounts();
-  void stopQueueingBoundCounts();
-  void addToBoundQueue(ArithVar v, const BoundsInfo& prev);
-
-  BoundsInfo selectBoundsInfo(ArithVar v, bool old) const;
-
-  bool boundsQueueEmpty() const;
-  void processBoundsQueue(BoundUpdateCallback& changed);
-
-  void printEntireModel(std::ostream& out) const;
-
-
-  /**
-   * Precondition: assumes boundsAreEqual(x).
-   * If the either the lower/ upper bound is an equality, eq,
-   * this returns make_pair(eq, NullConstraint).
-   * Otherwise, this returns make_pair(lb, ub).
-   */
-  std::pair<ConstraintP, ConstraintP> explainEqualBounds(ArithVar x) const;
-
-private:
-
-  /**
-   * This function implements the mostly identical:
-   * revertAssignmentChanges() and commitAssignmentChanges().
-   */
-  void clearSafeAssignments(bool revert);
-
-  bool debugEqualSizes();
-
-  bool inMaps(ArithVar x) const;
-
-};/* class ArithVariables */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__PARTIAL_MODEL_H */
index 93ffb542d841e438bdc9e1366e211dfbda08864a..f05bd218546efab6e27996b1b86c3ce40b47b432 100644 (file)
@@ -21,8 +21,7 @@
 #include "expr/skolem_manager.h"
 #include "theory/arith/arith_poly_norm.h"
 #include "theory/arith/arith_utilities.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/normal_form.h"
+#include "theory/arith/linear/constraint.h"
 #include "theory/arith/operator_elim.h"
 
 using namespace cvc5::internal::kind;
diff --git a/src/theory/arith/simplex.cpp b/src/theory/arith/simplex.cpp
deleted file mode 100644 (file)
index 66aba10..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- */
-
-#include "theory/arith/simplex.h"
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "options/smt_options.h"
-#include "smt/env.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/error_set.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/tableau.h"
-#include "util/statistics_value.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-SimplexDecisionProcedure::SimplexDecisionProcedure(
-    Env& env,
-    LinearEqualityModule& linEq,
-    ErrorSet& errors,
-    RaiseConflict conflictChannel,
-    TempVarMalloc tvmalloc)
-    : EnvObj(env),
-      d_pivots(0),
-      d_conflictVariables(),
-      d_linEq(linEq),
-      d_variables(d_linEq.getVariables()),
-      d_tableau(d_linEq.getTableau()),
-      d_errorSet(errors),
-      d_numVariables(0),
-      d_conflictChannel(conflictChannel),
-      d_conflictBuilder(NULL),
-      d_arithVarMalloc(tvmalloc),
-      d_errorSize(0),
-      d_zero(0),
-      d_posOne(1),
-      d_negOne(-1)
-{
-  d_heuristicRule = options().arith.arithErrorSelectionRule;
-  d_errorSet.setSelectionRule(d_heuristicRule);
-  d_conflictBuilder = new FarkasConflictBuilder(options().smt.produceProofs);
-}
-
-SimplexDecisionProcedure::~SimplexDecisionProcedure(){
-  delete d_conflictBuilder;
-}
-
-
-bool SimplexDecisionProcedure::standardProcessSignals(TimerStat &timer, IntStat& conflicts) {
-  TimerStat::CodeTimer codeTimer(timer);
-  Assert(d_conflictVariables.empty());
-
-  while(d_errorSet.moreSignals()){
-    ArithVar curr = d_errorSet.topSignal();
-    if(d_tableau.isBasic(curr) && !d_variables.assignmentIsConsistent(curr)){
-      Assert(d_linEq.basicIsTracked(curr));
-
-      if(!d_conflictVariables.isMember(curr) && checkBasicForConflict(curr)){
-
-        Trace("recentlyViolated")
-          << "It worked? "
-          << conflicts.get()
-          << " " << curr
-          << " "  << checkBasicForConflict(curr) << endl;
-        reportConflict(curr);
-        ++conflicts;
-      }
-    }
-    // Pop signal afterwards in case d_linEq.trackVariable(curr);
-    // is needed for for the ErrorSet
-    d_errorSet.popSignal();
-  }
-  d_errorSize = d_errorSet.errorSize();
-
-  Assert(d_errorSet.noSignals());
-  return !d_conflictVariables.empty();
-}
-
-/** Reports a conflict to on the output channel. */
-void SimplexDecisionProcedure::reportConflict(ArithVar basic){
-  Assert(!d_conflictVariables.isMember(basic));
-  Assert(checkBasicForConflict(basic));
-
-  ConstraintCP conflicted = generateConflictForBasic(basic);
-  Assert(conflicted != NullConstraint);
-  d_conflictChannel.raiseConflict(conflicted, InferenceId::ARITH_CONF_SIMPLEX);
-
-  d_conflictVariables.add(basic);
-}
-
-ConstraintCP SimplexDecisionProcedure::generateConflictForBasic(ArithVar basic) const {
-  Assert(d_tableau.isBasic(basic));
-  Assert(checkBasicForConflict(basic));
-
-  if(d_variables.cmpAssignmentLowerBound(basic) < 0){
-    Assert(d_linEq.nonbasicsAtUpperBounds(basic));
-    return d_linEq.generateConflictBelowLowerBound(basic, *d_conflictBuilder);
-  }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){
-    Assert(d_linEq.nonbasicsAtLowerBounds(basic));
-    return d_linEq.generateConflictAboveUpperBound(basic, *d_conflictBuilder);
-  }else{
-    Unreachable();
-    return NullConstraint;
-  }
-}
-bool SimplexDecisionProcedure::maybeGenerateConflictForBasic(ArithVar basic) const {
-  if(checkBasicForConflict(basic)){
-    ConstraintCP conflicted = generateConflictForBasic(basic);
-    d_conflictChannel.raiseConflict(conflicted, InferenceId::UNKNOWN);
-    return true;
-  }else{
-    return false;
-  }
-}
-
-bool SimplexDecisionProcedure::checkBasicForConflict(ArithVar basic) const {
-  Assert(d_tableau.isBasic(basic));
-  Assert(d_linEq.basicIsTracked(basic));
-
-  if(d_variables.cmpAssignmentLowerBound(basic) < 0){
-    if(d_linEq.nonbasicsAtUpperBounds(basic)){
-      return true;
-    }
-  }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){
-    if(d_linEq.nonbasicsAtLowerBounds(basic)){
-      return true;
-    }
-  }
-  return false;
-}
-
-void SimplexDecisionProcedure::tearDownInfeasiblityFunction(TimerStat& timer, ArithVar tmp){
-  TimerStat::CodeTimer codeTimer(timer);
-  Assert(tmp != ARITHVAR_SENTINEL);
-  Assert(d_tableau.isBasic(tmp));
-
-  RowIndex ri = d_tableau.basicToRowIndex(tmp);
-  d_linEq.stopTrackingRowIndex(ri);
-  d_tableau.removeBasicRow(tmp);
-  releaseVariable(tmp);
-}
-
-void SimplexDecisionProcedure::shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped){
-  TimerStat::CodeTimer codeTimer(timer);
-  for(ArithVarVec::const_iterator i=dropped.begin(), i_end = dropped.end(); i != i_end; ++i){
-    ArithVar back = *i;
-
-    int focusSgn = d_errorSet.focusSgn(back);
-    Rational chg(-focusSgn);
-
-    d_linEq.substitutePlusTimesConstant(inf, back, chg);
-  }
-}
-
-void SimplexDecisionProcedure::adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges){
-  TimerStat::CodeTimer codeTimer(timer);
-  for(AVIntPairVec::const_iterator i=focusChanges.begin(), i_end = focusChanges.end(); i != i_end; ++i){
-    ArithVar v = (*i).first;
-    int focusChange = (*i).second;
-
-    Rational chg(focusChange);
-    if(d_tableau.isBasic(v)){
-      d_linEq.substitutePlusTimesConstant(inf, v, chg);
-    }else{
-      d_linEq.directlyAddToCoefficient(inf, v, chg);
-    }
-  }
-}
-
-void SimplexDecisionProcedure::addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e){
-  AVIntPairVec justE;
-  int sgn  = d_errorSet.getSgn(e);
-  justE.push_back(make_pair(e, sgn));
-  adjustInfeasFunc(timer, inf, justE);
-}
-
-void SimplexDecisionProcedure::removeFromInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e){
-  AVIntPairVec justE;
-  int opSgn  = -d_errorSet.getSgn(e);
-  justE.push_back(make_pair(e, opSgn));
-  adjustInfeasFunc(timer, inf, justE);
-}
-
-ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set){
-  Trace("constructInfeasiblityFunction") << "constructInfeasiblityFunction start" << endl;
-
-  TimerStat::CodeTimer codeTimer(timer);
-  Assert(!d_errorSet.focusEmpty());
-  Assert(debugIsASet(set));
-
-  ArithVar inf = requestVariable();
-  Assert(inf != ARITHVAR_SENTINEL);
-
-  std::vector<Rational> coeffs;
-  std::vector<ArithVar> variables;
-
-  for(ArithVarVec::const_iterator iter = set.begin(), iend = set.end(); iter != iend; ++iter){
-    ArithVar e = *iter;
-
-    Assert(d_tableau.isBasic(e));
-    Assert(!d_variables.assignmentIsConsistent(e));
-
-    int sgn = d_errorSet.getSgn(e);
-    Assert(sgn == -1 || sgn == 1);
-    const Rational& violatedCoeff = sgn < 0 ? d_negOne : d_posOne;
-    coeffs.push_back(violatedCoeff);
-    variables.push_back(e);
-
-    Trace("constructInfeasiblityFunction") << violatedCoeff << " " << e << endl;
-
-  }
-  d_tableau.addRow(inf, coeffs, variables);
-  DeltaRational newAssignment = d_linEq.computeRowValue(inf, false);
-  d_variables.setAssignment(inf, newAssignment);
-
-  //d_linEq.trackVariable(inf);
-  d_linEq.trackRowIndex(d_tableau.basicToRowIndex(inf));
-
-  Trace("constructInfeasiblityFunction") << inf << " " << newAssignment << endl;
-  Trace("constructInfeasiblityFunction") << "constructInfeasiblityFunction done" << endl;
-  return inf;
-}
-
-ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer){
-  ArithVarVec inError;
-  d_errorSet.pushFocusInto(inError);
-  return constructInfeasiblityFunction(timer, inError);
-}
-
-ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, ArithVar e){
-  ArithVarVec justE;
-  justE.push_back(e);
-  return constructInfeasiblityFunction(timer, justE);
-}
-
-void SimplexDecisionProcedure::addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic){
-  pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
-  sgns[p].push_back(basic);
-}
-
-void SimplexDecisionProcedure::addRowSgns(sgn_table& sgns, ArithVar basic, int norm){
-  for(Tableau::RowIterator i = d_tableau.basicRowIterator(basic); !i.atEnd(); ++i){
-    const Tableau::Entry& entry = *i;
-    ArithVar v = entry.getColVar();
-    int sgn = (entry.getCoefficient().sgn());
-    addSgn(sgns, v, norm * sgn, basic);
-  }
-}
-
-ArithVar SimplexDecisionProcedure::find_basic_in_sgns(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m, bool inside){
-  pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
-  sgn_table::const_iterator i = sgns.find(p);
-
-  if(i != sgns.end()){
-    const ArithVarVec& vec = (*i).second;
-    for(ArithVarVec::const_iterator viter = vec.begin(), vend = vec.end(); viter != vend; ++viter){
-      ArithVar curr = *viter;
-      if(inside == m.isMember(curr)){
-        return curr;
-      }
-    }
-  }
-  return ARITHVAR_SENTINEL;
-}
-
-SimplexDecisionProcedure::sgn_table::const_iterator SimplexDecisionProcedure::find_sgns(const sgn_table& sgns, ArithVar col, int sgn){
-  pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
-  return sgns.find(p);
-}
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/simplex.h b/src/theory/arith/simplex.h
deleted file mode 100644 (file)
index fd50a85..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- *
- * This implements the Simplex module for the Simpelx for DPLL(T) decision
- * procedure.
- * See the Simplex for DPLL(T) technical report for more background.(citation?)
- * This shares with the theory a Tableau, and a PartialModel that:
- *  - satisfies the equalities in the Tableau, and
- *  - the assignment for the non-basic variables satisfies their bounds.
- * This is required to either produce a conflict or satisifying PartialModel.
- * Further, we require being told when a basic variable updates its value.
- *
- * During the Simplex search we maintain a queue of variables.
- * The queue is required to contain all of the basic variables that voilate
- * their bounds.
- * As elimination from the queue is more efficient to be done lazily,
- * we do not maintain that the queue of variables needs to be only basic
- * variables or only variables that satisfy their bounds.
- *
- * The simplex procedure roughly follows Alberto's thesis. (citation?)
- * There is one round of selecting using a heuristic pivoting rule.
- * (See PreferenceFunction Documentation for the available options.)
- * The non-basic variable is the one that appears in the fewest pivots.
- * (Bruno says that Leonardo invented this first.)
- * After this, Bland's pivot rule is invoked.
- *
- * During this proccess, we periodically inspect the queue of variables to
- * 1) remove now extraneous extries,
- * 2) detect conflicts that are "waiting" on the queue but may not be detected
- *    by the current queue heuristics, and
- * 3) detect multiple conflicts.
- *
- * Conflicts are greedily slackened to use the weakest bounds that still
- * produce the conflict.
- *
- * Extra things tracked atm: (Subject to change at Tim's whims)
- * - A superset of all of the newly pivoted variables.
- * - A queue of additional conflicts that were discovered by Simplex.
- *   These are theory valid and are currently turned into lemmas
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <unordered_map>
-
-#include "options/arith_options.h"
-#include "smt/env_obj.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/partial_model.h"
-#include "util/dense_map.h"
-#include "util/result.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class ErrorSet;
-class LinearEqualityModule;
-class Tableau;
-
-class SimplexDecisionProcedure : protected EnvObj
-{
- protected:
-  typedef std::vector< std::pair<ArithVar, int> > AVIntPairVec;
-
-  /** Pivot count of the current round of pivoting. */
-  uint32_t d_pivots;
-
-  /** The set of variables that are in conflict in this round. */
-  DenseSet d_conflictVariables;
-
-  /** The rule to use for heuristic selection mode. */
-  options::ErrorSelectionRule d_heuristicRule;
-
-  /** Linear equality module. */
-  LinearEqualityModule& d_linEq;
-
-  /**
-   * Manages information about the assignment and upper and lower bounds on
-   * variables.
-   * Partial model matches that in LinearEqualityModule.
-   */
-  ArithVariables& d_variables;
-
-  /**
-   * Stores the linear equalities used by Simplex.
-   * Tableau from the LinearEquality module.
-   */
-  Tableau& d_tableau;
-
-  /** Contains a superset of the basic variables in violation of their bounds. */
-  ErrorSet& d_errorSet;
-
-  /** Number of variables in the system. This is used for tuning heuristics. */
-  ArithVar d_numVariables;
-
-  /** This is the call back channel for Simplex to report conflicts. */
-  RaiseConflict d_conflictChannel;
-
-  /** This is the call back channel for Simplex to report conflicts. */
-  FarkasConflictBuilder* d_conflictBuilder;
-
-  /** Used for requesting d_opt, bound and error variables for primal.*/
-  TempVarMalloc d_arithVarMalloc;
-
-  /** The size of the error set. */
-  uint32_t d_errorSize;
-
-  /** A local copy of 0. */
-  const Rational d_zero;
-
-  /** A local copy of 1. */
-  const Rational d_posOne;
-
-  /** A local copy of -1. */
-  const Rational d_negOne;
-
-  /**
-   * Locally cached value of arithStandardCheckVarOrderPivots option. It is
-   * cached here to allow for single runs with a different (lower) limit.
-   */
-  int64_t d_varOrderPivotLimit = -1;
-
-  ArithVar constructInfeasiblityFunction(TimerStat& timer);
-  ArithVar constructInfeasiblityFunction(TimerStat& timer, ArithVar e);
-  ArithVar constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set);
-
-  void tearDownInfeasiblityFunction(TimerStat& timer, ArithVar inf);
-  void adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges);
-  void addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e);
-  void removeFromInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e);
-  void shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped);
-
-public:
- SimplexDecisionProcedure(Env& env,
-                          LinearEqualityModule& linEq,
-                          ErrorSet& errors,
-                          RaiseConflict conflictChannel,
-                          TempVarMalloc tvmalloc);
- virtual ~SimplexDecisionProcedure();
-
- /**
-  * Tries to update the assignments of variables such that all of the
-  * assignments are consistent with their bounds.
-  * This is done by a simplex search through the possible bases of the tableau.
-  *
-  * If all of the variables can be made consistent with their bounds
-  * SAT is returned. Otherwise UNSAT is returned, and at least 1 conflict
-  * was reported on the conflictCallback passed to the Module.
-  *
-  * Tableau pivoting is performed so variables may switch from being basic to
-  * nonbasic and vice versa.
-  *
-  * Corresponds to the "check()" procedure in [Cav06].
-  */
- virtual Result::Status findModel(bool exactResult) = 0;
-
- void increaseMax() { d_numVariables++; }
-
- uint32_t getPivots() const { return d_pivots; }
-
- /** Set the variable ordering pivot limit */
- void setVarOrderPivotLimit(int64_t value) { d_varOrderPivotLimit = value; }
-
-protected:
- /** Reports a conflict to on the output channel. */
- void reportConflict(ArithVar basic);
-
- /**
-  * Checks a basic variable, b, to see if it is in conflict.
-  * If a conflict is discovered a node summarizing the conflict is returned.
-  * Otherwise, Node::null() is returned.
-  */
- bool maybeGenerateConflictForBasic(ArithVar basic) const;
-
- /** Returns true if a tracked basic variable has a conflict on it. */
- bool checkBasicForConflict(ArithVar b) const;
-
- /**
-  * If a basic variable has a conflict on its row,
-  * this produces a minimized row on the conflict channel.
-  */
- ConstraintCP generateConflictForBasic(ArithVar basic) const;
-
- /** Gets a fresh variable from TheoryArith. */
- ArithVar requestVariable() { return d_arithVarMalloc.request(); }
-
- /** Releases a requested variable from TheoryArith.*/
- void releaseVariable(ArithVar v) { d_arithVarMalloc.release(v); }
-
- /** Post condition: !d_queue.moreSignals() */
- bool standardProcessSignals(TimerStat& timer, IntStat& conflictStat);
-
- struct ArithVarIntPairHashFunc
- {
-   size_t operator()(const std::pair<ArithVar, int>& p) const
-   {
-     size_t h1 = std::hash<ArithVar>()(p.first);
-     size_t h2 = std::hash<int>()(p.second);
-     return h1 + 3389 * h2;
-   }
-  };
-
-  typedef std::unordered_map< std::pair<ArithVar, int>, ArithVarVec, ArithVarIntPairHashFunc> sgn_table;
-
-  static inline int determinizeSgn(int sgn){
-    return sgn < 0 ? -1 : (sgn == 0 ? 0 : 1);
-  }
-
-  void addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic);
-  void addRowSgns(sgn_table& sgns, ArithVar basic, int norm);
-  ArithVar find_basic_in_sgns(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m, bool inside);
-
-  sgn_table::const_iterator find_sgns(const sgn_table& sgns, ArithVar col, int sgn);
-
-}; /* class SimplexDecisionProcedure */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/simplex_update.cpp b/src/theory/arith/simplex_update.cpp
deleted file mode 100644 (file)
index 19d5fcc..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Andres Noetzli, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This implements the UpdateInfo.
- */
-
-#include "theory/arith/simplex_update.h"
-
-#include "theory/arith/constraint.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/*
- * Generates a string representation of std::optional and inserts it into a
- * stream.
- *
- * Note: We define this function here in the cvc5::internal::theory::arith namespace,
- * because it would otherwise not be found for std::optional<int>. This is due
- * to the argument-dependent lookup rules.
- *
- * @param out The stream
- * @param m The value
- * @return The stream
- */
-std::ostream& operator<<(std::ostream& out, const std::optional<int>& m)
-{
-  return cvc5::internal::operator<<(out, m);
-}
-
-UpdateInfo::UpdateInfo():
-  d_nonbasic(ARITHVAR_SENTINEL),
-  d_nonbasicDirection(0),
-  d_nonbasicDelta(),
-  d_foundConflict(false),
-  d_errorsChange(),
-  d_focusDirection(),
-  d_tableauCoefficient(),
-  d_limiting(NullConstraint),
-  d_witness(AntiProductive)
-{}
-
-UpdateInfo::UpdateInfo(ArithVar nb, int dir):
-  d_nonbasic(nb),
-  d_nonbasicDirection(dir),
-  d_nonbasicDelta(),
-  d_foundConflict(false),
-  d_errorsChange(),
-  d_focusDirection(),
-  d_tableauCoefficient(),
-  d_limiting(NullConstraint),
-  d_witness(AntiProductive)
-{
-  Assert(dir == 1 || dir == -1);
-}
-
-UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP c):
-  d_nonbasic(nb),
-  d_nonbasicDirection(delta.sgn()),
-  d_nonbasicDelta(delta),
-  d_foundConflict(true),
-  d_errorsChange(),
-  d_focusDirection(),
-  d_tableauCoefficient(&r),
-  d_limiting(c),
-  d_witness(ConflictFound)
-{
-  Assert(conflict);
-}
-
-UpdateInfo UpdateInfo::conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim){
-  return UpdateInfo(true, nb, delta, r, lim);
-}
-
-void UpdateInfo::updateUnbounded(const DeltaRational& delta, int ec, int f){
-  d_limiting = NullConstraint;
-  d_nonbasicDelta = delta;
-  d_errorsChange = ec;
-  d_focusDirection = f;
-  d_tableauCoefficient.reset();
-  updateWitness();
-  Assert(unbounded());
-  Assert(improvement(d_witness));
-  Assert(!describesPivot());
-  Assert(debugSgnAgreement());
-}
-void UpdateInfo::updatePureFocus(const DeltaRational& delta, ConstraintP c){
-  d_limiting = c;
-  d_nonbasicDelta = delta;
-  d_errorsChange.reset();
-  d_focusDirection = 1;
-  d_tableauCoefficient.reset();
-  updateWitness();
-  Assert(!describesPivot());
-  Assert(improvement(d_witness));
-  Assert(debugSgnAgreement());
-}
-
-void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c){
-  d_limiting = c;
-  d_nonbasicDelta = delta;
-  d_errorsChange.reset();
-  d_focusDirection.reset();
-  updateWitness();
-  Assert(describesPivot());
-  Assert(debugSgnAgreement());
-}
-
-void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec){
-  d_limiting = c;
-  d_nonbasicDelta = delta;
-  d_errorsChange = ec;
-  d_focusDirection.reset();
-  d_tableauCoefficient = &r;
-  updateWitness();
-  Assert(describesPivot());
-  Assert(debugSgnAgreement());
-}
-
-void UpdateInfo::witnessedUpdate(const DeltaRational& delta, ConstraintP c, int ec, int fd){
-  d_limiting = c;
-  d_nonbasicDelta = delta;
-  d_errorsChange = ec;
-  d_focusDirection = fd;
-  d_tableauCoefficient.reset();
-  updateWitness();
-  Assert(describesPivot() || improvement(d_witness));
-  Assert(debugSgnAgreement());
-}
-
-void UpdateInfo::update(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec, int fd){
-  d_limiting = c;
-  d_nonbasicDelta = delta;
-  d_errorsChange = ec;
-  d_focusDirection = fd;
-  d_tableauCoefficient = &r;
-  updateWitness();
-  Assert(describesPivot() || improvement(d_witness));
-  Assert(debugSgnAgreement());
-}
-
-bool UpdateInfo::describesPivot() const {
-  return !unbounded() && d_nonbasic != d_limiting->getVariable();
-}
-
-void UpdateInfo::output(std::ostream& out) const{
-  out << "{UpdateInfo"
-      << ", nb = " << d_nonbasic
-      << ", dir = " << d_nonbasicDirection
-      << ", delta = " << d_nonbasicDelta
-      << ", conflict = " << d_foundConflict
-      << ", errorChange = " << d_errorsChange
-      << ", focusDir = " << d_focusDirection
-      << ", witness = " << d_witness
-      << ", limiting = " << d_limiting
-      << "}";
-}
-
-ArithVar UpdateInfo::leaving() const{
-  Assert(describesPivot());
-
-  return d_limiting->getVariable();
-}
-
-std::ostream& operator<<(std::ostream& out, const UpdateInfo& up){
-  up.output(out);
-  return out;
-}
-
-
-std::ostream& operator<<(std::ostream& out,  WitnessImprovement w){
-  switch(w){
-  case ConflictFound:
-    out << "ConflictFound"; break;
-  case ErrorDropped:
-    out << "ErrorDropped"; break;
-  case FocusImproved:
-    out << "FocusImproved"; break;
-  case FocusShrank:
-    out << "FocusShrank"; break;
-  case Degenerate:
-    out << "Degenerate"; break;
-  case BlandsDegenerate:
-    out << "BlandsDegenerate"; break;
-  case HeuristicDegenerate:
-    out << "HeuristicDegenerate"; break;
-  case AntiProductive:
-    out << "AntiProductive"; break;
-  }
-  return out;
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/simplex_update.h b/src/theory/arith/simplex_update.h
deleted file mode 100644 (file)
index 4bcadbb..0000000
+++ /dev/null
@@ -1,360 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Andres Noetzli, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This provides a class for summarizing pivot proposals.
- *
- * This shares with the theory a Tableau, and a PartialModel that:
- *  - satisfies the equalities in the Tableau, and
- *  - the assignment for the non-basic variables satisfies their bounds.
- * This maintains the relationship needed by the SimplexDecisionProcedure.
- *
- * In the language of Simplex for DPLL(T), this provides:
- * - update()
- * - pivotAndUpdate()
- *
- * This class also provides utility functions that require
- * using both the Tableau and PartialModel.
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <optional>
-
-#include "theory/arith/arithvar.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/arith/delta_rational.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-enum WitnessImprovement {
-  ConflictFound = 0,
-  ErrorDropped = 1,
-  FocusImproved = 2,
-  FocusShrank = 3,
-  Degenerate = 4,
-  BlandsDegenerate = 5,
-  HeuristicDegenerate = 6,
-  AntiProductive = 7
-};
-
-inline bool strongImprovement(WitnessImprovement w){
-  return w <= FocusImproved;
-}
-
-inline bool improvement(WitnessImprovement w){
-  return w <= FocusShrank;
-}
-
-inline bool degenerate(WitnessImprovement w){
-  switch(w){
-  case Degenerate:
-  case BlandsDegenerate:
-  case HeuristicDegenerate:
-    return true;
-  default:
-    return false;
-  }
-}
-
-std::ostream& operator<<(std::ostream& out,  WitnessImprovement w);
-
-/**
- * This class summarizes both potential:
- * - pivot-and-update operations or
- * - a pure update operation.
- * This stores enough information for the various algorithms  hat consider these operations.
- * These require slightly different pieces of information at different points
- * so they are a bit verbose and paranoid.
- */
-class UpdateInfo {
-private:
-
-  /**
-   * The nonbasic variables under consideration.
-   * This is either the entering variable on a pivot and update
-   * or the variable being updated.
-   * This can only be set in the constructor or assignment.
-   *
-   * If this uninitialized, then this is ARITHVAR_SENTINEL.
-   */
-  ArithVar d_nonbasic;
-
-  /**
-   * The sgn of the "intended" derivative (delta) of the update to d_nonbasic.
-   * This is either 1, -1, or 0.
-   * It is "intended" as the delta is always allowed to be 0.
-   * (See debugSgnAgreement().)
-   *
-   * If this uninitialized, then this is 0.
-   * If this is initialized, then it is -1 or 1.
-   *
-   * This can only be set in the constructor or assignment.
-   */
-  int d_nonbasicDirection;
-
-  /**
-   * The change in the assignment of d_nonbasic.
-   * This is changed via the updateProposal(...) methods.
-   * The value needs to satisfy debugSgnAgreement() or it is in conflict.
-   */
-  std::optional<DeltaRational> d_nonbasicDelta;
-
-  /**
-   * This is true if the pivot-and-update is *known* to cause a conflict.
-   * This can only be true if it was constructed through the static conflict(...) method.
-   */
-  bool d_foundConflict;
-
-  /** This is the change in the size of the error set. */
-  std::optional<int> d_errorsChange;
-
-  /** This is the sgn of the change in the value of the focus set.*/
-  std::optional<int> d_focusDirection;
-
-  /** This is the sgn of the change in the value of the focus set.*/
-  std::optional<DeltaRational> d_focusChange;
-
-  /** This is the coefficient in the tableau for the entry.*/
-  std::optional<const Rational*> d_tableauCoefficient;
-
-  /**
-   * This is the constraint that nonbasic is basic is updating s.t. its variable is against it.
-   * This has 3 different possibilities:
-   * - Unbounded : then this is NullConstraint and unbounded() is true.
-   * - Pivot-And-Update: then this is not NullConstraint and the variable is not d_nonbasic.
-   * - Update: then this is not NullConstraint and the variable is d_nonbasic.
-   */
-  ConstraintP d_limiting;
-
-  WitnessImprovement d_witness;
-
-  /**
-   * This returns true if
-   * d_nonbasicDelta is zero() or its sgn() must agree with d_nonbasicDirection.
-   */
-  bool debugSgnAgreement() const {
-    int deltaSgn = d_nonbasicDelta.value().sgn();
-    return deltaSgn == 0 || deltaSgn == d_nonbasicDirection;
-  }
-
-  /** This private constructor allows for setting conflict to true. */
-  UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim);
-
-public:
-
-  /** This constructs an uninitialized UpdateInfo. */
-  UpdateInfo();
-
-  /**
-   * This constructs an initialized UpdateInfo.
-   * dir must be 1 or -1.
-   */
-  UpdateInfo(ArithVar nb, int dir);
-
-  /**
-   * This updates the nonBasicDelta to d and limiting to NullConstraint.
-   * This describes an unbounded() update.
-   */
-  void updateUnbounded(const DeltaRational& d, int ec, int f);
-
-
-  void updatePureFocus(const DeltaRational& d, ConstraintP c);
-  //void updatePureError(const DeltaRational& d, Constraint c, int e);
-  //void updatePure(const DeltaRational& d, Constraint c, int e, int f);
-
-  /**
-   * This updates the nonBasicDelta to d and limiting to c.
-   * This clears errorChange() and focusDir().
-   */
-  void updatePivot(const DeltaRational& d, const Rational& r,  ConstraintP c);
-
-  /**
-   * This updates the nonBasicDelta to d, limiting to c, and errorChange to e.
-   * This clears focusDir().
-   */
-  void updatePivot(const DeltaRational& d, const Rational& r, ConstraintP c, int e);
-
-  /**
-   * This updates the nonBasicDelta to d, limiting to c, errorChange to e and
-   * focusDir to f.
-   */
-  void witnessedUpdate(const DeltaRational& d, ConstraintP c, int e, int f);
-  void update(const DeltaRational& d, const Rational& r, ConstraintP c, int e, int f);
-
-
-  static UpdateInfo conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim);
-
-  inline ArithVar nonbasic() const { return d_nonbasic; }
-  inline bool uninitialized() const {
-    return d_nonbasic == ARITHVAR_SENTINEL;
-  }
-
-  /**
-   * There is no limiting value to the improvement of the focus.
-   * If this is true, this never describes an update.
-   */
-  inline bool unbounded() const {
-    return d_limiting == NullConstraint;
-  }
-
-  /**
-   * The update either describes a pivotAndUpdate operation
-   * or it describes just an update.
-   */
-  bool describesPivot() const;
-
-  /** Returns the . describesPivot() must be true. */
-  ArithVar leaving() const;
-
-  /**
-   * Returns true if this is *known* to find a conflict.
-   * If true, this must have been made through the static conflict(...) function.
-   */
-  bool foundConflict() const { return d_foundConflict; }
-
-  /** Returns the direction nonbasic is supposed to move. */
-  inline int nonbasicDirection() const{  return d_nonbasicDirection; }
-
-  /** Requires errorsChange to be set through setErrorsChange or updateProposal. */
-  inline int errorsChange() const { return d_errorsChange.value(); }
-
-  /**
-   * If errorsChange has been set, return errorsChange().
-   * Otherwise, return def.
-   */
-  inline int errorsChangeSafe(int def) const {
-    if (d_errorsChange)
-    {
-      return d_errorsChange.value();
-    }
-    else
-    {
-      return def;
-    }
-  }
-
-  /** Sets the errorChange. */
-  void setErrorsChange(int ec){
-    d_errorsChange = ec;
-    updateWitness();
-  }
-
-
-  /** Requires errorsChange to be set through setErrorsChange or updateProposal. */
-  inline int focusDirection() const { return d_focusDirection.value(); }
-
-  /** Sets the focusDirection. */
-  void setFocusDirection(int fd){
-    Assert(-1 <= fd && fd <= 1);
-    d_focusDirection = fd;
-    updateWitness();
-  }
-
-  /**
-   * nonbasicDirection must be the same as the sign for the focus function's
-   * coefficient for this to be safe.
-   * The burden for this being safe is on the user!
-   */
-  void determineFocusDirection(){
-    const int deltaSgn = d_nonbasicDelta.value().sgn();
-    setFocusDirection(deltaSgn * d_nonbasicDirection);
-  }
-
-  /** Requires nonbasicDelta to be set through updateProposal(...). */
-  const DeltaRational& nonbasicDelta() const { return d_nonbasicDelta.value(); }
-  const Rational& getCoefficient() const {
-    Assert(describesPivot());
-    Assert(d_tableauCoefficient.value() != NULL);
-    return *(d_tableauCoefficient.value());
-  }
-  int basicDirection() const {
-    return nonbasicDirection() * (getCoefficient().sgn());
-  }
-
-  /** Returns the limiting constraint. */
-  inline ConstraintP limiting() const {
-    return d_limiting;
-  }
-
-  WitnessImprovement getWitness(bool useBlands = false) const{
-    Assert(d_witness == computeWitness());
-
-    if(d_witness == Degenerate){
-      if(useBlands){
-        return BlandsDegenerate;
-      }else{
-        return HeuristicDegenerate;
-      }
-    }else{
-      return d_witness;
-    }
-  }
-
-  const DeltaRational& focusChange() const { return d_focusChange.value(); }
-  void setFocusChange(const DeltaRational& fc) {
-    d_focusChange = fc;
-  }
-
-  /** Outputs the UpdateInfo into out. */
-  void output(std::ostream& out) const;
-
-private:
-  void updateWitness() {
-    d_witness = computeWitness();
-    Assert(describesPivot() || improvement(d_witness));
-  }
-
-  /**
-   * Determines the appropriate WitnessImprovement for the update.
-   * useBlands breaks ties for degenerate pivots.
-   *
-   * This is safe if:
-   * - d_foundConflict is true, or
-   * - d_foundConflict is false and d_errorsChange has been set and d_errorsChange < 0, or
-   * - d_foundConflict is false and d_errorsChange has been set and d_errorsChange >= 0 and d_focusDirection has been set.
-   */
-  WitnessImprovement computeWitness() const {
-    if(d_foundConflict){
-      return ConflictFound;
-    }
-    else if (d_errorsChange && d_errorsChange.value() < 0)
-    {
-      return ErrorDropped;
-    }
-    else if (d_errorsChange.value_or(0) == 0)
-    {
-      if (d_focusDirection)
-      {
-        if (*d_focusDirection > 0)
-        {
-          return FocusImproved;
-        }
-        else if (*d_focusDirection == 0)
-        {
-          return Degenerate;
-        }
-      }
-    }
-    return AntiProductive;
-  }
-
-};
-
-std::ostream& operator<<(std::ostream& out, const UpdateInfo& up);
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/soi_simplex.cpp b/src/theory/arith/soi_simplex.cpp
deleted file mode 100644 (file)
index ad5a71d..0000000
+++ /dev/null
@@ -1,912 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- */
-#include "theory/arith/soi_simplex.h"
-
-#include <algorithm>
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/error_set.h"
-#include "theory/arith/tableau.h"
-#include "util/statistics_stats.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-SumOfInfeasibilitiesSPD::SumOfInfeasibilitiesSPD(Env& env,
-                                                 LinearEqualityModule& linEq,
-                                                 ErrorSet& errors,
-                                                 RaiseConflict conflictChannel,
-                                                 TempVarMalloc tvmalloc)
-    : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
-      d_soiVar(ARITHVAR_SENTINEL),
-      d_pivotBudget(0),
-      d_prevWitnessImprovement(AntiProductive),
-      d_witnessImprovementInARow(0),
-      d_sgnDisagreements(),
-      d_statistics("theory::arith::SOI", d_pivots)
-{ }
-
-SumOfInfeasibilitiesSPD::Statistics::Statistics(const std::string& name,
-                                                uint32_t& pivots)
-    : d_initialSignalsTime(
-        smtStatisticsRegistry().registerTimer(name + "initialProcessTime")),
-      d_initialConflicts(
-          smtStatisticsRegistry().registerInt(name + "UpdateConflicts")),
-      d_soiFoundUnsat(smtStatisticsRegistry().registerInt(name + "FoundUnsat")),
-      d_soiFoundSat(smtStatisticsRegistry().registerInt(name + "FoundSat")),
-      d_soiMissed(smtStatisticsRegistry().registerInt(name + "Missed")),
-      d_soiConflicts(
-          smtStatisticsRegistry().registerInt(name + "ConfMin::num")),
-      d_hasToBeMinimal(
-          smtStatisticsRegistry().registerInt(name + "HasToBeMin")),
-      d_maybeNotMinimal(
-          smtStatisticsRegistry().registerInt(name + "MaybeNotMin")),
-      d_soiTimer(smtStatisticsRegistry().registerTimer(name + "Time")),
-      d_soiFocusConstructionTimer(
-          smtStatisticsRegistry().registerTimer(name + "Construction")),
-      d_soiConflictMinimization(smtStatisticsRegistry().registerTimer(
-          name + "Conflict::Minimization")),
-      d_selectUpdateForSOI(
-          smtStatisticsRegistry().registerTimer(name + "selectSOI")),
-      d_finalCheckPivotCounter(
-          smtStatisticsRegistry().registerReference<uint32_t>(
-              name + "lastPivots", pivots))
-{
-}
-
-Result::Status SumOfInfeasibilitiesSPD::findModel(bool exactResult)
-{
-  Assert(d_conflictVariables.empty());
-  Assert(d_sgnDisagreements.empty());
-
-  d_pivots = 0;
-
-  if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
-    Trace("soi::findModel") << "soiFindModel() trivial" << endl;
-    Assert(d_conflictVariables.empty());
-    return Result::SAT;
-  }
-
-  // We need to reduce this because of
-  d_errorSet.reduceToSignals();
-
-  // We must start tracking NOW
-  d_errorSet.setSelectionRule(options::ErrorSelectionRule::SUM_METRIC);
-
-  if(initialProcessSignals()){
-    d_conflictVariables.purge();
-    Trace("soi::findModel") << "fcFindModel() early conflict" << endl;
-    Assert(d_conflictVariables.empty());
-    return Result::UNSAT;
-  }else if(d_errorSet.errorEmpty()){
-    Trace("soi::findModel") << "fcFindModel() fixed itself" << endl;
-    Assert(!d_errorSet.moreSignals());
-    Assert(d_conflictVariables.empty());
-    return Result::SAT;
-  }
-
-  Trace("soi::findModel") << "fcFindModel() start non-trivial" << endl;
-
-  exactResult |= d_varOrderPivotLimit < 0;
-
-  d_prevWitnessImprovement = HeuristicDegenerate;
-  d_witnessImprovementInARow = 0;
-
-  Result::Status result = Result::UNKNOWN;
-
-  if (result == Result::UNKNOWN)
-  {
-    if(exactResult){
-      d_pivotBudget = -1;
-    }else{
-      d_pivotBudget = d_varOrderPivotLimit;
-    }
-
-    result = sumOfInfeasibilities();
-
-    if(result ==  Result::UNSAT){
-      ++(d_statistics.d_soiFoundUnsat);
-    }else if(d_errorSet.errorEmpty()){
-      ++(d_statistics.d_soiFoundSat);
-    }else{
-      ++(d_statistics.d_soiMissed);
-    }
-  }
-
-  Assert(!d_errorSet.moreSignals());
-  if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
-  {
-    result = Result::SAT;
-  }
-
-  // ensure that the conflict variable is still in the queue.
-  d_conflictVariables.purge();
-
-  Trace("soi::findModel") << "end findModel() " << result << endl;
-
-  Assert(d_conflictVariables.empty());
-  return result;
-}
-
-
-void SumOfInfeasibilitiesSPD::logPivot(WitnessImprovement w){
-  if(d_pivotBudget > 0) {
-    --d_pivotBudget;
-  }
-  Assert(w != AntiProductive);
-
-  if(w == d_prevWitnessImprovement){
-    ++d_witnessImprovementInARow;
-    if(d_witnessImprovementInARow == 0){
-      --d_witnessImprovementInARow;
-    }
-  }else{
-    if(w != BlandsDegenerate){
-      d_witnessImprovementInARow = 1;
-    }
-    d_prevWitnessImprovement = w;
-  }
-  if(strongImprovement(w)){
-    d_leavingCountSinceImprovement.purge();
-  }
-
-  Trace("logPivot") << "logPivot " << d_prevWitnessImprovement << " "  << d_witnessImprovementInARow << endl;
-}
-
-uint32_t SumOfInfeasibilitiesSPD::degeneratePivotsInARow() const {
-  switch(d_prevWitnessImprovement){
-  case ConflictFound:
-  case ErrorDropped:
-  case FocusImproved:
-    return 0;
-  case HeuristicDegenerate:
-  case BlandsDegenerate:
-    return d_witnessImprovementInARow;
-  // Degenerate is unreachable for its own reasons
-  case Degenerate:
-  case FocusShrank:
-  case AntiProductive:
-    Unreachable();
-    return -1;
-  }
-  Unreachable();
-}
-
-void SumOfInfeasibilitiesSPD::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
-  uint32_t newErrorSize = d_errorSet.errorSize();
-  adjustInfeasFunc(d_statistics.d_soiFocusConstructionTimer, d_soiVar, focusChanges);
-  d_errorSize = newErrorSize;
-}
-
-
-UpdateInfo SumOfInfeasibilitiesSPD::selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
-  UpdateInfo selected;
-
-  Trace("soi::selectPrimalUpdate")
-      << "selectPrimalUpdate " << endl
-      << d_soiVar << " " << d_tableau.basicRowLength(d_soiVar) << " "
-      << d_linEq.debugBasicAtBoundCount(d_soiVar) << endl;
-
-  typedef std::vector<Cand> CandVector;
-  CandVector candidates;
-
-  for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_soiVar); !ri.atEnd(); ++ri){
-    const Tableau::Entry& e = *ri;
-    ArithVar curr = e.getColVar();
-    if(curr == d_soiVar){ continue; }
-
-    int sgn = e.getCoefficient().sgn();
-    bool candidate =
-      (sgn > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
-      (sgn < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
-
-    Trace("soi::selectPrimalUpdate")
-      << "storing " << d_soiVar
-      << " " << curr
-      << " " << candidate
-      << " " << e.getCoefficient()
-      << " " << sgn << endl;
-
-    if(candidate) {
-      candidates.push_back(Cand(curr, 0, sgn, &e.getCoefficient()));
-    }
-  }
-
-  CompPenaltyColLength colCmp(&d_linEq, options().arith.havePenalties);
-  CandVector::iterator i = candidates.begin();
-  CandVector::iterator end = candidates.end();
-  std::make_heap(i, end, colCmp);
-
-  // For the first 3 pivots take the best
-  // After that, once an improvement is found on look at a
-  // small number of pivots after finding an improvement
-  // the longer the search to more willing we are to look at more candidates
-  int maxCandidatesAfterImprove =
-    (d_pivots <= 2) ?  std::numeric_limits<int>::max() : d_pivots/5;
-
-  int candidatesAfterFocusImprove = 0;
-  while(i != end && candidatesAfterFocusImprove <= maxCandidatesAfterImprove){
-    std::pop_heap(i, end, colCmp);
-    --end;
-    Cand& cand = (*end);
-    ArithVar curr = cand.d_nb;
-    const Rational& coeff = *cand.d_coeff;
-
-    LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
-    UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
-
-    Trace("soi::selectPrimalUpdate")
-      << "selected " << selected << endl
-      << "currProp " << currProposal << endl
-      << "coeff " << coeff << endl;
-
-    Assert(!currProposal.uninitialized());
-
-    if(candidatesAfterFocusImprove > 0){
-      candidatesAfterFocusImprove++;
-    }
-
-    if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
-      selected = currProposal;
-      WitnessImprovement w = selected.getWitness(false);
-      Trace("soi::selectPrimalUpdate") << "selected " << w << endl;
-      //setPenalty(curr, w);
-      if(improvement(w)){
-        bool exitEarly;
-        switch(w){
-        case ConflictFound: exitEarly = true; break;
-        case FocusImproved:
-          candidatesAfterFocusImprove = 1;
-          exitEarly = false;
-          break;
-        default:
-          exitEarly = false; break;
-        }
-        if(exitEarly){ break; }
-      }
-    }else{
-      Trace("soi::selectPrimalUpdate") << "dropped "<< endl;
-    }
-
-  }
-  return selected;
-}
-
-bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
-  if(inf.getWitness(useBlands) == w){
-    switch(w){
-    case ConflictFound: return inf.foundConflict();
-    case ErrorDropped: return inf.errorsChange() < 0;
-    case FocusImproved: return inf.focusDirection() > 0;
-    case FocusShrank: return false; // This is not a valid output
-    case Degenerate: return false; // This is not a valid output
-    case BlandsDegenerate: return useBlands;
-    case HeuristicDegenerate: return !useBlands;
-    case AntiProductive: return false;
-    }
-  }
-  return false;
-}
-
-
-void SumOfInfeasibilitiesSPD::debugPrintSignal(ArithVar updated) const{
-  Trace("updateAndSignal") << "updated basic " << updated;
-  Trace("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
-  Trace("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
-  int dir = !d_variables.assignmentIsConsistent(updated) ?
-    d_errorSet.getSgn(updated) : 0;
-  Trace("updateAndSignal") << " dir " << dir;
-  Trace("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
-}
-
-
-void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
-  ArithVar nonbasic = selected.nonbasic();
-
-  Trace("updateAndSignal") << "updateAndSignal " << selected << endl;
-
-  if(selected.describesPivot()){
-    ConstraintP limiting = selected.limiting();
-    ArithVar basic = limiting->getVariable();
-    Assert(d_linEq.basicIsTracked(basic));
-    d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
-  }else{
-    Assert(!selected.unbounded() || selected.errorsChange() < 0);
-
-    DeltaRational newAssignment =
-      d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
-
-    d_linEq.updateTracked(nonbasic, newAssignment);
-  }
-  d_pivots++;
-
-  increaseLeavingCount(nonbasic);
-
-  vector< pair<ArithVar, int> > focusChanges;
-  while(d_errorSet.moreSignals()){
-    ArithVar updated = d_errorSet.topSignal();
-    int prevFocusSgn = d_errorSet.popSignal();
-
-    if(d_tableau.isBasic(updated)){
-      Assert(!d_variables.assignmentIsConsistent(updated)
-             == d_errorSet.inError(updated));
-      if(TraceIsOn("updateAndSignal")){debugPrintSignal(updated);}
-      if(!d_variables.assignmentIsConsistent(updated)){
-        if(checkBasicForConflict(updated)){
-          reportConflict(updated);
-          //Assert(debugUpdatedBasic(selected, updated));
-        }
-      }
-    }else{
-      Trace("updateAndSignal") << "updated nonbasic " << updated << endl;
-    }
-    int currFocusSgn = d_errorSet.focusSgn(updated);
-    if(currFocusSgn != prevFocusSgn){
-      int change = currFocusSgn - prevFocusSgn;
-      focusChanges.push_back(make_pair(updated, change));
-    }
-  }
-
-  if(TraceIsOn("error")){ d_errorSet.debugPrint(Trace("error")); }
-
-  //Assert(debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
-
-  adjustFocusAndError(selected, focusChanges);
-}
-
-void SumOfInfeasibilitiesSPD::qeAddRange(uint32_t begin, uint32_t end){
-  Assert(!d_qeInSoi.empty());
-  for(uint32_t i = begin; i != end; ++i){
-    ArithVar v = d_qeConflict[i];
-    addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v);
-    d_qeInSoi.add(v);
-  }
-}
-
-void SumOfInfeasibilitiesSPD::qeRemoveRange(uint32_t begin, uint32_t end){
-  for(uint32_t i = begin; i != end; ++i){
-    ArithVar v = d_qeConflict[i];
-    removeFromInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v);
-    d_qeInSoi.remove(v);
-  }
-  Assert(!d_qeInSoi.empty());
-}
-
-void SumOfInfeasibilitiesSPD::qeSwapRange(uint32_t N, uint32_t r, uint32_t s){
-  for(uint32_t i = 0; i < N; ++i){
-    std::swap(d_qeConflict[r+i], d_qeConflict[s+i]);
-  }
-}
-
-/**
- * Region notation:
- * A region is either
- *  - A single element X@i with the name X at the position i
- *  - A sequence of indices X@[i,j) with the name X and the elements between i [inclusive] and j exclusive
- *  - A concatenation of regions R1 and R2, R1;R2
- *
- * Given the fixed assumptions C @ [0,cEnd) and a set of candidate minimizations U@[cEnd, uEnd)
- * s.t. C \cup U is known to be in conflict ([0,uEnd) has a conflict), find a minimal
- * subset of U, Delta, s.t. C \cup Delta is in conflict.
- *
- * Pre:
- *  [0, uEnd) is a set and is in conflict.
- *    uEnd <= assumptions.size()
- *  [0, cEnd) is in d_inSoi.
- *
- * Invariants: [0,cEnd) is never modified
- *
- * Post:
- *  [0, cEnd); [cEnd, deltaEnd) is in conflict
- *  [0, deltaEnd) is a set
- *  [0, deltaEnd) is in d_inSoi
- */
-uint32_t SumOfInfeasibilitiesSPD::quickExplainRec(uint32_t cEnd, uint32_t uEnd){
-  Assert(cEnd <= uEnd);
-  Assert(d_qeInUAndNotInSoi.empty());
-  Assert(d_qeGreedyOrder.empty());
-
-  const Tableau::Entry* spoiler = NULL;
-
-  if(d_soiVar != ARITHVAR_SENTINEL && d_linEq.selectSlackEntry(d_soiVar, false) == NULL){
-    // already in conflict
-    return cEnd;
-  }
-
-  Assert(cEnd < uEnd);
-
-  // Phase 1 : Construct the conflict greedily
-
-  for(uint32_t i = cEnd; i < uEnd; ++i){
-    d_qeInUAndNotInSoi.add(d_qeConflict[i]);
-  }
-  if(d_soiVar == ARITHVAR_SENTINEL){ // special case for d_soiVar being empty
-    ArithVar first = d_qeConflict[cEnd];
-    d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, first);
-    d_qeInSoi.add(first);
-    d_qeInUAndNotInSoi.remove(first);
-    d_qeGreedyOrder.push_back(first);
-  }
-  while((spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
-    Assert(!d_qeInUAndNotInSoi.empty());
-
-    ArithVar nb = spoiler->getColVar();
-    int oppositeSgn = -(spoiler->getCoefficient().sgn());
-    Assert(oppositeSgn != 0);
-
-    ArithVar basicWithOp = find_basic_in_sgns(d_qeSgns, nb, oppositeSgn, d_qeInUAndNotInSoi, true);
-    Assert(basicWithOp != ARITHVAR_SENTINEL);
-
-    addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
-    d_qeInSoi.add(basicWithOp);
-    d_qeInUAndNotInSoi.remove(basicWithOp);
-    d_qeGreedyOrder.push_back(basicWithOp);
-  }
-  Assert(spoiler == NULL);
-
-  // Compact the set u
-  uint32_t newEnd = cEnd + d_qeGreedyOrder.size();
-  std::copy(d_qeGreedyOrder.begin(), d_qeGreedyOrder.end(), d_qeConflict.begin()+cEnd);
-
-  d_qeInUAndNotInSoi.purge();
-  d_qeGreedyOrder.clear();
-
-   // Phase 2 : Recursively determine the minimal set of rows
-
-  uint32_t xPos = cEnd;
-  std::swap(d_qeGreedyOrder[xPos], d_qeGreedyOrder[newEnd - 1]);
-  uint32_t uBegin = xPos + 1;
-  uint32_t split = (newEnd - uBegin)/2 + uBegin;
-
-  //assumptions : C @ [0, cEnd); X @ xPos; U1 @ [u1Begin, split); U2 @ [split, newEnd)
-  // [0, newEnd) == d_inSoi
-
-  uint32_t compactU2;
-  if(split == newEnd){ // U2 is empty
-    compactU2 = newEnd;
-  }else{
-    // Remove U2 from Soi
-    qeRemoveRange(split, newEnd);
-    // [0, split) == d_inSoi
-
-    // pre assumptions: C + X + U1 @ [0,split); U2 [split, newEnd)
-    compactU2 = quickExplainRec(split, newEnd);
-    // post:
-    //  assumptions: C + X + U1 @ [0, split); delta2 @ [split - compactU2)
-    //  d_inSoi = [0, compactU2)
-  }
-  uint32_t deltaSize = compactU2 - split;
-  qeSwapRange(deltaSize, uBegin, split);
-  uint32_t d2End = uBegin+deltaSize;
-  // assumptions : C @ [0, cEnd); X @ xPos; delta2 @ [uBegin, d2End); U1 @ [d2End, compactU2)
-  //  d_inSoi == [0, compactU2)
-
-  uint32_t d1End;
-  if(d2End == compactU2){ // U1 is empty
-    d1End = d2End;
-  }else{
-    qeRemoveRange(d2End, compactU2);
-
-    //pre assumptions : C + X + delta2 @ [0, d2End); U1 @ [d2End, compactU2);
-    d1End = quickExplainRec(d2End, compactU2);
-    //post:
-    // assumptions : C + X + delta2 @ [0, d2End); delta1 @ [d2End, d1End);
-    // d_inSoi = [0, d1End)
-  }
-  //After both:
-  // d_inSoi == [0, d1End), C @ [0, cEnd); X + delta2 + delta 1 @ [xPos, d1End);
-
-  Assert(d_qeInUAndNotInSoi.empty());
-  Assert(d_qeGreedyOrder.empty());
-  return d1End;
-}
-
-void SumOfInfeasibilitiesSPD::quickExplain(){
-  Assert(d_qeInSoi.empty());
-  Assert(d_qeInUAndNotInSoi.empty());
-  Assert(d_qeGreedyOrder.empty());
-  Assert(d_soiVar == ARITHVAR_SENTINEL);
-  Assert(d_qeSgns.empty());
-
-  d_qeConflict.clear();
-  d_errorSet.pushFocusInto(d_qeConflict);
-
-  //cout <<  d_qeConflict.size() << " ";
-  uint32_t size = d_qeConflict.size();
-
-  if(size > 2){
-    for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
-      ArithVar e = *iter;
-      addRowSgns(d_qeSgns, e, d_errorSet.getSgn(e));
-    }
-    uint32_t end = quickExplainRec(0u, size);
-    Assert(end <= d_qeConflict.size());
-    Assert(d_soiVar != ARITHVAR_SENTINEL);
-    Assert(!d_qeInSoi.empty());
-
-    d_qeConflict.resize(end);
-    tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
-    d_soiVar = ARITHVAR_SENTINEL;
-    d_qeInSoi.purge();
-    d_qeSgns.clear();
-  }
-
-  //cout << d_qeConflict.size() << endl;
-
-  Assert(d_qeInSoi.empty());
-  Assert(d_qeInUAndNotInSoi.empty());
-  Assert(d_qeGreedyOrder.empty());
-  Assert(d_soiVar == ARITHVAR_SENTINEL);
-  Assert(d_qeSgns.empty());
-}
-
-unsigned SumOfInfeasibilitiesSPD::trySet(const ArithVarVec& set){
-  Assert(d_soiVar == ARITHVAR_SENTINEL);
-  bool success = false;
-  if(set.size() >= 2){
-    d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, set);
-    success = d_linEq.selectSlackEntry(d_soiVar, false) == NULL;
-
-    tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
-    d_soiVar = ARITHVAR_SENTINEL;
-  }
-  return success ? set.size() : std::numeric_limits<int>::max();
-}
-
-std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){
-  Trace("arith::greedyConflictSubsets") << "greedyConflictSubsets start" << endl;
-
-  std::vector< ArithVarVec > subsets;
-  Assert(d_soiVar == ARITHVAR_SENTINEL);
-
-  if(d_errorSize <= 2){
-    ArithVarVec inError;
-    d_errorSet.pushFocusInto(inError);
-
-    Assert(debugIsASet(inError));
-    subsets.push_back(inError);
-    return subsets;
-  }
-  Assert(d_errorSize > 2);
-
-  //sgns_table< <nonbasic,sgn>, [basics] >;
-  // Phase 0: Construct the sgns table
-  sgn_table sgns;
-  DenseSet hasParticipated; //Has participated in a conflict
-  for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
-    ArithVar e = *iter;
-    addRowSgns(sgns, e, d_errorSet.getSgn(e));
-
-    Trace("arith::greedyConflictSubsets") << "basic error var: " << e << endl;
-    if(TraceIsOn("arith::greedyConflictSubsets")){
-      d_tableau.debugPrintIsBasic(e);
-      d_tableau.printBasicRow(e, Trace("arith::greedyConflictSubsets"));
-    }
-  }
-
-  // Phase 1: Try to find at least 1 pair for every element
-  ArithVarVec tmp;
-  tmp.push_back(0);
-  tmp.push_back(0);
-  for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
-    ArithVar e = *iter;
-    tmp[0] = e;
-
-    int errSgn = d_errorSet.getSgn(e);
-    bool decreasing = errSgn < 0;
-    const Tableau::Entry* spoiler = d_linEq.selectSlackEntry(e, decreasing);
-    Assert(spoiler != NULL);
-    ArithVar nb = spoiler->getColVar();
-    int oppositeSgn = -(errSgn * (spoiler->getCoefficient().sgn()));
-
-    sgn_table::const_iterator opposites = find_sgns(sgns, nb, oppositeSgn);
-    Assert(opposites != sgns.end());
-
-    const ArithVarVec& choices = (*opposites).second;
-    for(ArithVarVec::const_iterator j = choices.begin(), jend = choices.end(); j != jend; ++j){
-      ArithVar b = *j;
-      if(b < e){ continue; }
-      tmp[0] = e;
-      tmp[1] = b;
-      if(trySet(tmp) == 2){
-        Trace("arith::greedyConflictSubsets")  << "found a pair " << b << " " << e << endl;
-        hasParticipated.softAdd(b);
-        hasParticipated.softAdd(e);
-        Assert(debugIsASet(tmp));
-        subsets.push_back(tmp);
-        ++(d_statistics.d_soiConflicts);
-        ++(d_statistics.d_hasToBeMinimal);
-      }
-    }
-  }
-
-
-  // Phase 2: If there is a variable that has not participated attempt to start a conflict
-  ArithVarVec possibleStarts; //List of elements that can be tried for starts.
-  d_errorSet.pushFocusInto(possibleStarts);
-  while(!possibleStarts.empty()){
-    Assert(d_soiVar == ARITHVAR_SENTINEL);
-
-    ArithVar v = possibleStarts.back();
-    possibleStarts.pop_back();
-    if(hasParticipated.isMember(v)){ continue; }
-
-    hasParticipated.add(v);
-
-    Assert(d_soiVar == ARITHVAR_SENTINEL);
-    //d_soiVar's row =  \sumofinfeasibilites underConstruction
-    ArithVarVec underConstruction;
-    underConstruction.push_back(v);
-    d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, v);
-
-    Trace("arith::greedyConflictSubsets") << "trying " << v << endl;
-
-    const Tableau::Entry* spoiler = NULL;
-    while( (spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
-      ArithVar nb = spoiler->getColVar();
-      int oppositeSgn = -(spoiler->getCoefficient().sgn());
-      Assert(oppositeSgn != 0);
-
-      Trace("arith::greedyConflictSubsets") << "looking for " << nb << " " << oppositeSgn << endl;
-
-      ArithVar basicWithOp = find_basic_in_sgns(sgns, nb, oppositeSgn, hasParticipated, false);
-
-      if(basicWithOp == ARITHVAR_SENTINEL){
-        Trace("arith::greedyConflictSubsets") << "search did not work  for " << nb << endl;
-        // greedy construction has failed
-        break;
-      }else{
-        Trace("arith::greedyConflictSubsets") << "found  " << basicWithOp << endl;
-
-        addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
-        hasParticipated.softAdd(basicWithOp);
-        underConstruction.push_back(basicWithOp);
-      }
-    }
-    if(spoiler == NULL){
-      Trace("arith::greedyConflictSubsets") << "success" << endl;
-      //then underConstruction contains a conflicting subset
-      Assert(debugIsASet(underConstruction));
-      subsets.push_back(underConstruction);
-      ++d_statistics.d_soiConflicts;
-      if(underConstruction.size() == 3){
-        ++d_statistics.d_hasToBeMinimal;
-      }else{
-        ++d_statistics.d_maybeNotMinimal;
-      }
-    }else{
-      Trace("arith::greedyConflictSubsets") << "failure" << endl;
-    }
-    tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
-    d_soiVar = ARITHVAR_SENTINEL;
-    // if(false && spoiler == NULL){
-    //   ArithVarVec tmp;
-    //   int smallest = tryAllSubsets(underConstruction, 0, tmp);
-    //   cout << underConstruction.size() << " " << smallest << endl;
-    //   Assert(smallest >= underConstruction.size());
-    //   if(smallest < underConstruction.size()){
-    //     exit(-1);
-    //   }
-    // }
-  }
-
-  Assert(d_soiVar == ARITHVAR_SENTINEL);
-  Trace("arith::greedyConflictSubsets") << "greedyConflictSubsets done" << endl;
-  return subsets;
-}
-
-bool SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){
-  Assert(d_soiVar == ARITHVAR_SENTINEL);
-  d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, subset);
-  Assert(!subset.empty());
-  Assert(!d_conflictBuilder->underConstruction());
-
-  Trace("arith::generateSOIConflict") << "SumOfInfeasibilitiesSPD::generateSOIConflict(...) start" << endl;
-
-  bool success = false;
-    
-  for(ArithVarVec::const_iterator iter = subset.begin(), end = subset.end(); iter != end; ++iter){
-    ArithVar e = *iter;
-    ConstraintP violated = d_errorSet.getViolated(e);
-    Assert(violated != NullConstraint);
-
-    int sgn = d_errorSet.getSgn(e);
-    const Rational& violatedCoeff = sgn > 0 ? d_negOne : d_posOne;
-    Trace("arith::generateSOIConflict") << "basic error var: "
-                                        << "(" <<  violatedCoeff << ")"
-                                        << " " << violated
-                                        << endl;
-
-
-    d_conflictBuilder->addConstraint(violated, violatedCoeff);
-    Assert(violated->hasProof());
-    if(!success && !violated->negationHasProof()){
-      success = true;
-      d_conflictBuilder->makeLastConsequent();
-    }
-  }
-  
-  if(!success){
-    // failure
-    d_conflictBuilder->reset();
-  } else {
-    // pick a violated constraint arbitrarily. any of them may be selected for the conflict
-    Assert(d_conflictBuilder->underConstruction());
-    Assert(d_conflictBuilder->consequentIsSet());
-
-    for(Tableau::RowIterator i = d_tableau.basicRowIterator(d_soiVar); !i.atEnd(); ++i){
-      const Tableau::Entry& entry = *i;
-      ArithVar v = entry.getColVar();
-      if(v == d_soiVar){ continue; }
-      const Rational& coeff = entry.getCoefficient();
-
-      ConstraintP c = (coeff.sgn() > 0) ?
-        d_variables.getUpperBoundConstraint(v) :
-        d_variables.getLowerBoundConstraint(v);
-      
-      Trace("arith::generateSOIConflict") << "non-basic var: "
-                                          << "(" <<  coeff << ")"
-                                          << " " << c
-                                          << endl;
-      d_conflictBuilder->addConstraint(c, coeff);
-    }
-    ConstraintCP conflicted = d_conflictBuilder->commitConflict();
-    d_conflictChannel.raiseConflict(conflicted,
-                                    InferenceId::ARITH_CONF_SOI_SIMPLEX);
-  }
-
-  tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
-  d_soiVar = ARITHVAR_SENTINEL;
-  Trace("arith::generateSOIConflict") << "SumOfInfeasibilitiesSPD::generateSOIConflict(...) done" << endl;
-  Assert(d_soiVar == ARITHVAR_SENTINEL);
-  Assert(!d_conflictBuilder->underConstruction());
-  return success;
-}
-
-
-WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){
-  Trace("arith::SOIConflict") << "SumOfInfeasibilitiesSPD::SOIConflict() start "
-                              << ": |E| = " << d_errorSize << endl;
-  if(TraceIsOn("arith::SOIConflict")){
-    d_errorSet.debugPrint(cout);
-  }
-  Trace("arith::SOIConflict") << endl;
-
-  tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
-  d_soiVar = ARITHVAR_SENTINEL;
-
-  if (options().arith.soiQuickExplain)
-  {
-    quickExplain();
-    generateSOIConflict(d_qeConflict);
-  }
-  else
-  {
-    vector<ArithVarVec> subsets = greedyConflictSubsets();
-    Assert(d_soiVar == ARITHVAR_SENTINEL);
-    bool anySuccess = false;
-    Assert(!subsets.empty());
-    for(vector<ArithVarVec>::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){
-      const ArithVarVec& subset = *i;
-      Assert(debugIsASet(subset));
-      anySuccess = generateSOIConflict(subset) || anySuccess;
-      //Node conflict = generateSOIConflict(subset);
-      //cout << conflict << endl;
-
-      //reportConflict(conf); do not do this. We need a custom explanations!
-      //d_conflictChannel(conflict);
-    }
-    Assert(anySuccess);
-  }
-  Assert(d_soiVar == ARITHVAR_SENTINEL);
-  d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization);
-
-  //reportConflict(conf); do not do this. We need a custom explanations!
-  d_conflictVariables.add(d_soiVar);
-
-  Trace("arith::SOIConflict")
-      << "SumOfInfeasibilitiesSPD::SOIConflict() end" << endl;
-  return ConflictFound;
-}
-
-WitnessImprovement SumOfInfeasibilitiesSPD::soiRound() {
-  Assert(d_soiVar != ARITHVAR_SENTINEL);
-
-  bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
-  LinearEqualityModule::UpdatePreferenceFunction upf;
-  if(useBlands) {
-    upf = &LinearEqualityModule::preferWitness<false>;
-  } else {
-    upf = &LinearEqualityModule::preferWitness<true>;
-  }
-
-  LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
-    &LinearEqualityModule::minVarOrder :
-    &LinearEqualityModule::minRowLength;
-  bpf = &LinearEqualityModule::minVarOrder;
-
-  UpdateInfo selected = selectUpdate(upf, bpf);
-
-  if(selected.uninitialized()){
-    Trace("selectFocusImproving") << "SOI is optimum, but we don't have sat/conflict yet" << endl;
-    return SOIConflict();
-  }else{
-    Assert(!selected.uninitialized());
-    WitnessImprovement w = selected.getWitness(false);
-    Assert(debugCheckWitness(selected, w, false));
-
-    updateAndSignal(selected, w);
-    logPivot(w);
-    return w;
-  }
-}
-
-Result::Status SumOfInfeasibilitiesSPD::sumOfInfeasibilities()
-{
-  TimerStat::CodeTimer codeTimer(d_statistics.d_soiTimer);
-
-  Assert(d_sgnDisagreements.empty());
-  Assert(d_pivotBudget != 0);
-  Assert(d_errorSize == d_errorSet.errorSize());
-  Assert(d_errorSize > 0);
-  Assert(d_conflictVariables.empty());
-  Assert(d_soiVar == ARITHVAR_SENTINEL);
-
-  //d_scores.purge();
-  d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer);
-
-
-  while(d_pivotBudget != 0  && d_errorSize > 0 && d_conflictVariables.empty()){
-    Trace("dualLike") << "dualLike" << endl;
-
-    Assert(d_errorSet.noSignals());
-    // Possible outcomes:
-    // - conflict
-    // - budget was exhausted
-    // - focus went down
-    WitnessImprovement w = soiRound();
-    Trace("dualLike") << "selectFocusImproving -> " << w << endl;
-
-    Assert(d_errorSize == d_errorSet.errorSize());
-  }
-
-
-  if(d_soiVar != ARITHVAR_SENTINEL){
-    tearDownInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer, d_soiVar);
-    d_soiVar = ARITHVAR_SENTINEL;
-  }
-
-  Assert(d_soiVar == ARITHVAR_SENTINEL);
-  if(!d_conflictVariables.empty()){
-    return Result::UNSAT;
-  }else if(d_errorSet.errorEmpty()){
-    Assert(d_errorSet.noSignals());
-    return Result::SAT;
-  }else{
-    Assert(d_pivotBudget == 0);
-    return Result::UNKNOWN;
-  }
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/soi_simplex.h b/src/theory/arith/soi_simplex.h
deleted file mode 100644 (file)
index 56483ee..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- *
- * This implements the Simplex module for the Simpelx for DPLL(T) decision
- * procedure.
- * See the Simplex for DPLL(T) technical report for more background.(citation?)
- * This shares with the theory a Tableau, and a PartialModel that:
- *  - satisfies the equalities in the Tableau, and
- *  - the assignment for the non-basic variables satisfies their bounds.
- * This is required to either produce a conflict or satisifying PartialModel.
- * Further, we require being told when a basic variable updates its value.
- *
- * During the Simplex search we maintain a queue of variables.
- * The queue is required to contain all of the basic variables that voilate
- * their bounds.
- * As elimination from the queue is more efficient to be done lazily,
- * we do not maintain that the queue of variables needs to be only basic
- * variables or only variables that satisfy their bounds.
- *
- * The simplex procedure roughly follows Alberto's thesis. (citation?)
- * There is one round of selecting using a heuristic pivoting rule.
- * (See PreferenceFunction Documentation for the available options.)
- * The non-basic variable is the one that appears in the fewest pivots.
- * (Bruno says that Leonardo invented this first.)
- * After this, Bland's pivot rule is invoked.
- *
- * During this proccess, we periodically inspect the queue of variables to
- * 1) remove now extraneous extries,
- * 2) detect conflicts that are "waiting" on the queue but may not be detected
- *    by the current queue heuristics, and
- * 3) detect multiple conflicts.
- *
- * Conflicts are greedily slackened to use the weakest bounds that still
- * produce the conflict.
- *
- * Extra things tracked atm: (Subject to change at Tim's whims)
- * - A superset of all of the newly pivoted variables.
- * - A queue of additional conflicts that were discovered by Simplex.
- *   These are theory valid and are currently turned into lemmas
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/simplex.h"
-#include "theory/arith/simplex_update.h"
-#include "util/dense_map.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class SumOfInfeasibilitiesSPD : public SimplexDecisionProcedure {
-public:
- SumOfInfeasibilitiesSPD(Env& env,
-                         LinearEqualityModule& linEq,
-                         ErrorSet& errors,
-                         RaiseConflict conflictChannel,
-                         TempVarMalloc tvmalloc);
-
- Result::Status findModel(bool exactResult) override;
-
- // other error variables are dropping
- WitnessImprovement dualLikeImproveError(ArithVar evar);
- WitnessImprovement primalImproveError(ArithVar evar);
-
-private:
-  /** The current sum of infeasibilities variable. */
-  ArithVar d_soiVar;
-
-  // dual like
-  // - found conflict
-  // - satisfied error set
-  Result::Status sumOfInfeasibilities();
-
-  int32_t d_pivotBudget;
-
-  WitnessImprovement d_prevWitnessImprovement;
-  uint32_t d_witnessImprovementInARow;
-
-  uint32_t degeneratePivotsInARow() const;
-
-  static constexpr uint32_t s_focusThreshold = 6;
-  static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnLeaving = 100;
-  static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnEntering = 10;
-
-  DenseMap<uint32_t> d_leavingCountSinceImprovement;
-  void increaseLeavingCount(ArithVar x){
-    if(!d_leavingCountSinceImprovement.isKey(x)){
-      d_leavingCountSinceImprovement.set(x,1);
-    }else{
-      (d_leavingCountSinceImprovement.get(x))++;
-    }
-  }
-  LinearEqualityModule::UpdatePreferenceFunction selectLeavingFunction(ArithVar x){
-    bool useBlands = d_leavingCountSinceImprovement.isKey(x) &&
-      d_leavingCountSinceImprovement[x] >= s_maxDegeneratePivotsBeforeBlandsOnEntering;
-    if(useBlands) {
-      return &LinearEqualityModule::preferWitness<false>;
-    } else {
-      return &LinearEqualityModule::preferWitness<true>;
-    }
-  }
-
-  void debugPrintSignal(ArithVar updated) const;
-
-  ArithVarVec d_sgnDisagreements;
-
-  void logPivot(WitnessImprovement w);
-
-  void updateAndSignal(const UpdateInfo& selected, WitnessImprovement w);
-
-  UpdateInfo selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf,
-                          LinearEqualityModule::VarPreferenceFunction bpf);
-
-
-  // UpdateInfo selectUpdateForDualLike(ArithVar basic){
-  //   TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForDualLike);
-
-  //   LinearEqualityModule::UpdatePreferenceFunction upf =
-  //     &LinearEqualityModule::preferWitness<true>;
-  //   LinearEqualityModule::VarPreferenceFunction bpf =
-  //     &LinearEqualityModule::minVarOrder;
-  //   return selectPrimalUpdate(basic, upf, bpf);
-  // }
-
-  // UpdateInfo selectUpdateForPrimal(ArithVar basic, bool useBlands){
-  //   TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForPrimal);
-
-  //   LinearEqualityModule::UpdatePreferenceFunction upf = useBlands ?
-  //     &LinearEqualityModule::preferWitness<false>:
-  //     &LinearEqualityModule::preferWitness<true>;
-
-  //   LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
-  //     &LinearEqualityModule::minVarOrder :
-  //     &LinearEqualityModule::minRowLength;
-  //   bpf = &LinearEqualityModule::minVarOrder;
-
-  //   return selectPrimalUpdate(basic, upf, bpf);
-  // }
-  // WitnessImprovement selectFocusImproving() ;
-  WitnessImprovement soiRound();
-  WitnessImprovement SOIConflict();
-  std::vector< ArithVarVec > greedyConflictSubsets();
-  bool generateSOIConflict(const ArithVarVec& subset);
-
-  // WitnessImprovement focusUsingSignDisagreements(ArithVar basic);
-  // WitnessImprovement focusDownToLastHalf();
-  // WitnessImprovement adjustFocusShrank(const ArithVarVec& drop);
-  // WitnessImprovement focusDownToJust(ArithVar v);
-
-
-  void adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges);
-
-  /**
-   * This is the main simplex for DPLL(T) loop.
-   * It runs for at most maxIterations.
-   *
-   * Returns true iff it has found a conflict.
-   * d_conflictVariable will be set and the conflict for this row is reported.
-   */
-  bool searchForFeasibleSolution(uint32_t maxIterations);
-
-  bool initialProcessSignals(){
-    TimerStat &timer = d_statistics.d_initialSignalsTime;
-    IntStat& conflictStat  = d_statistics.d_initialConflicts;
-    return standardProcessSignals(timer, conflictStat);
-  }
-
-  void quickExplain();
-  DenseSet d_qeInSoi;
-  DenseSet d_qeInUAndNotInSoi;
-  ArithVarVec d_qeConflict;
-  ArithVarVec d_qeGreedyOrder;
-  sgn_table d_qeSgns;
-
-  uint32_t quickExplainRec(uint32_t cEnd, uint32_t uEnd);
-  void qeAddRange(uint32_t begin, uint32_t end);
-  void qeRemoveRange(uint32_t begin, uint32_t end);
-  void qeSwapRange(uint32_t N, uint32_t r, uint32_t s);
-
-  unsigned trySet(const ArithVarVec& set);
-  unsigned tryAllSubsets(const ArithVarVec& set, unsigned depth, ArithVarVec& tmp);
-
-  /** These fields are designed to be accessible to TheoryArith methods. */
-  class Statistics {
-  public:
-    TimerStat d_initialSignalsTime;
-    IntStat d_initialConflicts;
-
-    IntStat d_soiFoundUnsat;
-    IntStat d_soiFoundSat;
-    IntStat d_soiMissed;
-
-    IntStat d_soiConflicts;
-    IntStat d_hasToBeMinimal;
-    IntStat d_maybeNotMinimal;
-
-    TimerStat d_soiTimer;
-    TimerStat d_soiFocusConstructionTimer;
-    TimerStat d_soiConflictMinimization;
-    TimerStat d_selectUpdateForSOI;
-
-    ReferenceStat<uint32_t> d_finalCheckPivotCounter;
-
-    Statistics(const std::string& name, uint32_t& pivots);
-  } d_statistics;
-};/* class FCSimplexDecisionProcedure */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/tableau.cpp b/src/theory/arith/tableau.cpp
deleted file mode 100644 (file)
index 9a96ca9..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "base/output.h"
-#include "theory/arith/tableau.h"
-
-using namespace std;
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-
-void Tableau::pivot(ArithVar oldBasic, ArithVar newBasic, CoefficientChangeCallback& cb){
-  Assert(isBasic(oldBasic));
-  Assert(!isBasic(newBasic));
-  Assert(d_mergeBuffer.empty());
-
-  Trace("tableau") << "Tableau::pivot(" <<  oldBasic <<", " << newBasic <<")"  << endl;
-
-  RowIndex ridx = basicToRowIndex(oldBasic);
-
-  rowPivot(oldBasic, newBasic, cb);
-  Assert(ridx == basicToRowIndex(newBasic));
-
-  loadRowIntoBuffer(ridx);
-
-  ColIterator colIter = colIterator(newBasic);
-  while(!colIter.atEnd()){
-    EntryID id = colIter.getID();
-    Entry& entry = d_entries.get(id);
-
-    ++colIter; //needs to be incremented before the variable is removed
-    if(entry.getRowIndex() == ridx){ continue; }
-
-    RowIndex to = entry.getRowIndex();
-    Rational coeff = entry.getCoefficient();
-    if(cb.canUseRow(to)){
-      rowPlusBufferTimesConstant(to, coeff, cb);
-    }else{
-      rowPlusBufferTimesConstant(to, coeff);
-    }
-  }
-  clearBuffer();
-
-  //Clear the column for used for this variable
-
-  Assert(d_mergeBuffer.empty());
-  Assert(!isBasic(oldBasic));
-  Assert(isBasic(newBasic));
-  Assert(getColLength(newBasic) == 1);
-}
-
-/**
- * Changes basic to newbasic (a variable on the row).
- */
-void Tableau::rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb){
-  Assert(isBasic(basicOld));
-  Assert(!isBasic(basicNew));
-
-  RowIndex rid = basicToRowIndex(basicOld);
-
-  EntryID newBasicID = findOnRow(rid, basicNew);
-
-  Assert(newBasicID != ENTRYID_SENTINEL);
-
-  Tableau::Entry& newBasicEntry = d_entries.get(newBasicID);
-  const Rational& a_rs = newBasicEntry.getCoefficient();
-  int a_rs_sgn = a_rs.sgn();
-  Rational negInverseA_rs = -(a_rs.inverse());
-
-  for(RowIterator i = basicRowIterator(basicOld); !i.atEnd(); ++i){
-    EntryID id = i.getID();
-    Tableau::Entry& entry = d_entries.get(id);
-
-    entry.getCoefficient() *=  negInverseA_rs;
-  }
-
-  d_basic2RowIndex.remove(basicOld);
-  d_basic2RowIndex.set(basicNew, rid);
-  d_rowIndex2basic.set(rid, basicNew);
-
-  cb.multiplyRow(rid, -a_rs_sgn);
-}
-
-void Tableau::addRow(ArithVar basic,
-                     const std::vector<Rational>& coefficients,
-                     const std::vector<ArithVar>& variables)
-{
-  Assert(basic < getNumColumns());
-  Assert(debugIsASet(variables));
-  Assert(coefficients.size() == variables.size());
-  Assert(!isBasic(basic));
-
-  RowIndex newRow = Matrix<Rational>::addRow(coefficients, variables);
-  addEntry(newRow, basic, Rational(-1));
-
-  Assert(!d_basic2RowIndex.isKey(basic));
-  Assert(!d_rowIndex2basic.isKey(newRow));
-
-  d_basic2RowIndex.set(basic, newRow);
-  d_rowIndex2basic.set(newRow, basic);
-
-
-  if(TraceIsOn("matrix")){ printMatrix(); }
-
-  NoEffectCCCB noeffect;
-  NoEffectCCCB* nep = &noeffect;
-  CoefficientChangeCallback* cccb = static_cast<CoefficientChangeCallback*>(nep);
-
-  vector<Rational>::const_iterator coeffIter = coefficients.begin();
-  vector<ArithVar>::const_iterator varsIter = variables.begin();
-  vector<ArithVar>::const_iterator varsEnd = variables.end();
-  for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
-    ArithVar var = *varsIter;
-
-    if(isBasic(var)){
-      Rational coeff = *coeffIter;
-
-      RowIndex ri = basicToRowIndex(var);
-
-      loadRowIntoBuffer(ri);
-      rowPlusBufferTimesConstant(newRow, coeff, *cccb);
-      clearBuffer();
-    }
-  }
-
-  if(TraceIsOn("matrix")) { printMatrix(); }
-
-  Assert(debugNoZeroCoefficients(newRow));
-  Assert(debugMatchingCountsForRow(newRow));
-  Assert(getColLength(basic) == 1);
-}
-
-void Tableau::removeBasicRow(ArithVar basic){
-  RowIndex rid = basicToRowIndex(basic);
-
-  removeRow(rid);
-  d_basic2RowIndex.remove(basic);
-  d_rowIndex2basic.remove(rid);
-}
-
-void Tableau::substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult,  CoefficientChangeCallback& cb){
-  if(!mult.isZero()){
-    RowIndex to_idx = basicToRowIndex(to);
-    addEntry(to_idx, from, mult); // Add an entry to be cancelled out
-    RowIndex from_idx = basicToRowIndex(from);
-
-    cb.update(to_idx, from, 0, mult.sgn());
-
-    loadRowIntoBuffer(from_idx);
-    rowPlusBufferTimesConstant(to_idx, mult, cb);
-    clearBuffer();
-  }
-}
-
-uint32_t Tableau::rowComplexity(ArithVar basic) const{
-  uint32_t complexity = 0;
-  for(RowIterator i = basicRowIterator(basic); !i.atEnd(); ++i){
-    const Entry& e = *i;
-    complexity += e.getCoefficient().complexity();
-  }
-  return complexity;
-}
-
-double Tableau::avgRowComplexity() const{
-  double sum = 0;
-  uint32_t rows = 0;
-  for(BasicIterator i = beginBasic(), i_end = endBasic(); i != i_end; ++i){
-    sum += rowComplexity(*i);
-    rows++;
-  }
-  return (rows == 0) ? 0 : (sum/rows);
-}
-
-void Tableau::printBasicRow(ArithVar basic, std::ostream& out){
-  printRow(basicToRowIndex(basic), out);
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/tableau.h b/src/theory/arith/tableau.h
deleted file mode 100644 (file)
index 2c5fc3c..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <vector>
-
-#include "theory/arith/arithvar.h"
-#include "theory/arith/matrix.h"
-#include "util/dense_map.h"
-#include "util/rational.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/**
- * A Tableau is a Rational matrix that keeps its rows in solved form.
- * Each row has a basic variable with coefficient -1 that is solved.
- * Tableau is optimized for pivoting.
- * The tableau should only be updated via pivot calls.
- */
-class Tableau : public Matrix<Rational> {
-public:
-private:
-  typedef DenseMap<RowIndex> BasicToRowMap;
-  // Set of all of the basic variables in the tableau.
-  // ArithVarMap<RowIndex> : ArithVar |-> RowIndex
-  BasicToRowMap d_basic2RowIndex;
-
-  // RowIndex |-> Basic Variable
-  typedef DenseMap<ArithVar> RowIndexToBasicMap;
-  RowIndexToBasicMap d_rowIndex2basic;
-
-public:
-
-  Tableau() : Matrix<Rational>(Rational(0)) {}
-
-  typedef Matrix<Rational>::ColIterator ColIterator;
-  typedef Matrix<Rational>::RowIterator RowIterator;
-  typedef BasicToRowMap::const_iterator BasicIterator;
-
-  typedef MatrixEntry<Rational> Entry;
-
-  bool isBasic(ArithVar v) const{
-    return d_basic2RowIndex.isKey(v);
-  }
-
-  void debugPrintIsBasic(ArithVar v) const {
-    if(isBasic(v)){
-      Trace("model") << v << " is basic." << std::endl;
-    }else{
-      Trace("model") << v << " is non-basic." << std::endl;
-    }
-  }
-
-  BasicIterator beginBasic() const {
-    return d_basic2RowIndex.begin();
-  }
-  BasicIterator endBasic() const {
-    return d_basic2RowIndex.end();
-  }
-
-  RowIndex basicToRowIndex(ArithVar x) const {
-    return d_basic2RowIndex[x];
-  }
-
-  ArithVar rowIndexToBasic(RowIndex rid) const {
-    Assert(d_rowIndex2basic.isKey(rid));
-    return d_rowIndex2basic[rid];
-  }
-
-  ColIterator colIterator(ArithVar x) const {
-    return getColumn(x).begin();
-  }
-
-  RowIterator ridRowIterator(RowIndex rid) const {
-    return getRow(rid).begin();
-  }
-
-  RowIterator basicRowIterator(ArithVar basic) const {
-    return ridRowIterator(basicToRowIndex(basic));
-  }
-
-  const Entry& basicFindEntry(ArithVar basic, ArithVar col) const {
-    return findEntry(basicToRowIndex(basic), col);
-  }
-
-  /**
-   * Adds a row to the tableau.
-   * The new row is equivalent to:
-   *   basicVar = \f$\sum_i\f$ coeffs[i] * variables[i]
-   * preconditions:
-   *   basicVar is already declared to be basic
-   *   basicVar does not have a row associated with it in the tableau.
-   *
-   * Note: each variables[i] does not have to be non-basic.
-   * Pivoting will be mimicked if it is basic.
-   */
-  void addRow(ArithVar basicVar,
-              const std::vector<Rational>& coeffs,
-              const std::vector<ArithVar>& variables);
-
-  /**
-   * preconditions:
-   *   x_r is basic,
-   *   x_s is non-basic, and
-   *   a_rs != 0.
-   */
-  void pivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb);
-
-  void removeBasicRow(ArithVar basic);
-
-  uint32_t basicRowLength(ArithVar basic) const{
-    RowIndex ridx = basicToRowIndex(basic);
-    return getRowLength(ridx);
-  }
-
-  /**
-   *  to += mult * from
-   * replacing from with its row.
-   */
-  void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult,  CoefficientChangeCallback& cb);
-
-  void directlyAddToCoefficient(ArithVar rowVar, ArithVar col, const Rational& mult,  CoefficientChangeCallback& cb){
-    RowIndex ridx = basicToRowIndex(rowVar);
-    manipulateRowEntry(ridx, col, mult, cb);
-  }
-
-  /* Returns the complexity of a row in the tableau. */
-  uint32_t rowComplexity(ArithVar basic) const;
-
-  /* Returns the average complexity of the rows in the tableau. */
-  double avgRowComplexity() const;
-
-  void printBasicRow(ArithVar basic, std::ostream& out);
-
-private:
-  /* Changes the basic variable on the row for basicOld to basicNew. */
-  void rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb);
-
-};/* class Tableau */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/tableau_sizes.cpp b/src/theory/arith/tableau_sizes.cpp
deleted file mode 100644 (file)
index b353812..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "base/output.h"
-#include "theory/arith/tableau_sizes.h"
-#include "theory/arith/tableau.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-uint32_t TableauSizes::getRowLength(ArithVar b) const {
-  return d_tab->basicRowLength(b);
-}
-
-uint32_t TableauSizes::getColumnLength(ArithVar x) const {
-  return d_tab->getColLength(x);
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/tableau_sizes.h b/src/theory/arith/tableau_sizes.h
deleted file mode 100644 (file)
index cf189a1..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "theory/arith/arithvar.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class Tableau;
-
-class TableauSizes {
-private:
-  const Tableau* d_tab;
-public:
-  TableauSizes(const Tableau* tab): d_tab(tab){}
-
-  uint32_t getRowLength(ArithVar b) const;
-  uint32_t getColumnLength(ArithVar x) const;
-}; /* TableauSizes */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
index f6496166d371c108be4c51a26ed98d957aca9bb1..fe04a9c2632644b0d47990e97d6ba3233c3370bb 100644 (file)
@@ -22,9 +22,8 @@
 #include "theory/arith/arith_evaluator.h"
 #include "theory/arith/arith_rewriter.h"
 #include "theory/arith/equality_solver.h"
-#include "theory/arith/infer_bounds.h"
 #include "theory/arith/nl/nonlinear_extension.h"
-#include "theory/arith/theory_arith_private.h"
+#include "theory/arith/linear/theory_arith_private.h"
 #include "theory/ext_theory.h"
 #include "theory/rewriter.h"
 #include "theory/theory_model.h"
@@ -45,7 +44,7 @@ TheoryArith::TheoryArith(Env& env, OutputChannel& out, Valuation valuation)
       d_ppre(d_env),
       d_bab(env, d_astate, d_im, d_ppre, d_pnm),
       d_eqSolver(nullptr),
-      d_internal(new TheoryArithPrivate(*this, env, d_bab)),
+      d_internal(new linear::TheoryArithPrivate(*this, env, d_bab)),
       d_nonlinearExtension(nullptr),
       d_opElim(d_env),
       d_arithPreproc(env, d_astate, d_im, d_pnm, d_opElim),
@@ -363,12 +362,9 @@ Node TheoryArith::getModelValue(TNode var) {
 
 std::pair<bool, Node> TheoryArith::entailmentCheck(TNode lit)
 {
-  ArithEntailmentCheckParameters def;
-  def.addLookupRowSumAlgorithms();
-  ArithEntailmentCheckSideEffects ase;
-  std::pair<bool, Node> res = d_internal->entailmentCheck(lit, def, ase);
-  return res;
+  return d_internal->entailmentCheck(lit);
 }
+
 eq::ProofEqEngine* TheoryArith::getProofEqEngine()
 {
   return d_im.getProofEqEngine();
index 2682b4d71d9c1f6a0788b2ca0b17e8da6c39f42b..c6ced57815dc7c62e490cbbdb4793086666aca67 100644 (file)
@@ -34,7 +34,10 @@ class NonlinearExtension;
 }
 
 class EqualitySolver;
+
+namespace linear {
 class TheoryArithPrivate;
+}
 
 /**
  * Implementation of linear and non-linear integer and real arithmetic.
@@ -42,7 +45,7 @@ class TheoryArithPrivate;
  * http://research.microsoft.com/en-us/um/people/leonardo/cav06.pdf
  */
 class TheoryArith : public Theory {
-  friend class TheoryArithPrivate;
+  friend class linear::TheoryArithPrivate;
  public:
   TheoryArith(Env& env, OutputChannel& out, Valuation valuation);
   virtual ~TheoryArith();
@@ -157,7 +160,7 @@ class TheoryArith : public Theory {
   /** The equality solver */
   std::unique_ptr<EqualitySolver> d_eqSolver;
   /** The (old) linear arithmetic solver */
-  TheoryArithPrivate* d_internal;
+  linear::TheoryArithPrivate* d_internal;
 
   /**
    * The non-linear extension, responsible for all approaches for non-linear
diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp
deleted file mode 100644 (file)
index 58ec5ee..0000000
+++ /dev/null
@@ -1,4995 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Gereon Kremer, Alex Ozdemir
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/theory_arith_private.h"
-
-#include <map>
-#include <optional>
-#include <queue>
-#include <vector>
-
-#include "base/output.h"
-#include "context/cdhashset.h"
-#include "context/cdinsert_hashmap.h"
-#include "context/cdlist.h"
-#include "context/cdqueue.h"
-#include "context/context.h"
-#include "expr/kind.h"
-#include "expr/metakind.h"
-#include "expr/node.h"
-#include "expr/node_algorithm.h"
-#include "expr/node_builder.h"
-#include "expr/skolem_manager.h"
-#include "options/arith_options.h"
-#include "options/base_options.h"
-#include "options/smt_options.h"
-#include "preprocessing/util/ite_utilities.h"
-#include "proof/proof_generator.h"
-#include "proof/proof_node_manager.h"
-#include "proof/proof_rule.h"
-#include "smt/logic_exception.h"
-#include "smt/smt_statistics_registry.h"
-#include "smt_util/boolean_simplification.h"
-#include "theory/arith/approx_simplex.h"
-#include "theory/arith/arith_rewriter.h"
-#include "theory/arith/arith_static_learner.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/congruence_manager.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/cut_log.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/dio_solver.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/matrix.h"
-#include "theory/arith/nl/nonlinear_extension.h"
-#include "theory/arith/normal_form.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/simplex.h"
-#include "theory/arith/theory_arith.h"
-#include "theory/ext_theory.h"
-#include "theory/quantifiers/fmf/bounded_integers.h"
-#include "theory/rewriter.h"
-#include "theory/theory_model.h"
-#include "theory/trust_substitutions.h"
-#include "theory/valuation.h"
-#include "util/dense_map.h"
-#include "util/integer.h"
-#include "util/random.h"
-#include "util/rational.h"
-#include "util/result.h"
-#include "util/statistics_stats.h"
-
-using namespace std;
-using namespace cvc5::internal::kind;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-static Node toSumNode(const ArithVariables& vars, const DenseMap<Rational>& sum);
-static bool complexityBelow(const DenseMap<Rational>& row, uint32_t cap);
-
-TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing,
-                                       Env& env,
-                                       BranchAndBound& bab)
-    : EnvObj(env),
-      d_containing(containing),
-      d_foundNl(false),
-      d_rowTracking(),
-      d_bab(bab),
-      d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
-                                           : nullptr),
-      d_checker(),
-      d_pfGen(new EagerProofGenerator(d_pnm, userContext())),
-      d_constraintDatabase(d_env,
-                           d_partialModel,
-                           d_congruenceManager,
-                           RaiseConflict(*this),
-                           d_pfGen.get()),
-      d_qflraStatus(Result::UNKNOWN),
-      d_unknownsInARow(0),
-      d_hasDoneWorkSinceCut(false),
-      d_learner(userContext()),
-      d_assertionsThatDoNotMatchTheirLiterals(context()),
-      d_nextIntegerCheckVar(0),
-      d_constantIntegerVariables(context()),
-      d_diseqQueue(context(), false),
-      d_currentPropagationList(),
-      d_learnedBounds(context()),
-      d_preregisteredNodes(context()),
-      d_partialModel(context(), DeltaComputeCallback(*this)),
-      d_errorSet(
-          d_partialModel, TableauSizes(&d_tableau), BoundCountingLookup(*this)),
-      d_tableau(),
-      d_linEq(d_partialModel,
-              d_tableau,
-              d_rowTracking,
-              BasicVarModelUpdateCallBack(*this)),
-      d_diosolver(env),
-      d_restartsCounter(0),
-      d_tableauSizeHasBeenModified(false),
-      d_tableauResetDensity(1.6),
-      d_tableauResetPeriod(10),
-      d_conflicts(context()),
-      d_blackBoxConflict(context(), Node::null()),
-      d_blackBoxConflictPf(context(), std::shared_ptr<ProofNode>(nullptr)),
-      d_congruenceManager(d_env,
-                          d_constraintDatabase,
-                          SetupLiteralCallBack(*this),
-                          d_partialModel,
-                          RaiseEqualityEngineConflict(*this)),
-      d_cmEnabled(context(), options().arith.arithCongMan),
-
-      d_dualSimplex(
-          env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
-      d_fcSimplex(
-          env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
-      d_soiSimplex(
-          env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
-      d_attemptSolSimplex(
-          env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
-      d_pass1SDP(NULL),
-      d_otherSDP(NULL),
-      d_lastContextIntegerAttempted(context(), -1),
-
-      d_DELTA_ZERO(0),
-      d_approxCuts(context()),
-      d_fullCheckCounter(0),
-      d_cutCount(context(), 0),
-      d_cutInContext(context()),
-      d_likelyIntegerInfeasible(context(), false),
-      d_guessedCoeffSet(context(), false),
-      d_guessedCoeffs(),
-      d_treeLog(NULL),
-      d_replayVariables(),
-      d_replayConstraints(),
-      d_lhsTmp(),
-      d_approxStats(NULL),
-      d_attemptSolveIntTurnedOff(userContext(), 0),
-      d_dioSolveResources(0),
-      d_solveIntMaybeHelp(0u),
-      d_solveIntAttempts(0u),
-      d_newFacts(false),
-      d_previousStatus(Result::UNKNOWN),
-      d_statistics(statisticsRegistry(), "theory::arith::")
-{
-}
-
-TheoryArithPrivate::~TheoryArithPrivate(){
-  if(d_treeLog != NULL){ delete d_treeLog; }
-  if(d_approxStats != NULL) { delete d_approxStats; }
-}
-
-bool TheoryArithPrivate::needsEqualityEngine(EeSetupInfo& esi)
-{
-  if (!d_cmEnabled)
-  {
-    return false;
-  }
-  return d_congruenceManager.needsEqualityEngine(esi);
-}
-void TheoryArithPrivate::finishInit()
-{
-  if (d_cmEnabled)
-  {
-    eq::EqualityEngine* ee = d_containing.getEqualityEngine();
-    Assert(ee != nullptr);
-    d_congruenceManager.finishInit(ee);
-  }
-}
-
-static bool contains(const ConstraintCPVec& v, ConstraintP con){
-  for(unsigned i = 0, N = v.size(); i < N; ++i){
-    if(v[i] == con){
-      return true;
-    }
-  }
-  return false;
-}
-static void drop( ConstraintCPVec& v, ConstraintP con){
-  size_t readPos, writePos, N;
-  for(readPos = 0, writePos = 0, N = v.size(); readPos < N; ++readPos){
-    ConstraintCP curr = v[readPos];
-    if(curr != con){
-      v[writePos] = curr;
-      writePos++;
-    }
-  }
-  v.resize(writePos);
-}
-
-
-static void resolve(ConstraintCPVec& buf, ConstraintP c, const ConstraintCPVec& pos, const ConstraintCPVec& neg){
-  unsigned posPos CVC5_UNUSED = pos.size();
-  for(unsigned i = 0, N = pos.size(); i < N; ++i){
-    if(pos[i] == c){
-      posPos = i;
-    }else{
-      buf.push_back(pos[i]);
-    }
-  }
-  Assert(posPos < pos.size());
-  ConstraintP negc = c->getNegation();
-  unsigned negPos CVC5_UNUSED = neg.size();
-  for(unsigned i = 0, N = neg.size(); i < N; ++i){
-    if(neg[i] == negc){
-      negPos = i;
-    }else{
-      buf.push_back(neg[i]);
-    }
-  }
-  Assert(negPos < neg.size());
-}
-
-TheoryArithPrivate::ModelException::ModelException(TNode n, const char* msg)
-{
-  stringstream ss;
-  ss << "Cannot construct a model for " << n << " as " << endl << msg;
-  setMessage(ss.str());
-}
-TheoryArithPrivate::ModelException::~ModelException() {}
-
-TheoryArithPrivate::Statistics::Statistics(StatisticsRegistry& reg,
-                                           const std::string& name)
-    : d_statAssertUpperConflicts(
-        reg.registerInt(name + "AssertUpperConflicts")),
-      d_statAssertLowerConflicts(
-          reg.registerInt(name + "AssertLowerConflicts")),
-      d_statUserVariables(reg.registerInt(name + "UserVariables")),
-      d_statAuxiliaryVariables(reg.registerInt(name + "AuxiliaryVariables")),
-      d_statDisequalitySplits(reg.registerInt(name + "DisequalitySplits")),
-      d_statDisequalityConflicts(
-          reg.registerInt(name + "DisequalityConflicts")),
-      d_simplifyTimer(reg.registerTimer(name + "simplifyTimer")),
-      d_staticLearningTimer(reg.registerTimer(name + "staticLearningTimer")),
-      d_presolveTime(reg.registerTimer(name + "presolveTime")),
-      d_newPropTime(reg.registerTimer(name + "newPropTimer")),
-      d_externalBranchAndBounds(
-          reg.registerInt(name + "externalBranchAndBounds")),
-      d_initialTableauSize(reg.registerInt(name + "initialTableauSize")),
-      d_currSetToSmaller(reg.registerInt(name + "currSetToSmaller")),
-      d_smallerSetToCurr(reg.registerInt(name + "smallerSetToCurr")),
-      d_restartTimer(reg.registerTimer(name + "restartTimer")),
-      d_boundComputationTime(reg.registerTimer(name + "bound::time")),
-      d_boundComputations(reg.registerInt(name + "bound::boundComputations")),
-      d_boundPropagations(reg.registerInt(name + "bound::boundPropagations")),
-      d_unknownChecks(reg.registerInt(name + "status::unknowns")),
-      d_maxUnknownsInARow(reg.registerInt(name + "status::maxUnknownsInARow")),
-      d_avgUnknownsInARow(
-          reg.registerAverage(name + "status::avgUnknownsInARow")),
-      d_revertsOnConflicts(
-          reg.registerInt(name + "status::revertsOnConflicts")),
-      d_commitsOnConflicts(
-          reg.registerInt(name + "status::commitsOnConflicts")),
-      d_nontrivialSatChecks(
-          reg.registerInt(name + "status::nontrivialSatChecks")),
-      d_replayLogRecCount(reg.registerInt(name + "z::approx::replay::rec")),
-      d_replayLogRecConflictEscalation(
-          reg.registerInt(name + "z::approx::replay::rec::escalation")),
-      d_replayLogRecEarlyExit(
-          reg.registerInt(name + "z::approx::replay::rec::earlyexit")),
-      d_replayBranchCloseFailures(reg.registerInt(
-          name + "z::approx::replay::rec::branch::closefailures")),
-      d_replayLeafCloseFailures(reg.registerInt(
-          name + "z::approx::replay::rec::leaf::closefailures")),
-      d_replayBranchSkips(
-          reg.registerInt(name + "z::approx::replay::rec::branch::skips")),
-      d_mirCutsAttempted(
-          reg.registerInt(name + "z::approx::cuts::mir::attempted")),
-      d_gmiCutsAttempted(
-          reg.registerInt(name + "z::approx::cuts::gmi::attempted")),
-      d_branchCutsAttempted(
-          reg.registerInt(name + "z::approx::cuts::branch::attempted")),
-      d_cutsReconstructed(
-          reg.registerInt(name + "z::approx::cuts::reconstructed")),
-      d_cutsReconstructionFailed(
-          reg.registerInt(name + "z::approx::cuts::reconstructed::failed")),
-      d_cutsProven(reg.registerInt(name + "z::approx::cuts::proofs")),
-      d_cutsProofFailed(
-          reg.registerInt(name + "z::approx::cuts::proofs::failed")),
-      d_mipReplayLemmaCalls(
-          reg.registerInt(name + "z::approx::external::calls")),
-      d_mipExternalCuts(reg.registerInt(name + "z::approx::external::cuts")),
-      d_mipExternalBranch(
-          reg.registerInt(name + "z::approx::external::branches")),
-      d_inSolveInteger(reg.registerInt(name + "z::approx::inSolverInteger")),
-      d_branchesExhausted(
-          reg.registerInt(name + "z::approx::exhausted::branches")),
-      d_execExhausted(reg.registerInt(name + "z::approx::exhausted::exec")),
-      d_pivotsExhausted(reg.registerInt(name + "z::approx::exhausted::pivots")),
-      d_panicBranches(reg.registerInt(name + "z::arith::paniclemmas")),
-      d_relaxCalls(reg.registerInt(name + "z::arith::relax::calls")),
-      d_relaxLinFeas(reg.registerInt(name + "z::arith::relax::feasible::res")),
-      d_relaxLinFeasFailures(
-          reg.registerInt(name + "z::arith::relax::feasible::failures")),
-      d_relaxLinInfeas(reg.registerInt(name + "z::arith::relax::infeasible")),
-      d_relaxLinInfeasFailures(
-          reg.registerInt(name + "z::arith::relax::infeasible::failures")),
-      d_relaxLinExhausted(reg.registerInt(name + "z::arith::relax::exhausted")),
-      d_relaxOthers(reg.registerInt(name + "z::arith::relax::other")),
-      d_applyRowsDeleted(
-          reg.registerInt(name + "z::arith::cuts::applyRowsDeleted")),
-      d_replaySimplexTimer(
-          reg.registerTimer(name + "z::approx::replay::simplex::timer")),
-      d_replayLogTimer(
-          reg.registerTimer(name + "z::approx::replay::log::timer")),
-      d_solveIntTimer(reg.registerTimer(name + "z::solveInt::timer")),
-      d_solveRealRelaxTimer(
-          reg.registerTimer(name + "z::solveRealRelax::timer")),
-      d_solveIntCalls(reg.registerInt(name + "z::solveInt::calls")),
-      d_solveStandardEffort(
-          reg.registerInt(name + "z::solveInt::calls::standardEffort")),
-      d_approxDisabled(reg.registerInt(name + "z::approxDisabled")),
-      d_replayAttemptFailed(reg.registerInt(name + "z::replayAttemptFailed")),
-      d_cutsRejectedDuringReplay(
-          reg.registerInt(name + "z::approx::replay::cuts::rejected")),
-      d_cutsRejectedDuringLemmas(
-          reg.registerInt(name + "z::approx::external::cuts::rejected")),
-      d_satPivots(reg.registerHistogram<uint32_t>(name + "pivots::sat")),
-      d_unsatPivots(reg.registerHistogram<uint32_t>(name + "pivots::unsat")),
-      d_unknownPivots(
-          reg.registerHistogram<uint32_t>(name + "pivots::unknown")),
-      d_solveIntModelsAttempts(
-          reg.registerInt(name + "z::solveInt::models::attempts")),
-      d_solveIntModelsSuccessful(
-          reg.registerInt(name + "zzz::solveInt::models::successful")),
-      d_mipTimer(reg.registerTimer(name + "z::approx::mip::timer")),
-      d_lpTimer(reg.registerTimer(name + "z::approx::lp::timer")),
-      d_mipProofsAttempted(reg.registerInt(name + "z::mip::proofs::attempted")),
-      d_mipProofsSuccessful(
-          reg.registerInt(name + "z::mip::proofs::successful")),
-      d_numBranchesFailed(
-          reg.registerInt(name + "z::mip::branch::proof::failed"))
-{
-}
-
-bool complexityBelow(const DenseMap<Rational>& row, uint32_t cap){
-  DenseMap<Rational>::const_iterator riter, rend;
-  for(riter=row.begin(), rend=row.end(); riter != rend; ++riter){
-    ArithVar v = *riter;
-    const Rational& q = row[v];
-    if(q.complexity() > cap){
-      return false;
-    }
-  }
-  return true;
-}
-
-bool TheoryArithPrivate::isProofEnabled() const
-{
-  return d_pnm != nullptr;
-}
-
-void TheoryArithPrivate::raiseConflict(ConstraintCP a, InferenceId id){
-  Assert(a->inConflict());
-  Assert(id != InferenceId::UNKNOWN)
-      << "Must provide an inference id in TheoryArithPrivate::raiseConflict";
-  d_conflicts.push_back(std::make_pair(a, id));
-}
-
-void TheoryArithPrivate::raiseBlackBoxConflict(Node bb,
-                                               std::shared_ptr<ProofNode> pf)
-{
-  Trace("arith::bb") << "raiseBlackBoxConflict: " << bb << std::endl;
-  if (d_blackBoxConflict.get().isNull())
-  {
-    if (isProofEnabled())
-    {
-      Trace("arith::bb") << "  with proof " << pf << std::endl;
-      d_blackBoxConflictPf.set(pf);
-    }
-    d_blackBoxConflict = bb;
-  }
-}
-
-bool TheoryArithPrivate::anyConflict() const
-{
-  return !conflictQueueEmpty() || !d_blackBoxConflict.get().isNull();
-}
-
-void TheoryArithPrivate::revertOutOfConflict(){
-  d_partialModel.revertAssignmentChanges();
-  clearUpdates();
-  d_currentPropagationList.clear();
-}
-
-void TheoryArithPrivate::clearUpdates(){
-  d_updatedBounds.purge();
-}
-
-void TheoryArithPrivate::zeroDifferenceDetected(ArithVar x){
-  if(d_cmEnabled){
-    Assert(d_congruenceManager.isWatchedVariable(x));
-    Assert(d_partialModel.upperBoundIsZero(x));
-    Assert(d_partialModel.lowerBoundIsZero(x));
-
-    ConstraintP lb = d_partialModel.getLowerBoundConstraint(x);
-    ConstraintP ub = d_partialModel.getUpperBoundConstraint(x);
-
-    if(lb->isEquality()){
-      d_congruenceManager.watchedVariableIsZero(lb);
-    }else if(ub->isEquality()){
-      d_congruenceManager.watchedVariableIsZero(ub);
-    }else{
-      d_congruenceManager.watchedVariableIsZero(lb, ub);
-    }
-  }
-}
-
-bool TheoryArithPrivate::getSolveIntegerResource(){
-  if(d_attemptSolveIntTurnedOff > 0){
-    d_attemptSolveIntTurnedOff = d_attemptSolveIntTurnedOff - 1;
-    return false;
-  }else{
-    return true;
-  }
-}
-
-bool TheoryArithPrivate::getDioCuttingResource(){
-  if(d_dioSolveResources > 0){
-    d_dioSolveResources--;
-    if(d_dioSolveResources == 0){
-      d_dioSolveResources = -options().arith.rrTurns;
-    }
-    return true;
-  }else{
-    d_dioSolveResources++;
-    if(d_dioSolveResources >= 0){
-      d_dioSolveResources = options().arith.dioSolverTurns;
-    }
-    return false;
-  }
-}
-
-/* procedure AssertLower( x_i >= c_i ) */
-bool TheoryArithPrivate::AssertLower(ConstraintP constraint){
-  Assert(constraint != NullConstraint);
-  Assert(constraint->isLowerBound());
-  Assert(constraint->isTrue());
-  Assert(!constraint->negationHasProof());
-
-  ArithVar x_i = constraint->getVariable();
-  const DeltaRational& c_i = constraint->getValue();
-
-  Trace("arith") << "AssertLower(" << x_i << " " << c_i << ")"<< std::endl;
-
-  Assert(!isInteger(x_i) || c_i.isIntegral());
-
-  //TODO Relax to less than?
-  if(d_partialModel.lessThanLowerBound(x_i, c_i)){
-    return false; //sat
-  }
-
-  int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
-  if(cmpToUB > 0){ //  c_i < \lowerbound(x_i)
-    ConstraintP ubc = d_partialModel.getUpperBoundConstraint(x_i);
-    ConstraintP negation = constraint->getNegation();
-    negation->impliedByUnate(ubc, true);
-
-    raiseConflict(constraint, InferenceId::ARITH_CONF_LOWER);
-
-    ++(d_statistics.d_statAssertLowerConflicts);
-    return true;
-  }else if(cmpToUB == 0){
-    if(isInteger(x_i)){
-      d_constantIntegerVariables.push_back(x_i);
-      Trace("dio::push") << "dio::push " << x_i << endl;
-    }
-    ConstraintP ub = d_partialModel.getUpperBoundConstraint(x_i);
-
-    if(d_cmEnabled){
-      if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){
-        // if it is not a watched variable report it
-        // if it is is a watched variable and c_i == 0,
-        // let zeroDifferenceDetected(x_i) catch this
-        d_congruenceManager.equalsConstant(constraint, ub);
-      }
-    }
-
-    const ValueCollection& vc = constraint->getValueCollection();
-    if(vc.hasEquality()){
-      Assert(vc.hasDisequality());
-      ConstraintP eq = vc.getEquality();
-      ConstraintP diseq = vc.getDisequality();
-      // x <= b, x >= b |= x = b
-      // (x > b or x < b or x = b)
-      Trace("arith::eq") << "lb == ub, propagate eq" << eq << endl;
-      bool triConflict = diseq->isTrue();
-
-      if(!eq->isTrue()){
-        eq->impliedByTrichotomy(constraint, ub, triConflict);
-        eq->tryToPropagate();
-      }
-      if(triConflict){
-        ++(d_statistics.d_statDisequalityConflicts);
-        raiseConflict(eq, InferenceId::ARITH_CONF_TRICHOTOMY);
-        return true;
-      }
-    }
-  }else{
-    // l <= x <= u and l < u
-    Assert(cmpToUB < 0);
-    const ValueCollection& vc = constraint->getValueCollection();
-
-    if(vc.hasDisequality()){
-      const ConstraintP diseq = vc.getDisequality();
-      if(diseq->isTrue()){
-        const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
-        ConstraintP negUb = ub->getNegation();
-
-        // l <= x, l != x |= l < x
-        // |= not (l >= x)
-        bool ubInConflict = ub->hasProof();
-        bool learnNegUb = !(negUb->hasProof());
-        if(learnNegUb){
-          negUb->impliedByTrichotomy(constraint, diseq, ubInConflict);
-          negUb->tryToPropagate();
-        }
-        if(ubInConflict){
-          raiseConflict(ub, InferenceId::ARITH_CONF_TRICHOTOMY);
-          return true;
-        }else if(learnNegUb){
-          d_learnedBounds.push_back(negUb);
-        }
-      }
-    }
-  }
-
-  d_currentPropagationList.push_back(constraint);
-  d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
-
-  d_partialModel.setLowerBoundConstraint(constraint);
-
-  if(d_cmEnabled){
-    if(d_congruenceManager.isWatchedVariable(x_i)){
-      int sgn = c_i.sgn();
-      if(sgn > 0){
-        d_congruenceManager.watchedVariableCannotBeZero(constraint);
-      }else if(sgn == 0 && d_partialModel.upperBoundIsZero(x_i)){
-        zeroDifferenceDetected(x_i);
-      }
-    }
-  }
-
-  d_updatedBounds.softAdd(x_i);
-
-  if(TraceIsOn("model")) {
-    Trace("model") << "before" << endl;
-    d_partialModel.printModel(x_i);
-    d_tableau.debugPrintIsBasic(x_i);
-  }
-
-  if(!d_tableau.isBasic(x_i)){
-    if(d_partialModel.getAssignment(x_i) < c_i){
-      d_linEq.update(x_i, c_i);
-    }
-  }else{
-    d_errorSet.signalVariable(x_i);
-  }
-
-  if(TraceIsOn("model")) {
-    Trace("model") << "after" << endl;
-    d_partialModel.printModel(x_i);
-    d_tableau.debugPrintIsBasic(x_i);
- }
-
-  return false; //sat
-}
-
-/* procedure AssertUpper( x_i <= c_i) */
-bool TheoryArithPrivate::AssertUpper(ConstraintP constraint){
-  Assert(constraint != NullConstraint);
-  Assert(constraint->isUpperBound());
-  Assert(constraint->isTrue());
-  Assert(!constraint->negationHasProof());
-
-  ArithVar x_i = constraint->getVariable();
-  const DeltaRational& c_i = constraint->getValue();
-
-  Trace("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
-
-
-  //Too strong because of rounding with integers
-  //Assert(!constraint->hasLiteral() || original == constraint->getLiteral());
-  Assert(!isInteger(x_i) || c_i.isIntegral());
-
-  Trace("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
-
-  if(d_partialModel.greaterThanUpperBound(x_i, c_i) ){ // \upperbound(x_i) <= c_i
-    return false; //sat
-  }
-
-  // cmpToLb =  \lowerbound(x_i).cmp(c_i)
-  int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
-  if( cmpToLB < 0 ){ //  \upperbound(x_i) < \lowerbound(x_i)
-    // l_i <= x_i and c_i < l_i |= c_i < x_i
-    // or ... |= not (x_i <= c_i)
-    ConstraintP lbc = d_partialModel.getLowerBoundConstraint(x_i);
-    ConstraintP negConstraint = constraint->getNegation();
-    negConstraint->impliedByUnate(lbc, true);
-    raiseConflict(constraint, InferenceId::ARITH_CONF_UPPER);
-    ++(d_statistics.d_statAssertUpperConflicts);
-    return true;
-  }else if(cmpToLB == 0){ // \lowerBound(x_i) == \upperbound(x_i)
-    if(isInteger(x_i)){
-      d_constantIntegerVariables.push_back(x_i);
-      Trace("dio::push") << "dio::push " << x_i << endl;
-    }
-
-    const ValueCollection& vc = constraint->getValueCollection();
-    ConstraintP lb = d_partialModel.getLowerBoundConstraint(x_i);
-    if(d_cmEnabled){
-      if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){
-        // if it is not a watched variable report it
-        // if it is is a watched variable and c_i == 0,
-        // let zeroDifferenceDetected(x_i) catch this
-        d_congruenceManager.equalsConstant(lb, constraint);
-      }
-    }
-
-    if(vc.hasDisequality()){
-      Assert(vc.hasDisequality());
-      ConstraintP eq = vc.getEquality();
-      ConstraintP diseq = vc.getDisequality();
-      // x <= b, x >= b |= x = b
-      // (x > b or x < b or x = b)
-      Trace("arith::eq") << "lb == ub, propagate eq" << eq << endl;
-      bool triConflict = diseq->isTrue();
-      if(!eq->isTrue()){
-        eq->impliedByTrichotomy(constraint, lb, triConflict);
-        eq->tryToPropagate();
-      }
-      if(triConflict){
-        ++(d_statistics.d_statDisequalityConflicts);
-        raiseConflict(eq, InferenceId::ARITH_CONF_TRICHOTOMY);
-        return true;
-      }
-    }
-  }else if(cmpToLB > 0){
-    // l <= x <= u and l < u
-    Assert(cmpToLB > 0);
-    const ValueCollection& vc = constraint->getValueCollection();
-
-    if(vc.hasDisequality()){
-      const ConstraintP diseq = vc.getDisequality();
-      if(diseq->isTrue()){
-        const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
-        ConstraintP negLb = lb->getNegation();
-
-        // x <= u, u != x |= u < x
-        // |= not (u >= x)
-        bool lbInConflict = lb->hasProof();
-        bool learnNegLb = !(negLb->hasProof());
-        if(learnNegLb){
-          negLb->impliedByTrichotomy(constraint, diseq, lbInConflict);
-          negLb->tryToPropagate();
-        }
-        if(lbInConflict){
-          raiseConflict(lb, InferenceId::ARITH_CONF_TRICHOTOMY);
-          return true;
-        }else if(learnNegLb){
-          d_learnedBounds.push_back(negLb);
-        }
-      }
-    }
-  }
-
-  d_currentPropagationList.push_back(constraint);
-  d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
-  //It is fine if this is NullConstraint
-
-  d_partialModel.setUpperBoundConstraint(constraint);
-
-  if(d_cmEnabled){
-    if(d_congruenceManager.isWatchedVariable(x_i)){
-      int sgn = c_i.sgn();
-      if(sgn < 0){
-        d_congruenceManager.watchedVariableCannotBeZero(constraint);
-      }else if(sgn == 0 && d_partialModel.lowerBoundIsZero(x_i)){
-        zeroDifferenceDetected(x_i);
-      }
-    }
-  }
-
-  d_updatedBounds.softAdd(x_i);
-
-  if(TraceIsOn("model")) {
-    Trace("model") << "before" << endl;
-    d_partialModel.printModel(x_i);
-    d_tableau.debugPrintIsBasic(x_i);
-  }
-
-  if(!d_tableau.isBasic(x_i)){
-    if(d_partialModel.getAssignment(x_i) > c_i){
-      d_linEq.update(x_i, c_i);
-    }
-  }else{
-    d_errorSet.signalVariable(x_i);
-  }
-
-  if(TraceIsOn("model")) {
-    Trace("model") << "after" << endl;
-    d_partialModel.printModel(x_i);
-    d_tableau.debugPrintIsBasic(x_i);
-  }
-
-  return false; //sat
-}
-
-
-/* procedure AssertEquality( x_i == c_i ) */
-bool TheoryArithPrivate::AssertEquality(ConstraintP constraint){
-  Assert(constraint != NullConstraint);
-  Assert(constraint->isEquality());
-  Assert(constraint->isTrue());
-  Assert(!constraint->negationHasProof());
-
-  ArithVar x_i = constraint->getVariable();
-  const DeltaRational& c_i = constraint->getValue();
-
-  Trace("arith") << "AssertEquality(" << x_i << " " << c_i << ")"<< std::endl;
-
-  //Should be fine in integers
-  Assert(!isInteger(x_i) || c_i.isIntegral());
-
-  int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
-  int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
-
-  // u_i <= c_i <= l_i
-  // This can happen if both c_i <= x_i and x_i <= c_i are in the system.
-  if(cmpToUB >= 0 && cmpToLB <= 0){
-    return false; //sat
-  }
-
-  if(cmpToUB > 0 || cmpToLB < 0){
-    ConstraintP cb = (cmpToUB > 0) ?  d_partialModel.getUpperBoundConstraint(x_i) :
-      d_partialModel.getLowerBoundConstraint(x_i);
-    ConstraintP diseq = constraint->getNegation();
-    Assert(!diseq->isTrue());
-    diseq->impliedByUnate(cb, true);
-    raiseConflict(constraint, InferenceId::ARITH_CONF_EQ);
-    return true;
-  }
-
-  Assert(cmpToUB <= 0);
-  Assert(cmpToLB >= 0);
-  Assert(cmpToUB < 0 || cmpToLB > 0);
-
-  if(isInteger(x_i)){
-    d_constantIntegerVariables.push_back(x_i);
-    Trace("dio::push") << "dio::push " << x_i << endl;
-  }
-
-  // Don't bother to check whether x_i != c_i is in d_diseq
-  // The a and (not a) should never be on the fact queue
-  d_currentPropagationList.push_back(constraint);
-  d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
-  d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
-
-  d_partialModel.setUpperBoundConstraint(constraint);
-  d_partialModel.setLowerBoundConstraint(constraint);
-
-  if(d_cmEnabled){
-    if(d_congruenceManager.isWatchedVariable(x_i)){
-      int sgn = c_i.sgn();
-      if(sgn == 0){
-        zeroDifferenceDetected(x_i);
-      }else{
-        d_congruenceManager.watchedVariableCannotBeZero(constraint);
-        d_congruenceManager.equalsConstant(constraint);
-      }
-    }else{
-      d_congruenceManager.equalsConstant(constraint);
-    }
-  }
-
-  d_updatedBounds.softAdd(x_i);
-
-  if(TraceIsOn("model")) {
-    Trace("model") << "before" << endl;
-    d_partialModel.printModel(x_i);
-    d_tableau.debugPrintIsBasic(x_i);
-  }
-
-  if(!d_tableau.isBasic(x_i)){
-    if(!(d_partialModel.getAssignment(x_i) == c_i)){
-      d_linEq.update(x_i, c_i);
-    }
-  }else{
-    d_errorSet.signalVariable(x_i);
-  }
-
-  if(TraceIsOn("model")) {
-    Trace("model") << "after" << endl;
-    d_partialModel.printModel(x_i);
-    d_tableau.debugPrintIsBasic(x_i);
-  }
-
-  return false;
-}
-
-
-/* procedure AssertDisequality( x_i != c_i ) */
-bool TheoryArithPrivate::AssertDisequality(ConstraintP constraint){
-  Assert(constraint != NullConstraint);
-  Assert(constraint->isDisequality());
-  Assert(constraint->isTrue());
-  Assert(!constraint->negationHasProof());
-
-  ArithVar x_i = constraint->getVariable();
-  const DeltaRational& c_i = constraint->getValue();
-  Trace("arith") << "AssertDisequality(" << x_i << " " << c_i << ")"<< std::endl;
-
-  //Should be fine in integers
-  Assert(!isInteger(x_i) || c_i.isIntegral());
-
-  if(d_cmEnabled){
-    if(d_congruenceManager.isWatchedVariable(x_i)){
-      int sgn = c_i.sgn();
-      if(sgn == 0){
-        d_congruenceManager.watchedVariableCannotBeZero(constraint);
-      }
-    }
-  }
-
-  const ValueCollection& vc = constraint->getValueCollection();
-  if(vc.hasLowerBound() && vc.hasUpperBound()){
-    const ConstraintP lb = vc.getLowerBound();
-    const ConstraintP ub = vc.getUpperBound();
-    if(lb->isTrue() && ub->isTrue()){
-      ConstraintP eq = constraint->getNegation();
-      eq->impliedByTrichotomy(lb, ub, true);
-      raiseConflict(constraint, InferenceId::ARITH_CONF_TRICHOTOMY);
-      //in conflict
-      ++(d_statistics.d_statDisequalityConflicts);
-      return true;
-    }
-  }
-  if(vc.hasLowerBound() ){
-    const ConstraintP lb = vc.getLowerBound();
-    if(lb->isTrue()){
-      const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
-      Assert(!ub->isTrue());
-      Trace("arith::eq") << "propagate UpperBound " << constraint << lb << ub << endl;
-      const ConstraintP negUb = ub->getNegation();
-      if(!negUb->isTrue()){
-        negUb->impliedByTrichotomy(constraint, lb, false);
-        negUb->tryToPropagate();
-        d_learnedBounds.push_back(negUb);
-      }
-    }
-  }
-  if(vc.hasUpperBound()){
-    const ConstraintP ub = vc.getUpperBound();
-    if(ub->isTrue()){
-      const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
-      Assert(!lb->isTrue());
-
-      Trace("arith::eq") << "propagate LowerBound " << constraint << lb << ub << endl;
-      const ConstraintP negLb = lb->getNegation();
-      if(!negLb->isTrue()){
-        negLb->impliedByTrichotomy(constraint, ub, false);
-        negLb->tryToPropagate();
-        d_learnedBounds.push_back(negLb);
-      }
-    }
-  }
-
-  bool split = constraint->isSplit();
-
-  if(!split && c_i == d_partialModel.getAssignment(x_i)){
-    Trace("arith::eq") << "lemma now! " << constraint << endl;
-    outputTrustedLemma(constraint->split(), InferenceId::ARITH_SPLIT_DEQ);
-    return false;
-  }else if(d_partialModel.strictlyLessThanLowerBound(x_i, c_i)){
-    Trace("arith::eq") << "can drop as less than lb" << constraint << endl;
-  }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, c_i)){
-    Trace("arith::eq") << "can drop as less than ub" << constraint << endl;
-  }else if(!split){
-    Trace("arith::eq") << "push back" << constraint << endl;
-    d_diseqQueue.push(constraint);
-    d_partialModel.invalidateDelta();
-  }else{
-    Trace("arith::eq") << "skipping already split " << constraint << endl;
-  }
-  return false;
-}
-
-void TheoryArithPrivate::notifySharedTerm(TNode n)
-{
-  Trace("arith::notifySharedTerm") << "notifySharedTerm: " << n << endl;
-  if(n.isConst()){
-    d_partialModel.invalidateDelta();
-  }
-  if(!n.isConst() && !isSetup(n)){
-    Polynomial poly = Polynomial::parsePolynomial(n);
-    Polynomial::iterator it = poly.begin();
-    Polynomial::iterator it_end = poly.end();
-    for (; it != it_end; ++ it) {
-      Monomial m = *it;
-      if (!m.isConstant() && !isSetup(m.getVarList().getNode())) {
-        setupVariableList(m.getVarList());
-      }
-    }
-  }
-}
-
-Node TheoryArithPrivate::getModelValue(TNode term) {
-  try{
-    const DeltaRational drv = getDeltaValue(term);
-    const Rational& delta = d_partialModel.getDelta();
-    const Rational qmodel = drv.substituteDelta( delta );
-    return NodeManager::currentNM()->mkConstRealOrInt(term.getType(), qmodel);
-  } catch (DeltaRationalException& dr) {
-    return Node::null();
-  } catch (ModelException& me) {
-    return Node::null();
-  }
-}
-
-Theory::PPAssertStatus TheoryArithPrivate::ppAssert(
-    TrustNode tin, TrustSubstitutionMap& outSubstitutions)
-{
-  TimerStat::CodeTimer codeTimer(d_statistics.d_simplifyTimer);
-  TNode in = tin.getNode();
-  Trace("simplify") << "TheoryArithPrivate::solve(" << in << ")" << endl;
-
-
-  // Solve equalities
-  Rational minConstant = 0;
-  Node minMonomial;
-  Node minVar;
-  if (in.getKind() == kind::EQUAL &&
-      Theory::theoryOf(in[0].getType()) == THEORY_ARITH) {
-    Comparison cmp = Comparison::parseNormalForm(in);
-
-    Polynomial left = cmp.getLeft();
-
-    Monomial m = left.getHead();
-    if (m.getVarList().singleton()){
-      VarList vl = m.getVarList();
-      Node var = vl.getNode();
-      if (var.isVar())
-      {
-        // if vl.isIntegral then m.getConstant().isOne()
-        if(!vl.isIntegral() || m.getConstant().isOne()){
-          minVar = var;
-        }
-      }
-    }
-
-    // Solve for variable
-    if (!minVar.isNull()) {
-      Polynomial right = cmp.getRight();
-      Node elim = right.getNode();
-      // ax + p = c -> (ax + p) -ax - c = -ax
-      // x = (p - ax - c) * -1/a
-      // Add the substitution if not recursive
-      Assert(elim == rewrite(elim));
-
-      if (right.size() > options().arith.ppAssertMaxSubSize)
-      {
-        Trace("simplify")
-            << "TheoryArithPrivate::solve(): did not substitute due to the "
-               "right hand side containing too many terms: "
-            << minVar << ":" << elim << endl;
-        Trace("simplify") << right.size() << endl;
-      }
-      else if (d_containing.isLegalElimination(minVar, elim))
-      {
-        // cannot eliminate integers here unless we know the resulting
-        // substitution is integral
-        Trace("simplify") << "TheoryArithPrivate::solve(): substitution "
-                          << minVar << " |-> " << elim << endl;
-
-        outSubstitutions.addSubstitutionSolved(minVar, elim, tin);
-        return Theory::PP_ASSERT_STATUS_SOLVED;
-      }
-      else
-      {
-        Trace("simplify") << "TheoryArithPrivate::solve(): can't substitute "
-                          << minVar << ":" << minVar.getType() << " |-> "
-                          << elim << ":" << elim.getType() << endl;
-      }
-    }
-  }
-
-  // If a relation, remember the bound
-  switch(in.getKind()) {
-  case kind::LEQ:
-  case kind::LT:
-  case kind::GEQ:
-  case kind::GT:
-    if (in[0].isVar()) {
-      d_learner.addBound(in);
-    }
-    break;
-  default:
-    // Do nothing
-    break;
-  }
-
-  return Theory::PP_ASSERT_STATUS_UNSOLVED;
-}
-
-void TheoryArithPrivate::ppStaticLearn(TNode n, NodeBuilder& learned)
-{
-  TimerStat::CodeTimer codeTimer(d_statistics.d_staticLearningTimer);
-
-  d_learner.staticLearning(n, learned);
-}
-
-ArithVar TheoryArithPrivate::findShortestBasicRow(ArithVar variable){
-  ArithVar bestBasic = ARITHVAR_SENTINEL;
-  uint64_t bestRowLength = std::numeric_limits<uint64_t>::max();
-
-  Tableau::ColIterator basicIter = d_tableau.colIterator(variable);
-  for(; !basicIter.atEnd(); ++basicIter){
-    const Tableau::Entry& entry = *basicIter;
-    Assert(entry.getColVar() == variable);
-    RowIndex ridx = entry.getRowIndex();
-    ArithVar basic = d_tableau.rowIndexToBasic(ridx);
-    uint32_t rowLength = d_tableau.getRowLength(ridx);
-    if((rowLength < bestRowLength) ||
-       (rowLength == bestRowLength && basic < bestBasic)){
-      bestBasic = basic;
-      bestRowLength = rowLength;
-    }
-  }
-  Assert(bestBasic == ARITHVAR_SENTINEL
-         || bestRowLength < std::numeric_limits<uint32_t>::max());
-  return bestBasic;
-}
-
-void TheoryArithPrivate::setupVariable(const Variable& x){
-  Node n = x.getNode();
-
-  Assert(!isSetup(n));
-
-  ++(d_statistics.d_statUserVariables);
-  requestArithVar(n, false,  false);
-  //ArithVar varN = requestArithVar(n,false);
-  //setupInitialValue(varN);
-
-  markSetup(n);
-}
-
-void TheoryArithPrivate::setupVariableList(const VarList& vl){
-  Assert(!vl.empty());
-
-  TNode vlNode = vl.getNode();
-  Assert(!isSetup(vlNode));
-  Assert(!d_partialModel.hasArithVar(vlNode));
-
-  for(VarList::iterator i = vl.begin(), end = vl.end(); i != end; ++i){
-    Variable var = *i;
-
-    if(!isSetup(var.getNode())){
-      setupVariable(var);
-    }
-  }
-
-  if(!vl.singleton()){
-    // vl is the product of at least 2 variables
-    // vl : (* v1 v2 ...)
-    if (logicInfo().isLinear())
-    {
-      throw LogicException("A non-linear fact was asserted to arithmetic in a linear logic.");
-    }
-    d_foundNl = true;
-
-    ++(d_statistics.d_statUserVariables);
-    requestArithVar(vlNode, false, false);
-    //ArithVar av = requestArithVar(vlNode, false);
-    //setupInitialValue(av);
-
-    markSetup(vlNode);
-  }
-  else if (vlNode.getKind() == kind::EXPONENTIAL
-           || vlNode.getKind() == kind::SINE || vlNode.getKind() == kind::COSINE
-           || vlNode.getKind() == kind::TANGENT)
-  {
-    d_foundNl = true;
-  }
-
-  /* Note:
-   * Only call markSetup if the VarList is not a singleton.
-   * See the comment in setupPolynomail for more.
-   */
-}
-
-void TheoryArithPrivate::cautiousSetupPolynomial(const Polynomial& p){
-  if(p.containsConstant()){
-    if(!p.isConstant()){
-      Polynomial noConstant = p.getTail();
-      if(!isSetup(noConstant.getNode())){
-        setupPolynomial(noConstant);
-      }
-    }
-  }else if(!isSetup(p.getNode())){
-    setupPolynomial(p);
-  }
-}
-
-
-void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) {
-  Assert(!poly.containsConstant());
-  TNode polyNode = poly.getNode();
-  Assert(!isSetup(polyNode));
-  Assert(!d_partialModel.hasArithVar(polyNode));
-
-  for(Polynomial::iterator i = poly.begin(), end = poly.end(); i != end; ++i){
-    Monomial mono = *i;
-    const VarList& vl = mono.getVarList();
-    if(!isSetup(vl.getNode())){
-      setupVariableList(vl);
-    }
-  }
-
-  if (polyNode.getKind() == ADD)
-  {
-    d_tableauSizeHasBeenModified = true;
-
-    vector<ArithVar> variables;
-    vector<Rational> coefficients;
-    asVectors(poly, coefficients, variables);
-
-    ArithVar varSlack = requestArithVar(polyNode, true, false);
-    d_tableau.addRow(varSlack, coefficients, variables);
-    setupBasicValue(varSlack);
-    d_linEq.trackRowIndex(d_tableau.basicToRowIndex(varSlack));
-
-    //Add differences to the difference manager
-    Polynomial::iterator i = poly.begin(), end = poly.end();
-    if(i != end){
-      Monomial first = *i;
-      ++i;
-      if(i != end){
-        Monomial second = *i;
-        ++i;
-        if(i == end){
-          if(first.getConstant().isOne() && second.getConstant().getValue() == -1){
-            VarList vl0 = first.getVarList();
-            VarList vl1 = second.getVarList();
-            if(vl0.singleton() && vl1.singleton()){
-              d_congruenceManager.addWatchedPair(varSlack, vl0.getNode(), vl1.getNode());
-            }
-          }
-        }
-      }
-    }
-
-    ++(d_statistics.d_statAuxiliaryVariables);
-    markSetup(polyNode);
-  }
-
-  /* Note:
-   * It is worth documenting that polyNode should only be marked as
-   * being setup by this function if it has kind ADD.
-   * Other kinds will be marked as being setup by lower levels of setup
-   * specifically setupVariableList.
-   */
-}
-
-void TheoryArithPrivate::setupAtom(TNode atom) {
-  Assert(isRelationOperator(atom.getKind())) << atom;
-  Assert(Comparison::isNormalAtom(atom));
-  Assert(!isSetup(atom));
-  Assert(!d_constraintDatabase.hasLiteral(atom));
-
-  Comparison cmp = Comparison::parseNormalForm(atom);
-  Polynomial nvp = cmp.normalizedVariablePart();
-  Assert(!nvp.isZero());
-
-  if(!isSetup(nvp.getNode())){
-    setupPolynomial(nvp);
-  }
-
-  d_constraintDatabase.addLiteral(atom);
-
-  markSetup(atom);
-}
-
-void TheoryArithPrivate::preRegisterTerm(TNode n) {
-  Trace("arith::preregister") <<"begin arith::preRegisterTerm("<< n <<")"<< endl;
-
-  d_preregisteredNodes.insert(n);
-
-  try {
-    if(isRelationOperator(n.getKind())){
-      if(!isSetup(n)){
-        setupAtom(n);
-      }
-      ConstraintP c = d_constraintDatabase.lookup(n);
-      Assert(c != NullConstraint);
-
-      Trace("arith::preregister") << "setup constraint" << c << endl;
-      Assert(!c->canBePropagated());
-      c->setPreregistered();
-    }
-  } catch(LogicException& le) {
-    std::stringstream ss;
-    ss << le.getMessage() << endl << "The fact in question: " << n << endl;
-    throw LogicException(ss.str());
-  }
-
-  Trace("arith::preregister") << "end arith::preRegisterTerm("<< n <<")" << endl;
-}
-
-void TheoryArithPrivate::releaseArithVar(ArithVar v){
-  //Assert(d_partialModel.hasNode(v));
-
-  d_constraintDatabase.removeVariable(v);
-  d_partialModel.releaseArithVar(v);
-}
-
-ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool aux, bool internal){
-  //TODO : The VarList trick is good enough?
-  Kind xk = x.getKind();
-  Assert(isLeaf(x) || VarList::isMember(x) || xk == ADD || internal);
-  if (logicInfo().isLinear()
-      && (Variable::isDivMember(x) || xk == IAND || isTranscendentalKind(xk)))
-  {
-    stringstream ss;
-    ss << "A non-linear fact was asserted to "
-          "arithmetic in a linear logic: "
-       << x << std::endl;
-    throw LogicException(ss.str());
-  }
-  Assert(!d_partialModel.hasArithVar(x));
-  Assert(x.getType().isRealOrInt());  // real or integer
-
-  ArithVar max = d_partialModel.getNumberOfVariables();
-  ArithVar varX = d_partialModel.allocate(x, aux);
-
-  bool reclaim =  max >= d_partialModel.getNumberOfVariables();;
-
-  if(!reclaim){
-    d_dualSimplex.increaseMax();
-
-    d_tableau.increaseSize();
-    d_tableauSizeHasBeenModified = true;
-  }
-  d_constraintDatabase.addVariable(varX);
-
-  Trace("arith::arithvar") << "@" << context()->getLevel() << " " << x
-                           << " |-> " << varX << "(relaiming " << reclaim << ")"
-                           << endl;
-
-  Assert(!d_partialModel.hasUpperBound(varX));
-  Assert(!d_partialModel.hasLowerBound(varX));
-
-  return varX;
-}
-
-void TheoryArithPrivate::asVectors(const Polynomial& p, std::vector<Rational>& coeffs, std::vector<ArithVar>& variables) {
-  for(Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i){
-    const Monomial& mono = *i;
-    const Constant& constant = mono.getConstant();
-    const VarList& variable = mono.getVarList();
-
-    Node n = variable.getNode();
-
-    Trace("arith::asVectors") << "should be var: " << n << endl;
-
-    // TODO: This VarList::isMember(n) can be stronger
-    Assert(isLeaf(n) || VarList::isMember(n));
-    Assert(theoryOf(n) != THEORY_ARITH || d_partialModel.hasArithVar(n));
-
-    Assert(d_partialModel.hasArithVar(n));
-    ArithVar av = d_partialModel.asArithVar(n);
-
-    coeffs.push_back(constant.getValue());
-    variables.push_back(av);
-  }
-}
-
-/* Requirements:
- * For basic variables the row must have been added to the tableau.
- */
-void TheoryArithPrivate::setupBasicValue(ArithVar x){
-  Assert(d_tableau.isBasic(x));
-  //If the variable is basic, assertions may have already happened and updates
-  //may have occured before setting this variable up.
-
-  //This can go away if the tableau creation is done at preregister
-  //time instead of register
-  DeltaRational safeAssignment = d_linEq.computeRowValue(x, true);
-  DeltaRational assignment = d_linEq.computeRowValue(x, false);
-  d_partialModel.setAssignment(x,safeAssignment,assignment);
-
-  Trace("arith") << "setupVariable("<<x<<")"<<std::endl;
-}
-
-ArithVar TheoryArithPrivate::determineArithVar(const Polynomial& p) const{
-  Assert(!p.containsConstant());
-  Assert(p.getHead().constantIsPositive());
-  TNode n = p.getNode();
-  Trace("determineArithVar") << "determineArithVar(" << n << ")" << endl;
-  return d_partialModel.asArithVar(n);
-}
-
-ArithVar TheoryArithPrivate::determineArithVar(TNode assertion) const{
-  Trace("determineArithVar") << "determineArithVar " << assertion << endl;
-  Comparison cmp = Comparison::parseNormalForm(assertion);
-  Polynomial variablePart = cmp.normalizedVariablePart();
-  return determineArithVar(variablePart);
-}
-
-
-bool TheoryArithPrivate::canSafelyAvoidEqualitySetup(TNode equality){
-  Assert(equality.getKind() == EQUAL);
-  return d_partialModel.hasArithVar(equality[0]);
-}
-
-Comparison TheoryArithPrivate::mkIntegerEqualityFromAssignment(ArithVar v){
-  const DeltaRational& beta = d_partialModel.getAssignment(v);
-
-  Assert(beta.isIntegral());
-  Polynomial betaAsPolynomial = Polynomial::mkPolynomial( Constant::mkConstant(beta.floor()) );
-
-  TNode var = d_partialModel.asNode(v);
-  Polynomial varAsPolynomial = Polynomial::parsePolynomial(var);
-  return Comparison::mkComparison(EQUAL, varAsPolynomial, betaAsPolynomial);
-}
-
-TrustNode TheoryArithPrivate::dioCutting()
-{
-  context::Context::ScopedPush speculativePush(context());
-  //DO NOT TOUCH THE OUTPUTSTREAM
-
-  for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
-    ArithVar v = *vi;
-    if(isInteger(v)){
-      if(d_partialModel.cmpAssignmentUpperBound(v) == 0 ||
-         d_partialModel.cmpAssignmentLowerBound(v) == 0){
-        if(!d_partialModel.boundsAreEqual(v)){
-          // If the bounds are equal this is already in the dioSolver
-          //Add v = dr as a speculation.
-          Comparison eq = mkIntegerEqualityFromAssignment(v);
-          Trace("dio::push") << "dio::push " << v << " " <<  eq.getNode() << endl;
-          Assert(!eq.isBoolean());
-          d_diosolver.pushInputConstraint(eq, eq.getNode());
-          // It does not matter what the explanation of eq is.
-          // It cannot be used in a conflict
-        }
-      }
-    }
-  }
-
-  SumPair plane = d_diosolver.processEquationsForCut();
-  if(plane.isZero()){
-    return TrustNode::null();
-  }else{
-    Polynomial p = plane.getPolynomial();
-    Polynomial c = Polynomial::mkPolynomial(plane.getConstant() * Constant::mkConstant(-1));
-    Integer gcd = p.gcd();
-    Assert(p.isIntegral());
-    Assert(c.isIntegral());
-    Assert(gcd > 1);
-    Assert(!gcd.divides(c.asConstant().getNumerator()));
-    Comparison leq = Comparison::mkComparison(LEQ, p, c);
-    Comparison geq = Comparison::mkComparison(GEQ, p, c);
-    Node lemma = NodeManager::currentNM()->mkNode(OR, leq.getNode(), geq.getNode());
-    Node rewrittenLemma = rewrite(lemma);
-    Trace("arith::dio::ex") << "dioCutting found the plane: " << plane.getNode() << endl;
-    Trace("arith::dio::ex") << "resulting in the cut: " << lemma << endl;
-    Trace("arith::dio::ex") << "rewritten " << rewrittenLemma << endl;
-    Trace("arith::dio") << "dioCutting found the plane: " << plane.getNode() << endl;
-    Trace("arith::dio") << "resulting in the cut: " << lemma << endl;
-    Trace("arith::dio") << "rewritten " << rewrittenLemma << endl;
-    if (proofsEnabled())
-    {
-      NodeManager* nm = NodeManager::currentNM();
-      Node gt = nm->mkNode(kind::GT, p.getNode(), c.getNode());
-      Node lt = nm->mkNode(kind::LT, p.getNode(), c.getNode());
-      TypeNode type = gt[0].getType();
-
-      Pf pfNotLeq = d_pnm->mkAssume(leq.getNode().negate());
-      Pf pfGt =
-          d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pfNotLeq}, {gt});
-      Pf pfNotGeq = d_pnm->mkAssume(geq.getNode().negate());
-      Pf pfLt =
-          d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pfNotGeq}, {lt});
-      Pf pfSum = d_pnm->mkNode(
-          PfRule::MACRO_ARITH_SCALE_SUM_UB,
-          {pfGt, pfLt},
-          {nm->mkConstRealOrInt(type, -1), nm->mkConstRealOrInt(type, 1)});
-      Pf pfBot = d_pnm->mkNode(
-          PfRule::MACRO_SR_PRED_TRANSFORM, {pfSum}, {nm->mkConst<bool>(false)});
-      std::vector<Node> assumptions = {leq.getNode().negate(),
-                                       geq.getNode().negate()};
-      Pf pfNotAndNot = d_pnm->mkScope(pfBot, assumptions);
-      Pf pfOr = d_pnm->mkNode(PfRule::NOT_AND, {pfNotAndNot}, {});
-      Pf pfRewritten = d_pnm->mkNode(
-          PfRule::MACRO_SR_PRED_TRANSFORM, {pfOr}, {rewrittenLemma});
-      return d_pfGen->mkTrustNode(rewrittenLemma, pfRewritten);
-    }
-    else
-    {
-      return TrustNode::mkTrustLemma(rewrittenLemma, nullptr);
-    }
-  }
-}
-
-Node TheoryArithPrivate::callDioSolver(){
-  while(!d_constantIntegerVariables.empty()){
-    ArithVar v = d_constantIntegerVariables.front();
-    d_constantIntegerVariables.pop();
-
-    Trace("arith::dio")  << "callDioSolver " << v << endl;
-
-    Assert(isInteger(v));
-    Assert(d_partialModel.boundsAreEqual(v));
-
-    ConstraintP lb = d_partialModel.getLowerBoundConstraint(v);
-    ConstraintP ub = d_partialModel.getUpperBoundConstraint(v);
-
-    Node orig = Node::null();
-    if(lb->isEquality()){
-      orig = Constraint::externalExplainByAssertions({lb});
-    }else if(ub->isEquality()){
-      orig = Constraint::externalExplainByAssertions({ub});
-    }else {
-      orig = Constraint::externalExplainByAssertions(ub, lb);
-    }
-
-    Assert(d_partialModel.assignmentIsConsistent(v));
-
-    Comparison eq = mkIntegerEqualityFromAssignment(v);
-
-    if(eq.isBoolean()){
-      //This can only be a conflict
-      Assert(!eq.getNode().getConst<bool>());
-
-      //This should be handled by the normal form earlier in the case of equality
-      Assert(orig.getKind() != EQUAL);
-      return orig;
-    }else{
-      Trace("dio::push") << "dio::push " << v << " " << eq.getNode() << " with reason " << orig << endl;
-      d_diosolver.pushInputConstraint(eq, orig);
-    }
-  }
-
-  return d_diosolver.processEquationsForConflict();
-}
-
-ConstraintP TheoryArithPrivate::constraintFromFactQueue(TNode assertion)
-{
-  Kind simpleKind = Comparison::comparisonKind(assertion);
-  ConstraintP constraint = d_constraintDatabase.lookup(assertion);
-  if(constraint == NullConstraint){
-    Assert(simpleKind == EQUAL || simpleKind == DISTINCT);
-    bool isDistinct = simpleKind == DISTINCT;
-    Node eq = (simpleKind == DISTINCT) ? assertion[0] : assertion;
-    Assert(!isSetup(eq));
-    Node reEq = rewrite(eq);
-    Trace("arith::distinct::const") << "Assertion: " << assertion << std::endl;
-    Trace("arith::distinct::const") << "Eq       : " << eq << std::endl;
-    Trace("arith::distinct::const") << "reEq     : " << reEq << std::endl;
-    if(reEq.getKind() == CONST_BOOLEAN){
-      if(reEq.getConst<bool>() == isDistinct){
-        // if is (not true), or false
-        Assert((reEq.getConst<bool>() && isDistinct)
-               || (!reEq.getConst<bool>() && !isDistinct));
-        if (proofsEnabled())
-        {
-          Pf assume = d_pnm->mkAssume(assertion);
-          std::vector<Node> assumptions = {assertion};
-          Pf pf = d_pnm->mkScope(d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
-                                               {d_pnm->mkAssume(assertion)},
-                                               {}),
-                                 assumptions);
-          raiseBlackBoxConflict(assertion, pf);
-        }
-        else
-        {
-          raiseBlackBoxConflict(assertion);
-        }
-      }
-      return NullConstraint;
-    }
-    Assert(reEq.getKind() != CONST_BOOLEAN);
-    if(!isSetup(reEq)){
-      setupAtom(reEq);
-    }
-    Node reAssertion = isDistinct ? reEq.notNode() : reEq;
-    constraint = d_constraintDatabase.lookup(reAssertion);
-
-    if(assertion != reAssertion){
-      Trace("arith::nf") << "getting non-nf assertion " << assertion << " |-> " <<  reAssertion << endl;
-      Assert(constraint != NullConstraint);
-      d_assertionsThatDoNotMatchTheirLiterals.insert(assertion, constraint);
-    }
-  }
-
-  Assert(constraint != NullConstraint);
-
-  if(constraint->assertedToTheTheory()){
-    //Do nothing
-    return NullConstraint;
-  }
-  Assert(!constraint->assertedToTheTheory());
-  bool inConflict = constraint->negationHasProof();
-  constraint->setAssertedToTheTheory(assertion, inConflict);
-
-  if(!constraint->hasProof()){
-    Trace("arith::constraint") << "marking as constraint as self explaining " << endl;
-    constraint->setAssumption(inConflict);
-  } else {
-    Trace("arith::constraint")
-        << "already has proof: "
-        << Constraint::externalExplainByAssertions({constraint});
-  }
-
-  if(TraceIsOn("arith::negatedassumption") && inConflict){
-    ConstraintP negation = constraint->getNegation();
-    if(TraceIsOn("arith::negatedassumption") && negation->isAssumption()){
-      debugPrintFacts();
-    }
-    Trace("arith::eq") << "negation has proof" << endl;
-    Trace("arith::eq") << constraint << endl;
-    Trace("arith::eq") << negation << endl;
-  }
-
-  if(inConflict){
-    ConstraintP negation = constraint->getNegation();
-    if(TraceIsOn("arith::negatedassumption") && negation->isAssumption()){
-      debugPrintFacts();
-    }
-    Trace("arith::eq") << "negation has proof" << endl;
-    Trace("arith::eq") << constraint << endl;
-    Trace("arith::eq") << negation << endl;
-    raiseConflict(negation, InferenceId::ARITH_CONF_FACT_QUEUE);
-    return NullConstraint;
-  }else{
-    return constraint;
-  }
-}
-
-bool TheoryArithPrivate::assertionCases(ConstraintP constraint){
-  Assert(constraint->hasProof());
-  Assert(!constraint->negationHasProof());
-
-  ArithVar x_i = constraint->getVariable();
-
-  switch(constraint->getType()){
-  case UpperBound:
-    if(isInteger(x_i) && constraint->isStrictUpperBound()){
-      ConstraintP floorConstraint = constraint->getFloor();
-      if(!floorConstraint->isTrue()){
-        bool inConflict = floorConstraint->negationHasProof();
-        if (TraceIsOn("arith::intbound")) {
-          Trace("arith::intbound") << "literal, before: " << constraint->getLiteral() << std::endl;
-          Trace("arith::intbound") << "constraint, after: " << floorConstraint << std::endl;
-        }
-        floorConstraint->impliedByIntTighten(constraint, inConflict);
-        floorConstraint->tryToPropagate();
-        if(inConflict){
-          raiseConflict(floorConstraint, InferenceId::ARITH_TIGHTEN_FLOOR);
-          return true;
-        }
-      }
-      return AssertUpper(floorConstraint);
-    }else{
-      return AssertUpper(constraint);
-    }
-  case LowerBound:
-    if(isInteger(x_i) && constraint->isStrictLowerBound()){
-      ConstraintP ceilingConstraint = constraint->getCeiling();
-      if(!ceilingConstraint->isTrue()){
-        bool inConflict = ceilingConstraint->negationHasProof();
-        if (TraceIsOn("arith::intbound")) {
-          Trace("arith::intbound") << "literal, before: " << constraint->getLiteral() << std::endl;
-          Trace("arith::intbound") << "constraint, after: " << ceilingConstraint << std::endl;
-        }
-        ceilingConstraint->impliedByIntTighten(constraint, inConflict);
-        ceilingConstraint->tryToPropagate();
-        if(inConflict){
-          raiseConflict(ceilingConstraint, InferenceId::ARITH_TIGHTEN_CEIL);
-          return true;
-        }
-      }
-      return AssertLower(ceilingConstraint);
-    }else{
-      return AssertLower(constraint);
-    }
-  case Equality:
-    return AssertEquality(constraint);
-  case Disequality:
-    return AssertDisequality(constraint);
-  default:
-    Unreachable();
-    return false;
-  }
-}
-/**
- * Looks for through the variables starting at d_nextIntegerCheckVar
- * for the first integer variable that is between its upper and lower bounds
- * that has a non-integer assignment.
- *
- * If assumeBounds is true, skip the check that the variable is in bounds.
- *
- * If there is no such variable, returns ARITHVAR_SENTINEL;
- */
-ArithVar TheoryArithPrivate::nextIntegerViolation(bool assumeBounds) const
-{
-  ArithVar numVars = d_partialModel.getNumberOfVariables();
-  ArithVar v = d_nextIntegerCheckVar;
-  if (numVars > 0)
-  {
-    const ArithVar rrEnd = d_nextIntegerCheckVar;
-    do
-    {
-      if (isIntegerInput(v))
-      {
-        if (!d_partialModel.integralAssignment(v))
-        {
-          if (assumeBounds || d_partialModel.assignmentIsConsistent(v))
-          {
-            return v;
-          }
-        }
-      }
-      v = (1 + v == numVars) ? 0 : (1 + v);
-    } while (v != rrEnd);
-  }
-  return ARITHVAR_SENTINEL;
-}
-
-/**
- * Checks the set of integer variables I to see if each variable
- * in I has an integer assignment.
- */
-bool TheoryArithPrivate::hasIntegerModel()
-{
-  ArithVar next = nextIntegerViolation(true);
-  if (next != ARITHVAR_SENTINEL)
-  {
-    d_nextIntegerCheckVar = next;
-    if (TraceIsOn("arith::hasIntegerModel"))
-    {
-      Trace("arith::hasIntegerModel") << "has int model? " << next << endl;
-      d_partialModel.printModel(next, Trace("arith::hasIntegerModel"));
-    }
-    return false;
-  }
-  else
-  {
-    return true;
-  }
-}
-
-Node flattenAndSort(Node n){
-  Kind k = n.getKind();
-  switch(k){
-  case kind::OR:
-  case kind::AND:
-  case kind::ADD:
-  case kind::MULT:
-    break;
-  default:
-    return n;
-  }
-
-  std::vector<Node> out;
-  std::vector<Node> process;
-  process.push_back(n);
-  while(!process.empty()){
-    Node b = process.back();
-    process.pop_back();
-    if(b.getKind() == k){
-      for(Node::iterator i=b.begin(), end=b.end(); i!=end; ++i){
-        process.push_back(*i);
-      }
-    } else {
-      out.push_back(b);
-    }
-  }
-  Assert(out.size() >= 2);
-  std::sort(out.begin(), out.end());
-  return NodeManager::currentNM()->mkNode(k, out);
-}
-
-
-
-/** Outputs conflicts to the output channel. */
-void TheoryArithPrivate::outputConflicts(){
-  Trace("arith::conflict") << "outputting conflicts" << std::endl;
-  Assert(anyConflict());
-
-  if(!conflictQueueEmpty()){
-    Assert(!d_conflicts.empty());
-    for(size_t i = 0, i_end = d_conflicts.size(); i < i_end; ++i){
-      const std::pair<ConstraintCP, InferenceId>& conf = d_conflicts[i];
-      const ConstraintCP& confConstraint = conf.first;
-      bool hasProof = confConstraint->hasProof();
-      Assert(confConstraint->inConflict());
-      const ConstraintRule& pf = confConstraint->getConstraintRule();
-      if (TraceIsOn("arith::conflict"))
-      {
-        pf.print(std::cout, options().smt.produceProofs);
-        std::cout << std::endl;
-      }
-      if (TraceIsOn("arith::pf::tree"))
-      {
-        Trace("arith::pf::tree") << "\n\nTree:\n";
-        confConstraint->printProofTree(Trace("arith::pf::tree"));
-        confConstraint->getNegation()->printProofTree(Trace("arith::pf::tree"));
-      }
-
-      TrustNode trustedConflict = confConstraint->externalExplainConflict();
-      Node conflict = trustedConflict.getNode();
-
-      Trace("arith::conflict")
-          << "d_conflicts[" << i << "] " << conflict
-          << " has proof: " << hasProof << ", id = " << conf.second << endl;
-      if(TraceIsOn("arith::normalize::external")){
-        conflict = flattenAndSort(conflict);
-        Trace("arith::conflict") << "(normalized to) " << conflict << endl;
-      }
-
-      if (isProofEnabled())
-      {
-        outputTrustedConflict(trustedConflict, conf.second);
-      }
-      else
-      {
-        outputConflict(conflict, conf.second);
-      }
-    }
-  }
-  if(!d_blackBoxConflict.get().isNull()){
-    Node bb = d_blackBoxConflict.get();
-    Trace("arith::conflict") << "black box conflict" << bb
-                             << endl;
-    if(TraceIsOn("arith::normalize::external")){
-      bb = flattenAndSort(bb);
-      Trace("arith::conflict") << "(normalized to) " << bb << endl;
-    }
-    if (isProofEnabled() && d_blackBoxConflictPf.get())
-    {
-      auto confPf = d_blackBoxConflictPf.get();
-      outputTrustedConflict(d_pfGen->mkTrustNode(bb, confPf, true), InferenceId::ARITH_BLACK_BOX);
-    }
-    else
-    {
-      outputConflict(bb, InferenceId::ARITH_BLACK_BOX);
-    }
-  }
-}
-
-bool TheoryArithPrivate::outputTrustedLemma(TrustNode lemma, InferenceId id)
-{
-  Trace("arith::channel") << "Arith trusted lemma: " << lemma << std::endl;
-  return d_containing.d_im.trustedLemma(lemma, id);
-}
-
-bool TheoryArithPrivate::outputLemma(TNode lem, InferenceId id) {
-  Trace("arith::channel") << "Arith lemma: " << lem << std::endl;
-  return d_containing.d_im.lemma(lem, id);
-}
-
-void TheoryArithPrivate::outputTrustedConflict(TrustNode conf, InferenceId id)
-{
-  Trace("arith::channel") << "Arith trusted conflict: " << conf << std::endl;
-  d_containing.d_im.trustedConflict(conf, id);
-}
-
-void TheoryArithPrivate::outputConflict(TNode lit, InferenceId id) {
-  Trace("arith::channel") << "Arith conflict: " << lit << std::endl;
-  d_containing.d_im.conflict(lit, id);
-}
-
-void TheoryArithPrivate::outputPropagate(TNode lit) {
-  Trace("arith::channel") << "Arith propagation: " << lit << std::endl;
-  // call the propagate lit method of the
-  d_containing.d_im.propagateLit(lit);
-}
-
-void TheoryArithPrivate::outputRestart() {
-  Trace("arith::channel") << "Arith restart!" << std::endl;
-  (d_containing.d_out)->demandRestart();
-}
-
-bool TheoryArithPrivate::attemptSolveInteger(Theory::Effort effortLevel, bool emmmittedLemmaOrSplit){
-  int level = context()->getLevel();
-  Trace("approx")
-    << "attemptSolveInteger " << d_qflraStatus
-    << " " << emmmittedLemmaOrSplit
-    << " " << effortLevel
-    << " " << d_lastContextIntegerAttempted
-    << " " << level
-    << endl;
-
-  if(d_qflraStatus == Result::UNSAT){ return false; }
-  if(emmmittedLemmaOrSplit){ return false; }
-  if (!options().arith.useApprox)
-  {
-    return false;
-  }
-  if(!ApproximateSimplex::enabled()){ return false; }
-
-  if(Theory::fullEffort(effortLevel)){
-    if(hasIntegerModel()){
-      return false;
-    }else{
-      return getSolveIntegerResource();
-    }
-  }
-
-  if(d_lastContextIntegerAttempted <= 0){
-    if(hasIntegerModel()){
-      d_lastContextIntegerAttempted = context()->getLevel();
-      return false;
-    }else{
-      return getSolveIntegerResource();
-    }
-  }
-
-  if (!options().arith.trySolveIntStandardEffort)
-  {
-    return false;
-  }
-
-  if (d_lastContextIntegerAttempted <= (level >> 2))
-  {
-    double d = (double)(d_solveIntMaybeHelp + 1)
-               / (d_solveIntAttempts + 1 + level * level);
-    if (Random::getRandom().pickWithProb(d))
-    {
-      return getSolveIntegerResource();
-    }
-  }
-  return false;
-}
-
-bool TheoryArithPrivate::replayLog(ApproximateSimplex* approx){
-  TimerStat::CodeTimer codeTimer(d_statistics.d_replayLogTimer);
-
-  ++d_statistics.d_mipProofsAttempted;
-
-  Assert(d_replayVariables.empty());
-  Assert(d_replayConstraints.empty());
-
-  size_t enteringPropN = d_currentPropagationList.size();
-  Assert(conflictQueueEmpty());
-  TreeLog& tl = getTreeLog();
-  //tl.applySelected(); /* set row ids */
-
-  d_replayedLemmas = false;
-
-  /* use the try block for the purpose of pushing the sat context */
-  context::Context::ScopedPush speculativePush(context());
-  d_cmEnabled = false;
-  std::vector<ConstraintCPVec> res =
-      replayLogRec(approx, tl.getRootId(), NullConstraint, 1);
-
-  if(res.empty()){
-    ++d_statistics.d_replayAttemptFailed;
-  }else{
-    unsigned successes = 0;
-    for(size_t i =0, N = res.size(); i < N; ++i){
-      ConstraintCPVec& vec = res[i];
-      Assert(vec.size() >= 2);
-      for(size_t j=0, M = vec.size(); j < M; ++j){
-        ConstraintCP at_j = vec[j];
-        Assert(at_j->isTrue());
-        if(!at_j->negationHasProof()){
-          successes++;
-          vec[j] = vec.back();
-          vec.pop_back();
-          ConstraintP neg_at_j = at_j->getNegation();
-
-          Trace("approx::replayLog") << "Setting the proof for the replayLog conflict on:" << endl
-                                     << "  (" << neg_at_j->isTrue() <<") " << neg_at_j << endl
-                                     << "  (" << at_j->isTrue() <<") " << at_j << endl;
-          neg_at_j->impliedByIntHole(vec, true);
-          raiseConflict(at_j, InferenceId::ARITH_CONF_REPLAY_LOG);
-          break;
-        }
-      }
-    }
-    if(successes > 0){
-      ++d_statistics.d_mipProofsSuccessful;
-    }
-  }
-
-  if(d_currentPropagationList.size() > enteringPropN){
-    d_currentPropagationList.resize(enteringPropN);
-  }
-
-  /* It is not clear what the d_qflraStatus is at this point */
-  d_qflraStatus = Result::UNKNOWN;
-
-  Assert(d_replayVariables.empty());
-  Assert(d_replayConstraints.empty());
-
-  return !conflictQueueEmpty();
-}
-
-std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(const DenseMap<Rational>& lhs, Kind k, const Rational& rhs, bool branch)
-{
-  ArithVar added = ARITHVAR_SENTINEL;
-  Node sum = toSumNode(d_partialModel, lhs);
-  if(sum.isNull()){ return make_pair(NullConstraint, added); }
-
-  Trace("approx::constraint") << "replayGetConstraint " << sum
-                              << " " << k
-                              << " " << rhs
-                              << endl;
-
-  Assert(k == kind::LEQ || k == kind::GEQ);
-
-  NodeManager* nm = NodeManager::currentNM();
-  Node comparison =
-      nm->mkNode(k, sum, nm->mkConstRealOrInt(sum.getType(), rhs));
-  Node rewritten = rewrite(comparison);
-  if(!(Comparison::isNormalAtom(rewritten))){
-    return make_pair(NullConstraint, added);
-  }
-
-  Comparison cmp = Comparison::parseNormalForm(rewritten);
-  if(cmp.isBoolean()){ return make_pair(NullConstraint, added); }
-
-  Polynomial nvp =  cmp.normalizedVariablePart();
-  if(nvp.isZero()){ return make_pair(NullConstraint, added); }
-
-  Node norm = nvp.getNode();
-
-  ConstraintType t = Constraint::constraintTypeOfComparison(cmp);
-  DeltaRational dr = cmp.normalizedDeltaRational();
-
-  Trace("approx::constraint") << "rewriting " << rewritten << endl
-                              << " |-> " << norm << " " << t << " " << dr << endl;
-
-  Assert(!branch || d_partialModel.hasArithVar(norm));
-  ArithVar v = ARITHVAR_SENTINEL;
-  if(d_partialModel.hasArithVar(norm)){
-
-    v = d_partialModel.asArithVar(norm);
-    Trace("approx::constraint")
-        << "replayGetConstraint found " << norm << " |-> " << v << " @ "
-        << context()->getLevel() << endl;
-    Assert(!branch || d_partialModel.isIntegerInput(v));
-  }else{
-    v = requestArithVar(norm, true, true);
-    d_replayVariables.push_back(v);
-
-    added = v;
-
-    Trace("approx::constraint")
-        << "replayGetConstraint adding " << norm << " |-> " << v << " @ "
-        << context()->getLevel() << endl;
-
-    Polynomial poly = Polynomial::parsePolynomial(norm);
-    vector<ArithVar> variables;
-    vector<Rational> coefficients;
-    asVectors(poly, coefficients, variables);
-    d_tableau.addRow(v, coefficients, variables);
-    setupBasicValue(v);
-    d_linEq.trackRowIndex(d_tableau.basicToRowIndex(v));
-  }
-  Assert(d_partialModel.hasArithVar(norm));
-  Assert(d_partialModel.asArithVar(norm) == v);
-  Assert(d_constraintDatabase.variableDatabaseIsSetup(v));
-
-  ConstraintP imp = d_constraintDatabase.getBestImpliedBound(v, t, dr);
-  if(imp != NullConstraint){
-    if(imp->getValue() == dr){
-      Assert(added == ARITHVAR_SENTINEL);
-      return make_pair(imp, added);
-    }
-  }
-
-  ConstraintP newc = d_constraintDatabase.getConstraint(v, t, dr);
-  d_replayConstraints.push_back(newc);
-  return make_pair(newc, added);
-}
-
-std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(
-    ApproximateSimplex* approx, const NodeLog& nl)
-{
-  Assert(nl.isBranch());
-  Assert(d_lhsTmp.empty());
-
-  ArithVar v = approx->getBranchVar(nl);
-  if(v != ARITHVAR_SENTINEL && d_partialModel.isIntegerInput(v)){
-    if(d_partialModel.hasNode(v)){
-      d_lhsTmp.set(v, Rational(1));
-      double dval = nl.branchValue();
-      std::optional<Rational> maybe_value =
-          ApproximateSimplex::estimateWithCFE(dval);
-      if (!maybe_value)
-      {
-        return make_pair(NullConstraint, ARITHVAR_SENTINEL);
-      }
-      Rational fl(maybe_value.value().floor());
-      pair<ConstraintP, ArithVar> p;
-      p = replayGetConstraint(d_lhsTmp, kind::LEQ, fl, true);
-      d_lhsTmp.purge();
-      return p;
-    }
-  }
-  return make_pair(NullConstraint, ARITHVAR_SENTINEL);
-}
-
-std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(const CutInfo& ci) {
-  Assert(ci.reconstructed());
-  const DenseMap<Rational>& lhs = ci.getReconstruction().lhs;
-  const Rational& rhs = ci.getReconstruction().rhs;
-  Kind k = ci.getKind();
-
-  return replayGetConstraint(lhs, k, rhs, ci.getKlass() == BranchCutKlass);
-}
-
-Node toSumNode(const ArithVariables& vars, const DenseMap<Rational>& sum){
-  Trace("arith::toSumNode") << "toSumNode() begin" << endl;
-  NodeManager* nm = NodeManager::currentNM();
-  DenseMap<Rational>::const_iterator iter, end;
-  iter = sum.begin(), end = sum.end();
-  std::vector<Node> children;
-  for(; iter != end; ++iter){
-    ArithVar x = *iter;
-    if(!vars.hasNode(x)){ return Node::null(); }
-    Node xNode = vars.asNode(x);
-    const Rational& q = sum[x];
-    Node mult = nm->mkNode(kind::MULT, mkRationalNode(q), xNode);
-    Trace("arith::toSumNode") << "toSumNode() " << x << " " << mult << endl;
-    children.push_back(mult);
-  }
-  Trace("arith::toSumNode") << "toSumNode() end" << endl;
-  if (children.empty())
-  {
-    // NOTE: real type assumed here
-    return nm->mkConstReal(Rational(0));
-  }
-  else if (children.size() == 1)
-  {
-    return children[0];
-  }
-  return nm->mkNode(kind::ADD, children);
-}
-
-ConstraintCP TheoryArithPrivate::vectorToIntHoleConflict(const ConstraintCPVec& conflict){
-  Assert(conflict.size() >= 2);
-  ConstraintCPVec exp(conflict.begin(), conflict.end()-1);
-  ConstraintCP back = conflict.back();
-  Assert(back->hasProof());
-  ConstraintP negBack = back->getNegation();
-  // This can select negBack multiple times so we need to test if negBack has a proof.
-  if(negBack->hasProof()){
-    // back is in conflict already
-  } else {
-    negBack->impliedByIntHole(exp, true);
-  }
-
-  return back;
-}
-
-void TheoryArithPrivate::intHoleConflictToVector(ConstraintCP conflicting, ConstraintCPVec& conflict){
-  ConstraintCP negConflicting = conflicting->getNegation();
-  Assert(conflicting->hasProof());
-  Assert(negConflicting->hasProof());
-
-  conflict.push_back(conflicting);
-  conflict.push_back(negConflicting);
-
-  Constraint::assertionFringe(conflict);
-}
-
-void TheoryArithPrivate::tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bci){
-  Assert(conflictQueueEmpty());
-  std::vector< ConstraintCPVec > conflicts;
-
-  approx->tryCut(nid, bci);
-  Trace("approx::branch") << "tryBranchCut" << bci << endl;
-  Assert(bci.reconstructed());
-  Assert(!bci.proven());
-  pair<ConstraintP, ArithVar> p = replayGetConstraint(bci);
-  Assert(p.second == ARITHVAR_SENTINEL);
-  ConstraintP bc = p.first;
-  Assert(bc != NullConstraint);
-  if(bc->hasProof()){
-    return;
-  }
-
-  ConstraintP bcneg = bc->getNegation();
-  {
-    context::Context::ScopedPush speculativePush(context());
-    replayAssert(bcneg);
-    if(conflictQueueEmpty()){
-      TimerStat::CodeTimer codeTimer(d_statistics.d_replaySimplexTimer);
-
-      //test for linear feasibility
-      d_partialModel.stopQueueingBoundCounts();
-      UpdateTrackingCallback utcb(&d_linEq);
-      d_partialModel.processBoundsQueue(utcb);
-      d_linEq.startTrackingBoundCounts();
-
-      SimplexDecisionProcedure& simplex = selectSimplex(true);
-      simplex.findModel(false);
-      // Can change d_qflraStatus
-
-      d_linEq.stopTrackingBoundCounts();
-      d_partialModel.startQueueingBoundCounts();
-    }
-    for(size_t i = 0, N = d_conflicts.size(); i < N; ++i){
-
-      conflicts.push_back(ConstraintCPVec());
-      intHoleConflictToVector(d_conflicts[i].first, conflicts.back());
-      Constraint::assertionFringe(conflicts.back());
-
-      // ConstraintCP conflicting = d_conflicts[i];
-      // ConstraintCP negConflicting = conflicting->getNegation();
-      // Assert(conflicting->hasProof());
-      // Assert(negConflicting->hasProof());
-
-      // conflicts.push_back(ConstraintCPVec());
-      // ConstraintCPVec& back = conflicts.back();
-      // back.push_back(conflicting);
-      // back.push_back(negConflicting);
-
-      // // remove the floor/ceiling contraint implied by bcneg
-      // Constraint::assertionFringe(back);
-    }
-
-    if(TraceIsOn("approx::branch")){
-      if(d_conflicts.empty()){
-        entireStateIsConsistent("branchfailure");
-      }
-    }
-  }
-
-  Trace("approx::branch") << "branch constraint " << bc << endl;
-  for(size_t i = 0, N = conflicts.size(); i < N; ++i){
-    ConstraintCPVec& conf = conflicts[i];
-
-    // make sure to be working on the assertion fringe!
-    if(!contains(conf, bcneg)){
-      Trace("approx::branch") << "reraise " << conf  << endl;
-      ConstraintCP conflicting = vectorToIntHoleConflict(conf);
-      raiseConflict(conflicting, InferenceId::ARITH_CONF_BRANCH_CUT);
-    }else if(!bci.proven()){
-      drop(conf, bcneg);
-      bci.setExplanation(conf);
-      Trace("approx::branch") << "dropped " << bci  << endl;
-    }
-  }
-}
-
-void TheoryArithPrivate::replayAssert(ConstraintP c) {
-  if(!c->assertedToTheTheory()){
-    bool inConflict = c->negationHasProof();
-    if(!c->hasProof()){
-      c->setInternalAssumption(inConflict);
-      Trace("approx::replayAssert") << "replayAssert " << c << " set internal" << endl;
-    }else{
-      Trace("approx::replayAssert") << "replayAssert " << c << " has explanation" << endl;
-    }
-    Trace("approx::replayAssert") << "replayAssertion " << c << endl;
-    if(inConflict){
-      raiseConflict(c, InferenceId::ARITH_CONF_REPLAY_ASSERT);
-    }else{
-      assertionCases(c);
-    }
-  }else{
-    Trace("approx::replayAssert")
-        << "replayAssert " << c << " already asserted" << endl;
-  }
-}
-
-
-void TheoryArithPrivate::resolveOutPropagated(std::vector<ConstraintCPVec>& confs, const std::set<ConstraintCP>& propagated) const {
-  Trace("arith::resolveOutPropagated")
-    << "starting resolveOutPropagated() " << confs.size() << endl;
-  for(size_t i =0, N = confs.size(); i < N; ++i){
-    ConstraintCPVec& conf = confs[i];
-    size_t orig = conf.size();
-    Constraint::assertionFringe(conf);
-    Trace("arith::resolveOutPropagated")
-      << "  conf["<<i<<"] " << orig << " to " << conf.size() << endl;
-  }
-  Trace("arith::resolveOutPropagated")
-    << "ending resolveOutPropagated() " << confs.size() << endl;
-}
-
-struct SizeOrd {
-  bool operator()(const ConstraintCPVec& a, const ConstraintCPVec& b) const{
-    return a.size() < b.size();
-  }
-};
-
-void TheoryArithPrivate::subsumption(
-    std::vector<ConstraintCPVec> &confs) const {
-  int checks CVC5_UNUSED = 0;
-  int subsumed CVC5_UNUSED = 0;
-
-  for (size_t i = 0, N = confs.size(); i < N; ++i) {
-    ConstraintCPVec &conf = confs[i];
-    std::sort(conf.begin(), conf.end());
-  }
-
-  std::sort(confs.begin(), confs.end(), SizeOrd());
-  for (size_t i = 0; i < confs.size(); i++) {
-    // i is not subsumed
-    for (size_t j = i + 1; j < confs.size();) {
-      ConstraintCPVec& a = confs[i];
-      ConstraintCPVec& b = confs[j];
-      checks++;
-      bool subsumes = std::includes(a.begin(), a.end(), b.begin(), b.end());
-      if (subsumes) {
-        ConstraintCPVec& back = confs.back();
-        b.swap(back);
-        confs.pop_back();
-        subsumed++;
-      } else {
-        j++;
-      }
-    }
-  }
-  Trace("arith::subsumption") << "subsumed " << subsumed << "/" << checks
-                              << endl;
-}
-
-std::vector<ConstraintCPVec> TheoryArithPrivate::replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth){
-  ++(d_statistics.d_replayLogRecCount);
-  Trace("approx::replayLogRec") << "replayLogRec()" << std::endl;
-
-  size_t rpvars_size = d_replayVariables.size();
-  size_t rpcons_size = d_replayConstraints.size();
-  std::vector<ConstraintCPVec> res;
-
-  { /* create a block for the purpose of pushing the sat context */
-    context::Context::ScopedPush speculativePush(context());
-    Assert(!anyConflict());
-    Assert(conflictQueueEmpty());
-    set<ConstraintCP> propagated;
-
-    TreeLog& tl = getTreeLog();
-
-    if(bc != NullConstraint){
-      replayAssert(bc);
-    }
-
-    const NodeLog& nl = tl.getNode(nid);
-    NodeLog::const_iterator iter = nl.begin(), end = nl.end();
-    for(; conflictQueueEmpty() && iter != end; ++iter){
-      CutInfo* ci = *iter;
-      bool reject = false;
-      //cout << "  trying " << *ci << endl;
-      if(ci->getKlass() == RowsDeletedKlass){
-        RowsDeleted* rd = dynamic_cast<RowsDeleted*>(ci);
-
-        tl.applyRowsDeleted(nid, *rd);
-        // The previous line modifies nl
-
-        ++d_statistics.d_applyRowsDeleted;
-      }else if(ci->getKlass() == BranchCutKlass){
-        BranchCutInfo* bci = dynamic_cast<BranchCutInfo*>(ci);
-        Assert(bci != NULL);
-        tryBranchCut(approx, nid, *bci);
-
-        ++d_statistics.d_branchCutsAttempted;
-        if(!(conflictQueueEmpty() || ci->reconstructed())){
-          ++d_statistics.d_numBranchesFailed;
-        }
-      }else{
-        approx->tryCut(nid, *ci);
-        if(ci->getKlass() == GmiCutKlass){
-          ++d_statistics.d_gmiCutsAttempted;
-        }else if(ci->getKlass() == MirCutKlass){
-          ++d_statistics.d_mirCutsAttempted;
-        }
-
-        if(ci->reconstructed() && ci->proven()){
-          const DenseMap<Rational>& row = ci->getReconstruction().lhs;
-          reject = !complexityBelow(row, options().arith.replayRejectCutSize);
-        }
-      }
-      if(conflictQueueEmpty()){
-        if(reject){
-          ++d_statistics.d_cutsRejectedDuringReplay;
-        }else if(ci->reconstructed()){
-          // success
-          ++d_statistics.d_cutsReconstructed;
-
-          pair<ConstraintP, ArithVar> p = replayGetConstraint(*ci);
-          if(p.second != ARITHVAR_SENTINEL){
-            Assert(ci->getRowId() >= 1);
-            tl.mapRowId(nl.getNodeId(), ci->getRowId(), p.second);
-          }
-          ConstraintP con = p.first;
-          if(TraceIsOn("approx::replayLogRec")){
-            Trace("approx::replayLogRec") << "cut was remade " << con << " " << *ci << endl;
-          }
-
-          if(ci->proven()){
-            ++d_statistics.d_cutsProven;
-
-            const ConstraintCPVec& exp = ci->getExplanation();
-            // success
-            if(con->isTrue()){
-              Trace("approx::replayLogRec") << "not asserted?" << endl;
-            }else if(!con->negationHasProof()){
-              con->impliedByIntHole(exp, false);
-              replayAssert(con);
-              Trace("approx::replayLogRec") << "cut prop" << endl;
-            }else {
-              con->impliedByIntHole(exp, true);
-              Trace("approx::replayLogRec") << "cut into conflict " << con << endl;
-              raiseConflict(con, InferenceId::ARITH_CONF_REPLAY_LOG_REC);
-            }
-          }else{
-            ++d_statistics.d_cutsProofFailed;
-            Trace("approx::replayLogRec") << "failed to get proof " << *ci << endl;
-          }
-        }else if(ci->getKlass() != RowsDeletedKlass){
-          ++d_statistics.d_cutsReconstructionFailed;
-        }
-      }
-    }
-
-    /* check if the system is feasible under with the cuts */
-    if(conflictQueueEmpty()){
-      Assert(options().arith.replayEarlyCloseDepths >= 1);
-      if (!nl.isBranch() || depth % options().arith.replayEarlyCloseDepths == 0)
-      {
-        TimerStat::CodeTimer codeTimer(d_statistics.d_replaySimplexTimer);
-        //test for linear feasibility
-        d_partialModel.stopQueueingBoundCounts();
-        UpdateTrackingCallback utcb(&d_linEq);
-        d_partialModel.processBoundsQueue(utcb);
-        d_linEq.startTrackingBoundCounts();
-
-        SimplexDecisionProcedure& simplex = selectSimplex(true);
-        simplex.findModel(false);
-        // can change d_qflraStatus
-
-        d_linEq.stopTrackingBoundCounts();
-        d_partialModel.startQueueingBoundCounts();
-      }
-    }else{
-      ++d_statistics.d_replayLogRecConflictEscalation;
-    }
-
-    if(!conflictQueueEmpty()){
-      /* if a conflict has been found stop */
-      for(size_t i = 0, N = d_conflicts.size(); i < N; ++i){
-        res.push_back(ConstraintCPVec());
-        intHoleConflictToVector(d_conflicts[i].first, res.back());
-      }
-      ++d_statistics.d_replayLogRecEarlyExit;
-    }else if(nl.isBranch()){
-      /* if it is a branch try the branch */
-      pair<ConstraintP, ArithVar> p = replayGetConstraint(approx, nl);
-      Assert(p.second == ARITHVAR_SENTINEL);
-      ConstraintP dnc = p.first;
-      if(dnc != NullConstraint){
-        ConstraintP upc = dnc->getNegation();
-
-        int dnid = nl.getDownId();
-        int upid = nl.getUpId();
-
-        NodeLog& dnlog = tl.getNode(dnid);
-        NodeLog& uplog = tl.getNode(upid);
-        dnlog.copyParentRowIds();
-        uplog.copyParentRowIds();
-
-        std::vector<ConstraintCPVec> dnres;
-        std::vector<ConstraintCPVec> upres;
-        std::vector<size_t> containsdn;
-        std::vector<size_t> containsup;
-        if(res.empty()){
-          dnres = replayLogRec(approx, dnid, dnc, depth+1);
-          for(size_t i = 0, N = dnres.size(); i < N; ++i){
-            ConstraintCPVec& conf = dnres[i];
-            if(contains(conf, dnc)){
-              containsdn.push_back(i);
-            }else{
-              res.push_back(conf);
-            }
-          }
-        }else{
-          Trace("approx::replayLogRec") << "replayLogRec() skipping" << dnlog << std::endl;
-          ++d_statistics.d_replayBranchSkips;
-        }
-
-        if(res.empty()){
-          upres = replayLogRec(approx, upid, upc, depth+1);
-
-          for(size_t i = 0, N = upres.size(); i < N; ++i){
-            ConstraintCPVec& conf = upres[i];
-            if(contains(conf, upc)){
-              containsup.push_back(i);
-            }else{
-              res.push_back(conf);
-            }
-          }
-        }else{
-          Trace("approx::replayLogRec") << "replayLogRec() skipping" << uplog << std::endl;
-          ++d_statistics.d_replayBranchSkips;
-        }
-
-        if(res.empty()){
-          for(size_t i = 0, N = containsdn.size(); i < N; ++i){
-            ConstraintCPVec& dnconf = dnres[containsdn[i]];
-            for(size_t j = 0, M = containsup.size(); j < M; ++j){
-              ConstraintCPVec& upconf = upres[containsup[j]];
-
-              res.push_back(ConstraintCPVec());
-              ConstraintCPVec& back = res.back();
-              resolve(back, dnc, dnconf, upconf);
-            }
-          }
-          if(res.size() >= 2u){
-            subsumption(res);
-
-            if(res.size() > 100u){
-              res.resize(100u);
-            }
-          }
-        }else{
-          Trace("approx::replayLogRec") << "replayLogRec() skipping resolving" << nl << std::endl;
-        }
-        Trace("approx::replayLogRec") << "found #"<<res.size()<<" conflicts on branch " << nid << endl;
-        if(res.empty()){
-          ++d_statistics.d_replayBranchCloseFailures;
-        }
-
-      }else{
-        Trace("approx::replayLogRec") << "failed to make a branch " << nid << endl;
-      }
-    }else{
-      ++d_statistics.d_replayLeafCloseFailures;
-      Trace("approx::replayLogRec") << "failed on node " << nid << endl;
-      Assert(res.empty());
-    }
-    resolveOutPropagated(res, propagated);
-    Trace("approx::replayLogRec") << "replayLogRec() ending" << std::endl;
-
-    if (options().arith.replayFailureLemma)
-    {
-      // must be done inside the sat context to get things
-      // propagated at this level
-      if(res.empty() && nid == getTreeLog().getRootId()){
-        Assert(!d_replayedLemmas);
-        d_replayedLemmas = replayLemmas(approx);
-        Assert(d_acTmp.empty());
-        while(!d_approxCuts.empty()){
-          TrustNode lem = d_approxCuts.front();
-          d_approxCuts.pop();
-          d_acTmp.push_back(lem);
-        }
-      }
-    }
-  } /* pop the sat context */
-
-  /* move into the current context. */
-  while(!d_acTmp.empty()){
-    TrustNode lem = d_acTmp.back();
-    d_acTmp.pop_back();
-    d_approxCuts.push_back(lem);
-  }
-  Assert(d_acTmp.empty());
-
-  /* Garbage collect the constraints from this call */
-  while(d_replayConstraints.size() > rpcons_size){
-    ConstraintP c = d_replayConstraints.back();
-    d_replayConstraints.pop_back();
-    d_constraintDatabase.deleteConstraintAndNegation(c);
-  }
-
-  /* Garbage collect the ArithVars made by this call */
-  if(d_replayVariables.size() > rpvars_size){
-    d_partialModel.stopQueueingBoundCounts();
-    UpdateTrackingCallback utcb(&d_linEq);
-    d_partialModel.processBoundsQueue(utcb);
-    d_linEq.startTrackingBoundCounts();
-    while(d_replayVariables.size() > rpvars_size){
-      ArithVar v = d_replayVariables.back();
-      d_replayVariables.pop_back();
-      Assert(d_partialModel.canBeReleased(v));
-      if(!d_tableau.isBasic(v)){
-        /* if it is not basic make it basic. */
-        auto ci = d_tableau.colIterator(v);
-        Assert(!ci.atEnd());
-        ArithVar b = d_tableau.rowIndexToBasic((*ci).getRowIndex());
-        Assert(b != ARITHVAR_SENTINEL);
-        DeltaRational cp = d_partialModel.getAssignment(b);
-        if(d_partialModel.cmpAssignmentLowerBound(b) < 0){
-          cp = d_partialModel.getLowerBound(b);
-        }else if(d_partialModel.cmpAssignmentUpperBound(b) > 0){
-          cp = d_partialModel.getUpperBound(b);
-        }
-        d_linEq.pivotAndUpdate(b, v, cp);
-      }
-      Assert(d_tableau.isBasic(v));
-      d_linEq.stopTrackingRowIndex(d_tableau.basicToRowIndex(v));
-      d_tableau.removeBasicRow(v);
-
-      releaseArithVar(v);
-      Trace("approx::vars") << "releasing " << v << endl;
-    }
-    d_linEq.stopTrackingBoundCounts();
-    d_partialModel.startQueueingBoundCounts();
-    d_partialModel.attemptToReclaimReleased();
-  }
-  return res;
-}
-
-TreeLog& TheoryArithPrivate::getTreeLog(){
-  if(d_treeLog == NULL){
-    d_treeLog = new TreeLog();
-  }
-  return *d_treeLog;
-}
-
-ApproximateStatistics& TheoryArithPrivate::getApproxStats(){
-  if(d_approxStats == NULL){
-    d_approxStats = new ApproximateStatistics();
-  }
-  return *d_approxStats;
-}
-
-Node TheoryArithPrivate::branchToNode(ApproximateSimplex* approx,
-                                      const NodeLog& bn) const
-{
-  Assert(bn.isBranch());
-  ArithVar v = approx->getBranchVar(bn);
-  if(v != ARITHVAR_SENTINEL && d_partialModel.isIntegerInput(v)){
-    if(d_partialModel.hasNode(v)){
-      Node n = d_partialModel.asNode(v);
-      double dval = bn.branchValue();
-      std::optional<Rational> maybe_value =
-          ApproximateSimplex::estimateWithCFE(dval);
-      if (!maybe_value)
-      {
-        return Node::null();
-      }
-      Rational fl(maybe_value.value().floor());
-      NodeManager* nm = NodeManager::currentNM();
-      Node leq =
-          nm->mkNode(kind::LEQ, n, nm->mkConstRealOrInt(n.getType(), fl));
-      Node norm = rewrite(leq);
-      return norm;
-    }
-  }
-  return Node::null();
-}
-
-Node TheoryArithPrivate::cutToLiteral(ApproximateSimplex* approx, const CutInfo& ci) const{
-  Assert(ci.reconstructed());
-
-  const DenseMap<Rational>& lhs = ci.getReconstruction().lhs;
-  Node sum = toSumNode(d_partialModel, lhs);
-  if(!sum.isNull()){
-    NodeManager* nm = NodeManager::currentNM();
-    Kind k = ci.getKind();
-    Assert(k == kind::LEQ || k == kind::GEQ);
-    Node rhs = nm->mkConstRealOrInt(sum.getType(), ci.getReconstruction().rhs);
-    Node ineq = nm->mkNode(k, sum, rhs);
-    return rewrite(ineq);
-  }
-  return Node::null();
-}
-
-bool TheoryArithPrivate::replayLemmas(ApproximateSimplex* approx){
-    ++(d_statistics.d_mipReplayLemmaCalls);
-    bool anythingnew = false;
-
-    TreeLog& tl = getTreeLog();
-    NodeLog& root = tl.getRootNode();
-    root.applySelected(); /* set row ids */
-
-    vector<const CutInfo*> cuts = approx->getValidCuts(root);
-    for(size_t i =0, N =cuts.size(); i < N; ++i){
-      const CutInfo* cut = cuts[i];
-      Assert(cut->reconstructed());
-      Assert(cut->proven());
-
-      const DenseMap<Rational>& row =  cut->getReconstruction().lhs;
-      if (!complexityBelow(row, options().arith.lemmaRejectCutSize))
-      {
-        ++(d_statistics.d_cutsRejectedDuringLemmas);
-        continue;
-      }
-
-      Node cutConstraint = cutToLiteral(approx, *cut);
-      if(!cutConstraint.isNull()){
-        const ConstraintCPVec& exp = cut->getExplanation();
-        Node asLemma = Constraint::externalExplainByAssertions(exp);
-
-        Node implied = rewrite(cutConstraint);
-        anythingnew = anythingnew || !isSatLiteral(implied);
-
-        Node implication = asLemma.impNode(implied);
-        // DO NOT CALL OUTPUT LEMMA!
-        // TODO (project #37): justify
-        d_approxCuts.push_back(TrustNode::mkTrustLemma(implication, nullptr));
-        Trace("approx::lemmas") << "cut["<<i<<"] " << implication << endl;
-        ++(d_statistics.d_mipExternalCuts);
-      }
-    }
-    if(root.isBranch()){
-      Node lit = branchToNode(approx, root);
-      if(!lit.isNull()){
-        anythingnew = anythingnew || !isSatLiteral(lit);
-        Node branch = lit.orNode(lit.notNode());
-        if (proofsEnabled())
-        {
-          d_pfGen->mkTrustNode(branch, PfRule::SPLIT, {}, {lit});
-        }
-        else
-        {
-          d_approxCuts.push_back(TrustNode::mkTrustLemma(branch, nullptr));
-        }
-        ++(d_statistics.d_mipExternalBranch);
-        Trace("approx::lemmas") << "branching "<< root <<" as " << branch << endl;
-      }
-    }
-    return anythingnew;
-}
-
-void TheoryArithPrivate::turnOffApproxFor(int32_t rounds){
-  d_attemptSolveIntTurnedOff = d_attemptSolveIntTurnedOff + rounds;
-  ++(d_statistics.d_approxDisabled);
-}
-
-bool TheoryArithPrivate::safeToCallApprox() const{
-  unsigned numRows = 0;
-  unsigned numCols = 0;
-  var_iterator vi = var_begin(), vi_end = var_end();
-  // Assign each variable to a row and column variable as it appears in the input
-  for(; vi != vi_end && !(numRows > 0 && numCols > 0); ++vi){
-    ArithVar v = *vi;
-
-    if(d_partialModel.isAuxiliary(v)){
-      ++numRows;
-    }else{
-      ++numCols;
-    }
-  }
-  return (numRows > 0 && numCols > 0);
-}
-
-// solve()
-//   res = solveRealRelaxation(effortLevel);
-//   switch(res){
-//   case LinFeas:
-//   case LinInfeas:
-//     return replay()
-//   case Unknown:
-//   case Error
-//     if()
-void TheoryArithPrivate::solveInteger(Theory::Effort effortLevel){
-  if(!safeToCallApprox()) { return; }
-
-  Assert(safeToCallApprox());
-  TimerStat::CodeTimer codeTimer0(d_statistics.d_solveIntTimer);
-
-  ++(d_statistics.d_solveIntCalls);
-  d_statistics.d_inSolveInteger = 1;
-
-  if(!Theory::fullEffort(effortLevel)){
-    d_solveIntAttempts++;
-    ++(d_statistics.d_solveStandardEffort);
-  }
-
-  // if integers are attempted,
-  Assert(options().arith.useApprox);
-  Assert(ApproximateSimplex::enabled());
-
-  int level = context()->getLevel();
-  d_lastContextIntegerAttempted = level;
-
-  static constexpr int32_t mipLimit = 200000;
-
-  TreeLog& tl = getTreeLog();
-  ApproximateStatistics& stats = getApproxStats();
-  ApproximateSimplex* approx =
-    ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, tl, stats);
-
-    approx->setPivotLimit(mipLimit);
-    if(!d_guessedCoeffSet){
-      d_guessedCoeffs = approx->heuristicOptCoeffs();
-      d_guessedCoeffSet = true;
-    }
-    if(!d_guessedCoeffs.empty()){
-      approx->setOptCoeffs(d_guessedCoeffs);
-    }
-    static constexpr int32_t depthForLikelyInfeasible = 10;
-    int maxDepthPass1 = d_likelyIntegerInfeasible
-                            ? depthForLikelyInfeasible
-                            : options().arith.maxApproxDepth;
-    approx->setBranchingDepth(maxDepthPass1);
-    approx->setBranchOnVariableLimit(100);
-    LinResult relaxRes = approx->solveRelaxation();
-    if( relaxRes == LinFeasible ){
-      MipResult mipRes = MipUnknown;
-      {
-        TimerStat::CodeTimer codeTimer1(d_statistics.d_mipTimer);
-        mipRes = approx->solveMIP(false);
-      }
-
-      Trace("arith::solveInteger") << "mipRes " << mipRes << endl;
-      switch(mipRes) {
-      case MipBingo:
-        // attempt the solution
-        {
-          ++(d_statistics.d_solveIntModelsAttempts);
-
-          d_partialModel.stopQueueingBoundCounts();
-          UpdateTrackingCallback utcb(&d_linEq);
-          d_partialModel.processBoundsQueue(utcb);
-          d_linEq.startTrackingBoundCounts();
-
-          ApproximateSimplex::Solution mipSolution;
-          mipSolution = approx->extractMIP();
-          importSolution(mipSolution);
-          solveRelaxationOrPanic(effortLevel);
-
-          if (d_qflraStatus == Result::SAT)
-          {
-            if (!anyConflict())
-            {
-              if (ARITHVAR_SENTINEL == nextIntegerViolation(false))
-              {
-                ++(d_statistics.d_solveIntModelsSuccessful);
-              }
-            }
-          }
-
-          // shutdown simplex
-          d_linEq.stopTrackingBoundCounts();
-          d_partialModel.startQueueingBoundCounts();
-        }
-        break;
-      case MipClosed:
-        /* All integer branches closed */
-        approx->setPivotLimit(2*mipLimit);
-        {
-          TimerStat::CodeTimer codeTimer2(d_statistics.d_mipTimer);
-          mipRes = approx->solveMIP(true);
-        }
-
-        if(mipRes == MipClosed){
-          d_likelyIntegerInfeasible = true;
-          replayLog(approx);
-          AlwaysAssert(anyConflict() || d_qflraStatus != Result::SAT);
-
-          if (!anyConflict())
-          {
-            solveRealRelaxation(effortLevel);
-          }
-        }
-        if(!(anyConflict() || !d_approxCuts.empty())){
-          turnOffApproxFor(options().arith.replayNumericFailurePenalty);
-        }
-        break;
-      case BranchesExhausted:
-      case ExecExhausted:
-      case PivotsExhauasted:
-        if(mipRes == BranchesExhausted){
-          ++d_statistics.d_branchesExhausted;
-        }else if(mipRes == ExecExhausted){
-          ++d_statistics.d_execExhausted;
-        }else if(mipRes == PivotsExhauasted){
-          ++d_statistics.d_pivotsExhausted;
-        }
-
-        approx->setPivotLimit(2*mipLimit);
-        approx->setBranchingDepth(2);
-        {
-          TimerStat::CodeTimer codeTimer3(d_statistics.d_mipTimer);
-          mipRes = approx->solveMIP(true);
-        }
-        replayLemmas(approx);
-        break;
-      case MipUnknown:
-        break;
-      }
-    }
-  delete approx;
-
-  if(!Theory::fullEffort(effortLevel)){
-    if(anyConflict() || !d_approxCuts.empty()){
-      d_solveIntMaybeHelp++;
-    }
-  }
-
-  d_statistics.d_inSolveInteger = 0;
-}
-
-SimplexDecisionProcedure& TheoryArithPrivate::selectSimplex(bool pass1){
-  if(pass1){
-    if(d_pass1SDP == NULL){
-      if (options().arith.useFC)
-      {
-        d_pass1SDP = (SimplexDecisionProcedure*)(&d_fcSimplex);
-      }
-      else if (options().arith.useSOI)
-      {
-        d_pass1SDP = (SimplexDecisionProcedure*)(&d_soiSimplex);
-      }
-      else
-      {
-        d_pass1SDP = (SimplexDecisionProcedure*)(&d_dualSimplex);
-      }
-    }
-    Assert(d_pass1SDP != NULL);
-    return *d_pass1SDP;
-  }else{
-     if(d_otherSDP == NULL){
-       if (options().arith.useFC)
-       {
-         d_otherSDP = (SimplexDecisionProcedure*)(&d_fcSimplex);
-       }
-       else if (options().arith.useSOI)
-       {
-         d_otherSDP = (SimplexDecisionProcedure*)(&d_soiSimplex);
-       }
-       else
-       {
-         d_otherSDP = (SimplexDecisionProcedure*)(&d_soiSimplex);
-       }
-    }
-    Assert(d_otherSDP != NULL);
-    return *d_otherSDP;
-  }
-}
-
-void TheoryArithPrivate::importSolution(const ApproximateSimplex::Solution& solution){
-  if(TraceIsOn("arith::importSolution")){
-    Trace("arith::importSolution") << "importSolution before " << d_qflraStatus << endl;
-    d_partialModel.printEntireModel(Trace("arith::importSolution"));
-  }
-
-  d_qflraStatus = d_attemptSolSimplex.attempt(solution);
-
-  if(TraceIsOn("arith::importSolution")){
-    Trace("arith::importSolution") << "importSolution intermediate " << d_qflraStatus << endl;
-    d_partialModel.printEntireModel(Trace("arith::importSolution"));
-  }
-
-  if(d_qflraStatus != Result::UNSAT){
-    static constexpr int64_t pass2Limit = 20;
-    SimplexDecisionProcedure& simplex = selectSimplex(false);
-    simplex.setVarOrderPivotLimit(pass2Limit);
-    d_qflraStatus = simplex.findModel(false);
-  }
-
-  if(TraceIsOn("arith::importSolution")){
-    Trace("arith::importSolution") << "importSolution after " << d_qflraStatus << endl;
-    d_partialModel.printEntireModel(Trace("arith::importSolution"));
-  }
-}
-
-bool TheoryArithPrivate::solveRelaxationOrPanic(Theory::Effort effortLevel)
-{
-  // if at this point the linear relaxation is still unknown,
-  //  attempt to branch an integer variable as a last ditch effort on full check
-  if (d_qflraStatus == Result::UNKNOWN)
-  {
-    d_qflraStatus = selectSimplex(true).findModel(false);
-  }
-
-  if (Theory::fullEffort(effortLevel) && d_qflraStatus == Result::UNKNOWN)
-  {
-    ArithVar canBranch = nextIntegerViolation(false);
-    if (canBranch != ARITHVAR_SENTINEL)
-    {
-      ++d_statistics.d_panicBranches;
-      TrustNode branch = branchIntegerVariable(canBranch);
-      Assert(branch.getNode().getKind() == kind::OR);
-      Node rwbranch = rewrite(branch.getNode()[0]);
-      if (!isSatLiteral(rwbranch))
-      {
-        d_approxCuts.push_back(branch);
-        return true;
-      }
-    }
-    d_qflraStatus = selectSimplex(false).findModel(true);
-  }
-  return false;
-}
-
-bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){
-  TimerStat::CodeTimer codeTimer0(d_statistics.d_solveRealRelaxTimer);
-  Assert(d_qflraStatus != Result::SAT);
-
-  d_partialModel.stopQueueingBoundCounts();
-  UpdateTrackingCallback utcb(&d_linEq);
-  d_partialModel.processBoundsQueue(utcb);
-  d_linEq.startTrackingBoundCounts();
-
-  bool noPivotLimit =
-      Theory::fullEffort(effortLevel) || !options().arith.restrictedPivots;
-
-  SimplexDecisionProcedure& simplex = selectSimplex(true);
-
-  bool useApprox = options().arith.useApprox && ApproximateSimplex::enabled()
-                   && getSolveIntegerResource();
-
-  Trace("TheoryArithPrivate::solveRealRelaxation")
-      << "solveRealRelaxation() approx"
-      << " " << options().arith.useApprox << " "
-      << ApproximateSimplex::enabled() << " " << useApprox << " "
-      << safeToCallApprox() << endl;
-
-  bool noPivotLimitPass1 = noPivotLimit && !useApprox;
-  d_qflraStatus = simplex.findModel(noPivotLimitPass1);
-
-  Trace("TheoryArithPrivate::solveRealRelaxation")
-    << "solveRealRelaxation()" << " pass1 " << d_qflraStatus << endl;
-
-  if (d_qflraStatus == Result::UNKNOWN && useApprox && safeToCallApprox())
-  {
-    // pass2: fancy-final
-    static constexpr int32_t relaxationLimit = 10000;
-    Assert(ApproximateSimplex::enabled());
-
-    TreeLog& tl = getTreeLog();
-    ApproximateStatistics& stats = getApproxStats();
-    ApproximateSimplex* approxSolver =
-      ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, tl, stats);
-
-    approxSolver->setPivotLimit(relaxationLimit);
-
-    if(!d_guessedCoeffSet){
-      d_guessedCoeffs = approxSolver->heuristicOptCoeffs();
-      d_guessedCoeffSet = true;
-    }
-    if(!d_guessedCoeffs.empty()){
-      approxSolver->setOptCoeffs(d_guessedCoeffs);
-    }
-
-    ++d_statistics.d_relaxCalls;
-
-    ApproximateSimplex::Solution relaxSolution;
-    LinResult relaxRes = LinUnknown;
-    {
-      TimerStat::CodeTimer codeTimer1(d_statistics.d_lpTimer);
-      relaxRes = approxSolver->solveRelaxation();
-    }
-      Trace("solveRealRelaxation") << "solve relaxation? " << endl;
-      switch(relaxRes){
-      case LinFeasible:
-        Trace("solveRealRelaxation") << "lin feasible? " << endl;
-        ++d_statistics.d_relaxLinFeas;
-        relaxSolution = approxSolver->extractRelaxation();
-        importSolution(relaxSolution);
-        if(d_qflraStatus != Result::SAT){
-          ++d_statistics.d_relaxLinFeasFailures;
-        }
-        break;
-      case LinInfeasible:
-        // todo attempt to recreate approximate conflict
-        ++d_statistics.d_relaxLinInfeas;
-        Trace("solveRealRelaxation") << "lin infeasible " << endl;
-        relaxSolution = approxSolver->extractRelaxation();
-        importSolution(relaxSolution);
-        if(d_qflraStatus != Result::UNSAT){
-          ++d_statistics.d_relaxLinInfeasFailures;
-        }
-        break;
-      case LinExhausted:
-        ++d_statistics.d_relaxLinExhausted;
-        Trace("solveRealRelaxation") << "exhuasted " << endl;
-        break;
-      case LinUnknown:
-      default:
-        ++d_statistics.d_relaxOthers;
-        break;
-      }
-    delete approxSolver;
-
-  }
-
-  bool emmittedConflictOrSplit = solveRelaxationOrPanic(effortLevel);
-
-  // TODO Save zeroes with no conflicts
-  d_linEq.stopTrackingBoundCounts();
-  d_partialModel.startQueueingBoundCounts();
-
-  return emmittedConflictOrSplit;
-}
-
-bool TheoryArithPrivate::hasFreshArithLiteral(Node n) const{
-  switch(n.getKind()){
-  case kind::LEQ:
-  case kind::GEQ:
-  case kind::GT:
-  case kind::LT:
-    return !isSatLiteral(n);
-  case kind::EQUAL:
-    if (n[0].getType().isRealOrInt())
-    {
-      return !isSatLiteral(n);
-    }
-    else if (n[0].getType().isBoolean())
-    {
-      return hasFreshArithLiteral(n[0]) ||
-        hasFreshArithLiteral(n[1]);
-    }
-    else
-    {
-      return false;
-    }
-  case kind::IMPLIES:
-    // try the rhs first
-    return hasFreshArithLiteral(n[1]) ||
-      hasFreshArithLiteral(n[0]);
-  default:
-    if(n.getType().isBoolean()){
-      for(Node::iterator ni=n.begin(), nend=n.end(); ni!=nend; ++ni){
-        Node child = *ni;
-        if(hasFreshArithLiteral(child)){
-          return true;
-        }
-      }
-    }
-    return false;
-  }
-}
-
-bool TheoryArithPrivate::preCheck(Theory::Effort level)
-{
-  Assert(d_currentPropagationList.empty());
-  if(TraceIsOn("arith::consistency")){
-    Assert(unenqueuedVariablesAreConsistent());
-  }
-
-  d_newFacts = !done();
-  // If d_previousStatus == SAT, then reverts on conflicts are safe
-  // Otherwise, they are not and must be committed.
-  d_previousStatus = d_qflraStatus;
-  if (d_newFacts)
-  {
-    d_qflraStatus = Result::UNKNOWN;
-    d_hasDoneWorkSinceCut = true;
-  }
-  return false;
-}
-
-void TheoryArithPrivate::preNotifyFact(TNode atom, bool pol, TNode fact)
-{
-  ConstraintP curr = constraintFromFactQueue(fact);
-  if (curr != NullConstraint)
-  {
-    bool res CVC5_UNUSED = assertionCases(curr);
-    Assert(!res || anyConflict());
-  }
-}
-
-bool TheoryArithPrivate::postCheck(Theory::Effort effortLevel)
-{
-  if(!anyConflict()){
-    while(!d_learnedBounds.empty()){
-      // we may attempt some constraints twice.  this is okay!
-      ConstraintP curr = d_learnedBounds.front();
-      d_learnedBounds.pop();
-      Trace("arith::learned") << curr << endl;
-
-      bool res CVC5_UNUSED = assertionCases(curr);
-      Assert(!res || anyConflict());
-
-      if(anyConflict()){ break; }
-    }
-  }
-
-  if(anyConflict()){
-    d_qflraStatus = Result::UNSAT;
-    if (options().arith.revertArithModels && d_previousStatus == Result::SAT)
-    {
-      ++d_statistics.d_revertsOnConflicts;
-      Trace("arith::bt") << "clearing here "
-                         << " " << d_newFacts << " " << d_previousStatus << " "
-                         << d_qflraStatus << endl;
-      revertOutOfConflict();
-      d_errorSet.clear();
-    }else{
-      ++d_statistics.d_commitsOnConflicts;
-      Trace("arith::bt") << "committing here "
-                         << " " << d_newFacts << " " << d_previousStatus << " "
-                         << d_qflraStatus << endl;
-      d_partialModel.commitAssignmentChanges();
-      revertOutOfConflict();
-    }
-    outputConflicts();
-    //cout << "unate conflict 1 " << effortLevel << std::endl;
-    return true;
-  }
-
-
-  if(TraceIsOn("arith::print_assertions")) {
-    debugPrintAssertions(Trace("arith::print_assertions"));
-  }
-
-  bool emmittedConflictOrSplit = false;
-  Assert(d_conflicts.empty());
-
-  bool useSimplex = d_qflraStatus != Result::SAT;
-  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
-                      << "pre realRelax" << endl;
-
-  if(useSimplex){
-    emmittedConflictOrSplit = solveRealRelaxation(effortLevel);
-  }
-  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
-                      << "post realRelax" << endl;
-
-
-  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
-                      << "pre solveInteger" << endl;
-
-  if(attemptSolveInteger(effortLevel, emmittedConflictOrSplit)){
-    solveInteger(effortLevel);
-    if(anyConflict()){
-      ++d_statistics.d_commitsOnConflicts;
-      Trace("arith::bt") << "committing here "
-                         << " " << d_newFacts << " " << d_previousStatus << " "
-                         << d_qflraStatus << endl;
-      revertOutOfConflict();
-      d_errorSet.clear();
-      outputConflicts();
-      return true;
-    }
-  }
-
-  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
-                      << "post solveInteger" << endl;
-
-  switch(d_qflraStatus){
-  case Result::SAT:
-    if (d_newFacts)
-    {
-      ++d_statistics.d_nontrivialSatChecks;
-    }
-
-    Trace("arith::bt") << "committing sap inConflit"
-                       << " " << d_newFacts << " " << d_previousStatus << " "
-                       << d_qflraStatus << endl;
-    d_partialModel.commitAssignmentChanges();
-    d_unknownsInARow = 0;
-    if(TraceIsOn("arith::consistency")){
-      Assert(entireStateIsConsistent("sat comit"));
-    }
-    if (useSimplex && options().arith.collectPivots)
-    {
-      if (options().arith.useFC)
-      {
-        d_statistics.d_satPivots << d_fcSimplex.getPivots();
-      }
-      else
-      {
-        d_statistics.d_satPivots << d_dualSimplex.getPivots();
-      }
-    }
-    break;
-  case Result::UNKNOWN:
-    ++d_unknownsInARow;
-    ++(d_statistics.d_unknownChecks);
-    Assert(!Theory::fullEffort(effortLevel));
-    Trace("arith::bt") << "committing unknown"
-                       << " " << d_newFacts << " " << d_previousStatus << " "
-                       << d_qflraStatus << endl;
-    d_partialModel.commitAssignmentChanges();
-    d_statistics.d_maxUnknownsInARow.maxAssign(d_unknownsInARow);
-
-    if (useSimplex && options().arith.collectPivots)
-    {
-      if (options().arith.useFC)
-      {
-        d_statistics.d_unknownPivots << d_fcSimplex.getPivots();
-      }
-      else
-      {
-        d_statistics.d_unknownPivots << d_dualSimplex.getPivots();
-      }
-    }
-    break;
-  case Result::UNSAT:
-    d_unknownsInARow = 0;
-
-    ++d_statistics.d_commitsOnConflicts;
-
-    Trace("arith::bt") << "committing on conflict"
-                       << " " << d_newFacts << " " << d_previousStatus << " "
-                       << d_qflraStatus << endl;
-    d_partialModel.commitAssignmentChanges();
-    revertOutOfConflict();
-
-    if(TraceIsOn("arith::consistency::comitonconflict")){
-      entireStateIsConsistent("commit on conflict");
-    }
-    outputConflicts();
-    emmittedConflictOrSplit = true;
-    Trace("arith::conflict") << "simplex conflict" << endl;
-
-    if (useSimplex && options().arith.collectPivots)
-    {
-      if (options().arith.useFC)
-      {
-        d_statistics.d_unsatPivots << d_fcSimplex.getPivots();
-      }
-      else
-      {
-        d_statistics.d_unsatPivots << d_dualSimplex.getPivots();
-      }
-    }
-    break;
-  default:
-    Unimplemented();
-  }
-  d_statistics.d_avgUnknownsInARow << d_unknownsInARow;
-
-  size_t nPivots = options().arith.useFC ? d_fcSimplex.getPivots()
-                                         : d_dualSimplex.getPivots();
-  for (std::size_t i = 0; i < nPivots; ++i)
-  {
-    d_containing.d_out->spendResource(
-        Resource::ArithPivotStep);
-  }
-
-  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
-                      << "pre approx cuts" << endl;
-  if(!d_approxCuts.empty()){
-    bool anyFresh = false;
-    while(!d_approxCuts.empty()){
-      TrustNode lem = d_approxCuts.front();
-      d_approxCuts.pop();
-      Trace("arith::approx::cuts") << "approximate cut:" << lem << endl;
-      anyFresh = anyFresh || hasFreshArithLiteral(lem.getNode());
-      Trace("arith::lemma") << "approximate cut:" << lem << endl;
-      outputTrustedLemma(lem, InferenceId::ARITH_APPROX_CUT);
-    }
-    if(anyFresh){
-      emmittedConflictOrSplit = true;
-    }
-  }
-
-  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
-                      << "post approx cuts" << endl;
-
-  // This should be fine if sat or unknown
-  if (!emmittedConflictOrSplit
-      && (options().arith.arithPropagationMode
-              == options::ArithPropagationMode::UNATE_PROP
-          || options().arith.arithPropagationMode
-                 == options::ArithPropagationMode::BOTH_PROP))
-  {
-    TimerStat::CodeTimer codeTimer0(d_statistics.d_newPropTime);
-    Assert(d_qflraStatus != Result::UNSAT);
-
-    while(!d_currentPropagationList.empty()  && !anyConflict()){
-      ConstraintP curr = d_currentPropagationList.front();
-      d_currentPropagationList.pop_front();
-
-      ConstraintType t = curr->getType();
-      Assert(t != Disequality)
-          << "Disequalities are not allowed in d_currentPropagation";
-
-      switch(t){
-      case LowerBound:
-        {
-          ConstraintP prev = d_currentPropagationList.front();
-          d_currentPropagationList.pop_front();
-          d_constraintDatabase.unatePropLowerBound(curr, prev);
-          break;
-        }
-      case UpperBound:
-        {
-          ConstraintP prev = d_currentPropagationList.front();
-          d_currentPropagationList.pop_front();
-          d_constraintDatabase.unatePropUpperBound(curr, prev);
-          break;
-        }
-      case Equality:
-        {
-          ConstraintP prevLB = d_currentPropagationList.front();
-          d_currentPropagationList.pop_front();
-          ConstraintP prevUB = d_currentPropagationList.front();
-          d_currentPropagationList.pop_front();
-          d_constraintDatabase.unatePropEquality(curr, prevLB, prevUB);
-          break;
-        }
-        default: Unhandled() << curr->getType();
-      }
-    }
-
-    if(anyConflict()){
-      Trace("arith::unate") << "unate conflict" << endl;
-      revertOutOfConflict();
-      d_qflraStatus = Result::UNSAT;
-      outputConflicts();
-      emmittedConflictOrSplit = true;
-      //cout << "unate conflict " << endl;
-      Trace("arith::bt") << "committing on unate conflict"
-                         << " " << d_newFacts << " " << d_previousStatus << " "
-                         << d_qflraStatus << endl;
-
-      Trace("arith::conflict") << "unate arith conflict" << endl;
-    }
-  }
-  else
-  {
-    TimerStat::CodeTimer codeTimer1(d_statistics.d_newPropTime);
-    d_currentPropagationList.clear();
-  }
-  Assert(d_currentPropagationList.empty());
-
-  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
-                      << "post unate" << endl;
-
-  if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
-    ++d_fullCheckCounter;
-  }
-  if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
-    emmittedConflictOrSplit = splitDisequalities();
-  }
-  Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
-                      << "pos splitting" << endl;
-
-
-  Trace("arith") << "integer? "
-       << " conf/split " << emmittedConflictOrSplit
-       << " fulleffort " << Theory::fullEffort(effortLevel) << endl;
-
-  if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel) && !hasIntegerModel()){
-    Node possibleConflict = Node::null();
-    if (!emmittedConflictOrSplit && options().arith.arithDioSolver)
-    {
-      possibleConflict = callDioSolver();
-      if(possibleConflict != Node::null()){
-        revertOutOfConflict();
-        Trace("arith::conflict") << "dio conflict   " << possibleConflict << endl;
-        // TODO (project #37): justify (proofs in the DIO solver)
-        raiseBlackBoxConflict(possibleConflict);
-        outputConflicts();
-        emmittedConflictOrSplit = true;
-      }
-    }
-
-    if (!emmittedConflictOrSplit && d_hasDoneWorkSinceCut
-        && options().arith.arithDioSolver)
-    {
-      if(getDioCuttingResource()){
-        TrustNode possibleLemma = dioCutting();
-        if(!possibleLemma.isNull()){
-          d_hasDoneWorkSinceCut = false;
-          d_cutCount = d_cutCount + 1;
-          Trace("arith::lemma") << "dio cut   " << possibleLemma << endl;
-          if (outputTrustedLemma(possibleLemma, InferenceId::ARITH_DIO_CUT))
-          {
-            emmittedConflictOrSplit = true;
-          }
-        }
-      }
-    }
-
-    if(!emmittedConflictOrSplit) {
-      TrustNode possibleLemma = roundRobinBranch();
-      if (!possibleLemma.getNode().isNull())
-      {
-        ++(d_statistics.d_externalBranchAndBounds);
-        d_cutCount = d_cutCount + 1;
-        Trace("arith::lemma") << "rrbranch lemma"
-                              << possibleLemma << endl;
-        if (outputTrustedLemma(possibleLemma, InferenceId::ARITH_BB_LEMMA))
-        {
-          emmittedConflictOrSplit = true;
-        }
-      }
-    }
-
-    if (options().arith.maxCutsInContext <= d_cutCount)
-    {
-      if(d_diosolver.hasMoreDecompositionLemmas()){
-        while(d_diosolver.hasMoreDecompositionLemmas()){
-          Node decompositionLemma = d_diosolver.nextDecompositionLemma();
-          Trace("arith::lemma") << "dio decomposition lemma "
-                                << decompositionLemma << endl;
-          outputLemma(decompositionLemma, InferenceId::ARITH_DIO_DECOMPOSITION);
-        }
-      }else{
-        Trace("arith::restart") << "arith restart!" << endl;
-        outputRestart();
-      }
-    }
-  }//if !emmittedConflictOrSplit && fullEffort(effortLevel) && !hasIntegerModel()
-
-  if(Theory::fullEffort(effortLevel)){
-    if(TraceIsOn("arith::consistency::final")){
-      entireStateIsConsistent("arith::consistency::final");
-    }
-  }
-
-  if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
-  if(TraceIsOn("arith::print_model")) {
-    debugPrintModel(Trace("arith::print_model"));
-  }
-  Trace("arith") << "TheoryArithPrivate::check end" << std::endl;
-  return emmittedConflictOrSplit;
-}
-
-bool TheoryArithPrivate::foundNonlinear() const { return d_foundNl; }
-
-TrustNode TheoryArithPrivate::branchIntegerVariable(ArithVar x) const
-{
-  const DeltaRational& d = d_partialModel.getAssignment(x);
-  Assert(!d.isIntegral());
-  const Rational& r = d.getNoninfinitesimalPart();
-  const Rational& i = d.getInfinitesimalPart();
-  Trace("integers") << "integers: assignment to [[" << d_partialModel.asNode(x) << "]] is " << r << "[" << i << "]" << endl;
-  Assert(!(r.getDenominator() == 1 && i.getNumerator() == 0));
-  TNode var = d_partialModel.asNode(x);
-  TrustNode lem = d_bab.branchIntegerVariable(var, r);
-  if (TraceIsOn("integers"))
-  {
-    Node l = lem.getNode();
-    if (isSatLiteral(l[0]))
-    {
-      Trace("integers") << "    " << l[0] << " == " << getSatValue(l[0])
-                        << endl;
-    }
-    else
-    {
-      Trace("integers") << "    " << l[0] << " is not assigned a SAT literal"
-                        << endl;
-    }
-    if (isSatLiteral(l[1]))
-    {
-      Trace("integers") << "    " << l[1] << " == " << getSatValue(l[1])
-                        << endl;
-    }
-    else
-    {
-      Trace("integers") << "    " << l[1] << " is not assigned a SAT literal"
-                        << endl;
-    }
-  }
-  return lem;
-}
-
-std::vector<ArithVar> TheoryArithPrivate::cutAllBounded() const{
-  vector<ArithVar> lemmas;
-  ArithVar max = d_partialModel.getNumberOfVariables();
-
-  if (options().arith.doCutAllBounded && max > 0)
-  {
-    for(ArithVar iter = 0; iter != max; ++iter){
-    //Do not include slack variables
-      const DeltaRational& d = d_partialModel.getAssignment(iter);
-      if(isIntegerInput(iter) &&
-         !d_cutInContext.contains(iter) &&
-         d_partialModel.hasUpperBound(iter) &&
-         d_partialModel.hasLowerBound(iter) &&
-         !d.isIntegral()){
-        lemmas.push_back(iter);
-      }
-    }
-  }
-  return lemmas;
-}
-
-/** Returns true if the roundRobinBranching() issues a lemma. */
-TrustNode TheoryArithPrivate::roundRobinBranch()
-{
-  if(hasIntegerModel()){
-    return TrustNode::null();
-  }else{
-    ArithVar v = d_nextIntegerCheckVar;
-
-    Assert(isInteger(v));
-    Assert(!isAuxiliaryVariable(v));
-    return branchIntegerVariable(v);
-  }
-}
-
-bool TheoryArithPrivate::splitDisequalities(){
-  bool splitSomething = false;
-
-  vector<ConstraintP> save;
-
-  while(!d_diseqQueue.empty()){
-    ConstraintP front = d_diseqQueue.front();
-    d_diseqQueue.pop();
-
-    if(front->isSplit()){
-      Trace("arith::eq") << "split already" << endl;
-    }else{
-      Trace("arith::eq") << "not split already" << endl;
-
-      ArithVar lhsVar = front->getVariable();
-
-      const DeltaRational& lhsValue = d_partialModel.getAssignment(lhsVar);
-      const DeltaRational& rhsValue = front->getValue();
-      if(lhsValue == rhsValue){
-        Trace("arith::lemma") << "Splitting on " << front << endl;
-        Trace("arith::lemma") << "LHS value = " << lhsValue << endl;
-        Trace("arith::lemma") << "RHS value = " << rhsValue << endl;
-        TrustNode lemma = front->split();
-        ++(d_statistics.d_statDisequalitySplits);
-
-        Trace("arith::lemma") << "Now " << rewrite(lemma.getNode()) << endl;
-        outputTrustedLemma(lemma, InferenceId::ARITH_SPLIT_DEQ);
-        // cout << "Now " << rewrite(lemma) << endl;
-        splitSomething = true;
-      }else if(d_partialModel.strictlyLessThanLowerBound(lhsVar, rhsValue)){
-        Trace("arith::eq") << "can drop as less than lb" << front << endl;
-      }else if(d_partialModel.strictlyGreaterThanUpperBound(lhsVar, rhsValue)){
-        Trace("arith::eq") << "can drop as greater than ub" << front << endl;
-      }else{
-        Trace("arith::eq") << "save" << front << ": " <<lhsValue << " != " << rhsValue << endl;
-        save.push_back(front);
-      }
-    }
-  }
-  vector<ConstraintP>::const_iterator i=save.begin(), i_end = save.end();
-  for(; i != i_end; ++i){
-    d_diseqQueue.push(*i);
-  }
-  return splitSomething;
-}
-
-/**
- * Should be guarded by at least TraceIsOn("arith::print_assertions").
- * Prints to Trace("arith::print_assertions")
- */
-void TheoryArithPrivate::debugPrintAssertions(std::ostream& out) const {
-  out << "Assertions:" << endl;
-  for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
-    ArithVar i = *vi;
-    if (d_partialModel.hasLowerBound(i)) {
-      ConstraintP lConstr = d_partialModel.getLowerBoundConstraint(i);
-      out << lConstr << endl;
-    }
-
-    if (d_partialModel.hasUpperBound(i)) {
-      ConstraintP uConstr = d_partialModel.getUpperBoundConstraint(i);
-      out << uConstr << endl;
-    }
-  }
-  context::CDQueue<ConstraintP>::const_iterator it = d_diseqQueue.begin();
-  context::CDQueue<ConstraintP>::const_iterator it_end = d_diseqQueue.end();
-  for(; it != it_end; ++ it) {
-    out << *it << endl;
-  }
-}
-
-void TheoryArithPrivate::debugPrintModel(std::ostream& out) const{
-  out << "Model:" << endl;
-  for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
-    ArithVar i = *vi;
-    if(d_partialModel.hasNode(i)){
-      out << d_partialModel.asNode(i) << " : " <<
-        d_partialModel.getAssignment(i);
-      if(d_tableau.isBasic(i)){
-        out << " (basic)";
-      }
-      out << endl;
-    }
-  }
-}
-
-TrustNode TheoryArithPrivate::explain(TNode n)
-{
-  Trace("arith::explain") << "explain @" << context()->getLevel() << ": " << n
-                          << endl;
-
-  ConstraintP c = d_constraintDatabase.lookup(n);
-  TrustNode exp;
-  if(c != NullConstraint){
-    Assert(!c->isAssumption());
-    exp = c->externalExplainForPropagation(n);
-    Trace("arith::explain") << "constraint explanation" << n << ":" << exp << endl;
-  }else if(d_assertionsThatDoNotMatchTheirLiterals.find(n) != d_assertionsThatDoNotMatchTheirLiterals.end()){
-    c = d_assertionsThatDoNotMatchTheirLiterals[n];
-    if(!c->isAssumption()){
-      exp = c->externalExplainForPropagation(n);
-      Trace("arith::explain") << "assertions explanation" << n << ":" << exp << endl;
-    }else{
-      Trace("arith::explain") << "this is a strange mismatch" << n << endl;
-      Assert(d_congruenceManager.canExplain(n));
-      exp = d_congruenceManager.explain(n);
-    }
-  }else{
-    Assert(d_congruenceManager.canExplain(n));
-    Trace("arith::explain") << "dm explanation" << n << endl;
-    exp = d_congruenceManager.explain(n);
-  }
-  return exp;
-}
-
-void TheoryArithPrivate::propagate(Theory::Effort e) {
-  // This uses model values for safety. Disable for now.
-  if (d_qflraStatus == Result::SAT
-      && (options().arith.arithPropagationMode
-              == options::ArithPropagationMode::BOUND_INFERENCE_PROP
-          || options().arith.arithPropagationMode
-                 == options::ArithPropagationMode::BOTH_PROP)
-      && hasAnyUpdates())
-  {
-    if (options().arith.newProp)
-    {
-      propagateCandidatesNew();
-    }
-    else
-    {
-      propagateCandidates();
-    }
-  }
-  else
-  {
-    clearUpdates();
-  }
-
-  while(d_constraintDatabase.hasMorePropagations()){
-    ConstraintCP c = d_constraintDatabase.nextPropagation();
-    Trace("arith::prop") << "next prop" << context()->getLevel() << ": " << c
-                         << endl;
-
-    if(c->negationHasProof()){
-      Trace("arith::prop") << "negation has proof " << c->getNegation() << endl;
-      Trace("arith::prop") << c->getNegation()->externalExplainByAssertions()
-                           << endl;
-    }
-    Assert(!c->negationHasProof())
-        << "A constraint has been propagated on the constraint propagation "
-           "queue, but the negation has been set to true.  Contact Tim now!";
-
-    if(!c->assertedToTheTheory()){
-      Node literal = c->getLiteral();
-      Trace("arith::prop") << "propagating @" << context()->getLevel() << " "
-                           << literal << endl;
-
-      outputPropagate(literal);
-    }else{
-      Trace("arith::prop") << "already asserted to the theory " <<  c->getLiteral() << endl;
-    }
-  }
-
-  NodeManager* nm = NodeManager::currentNM();
-  while(d_congruenceManager.hasMorePropagations()){
-    TNode toProp = d_congruenceManager.getNextPropagation();
-
-    //Currently if the flag is set this came from an equality detected by the
-    //equality engine in the the difference manager.
-    Node normalized = rewrite(toProp);
-
-    ConstraintP constraint = d_constraintDatabase.lookup(normalized);
-    if(constraint == NullConstraint){
-      Trace("arith::prop") << "propagating on non-constraint? "  << toProp << endl;
-
-      outputPropagate(toProp);
-    }else if(constraint->negationHasProof()){
-      // The congruence manager can prove: antecedents => toProp,
-      // ergo. antecedents ^ ~toProp is a conflict.
-      TrustNode exp = d_congruenceManager.explain(toProp);
-      Node notNormalized = normalized.negate();
-      std::vector<Node> ants(exp.getNode().begin(), exp.getNode().end());
-      ants.push_back(notNormalized);
-      Node lp = nm->mkAnd(ants);
-      Trace("arith::prop") << "propagate conflict" <<  lp << endl;
-      if (proofsEnabled())
-      {
-        // Assume all of antecedents and ~toProp (rewritten)
-        std::vector<Pf> pfAntList;
-        for (size_t i = 0; i < ants.size(); ++i)
-        {
-          pfAntList.push_back(d_pnm->mkAssume(ants[i]));
-        }
-        Pf pfAnt = pfAntList.size() > 1
-                       ? d_pnm->mkNode(PfRule::AND_INTRO, pfAntList, {})
-                       : pfAntList[0];
-        // Use modus ponens to get toProp (un rewritten)
-        Pf pfConc = d_pnm->mkNode(
-            PfRule::MODUS_PONENS,
-            {pfAnt, exp.getGenerator()->getProofFor(exp.getProven())},
-            {});
-        // prove toProp (rewritten)
-        Pf pfConcRewritten = d_pnm->mkNode(
-            PfRule::MACRO_SR_PRED_TRANSFORM, {pfConc}, {normalized});
-        Pf pfNotNormalized = d_pnm->mkAssume(notNormalized);
-        // prove bottom from toProp and ~toProp
-        Pf pfBot;
-        if (normalized.getKind() == kind::NOT)
-        {
-          pfBot = d_pnm->mkNode(
-              PfRule::CONTRA, {pfNotNormalized, pfConcRewritten}, {});
-        }
-        else
-        {
-          pfBot = d_pnm->mkNode(
-              PfRule::CONTRA, {pfConcRewritten, pfNotNormalized}, {});
-        }
-        // close scope
-        Pf pfNotAnd = d_pnm->mkScope(pfBot, ants);
-        raiseBlackBoxConflict(lp, pfNotAnd);
-      }
-      else
-      {
-        raiseBlackBoxConflict(lp);
-      }
-      outputConflicts();
-      return;
-    }else{
-      Trace("arith::prop") << "propagating still?" <<  toProp << endl;
-      outputPropagate(toProp);
-    }
-  }
-}
-
-DeltaRational TheoryArithPrivate::getDeltaValue(TNode term) const
-{
-  AlwaysAssert(d_qflraStatus != Result::UNKNOWN);
-  Trace("arith::value") << term << std::endl;
-
-  if (d_partialModel.hasArithVar(term)) {
-    ArithVar var = d_partialModel.asArithVar(term);
-    return d_partialModel.getAssignment(var);
-  }
-
-  switch (Kind kind = term.getKind()) {
-    case kind::CONST_RATIONAL:
-    case kind::CONST_INTEGER: return term.getConst<Rational>();
-
-    case kind::ADD:
-    {  // 2+ args
-      DeltaRational value(0);
-      for (TNode::iterator i = term.begin(), iend = term.end(); i != iend;
-           ++i) {
-        value = value + getDeltaValue(*i);
-      }
-      return value;
-    }
-
-    case kind::NONLINEAR_MULT:
-    case kind::MULT: {  // 2+ args
-      Assert(!isSetup(term));
-      DeltaRational value(1);
-      for (TNode::iterator i = term.begin(), iend = term.end(); i != iend;
-           ++i) {
-        value = value * getDeltaValue(*i);
-      }
-      return value;
-    }
-    case kind::SUB:
-    {  // 2 args
-      return getDeltaValue(term[0]) - getDeltaValue(term[1]);
-    }
-    case kind::NEG:
-    {  // 1 arg
-      return (-getDeltaValue(term[0]));
-    }
-
-    case kind::DIVISION: {  // 2 args
-      Assert(!isSetup(term));
-      return getDeltaValue(term[0]) / getDeltaValue(term[1]);
-    }
-    case kind::DIVISION_TOTAL:
-    case kind::INTS_DIVISION_TOTAL:
-    case kind::INTS_MODULUS_TOTAL: {  // 2 args
-      Assert(!isSetup(term));
-      DeltaRational denominator = getDeltaValue(term[1]);
-      if (denominator.isZero()) {
-        return DeltaRational(0, 0);
-      }
-      DeltaRational numerator = getDeltaValue(term[0]);
-      if (kind == kind::DIVISION_TOTAL) {
-        return numerator / denominator;
-      } else if (kind == kind::INTS_DIVISION_TOTAL) {
-        return Rational(numerator.euclidianDivideQuotient(denominator));
-      } else {
-        Assert(kind == kind::INTS_MODULUS_TOTAL);
-        return Rational(numerator.euclidianDivideRemainder(denominator));
-      }
-    }
-
-    default:
-      throw ModelException(term, "No model assignment.");
-  }
-}
-
-Rational TheoryArithPrivate::deltaValueForTotalOrder() const{
-  Rational min(2);
-  std::set<DeltaRational> relevantDeltaValues;
-  context::CDQueue<ConstraintP>::const_iterator qiter = d_diseqQueue.begin();
-  context::CDQueue<ConstraintP>::const_iterator qiter_end = d_diseqQueue.end();
-
-  for(; qiter != qiter_end; ++qiter){
-    ConstraintP curr = *qiter;
-
-    const DeltaRational& rhsValue = curr->getValue();
-    relevantDeltaValues.insert(rhsValue);
-  }
-
-  Theory::shared_terms_iterator shared_iter = d_containing.shared_terms_begin();
-  Theory::shared_terms_iterator shared_end = d_containing.shared_terms_end();
-  for(; shared_iter != shared_end; ++shared_iter){
-    Node sharedCurr = *shared_iter;
-
-    // ModelException is fatal as this point. Don't catch!
-    // DeltaRationalException is fatal as this point. Don't catch!
-    DeltaRational val = getDeltaValue(sharedCurr);
-    relevantDeltaValues.insert(val);
-  }
-
-  for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
-    ArithVar v = *vi;
-    const DeltaRational& value = d_partialModel.getAssignment(v);
-    relevantDeltaValues.insert(value);
-    if( d_partialModel.hasLowerBound(v)){
-      const DeltaRational& lb = d_partialModel.getLowerBound(v);
-      relevantDeltaValues.insert(lb);
-    }
-    if( d_partialModel.hasUpperBound(v)){
-      const DeltaRational& ub = d_partialModel.getUpperBound(v);
-      relevantDeltaValues.insert(ub);
-    }
-  }
-
-  if(relevantDeltaValues.size() >= 2){
-    std::set<DeltaRational>::const_iterator iter = relevantDeltaValues.begin();
-    std::set<DeltaRational>::const_iterator iter_end = relevantDeltaValues.end();
-    DeltaRational prev = *iter;
-    ++iter;
-    for(; iter != iter_end; ++iter){
-      const DeltaRational& curr = *iter;
-
-      Assert(prev < curr);
-
-      DeltaRational::seperatingDelta(min, prev, curr);
-      prev = curr;
-    }
-  }
-
-  Assert(min.sgn() > 0);
-  Rational belowMin = min/Rational(2);
-  return belowMin;
-}
-
-void TheoryArithPrivate::collectModelValues(const std::set<Node>& termSet,
-                                            std::map<Node, Node>& arithModel)
-{
-  AlwaysAssert(d_qflraStatus == Result::SAT);
-
-  if(TraceIsOn("arith::collectModelInfo")){
-    debugPrintFacts();
-  }
-
-  Trace("arith::collectModelInfo") << "collectModelInfo() begin " << endl;
-
-  // Delta lasts at least the duration of the function call
-  const Rational& delta = d_partialModel.getDelta();
-  std::unordered_set<TNode> shared = d_containing.currentlySharedTerms();
-
-  // TODO:
-  // This is not very good for user push/pop....
-  // Revisit when implementing push/pop
-  NodeManager* nm = NodeManager::currentNM();
-  for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
-    ArithVar v = *vi;
-
-    if(!isAuxiliaryVariable(v)){
-      Node term = d_partialModel.asNode(v);
-
-      if((theoryOf(term) == THEORY_ARITH || shared.find(term) != shared.end())
-         && termSet.find(term) != termSet.end()){
-
-        const DeltaRational& mod = d_partialModel.getAssignment(v);
-        Rational qmodel = mod.substituteDelta(delta);
-
-        Node qNode = nm->mkConstRealOrInt(term.getType(), qmodel);
-        Trace("arith::collectModelInfo") << "m->assertEquality(" << term << ", " << qmodel << ", true)" << endl;
-        // Add to the map
-        arithModel[term] = qNode;
-      }else{
-        Trace("arith::collectModelInfo") << "Skipping m->assertEquality(" << term << ", true)" << endl;
-
-      }
-    }
-  }
-
-  // Iterate over equivalence classes in LinearEqualityModule
-  // const eq::EqualityEngine& ee = d_congruenceManager.getEqualityEngine();
-  // m->assertEqualityEngine(&ee);
-
-  Trace("arith::collectModelInfo") << "collectModelInfo() end " << endl;
-}
-
-bool TheoryArithPrivate::safeToReset() const {
-  Assert(!d_tableauSizeHasBeenModified);
-  Assert(d_errorSet.noSignals());
-
-  ErrorSet::error_iterator error_iter = d_errorSet.errorBegin();
-  ErrorSet::error_iterator error_end = d_errorSet.errorEnd();
-  for(; error_iter != error_end; ++error_iter){
-    ArithVar basic = *error_iter;
-    if(!d_smallTableauCopy.isBasic(basic)){
-      return false;
-    }
-  }
-
-  return true;
-}
-
-void TheoryArithPrivate::notifyRestart(){
-  TimerStat::CodeTimer codeTimer(d_statistics.d_restartTimer);
-
-  if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
-
-  ++d_restartsCounter;
-  d_solveIntMaybeHelp = 0;
-  d_solveIntAttempts = 0;
-}
-
-bool TheoryArithPrivate::entireStateIsConsistent(const string& s){
-  bool result = true;
-  for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
-    ArithVar var = *vi;
-    //ArithVar var = d_partialModel.asArithVar(*i);
-    if(!d_partialModel.assignmentIsConsistent(var)){
-      d_partialModel.printModel(var);
-      warning() << s << ":" << "Assignment is not consistent for " << var << d_partialModel.asNode(var);
-      if(d_tableau.isBasic(var)){
-        warning() << " (basic)";
-      }
-      warning() << std::endl;
-      result = false;
-    }else if(d_partialModel.isInteger(var) && !d_partialModel.integralAssignment(var)){
-      d_partialModel.printModel(var);
-      warning() << s << ":" << "Assignment is not integer for integer variable " << var << d_partialModel.asNode(var);
-      if(d_tableau.isBasic(var)){
-        warning() << " (basic)";
-      }
-      warning() << std::endl;
-      result = false;
-    }
-  }
-  return result;
-}
-
-bool TheoryArithPrivate::unenqueuedVariablesAreConsistent(){
-  bool result = true;
-  for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
-    ArithVar var = *vi;
-    if(!d_partialModel.assignmentIsConsistent(var)){
-      if(!d_errorSet.inError(var)){
-
-        d_partialModel.printModel(var);
-        warning() << "Unenqueued var is not consistent for " << var <<  d_partialModel.asNode(var);
-        if(d_tableau.isBasic(var)){
-          warning() << " (basic)";
-        }
-        warning() << std::endl;
-        result = false;
-      } else if(TraceIsOn("arith::consistency::initial")){
-        d_partialModel.printModel(var);
-        warning() << "Initial var is not consistent for " << var <<  d_partialModel.asNode(var);
-        if(d_tableau.isBasic(var)){
-          warning() << " (basic)";
-        }
-        warning() << std::endl;
-      }
-     }
-  }
-  return result;
-}
-
-void TheoryArithPrivate::presolve(){
-  TimerStat::CodeTimer codeTimer(d_statistics.d_presolveTime);
-
-  d_statistics.d_initialTableauSize = d_tableau.size();
-
-  if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
-
-  if(TraceIsOn("arith::presolve")) {
-    Trace("arith::presolve") << "TheoryArithPrivate::presolve" << endl;
-  }
-
-  vector<TrustNode> lemmas;
-  if (!options().base.incrementalSolving)
-  {
-    switch (options().arith.arithUnateLemmaMode)
-    {
-      case options::ArithUnateLemmaMode::NO: break;
-      case options::ArithUnateLemmaMode::INEQUALITY:
-        d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
-        break;
-      case options::ArithUnateLemmaMode::EQUALITY:
-        d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
-        break;
-      case options::ArithUnateLemmaMode::ALL:
-        d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
-        d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
-        break;
-      default: Unhandled() << options().arith.arithUnateLemmaMode;
-    }
-  }
-
-  vector<TrustNode>::const_iterator i = lemmas.begin(), i_end = lemmas.end();
-  for(; i != i_end; ++i){
-    TrustNode lem = *i;
-    Trace("arith::oldprop") << " lemma lemma duck " <<lem << endl;
-    outputTrustedLemma(lem, InferenceId::ARITH_UNATE);
-  }
-}
-
-EqualityStatus TheoryArithPrivate::getEqualityStatus(TNode a, TNode b) {
-  if (d_qflraStatus == Result::UNKNOWN)
-  {
-    return EQUALITY_UNKNOWN;
-  }else{
-    try {
-      if (getDeltaValue(a) == getDeltaValue(b)) {
-        return EQUALITY_TRUE_IN_MODEL;
-      } else {
-        return EQUALITY_FALSE_IN_MODEL;
-      }
-    } catch (DeltaRationalException& dr) {
-      return EQUALITY_UNKNOWN;
-    } catch (ModelException& me) {
-      return EQUALITY_UNKNOWN;
-    }
-  }
-}
-
-bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound){
-  ++d_statistics.d_boundComputations;
-
-  RowIndex ridx = d_tableau.basicToRowIndex(basic);
-  DeltaRational bound = d_linEq.computeRowBound(ridx, upperBound, basic);
-
-  if((upperBound && d_partialModel.strictlyLessThanUpperBound(basic, bound)) ||
-     (!upperBound && d_partialModel.strictlyGreaterThanLowerBound(basic, bound))){
-
-    // TODO: "Policy point"
-    //We are only going to recreate the functionality for now.
-    //In the future this can be improved to generate a temporary constraint
-    //if none exists.
-    //Experiment with doing this every time or only when the new constraint
-    //implies an unknown fact.
-
-    ConstraintType t = upperBound ? UpperBound : LowerBound;
-    ConstraintP bestImplied = d_constraintDatabase.getBestImpliedBound(basic, t, bound);
-
-    // Node bestImplied = upperBound ?
-    //   d_apm.getBestImpliedUpperBound(basic, bound):
-    //   d_apm.getBestImpliedLowerBound(basic, bound);
-
-    if(bestImplied != NullConstraint){
-      //This should be stronger
-      Assert(!upperBound || bound <= bestImplied->getValue());
-      Assert(
-          !upperBound
-          || d_partialModel.lessThanUpperBound(basic, bestImplied->getValue()));
-
-      Assert(upperBound || bound >= bestImplied->getValue());
-      Assert(upperBound
-             || d_partialModel.greaterThanLowerBound(basic,
-                                                     bestImplied->getValue()));
-      //slightly changed
-
-      // ConstraintP c = d_constraintDatabase.lookup(bestImplied);
-      // Assert(c != NullConstraint);
-
-      bool assertedToTheTheory = bestImplied->assertedToTheTheory();
-      bool canBePropagated = bestImplied->canBePropagated();
-      bool hasProof = bestImplied->hasProof();
-
-      Trace("arith::prop") << "arith::prop" << basic
-                           << " " << assertedToTheTheory
-                           << " " << canBePropagated
-                           << " " << hasProof
-                           << endl;
-
-      if(bestImplied->negationHasProof()){
-        warning() << "the negation of " <<  bestImplied << " : " << std::endl
-                  << "has proof " << bestImplied->getNegation() << std::endl
-                  << bestImplied->getNegation()->externalExplainByAssertions()
-                  << std::endl;
-      }
-
-      if(!assertedToTheTheory && canBePropagated && !hasProof ){
-        d_linEq.propagateBasicFromRow(bestImplied, options().smt.produceProofs);
-        // I think this can be skipped if canBePropagated is true
-        //d_learnedBounds.push(bestImplied);
-        if(TraceIsOn("arith::prop")){
-          Trace("arith::prop") << "success " << bestImplied << endl;
-          d_partialModel.printModel(basic, Trace("arith::prop"));
-        }
-        return true;
-      }
-      if(TraceIsOn("arith::prop")){
-        Trace("arith::prop") << "failed " << basic
-                             << " " << bound
-                             << " " << assertedToTheTheory
-                             << " " << canBePropagated
-                             << " " << hasProof << endl;
-        d_partialModel.printModel(basic, Trace("arith::prop"));
-      }
-    }
-  }else if(TraceIsOn("arith::prop")){
-    Trace("arith::prop") << "false " << bound << " ";
-    d_partialModel.printModel(basic, Trace("arith::prop"));
-  }
-  return false;
-}
-
-void TheoryArithPrivate::propagateCandidate(ArithVar basic){
-  bool success = false;
-  RowIndex ridx = d_tableau.basicToRowIndex(basic);
-
-  bool tryLowerBound =
-    d_partialModel.strictlyAboveLowerBound(basic) &&
-    d_linEq.rowLacksBound(ridx, false, basic) == NULL;
-
-  bool tryUpperBound =
-    d_partialModel.strictlyBelowUpperBound(basic) &&
-    d_linEq.rowLacksBound(ridx, true, basic) == NULL;
-
-  if(tryLowerBound){
-    success |= propagateCandidateLowerBound(basic);
-  }
-  if(tryUpperBound){
-    success |= propagateCandidateUpperBound(basic);
-  }
-  if(success){
-    ++d_statistics.d_boundPropagations;
-  }
-}
-
-void TheoryArithPrivate::propagateCandidates(){
-  TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
-
-  Trace("arith::prop") << "propagateCandidates begin" << endl;
-
-  Assert(d_candidateBasics.empty());
-
-  if(d_updatedBounds.empty()){ return; }
-
-  DenseSet::const_iterator i = d_updatedBounds.begin();
-  DenseSet::const_iterator end = d_updatedBounds.end();
-  for(; i != end; ++i){
-    ArithVar var = *i;
-    if (d_tableau.isBasic(var)
-        && d_tableau.basicRowLength(var)
-               <= options().arith.arithPropagateMaxLength)
-    {
-      d_candidateBasics.softAdd(var);
-    }
-    else
-    {
-      Tableau::ColIterator basicIter = d_tableau.colIterator(var);
-      for(; !basicIter.atEnd(); ++basicIter){
-        const Tableau::Entry& entry = *basicIter;
-        RowIndex ridx = entry.getRowIndex();
-        ArithVar rowVar = d_tableau.rowIndexToBasic(ridx);
-        Assert(entry.getColVar() == var);
-        Assert(d_tableau.isBasic(rowVar));
-        if (d_tableau.getRowLength(ridx)
-            <= options().arith.arithPropagateMaxLength)
-        {
-          d_candidateBasics.softAdd(rowVar);
-        }
-      }
-    }
-  }
-  d_updatedBounds.purge();
-
-  while(!d_candidateBasics.empty()){
-    ArithVar candidate = d_candidateBasics.back();
-    d_candidateBasics.pop_back();
-    Assert(d_tableau.isBasic(candidate));
-    propagateCandidate(candidate);
-  }
-  Trace("arith::prop") << "propagateCandidates end" << endl << endl << endl;
-}
-
-void TheoryArithPrivate::propagateCandidatesNew(){
-  /* Four criteria must be met for progagation on a variable to happen using a row:
-   * 0: A new bound has to have been added to the row.
-   * 1: The hasBoundsCount for the row must be "full" or be full minus one variable
-   *    (This is O(1) to check, but requires book keeping.)
-   * 2: The current assignment must be strictly smaller/greater than the current bound.
-   *    assign(x) < upper(x)
-   *    (This is O(1) to compute.)
-   * 3: There is a bound that is strictly smaller/greater than the current assignment.
-   *    assign(x) < c for some x <= c literal
-   *    (This is O(log n) to compute.)
-   * 4: The implied bound on x is strictly smaller/greater than the current bound.
-   *    (This is O(n) to compute.)
-   */
-
-  TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
-  Trace("arith::prop") << "propagateCandidatesNew begin" << endl;
-
-  Assert(d_qflraStatus == Result::SAT);
-  if(d_updatedBounds.empty()){ return; }
-  dumpUpdatedBoundsToRows();
-  Assert(d_updatedBounds.empty());
-
-  if(!d_candidateRows.empty()){
-    UpdateTrackingCallback utcb(&d_linEq);
-    d_partialModel.processBoundsQueue(utcb);
-  }
-
-  while(!d_candidateRows.empty()){
-    RowIndex candidate = d_candidateRows.back();
-    d_candidateRows.pop_back();
-    propagateCandidateRow(candidate);
-  }
-  Trace("arith::prop") << "propagateCandidatesNew end" << endl << endl << endl;
-}
-
-bool TheoryArithPrivate::propagateMightSucceed(ArithVar v, bool ub) const{
-  int cmp = ub ? d_partialModel.cmpAssignmentUpperBound(v)
-    : d_partialModel.cmpAssignmentLowerBound(v);
-  bool hasSlack = ub ? cmp < 0 : cmp > 0;
-  if(hasSlack){
-    ConstraintType t = ub ? UpperBound : LowerBound;
-    const DeltaRational& a = d_partialModel.getAssignment(v);
-
-    if(isInteger(v) && !a.isIntegral()){
-      return true;
-    }
-
-    ConstraintP strongestPossible = d_constraintDatabase.getBestImpliedBound(v, t, a);
-    if(strongestPossible == NullConstraint){
-      return false;
-    }else{
-      bool assertedToTheTheory = strongestPossible->assertedToTheTheory();
-      bool canBePropagated = strongestPossible->canBePropagated();
-      bool hasProof = strongestPossible->hasProof();
-
-      return !assertedToTheTheory && canBePropagated && !hasProof;
-    }
-  }else{
-    return false;
-  }
-}
-
-bool TheoryArithPrivate::attemptSingleton(RowIndex ridx, bool rowUp){
-  Trace("arith::prop") << "  attemptSingleton" << ridx;
-
-  const Tableau::Entry* ep;
-  ep = d_linEq.rowLacksBound(ridx, rowUp, ARITHVAR_SENTINEL);
-  Assert(ep != NULL);
-
-  ArithVar v = ep->getColVar();
-  const Rational& coeff = ep->getCoefficient();
-
-  // 0 = c * v + \sum rest
-  // Suppose rowUp
-  // - c * v = \sum rest \leq D
-  // if c > 0, v \geq -D/c so !vUp
-  // if c < 0, v \leq -D/c so  vUp
-  // Suppose not rowUp
-  // - c * v = \sum rest \geq D
-  // if c > 0, v \leq -D/c so  vUp
-  // if c < 0, v \geq -D/c so !vUp
-  bool vUp = (rowUp == ( coeff.sgn() < 0));
-
-  Trace("arith::prop") << "  " << rowUp << " " << v << " " << coeff << " " << vUp << endl;
-  Trace("arith::prop") << "  " << propagateMightSucceed(v, vUp) << endl;
-
-  if(propagateMightSucceed(v, vUp)){
-    DeltaRational dr = d_linEq.computeRowBound(ridx, rowUp, v);
-    DeltaRational bound = dr / (- coeff);
-    return tryToPropagate(ridx, rowUp, v, vUp, bound);
-  }
-  return false;
-}
-
-bool TheoryArithPrivate::attemptFull(RowIndex ridx, bool rowUp){
-  Trace("arith::prop") << "  attemptFull" << ridx << endl;
-
-  vector<const Tableau::Entry*> candidates;
-
-  for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){
-    const Tableau::Entry& e =*i;
-    const Rational& c = e.getCoefficient();
-    ArithVar v = e.getColVar();
-    bool vUp = (rowUp == (c.sgn() < 0));
-    if(propagateMightSucceed(v, vUp)){
-      candidates.push_back(&e);
-    }
-  }
-  if(candidates.empty()){ return false; }
-
-  const DeltaRational slack =
-    d_linEq.computeRowBound(ridx, rowUp, ARITHVAR_SENTINEL);
-  bool any = false;
-  vector<const Tableau::Entry*>::const_iterator i, iend;
-  for(i = candidates.begin(), iend = candidates.end(); i != iend; ++i){
-    const Tableau::Entry* ep = *i;
-    const Rational& c = ep->getCoefficient();
-    ArithVar v = ep->getColVar();
-
-    // See the comment for attemptSingleton()
-    bool activeUp = (rowUp == (c.sgn() > 0));
-    bool vUb = (rowUp == (c.sgn() < 0));
-
-    const DeltaRational& activeBound = activeUp ?
-      d_partialModel.getUpperBound(v):
-      d_partialModel.getLowerBound(v);
-
-    DeltaRational contribution = activeBound * c;
-    DeltaRational impliedBound = (slack - contribution)/(-c);
-
-    bool success = tryToPropagate(ridx, rowUp, v, vUb, impliedBound);
-    any |= success;
-  }
-  return any;
-}
-
-bool TheoryArithPrivate::tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, bool vUb, const DeltaRational& bound){
-
-  bool weaker = vUb ? d_partialModel.strictlyLessThanUpperBound(v, bound):
-    d_partialModel.strictlyGreaterThanLowerBound(v, bound);
-  if(weaker){
-    ConstraintType t = vUb ? UpperBound : LowerBound;
-
-    ConstraintP implied = d_constraintDatabase.getBestImpliedBound(v, t, bound);
-    if(implied != NullConstraint){
-      return rowImplicationCanBeApplied(ridx, rowUp, implied);
-    }
-  }
-  return false;
-}
-
-Node flattenImplication(Node imp){
-  NodeBuilder nb(kind::OR);
-  std::unordered_set<Node> included;
-  Node left = imp[0];
-  Node right = imp[1];
-
-  if(left.getKind() == kind::AND){
-    for(Node::iterator i = left.begin(), iend = left.end(); i != iend; ++i) {
-      if (!included.count((*i).negate()))
-      {
-        nb << (*i).negate();
-        included.insert((*i).negate());
-      }
-    }
-  }else{
-    if (!included.count(left.negate()))
-    {
-      nb << left.negate();
-      included.insert(left.negate());
-    }
-  }
-
-  if(right.getKind() == kind::OR){
-    for(Node::iterator i = right.begin(), iend = right.end(); i != iend; ++i) {
-      if (!included.count(*i))
-      {
-        nb << *i;
-        included.insert(*i);
-      }
-    }
-  }else{
-    if (!included.count(right))
-    {
-      nb << right;
-      included.insert(right);
-    }
-  }
-
-  return nb;
-}
-
-bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, ConstraintP implied){
-  Assert(implied != NullConstraint);
-  ArithVar v = implied->getVariable();
-
-  bool assertedToTheTheory = implied->assertedToTheTheory();
-  bool canBePropagated = implied->canBePropagated();
-  bool hasProof = implied->hasProof();
-
-  Trace("arith::prop") << "arith::prop" << v
-                       << " " << assertedToTheTheory
-                       << " " << canBePropagated
-                       << " " << hasProof
-                       << endl;
-
-
-  if( !assertedToTheTheory && canBePropagated && !hasProof ){
-    ConstraintCPVec explain;
-    if (options().smt.produceProofs)
-    {
-      d_farkasBuffer.clear();
-    }
-    RationalVectorP coeffs =
-        options().smt.produceProofs ? &d_farkasBuffer : nullptr;
-
-    // After invoking `propegateRow`:
-    //   * coeffs[0] is for implied
-    //   * coeffs[i+1] is for explain[i]
-    d_linEq.propagateRow(explain, ridx, rowUp, implied, coeffs);
-    if (d_tableau.getRowLength(ridx) <= options().arith.arithPropAsLemmaLength)
-    {
-      if (TraceIsOn("arith::prop::pf")) {
-        for (const auto & constraint : explain) {
-          Assert(constraint->hasProof());
-          constraint->printProofTree(Trace("arith::prop::pf"));
-        }
-      }
-      Node implication = implied->externalImplication(explain);
-      Node clause = flattenImplication(implication);
-      std::shared_ptr<ProofNode> clausePf{nullptr};
-
-      if (isProofEnabled())
-      {
-        // We can prove this lemma from Farkas...
-        std::vector<std::shared_ptr<ProofNode>> conflictPfs;
-        Node pfLit = implied->getNegation()->getProofLiteral();
-        TypeNode type = pfLit[0].getType();
-        // Assume the negated getLiteral version of the implied constaint
-        // then rewrite it into proof normal form.
-        conflictPfs.push_back(
-            d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
-                          {d_pnm->mkAssume(implied->getLiteral().negate())},
-                          {pfLit}));
-        // Add the explaination proofs.
-        for (const auto constraint : explain)
-        {
-          NodeBuilder nb;
-          conflictPfs.push_back(constraint->externalExplainByAssertions(nb));
-        }
-        // Collect the farkas coefficients, as nodes.
-        std::vector<Node> farkasCoefficients;
-        farkasCoefficients.reserve(coeffs->size());
-        auto nm = NodeManager::currentNM();
-        std::transform(coeffs->begin(),
-                       coeffs->end(),
-                       std::back_inserter(farkasCoefficients),
-                       [nm, type](const Rational& r) {
-                         return nm->mkConstRealOrInt(type, r);
-                       });
-
-        // Prove bottom.
-        auto sumPf = d_pnm->mkNode(
-            PfRule::MACRO_ARITH_SCALE_SUM_UB, conflictPfs, farkasCoefficients);
-        auto botPf = d_pnm->mkNode(
-            PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
-
-        // Prove the conflict
-        std::vector<Node> assumptions;
-        assumptions.reserve(clause.getNumChildren());
-        std::transform(clause.begin(),
-                       clause.end(),
-                       std::back_inserter(assumptions),
-                       [](TNode r) { return r.negate(); });
-        auto notAndNotPf = d_pnm->mkScope(botPf, assumptions);
-
-        // Convert it to a clause
-        auto orNotNotPf = d_pnm->mkNode(PfRule::NOT_AND, {notAndNotPf}, {});
-        clausePf = d_pnm->mkNode(
-            PfRule::MACRO_SR_PRED_TRANSFORM, {orNotNotPf}, {clause});
-
-        // Output it
-        TrustNode trustedClause = d_pfGen->mkTrustNode(clause, clausePf);
-        outputTrustedLemma(trustedClause, InferenceId::ARITH_ROW_IMPL);
-      }
-      else
-      {
-        outputLemma(clause, InferenceId::ARITH_ROW_IMPL);
-      }
-    }
-    else
-    {
-      Assert(!implied->negationHasProof());
-      implied->impliedByFarkas(explain, coeffs, false);
-      implied->tryToPropagate();
-    }
-    return true;
-  }
-
-  if(TraceIsOn("arith::prop")){
-    Trace("arith::prop")
-      << "failed " << v << " " << assertedToTheTheory << " "
-      << canBePropagated << " " << hasProof << " " << implied << endl;
-    d_partialModel.printModel(v, Trace("arith::prop"));
-  }
-  return false;
-}
-
-bool TheoryArithPrivate::propagateCandidateRow(RowIndex ridx){
-  BoundCounts hasCount = d_linEq.hasBoundCount(ridx);
-  uint32_t rowLength = d_tableau.getRowLength(ridx);
-
-  bool success = false;
-
-  Trace("arith::prop") << "propagateCandidateRow attempt " << rowLength << " "
-                       << hasCount << endl;
-
-  if (rowLength >= options().arith.arithPropagateMaxLength
-      && Random::getRandom().pickWithProb(
-          1.0 - double(options().arith.arithPropagateMaxLength) / rowLength))
-  {
-    return false;
-  }
-
-  if(hasCount.lowerBoundCount() == rowLength){
-    success |= attemptFull(ridx, false);
-  }else if(hasCount.lowerBoundCount() + 1 == rowLength){
-    success |= attemptSingleton(ridx, false);
-  }
-
-  if(hasCount.upperBoundCount() == rowLength){
-    success |= attemptFull(ridx, true);
-  }else if(hasCount.upperBoundCount() + 1 == rowLength){
-    success |= attemptSingleton(ridx, true);
-  }
-  return success;
-}
-
-void TheoryArithPrivate::dumpUpdatedBoundsToRows(){
-  Assert(d_candidateRows.empty());
-  DenseSet::const_iterator i = d_updatedBounds.begin();
-  DenseSet::const_iterator end = d_updatedBounds.end();
-  for(; i != end; ++i){
-    ArithVar var = *i;
-    if(d_tableau.isBasic(var)){
-      RowIndex ridx = d_tableau.basicToRowIndex(var);
-      d_candidateRows.softAdd(ridx);
-    }else{
-      Tableau::ColIterator basicIter = d_tableau.colIterator(var);
-      for(; !basicIter.atEnd(); ++basicIter){
-        const Tableau::Entry& entry = *basicIter;
-        RowIndex ridx = entry.getRowIndex();
-        d_candidateRows.softAdd(ridx);
-      }
-    }
-  }
-  d_updatedBounds.purge();
-}
-
-const BoundsInfo& TheoryArithPrivate::boundsInfo(ArithVar basic) const{
-  RowIndex ridx = d_tableau.basicToRowIndex(basic);
-  return d_rowTracking[ridx];
-}
-
-std::pair<bool, Node> TheoryArithPrivate::entailmentCheck(TNode lit, const ArithEntailmentCheckParameters& params, ArithEntailmentCheckSideEffects& out){
-  using namespace inferbounds;
-
-  // l k r
-  // diff : (l - r) k 0
-  Trace("arith::entailCheck") << "TheoryArithPrivate::entailmentCheck(" << lit << ")"<< endl;
-  Kind k;
-  int primDir;
-  Rational lm, rm, dm;
-  Node lp, rp, dp;
-  DeltaRational sep;
-  bool successful = decomposeLiteral(lit, k, primDir, lm, lp, rm, rp, dm, dp, sep);
-  if(!successful) { return make_pair(false, Node::null()); }
-
-  if (dp.isConst())
-  {
-    Node eval = rewrite(lit);
-    Assert(eval.getKind() == kind::CONST_BOOLEAN);
-    // if true, true is an acceptable explaination
-    // if false, the node is uninterpreted and eval can be forgotten
-    return make_pair(eval.getConst<bool>(), eval);
-  }
-  Assert(dm != Rational(0));
-  Assert(primDir == 1 || primDir == -1);
-
-  int negPrim = -primDir;
-
-  int secDir = (k == EQUAL || k == DISTINCT) ? negPrim: 0;
-  int negSecDir = (k == EQUAL || k == DISTINCT) ? primDir: 0;
-
-  // primDir*[lm*( lp )] k primDir*[ [rm*( rp )] + sep ]
-  // primDir*[lm*( lp ) - rm*( rp ) ] k primDir*sep
-  // primDir*[dm * dp] k primDir*sep
-
-  std::pair<Node, DeltaRational> bestPrimLeft, bestNegPrimRight, bestPrimDiff, tmp;
-  std::pair<Node, DeltaRational> bestSecLeft, bestNegSecRight, bestSecDiff;
-  bestPrimLeft.first = Node::null(); bestNegPrimRight.first = Node::null(); bestPrimDiff.first = Node::null();
-  bestSecLeft.first = Node::null(); bestNegSecRight.first = Node::null(); bestSecDiff.first = Node::null();
-
-
-
-  ArithEntailmentCheckParameters::const_iterator alg, alg_end;
-  for( alg = params.begin(), alg_end = params.end(); alg != alg_end; ++alg ){
-    const inferbounds::InferBoundAlgorithm& ibalg = *alg;
-
-    Trace("arith::entailCheck") << "entailmentCheck trying " << (inferbounds::Algorithms) ibalg.getAlgorithm() << endl;
-    switch(ibalg.getAlgorithm()){
-    case inferbounds::None:
-      break;
-    case inferbounds::Lookup:
-    case inferbounds::RowSum:
-      {
-        typedef void (TheoryArithPrivate::*EntailmentCheckFunc)(std::pair<Node, DeltaRational>&, int, TNode) const;
-
-        EntailmentCheckFunc ecfunc =
-          (ibalg.getAlgorithm() == inferbounds::Lookup)
-          ? (&TheoryArithPrivate::entailmentCheckBoundLookup)
-          : (&TheoryArithPrivate::entailmentCheckRowSum);
-
-        (*this.*ecfunc)(tmp, primDir * lm.sgn(), lp);
-        setToMin(primDir * lm.sgn(), bestPrimLeft, tmp);
-
-        (*this.*ecfunc)(tmp, negPrim * rm.sgn(), rp);
-        setToMin(negPrim * rm.sgn(), bestNegPrimRight, tmp);
-
-        (*this.*ecfunc)(tmp, secDir * lm.sgn(), lp);
-        setToMin(secDir * lm.sgn(), bestSecLeft, tmp);
-
-        (*this.*ecfunc)(tmp, negSecDir * rm.sgn(), rp);
-        setToMin(negSecDir * rm.sgn(), bestNegSecRight, tmp);
-
-        (*this.*ecfunc)(tmp, primDir * dm.sgn(), dp);
-        setToMin(primDir * dm.sgn(), bestPrimDiff, tmp);
-
-        (*this.*ecfunc)(tmp, secDir * dm.sgn(), dp);
-        setToMin(secDir * dm.sgn(), bestSecDiff, tmp);
-      }
-      break;
-    default:
-      Unhandled();
-    }
-
-    // turn bounds on prim * left and -prim * right into bounds on prim * diff
-    if(!bestPrimLeft.first.isNull() && !bestNegPrimRight.first.isNull()){
-      //  primDir*lm* lp <= primDir*lm*L
-      // -primDir*rm* rp <= -primDir*rm*R
-      // primDir*lm* lp -primDir*rm* rp <=  primDir*lm*L - primDir*rm*R
-      // primDir [lm* lp -rm* rp] <= primDir[lm*L - *rm*R]
-      // primDir [dm * dp] <= primDir[lm*L - *rm*R]
-      // primDir [dm * dp] <= primDir * dm * ([lm*L - *rm*R]/dm)
-      tmp.second = ((bestPrimLeft.second * lm) - (bestNegPrimRight.second * rm)) / dm;
-      tmp.first = (bestPrimLeft.first).andNode(bestNegPrimRight.first);
-      setToMin(primDir, bestPrimDiff, tmp);
-    }
-
-    // turn bounds on sec * left and sec * right into bounds on sec * diff
-    if(secDir != 0 && !bestSecLeft.first.isNull() && !bestNegSecRight.first.isNull()){
-      //  secDir*lm* lp <= secDir*lm*L
-      // -secDir*rm* rp <= -secDir*rm*R
-      // secDir*lm* lp -secDir*rm* rp <=  secDir*lm*L - secDir*rm*R
-      // secDir [lm* lp -rm* rp] <= secDir[lm*L - *rm*R]
-      // secDir [dm * dp] <= secDir[lm*L - *rm*R]
-      // secDir [dm * dp] <= secDir * dm * ([lm*L - *rm*R]/dm)
-      tmp.second = ((bestSecLeft.second * lm) - (bestNegSecRight.second * rm)) / dm;
-      tmp.first = (bestSecLeft.first).andNode(bestNegSecRight.first);
-      setToMin(secDir, bestSecDiff, tmp);
-    }
-
-    switch(k){
-    case LEQ:
-      if(!bestPrimDiff.first.isNull()){
-        DeltaRational d = (bestPrimDiff.second * dm);
-        if((primDir > 0 && d <= sep) || (primDir < 0 && d >= sep) ){
-          Trace("arith::entailCheck") << "entailmentCheck found "
-                                      << primDir << "*" << dm << "*(" << dp<<")"
-                                      << " <= " << primDir << "*" << dm << "*" << bestPrimDiff.second
-                                      << " <= " << primDir << "*" << sep << endl
-                                      << " by " << bestPrimDiff.first << endl;
-          Assert(bestPrimDiff.second * (Rational(primDir) * dm)
-                 <= (sep * Rational(primDir)));
-          return make_pair(true, bestPrimDiff.first);
-        }
-      }
-      break;
-    case EQUAL:
-      if(!bestPrimDiff.first.isNull() && !bestSecDiff.first.isNull()){
-        // Is primDir [dm * dp] == primDir * sep entailed?
-        // Iff [dm * dp] == sep entailed?
-        // Iff dp == sep / dm entailed?
-        // Iff dp <= sep / dm and dp >= sep / dm entailed?
-
-        // primDir [dm * dp] <= primDir * dm * U
-        // secDir [dm * dp] <= secDir * dm * L
-
-        // Suppose primDir * dm > 0
-        // then secDir * dm < 0
-        //   dp >= (secDir * L) / secDir * dm
-        //   dp >= (primDir * L) / primDir * dm
-        //
-        //   dp <= U / dm
-        //   dp >= L / dm
-        //   dp == sep / dm entailed iff U == L == sep
-        // Suppose primDir * dm < 0
-        // then secDir * dm > 0
-        //   dp >= U / dm
-        //   dp <= L / dm
-        //   dp == sep / dm entailed iff U == L == sep
-        if(bestPrimDiff.second == bestSecDiff.second){
-          if(bestPrimDiff.second == sep){
-            return make_pair(true, (bestPrimDiff.first).andNode(bestSecDiff.first));
-          }
-        }
-      }
-      // intentionally fall through to DISTINCT case!
-      // entailments of negations are eager exit cases for EQUAL
-      CVC5_FALLTHROUGH;
-    case DISTINCT:
-      if(!bestPrimDiff.first.isNull()){
-        // primDir [dm * dp] <= primDir * dm * U < primDir * sep
-        if((primDir > 0 && (bestPrimDiff.second * dm  < sep)) ||
-           (primDir < 0 && (bestPrimDiff.second * dm  > sep))){
-          // entailment of negation
-          if(k == DISTINCT){
-            return make_pair(true, bestPrimDiff.first);
-          }else{
-            Assert(k == EQUAL);
-            return make_pair(false, Node::null());
-          }
-        }
-      }
-      if(!bestSecDiff.first.isNull()){
-        // If primDir [dm * dp] > primDir * sep, then this is not entailed.
-        // If primDir [dm * dp] >= primDir * dm * L > primDir * sep
-        // -primDir * dm * L < -primDir * sep
-        // secDir * dm * L < secDir * sep
-        if((secDir > 0 && (bestSecDiff.second * dm < sep)) ||
-           (secDir < 0 && (bestSecDiff.second * dm > sep))){
-          if(k == DISTINCT){
-            return make_pair(true, bestSecDiff.first);
-          }else{
-            Assert(k == EQUAL);
-            return make_pair(false, Node::null());
-          }
-        }
-      }
-
-      break;
-    default:
-      Unreachable();
-      break;
-    }
-  }
-  return make_pair(false, Node::null());
-}
-
-bool TheoryArithPrivate::decomposeTerm(Node t,
-                                       Rational& m,
-                                       Node& p,
-                                       Rational& c)
-{
-  if(!Polynomial::isMember(t)){
-    return false;
-  }
-
-  // TODO Speed up
-  preprocessing::util::ContainsTermITEVisitor ctv;
-  if(ctv.containsTermITE(t)){
-    return false;
-  }
-
-  Polynomial poly = Polynomial::parsePolynomial(t);
-  if(poly.isConstant()){
-    c = poly.getHead().getConstant().getValue();
-    p = mkRationalNode(Rational(0));
-    m = Rational(1);
-    return true;
-  }else if(poly.containsConstant()){
-    c = poly.getHead().getConstant().getValue();
-    poly = poly.getTail();
-  }else{
-    c = Rational(0);
-  }
-  Assert(!poly.isConstant());
-  Assert(!poly.containsConstant());
-
-  const bool intVars = poly.allIntegralVariables();
-
-  if(intVars){
-    m = Rational(1);
-    if(!poly.isIntegral()){
-      Integer denom = poly.denominatorLCM();
-      m /= denom;
-      poly = poly * denom;
-    }
-    Integer g = poly.gcd();
-    m *= g;
-    poly = poly * Rational(1,g);
-    Assert(poly.isIntegral());
-  }else{
-    Assert(!intVars);
-    m = poly.getHead().getConstant().getValue();
-    poly = poly * m.inverse();
-    Assert(poly.leadingCoefficientIsAbsOne());
-  }
-  p = poly.getNode();
-  return true;
-}
-
-void TheoryArithPrivate::setToMin(int sgn, std::pair<Node, DeltaRational>& min, const std::pair<Node, DeltaRational>& e){
-  if(sgn != 0){
-    if(min.first.isNull() && !e.first.isNull()){
-      min = e;
-    }else if(!min.first.isNull() && !e.first.isNull()){
-      if(sgn > 0 && min.second > e.second){
-        min = e;
-      }else if(sgn < 0 &&  min.second < e.second){
-        min = e;
-      }
-    }
-  }
-}
-
-// std::pair<bool, Node> TheoryArithPrivate::entailmentUpperCheck(const Rational& lm, Node lp, const Rational& rm, Node rp, const DeltaRational& sep, const ArithEntailmentCheckParameters& params, ArithEntailmentCheckSideEffects& out){
-
-//   Rational negRM = -rm;
-//   Node diff = NodeManager::currentNM()->mkNode(MULT, mkRationalConstan(lm), lp) + (negRM * rp);
-
-//   Rational diffm;
-//   Node diffp;
-//   decompose(diff, diffm, diffNode);
-
-
-//   std::pair<Node, DeltaRational> bestUbLeft, bestLbRight, bestUbDiff, tmp;
-//   bestUbLeft = bestLbRight = bestUbDiff = make_pair(Node::Null(), DeltaRational());
-
-//   return make_pair(false, Node::null());
-// }
-
-/**
- * Decomposes a literal into the form:
- *   dir*[lm*( lp )] k dir*[ [rm*( rp )] + sep ]
- *   dir*[dm* dp]  k dir *sep
- *   dir is either 1 or -1
- */
-bool TheoryArithPrivate::decomposeLiteral(Node lit, Kind& k, int& dir, Rational& lm,  Node& lp, Rational& rm, Node& rp, Rational& dm, Node& dp, DeltaRational& sep){
-  bool negated = (lit.getKind() == kind::NOT);
-  TNode atom = negated ? lit[0] : lit;
-
-  TNode left = atom[0];
-  TNode right = atom[1];
-
-  // left : lm*( lp ) + lc
-  // right: rm*( rp ) + rc
-  Rational lc, rc;
-  bool success = decomposeTerm(rewrite(left), lm, lp, lc);
-  if(!success){ return false; }
-  success = decomposeTerm(rewrite(right), rm, rp, rc);
-  if(!success){ return false; }
-
-  Node diff = rewrite(NodeManager::currentNM()->mkNode(kind::SUB, left, right));
-  Rational dc;
-  success = decomposeTerm(diff, dm, dp, dc);
-  Assert(success);
-
-  // reduce the kind of the to not include literals
-  // GT, NOT LEQ
-  // GEQ, NOT LT
-  // LT, NOT GEQ
-  // LEQ, NOT LT
-  Kind atomKind = atom.getKind();
-  Kind normKind = negated ? negateKind(atomKind) : atomKind;
-
-  if(normKind == GEQ || normKind == GT){
-    dir = -1;
-    normKind = (normKind == GEQ) ? LEQ : LT;
-  }else{
-    dir = 1;
-  }
-
-  Trace("arith::decomp") << "arith::decomp "
-                         << lit << "(" << normKind << "*" << dir << ")"<< endl
-                         << "  left:" << lc << " + " << lm << "*(" <<  lp << ") : " <<left << endl
-                         << "  right:" << rc << " + " << rm << "*(" <<  rp << ") : " << right << endl
-                         << "  diff: " << dc << " + " << dm << "*("<< dp <<"): " << diff << endl
-                         << "  sep: " << sep << endl;
-
-
-  // k in LT, LEQ, EQUAL, DISEQUAL
-  // [dir*lm*( lp ) + dir*lc] k [dir*rm*( rp ) + dir*rc]
-  Rational change = rc - lc;
-  Assert(change == (-dc));
-  // [dir*lm*( lp )] k [dir*rm*( rp ) + dir*(rc - lc)]
-  if(normKind == LT){
-    sep = DeltaRational(change, Rational(-1));
-    k = LEQ;
-  }else{
-    sep = DeltaRational(change);
-    k = normKind;
-  }
-  // k in LEQ, EQUAL, DISEQUAL
-  // dir*lm*( lp ) k [dir*rm*( rp )] + dir*(sep + d * delta)
-  return true;
-}
-
-/**
- *  Precondition:
- *   tp is a polynomial not containing an ite.
- *   either tp is constant or contains no constants.
- *  Post:
- *    if tmp.first is not null, then
- *      sgn * tp <= sgn * tmp.second
- */
-void TheoryArithPrivate::entailmentCheckBoundLookup(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const {
-  tmp.first = Node::null();
-  if(sgn == 0){ return; }
-
-  Assert(Polynomial::isMember(tp));
-  if (tp.isConst())
-  {
-    tmp.first = mkBoolNode(true);
-    tmp.second = DeltaRational(tp.getConst<Rational>());
-  }
-  else if (d_partialModel.hasArithVar(tp))
-  {
-    Assert(!tp.isConst());
-    ArithVar v = d_partialModel.asArithVar(tp);
-    Assert(v != ARITHVAR_SENTINEL);
-    ConstraintP c = (sgn > 0)
-      ? d_partialModel.getUpperBoundConstraint(v)
-      : d_partialModel.getLowerBoundConstraint(v);
-    if(c != NullConstraint){
-      tmp.first = Constraint::externalExplainByAssertions({c});
-      tmp.second = c->getValue();
-    }
-  }
-}
-
-void TheoryArithPrivate::entailmentCheckRowSum(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const {
-  tmp.first = Node::null();
-  if(sgn == 0){ return; }
-  if (tp.getKind() != ADD)
-  {
-    return;
-  }
-  Assert(Polynomial::isMember(tp));
-
-  tmp.second = DeltaRational(0);
-  NodeBuilder nb(kind::AND);
-
-  Polynomial p = Polynomial::parsePolynomial(tp);
-  for(Polynomial::iterator i = p.begin(), iend = p.end(); i != iend; ++i) {
-    Monomial m = *i;
-    Node x = m.getVarList().getNode();
-    if(d_partialModel.hasArithVar(x)){
-      ArithVar v = d_partialModel.asArithVar(x);
-      const Rational& coeff = m.getConstant().getValue();
-      int dir = sgn * coeff.sgn();
-      ConstraintP c = (dir > 0)
-        ? d_partialModel.getUpperBoundConstraint(v)
-        : d_partialModel.getLowerBoundConstraint(v);
-      if(c != NullConstraint){
-        tmp.second += c->getValue() * coeff;
-        c->externalExplainByAssertions(nb);
-      }else{
-        //failed
-        return;
-      }
-    }else{
-      // failed
-      return;
-    }
-  }
-  // success
-  tmp.first = nb;
-}
-
-ArithProofRuleChecker* TheoryArithPrivate::getProofChecker()
-{
-  return &d_checker;
-}
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
diff --git a/src/theory/arith/theory_arith_private.h b/src/theory/arith/theory_arith_private.h
deleted file mode 100644 (file)
index eab5669..0000000
+++ /dev/null
@@ -1,880 +0,0 @@
-/******************************************************************************
- * Top contributors (to current version):
- *   Tim King, Andrew Reynolds, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved.  See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#pragma once
-
-#include <map>
-#include <vector>
-
-#include "context/cdhashset.h"
-#include "context/cdinsert_hashmap.h"
-#include "context/cdlist.h"
-#include "context/cdqueue.h"
-#include "expr/kind.h"
-#include "expr/node.h"
-#include "expr/node_builder.h"
-#include "proof/trust_node.h"
-#include "theory/arith/arith_static_learner.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/attempt_solution_simplex.h"
-#include "theory/arith/branch_and_bound.h"
-#include "theory/arith/congruence_manager.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/dio_solver.h"
-#include "theory/arith/dual_simplex.h"
-#include "theory/arith/error_set.h"
-#include "theory/arith/fc_simplex.h"
-#include "theory/arith/infer_bounds.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/matrix.h"
-#include "theory/arith/normal_form.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/proof_checker.h"
-#include "theory/arith/soi_simplex.h"
-#include "theory/arith/theory_arith.h"
-#include "theory/valuation.h"
-#include "util/dense_map.h"
-#include "util/integer.h"
-#include "util/rational.h"
-#include "util/result.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-
-class EagerProofGenerator;
-
-namespace theory {
-
-class TheoryModel;
-
-namespace arith {
-
-class BranchCutInfo;
-class TreeLog;
-class ApproximateStatistics;
-
-class ArithEntailmentCheckParameters;
-class ArithEntailmentCheckSideEffects;
-namespace inferbounds {
-  class InferBoundAlgorithm;
-}
-class InferBoundsResult;
-
-/**
- * Implementation of QF_LRA.
- * Based upon:
- * http://research.microsoft.com/en-us/um/people/leonardo/cav06.pdf
- */
-class TheoryArithPrivate : protected EnvObj
-{
- private:
-  static constexpr uint32_t RESET_START = 2;
-
-  TheoryArith& d_containing;
-
-  /**
-   * Whether we encountered non-linear arithmetic at any time during solving.
-   */
-  bool d_foundNl;
-
-  BoundInfoMap d_rowTracking;
-  /** Branch and bound utility */
-  BranchAndBound& d_bab;
-  // For proofs
-  /** Manages the proof nodes of this theory. */
-  ProofNodeManager* d_pnm;
-  /** Checks the proof rules of this theory. */
-  ArithProofRuleChecker d_checker;
-  /** Stores proposition(node)/proof pairs. */
-  std::unique_ptr<EagerProofGenerator> d_pfGen;
-
-  /**
-   * The constraint database associated with the theory.
-   * This must be declared before ArithPartialModel.
-   */
-  ConstraintDatabase d_constraintDatabase;
-
-  enum Result::Status d_qflraStatus;
-  // check()
-  //   !done() -> d_qflraStatus = Unknown
-  //   fullEffort(e) -> simplex returns either sat or unsat
-  //   !fullEffort(e) -> simplex returns either sat, unsat or unknown
-  //                     if unknown, save the assignment
-  //                     if unknown, the simplex priority queue cannot be emptied
-  int d_unknownsInARow;
-
-  bool d_replayedLemmas;
-
-  /**
-   * This counter is false if nothing has been done since the last cut.
-   * This is used to break an infinite loop.
-   */
-  bool d_hasDoneWorkSinceCut;
-
-  /** Static learner. */
-  ArithStaticLearner d_learner;
-
-  //std::vector<ArithVar> d_pool;
-public:
-  void releaseArithVar(ArithVar v);
-  void signal(ArithVar v){ d_errorSet.signalVariable(v); }
-
-
-private:
-  // t does not contain constants
-  void entailmentCheckBoundLookup(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const;
-  void entailmentCheckRowSum(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const;
-
-  /**
-   * Infers either a new upper/lower bound on term in the real relaxation.
-   * Either:
-   * - term is malformed (see below)
-   * - a maximum/minimum is found with the result being a pair
-   * -- <dr, exp> where
-   * -- term <?> dr is implies by exp
-   * -- <?> is <= if inferring an upper bound, >= otherwise
-   * -- exp is in terms of the assertions to the theory.
-   * - No upper or lower bound is inferrable in the real relaxation.
-   * -- Returns <0, Null()>
-   * - the maximum number of rounds was exhausted:
-   * -- Returns <v, term> where v is the current feasible value of term
-   * - Threshold reached:
-   * -- If theshold != NULL, and a feasible value is found to exceed threshold
-   * -- Simplex stops and returns <threshold, term>
-   */
-  //std::pair<DeltaRational, Node> inferBound(TNode term, bool lb, int maxRounds = -1, const DeltaRational* threshold = NULL);
-
-private:
- static bool decomposeTerm(Node t, Rational& m, Node& p, Rational& c);
- bool decomposeLiteral(Node lit,
-                       Kind& k,
-                       int& dir,
-                       Rational& lm,
-                       Node& lp,
-                       Rational& rm,
-                       Node& rp,
-                       Rational& dm,
-                       Node& dp,
-                       DeltaRational& sep);
- static void setToMin(int sgn,
-                      std::pair<Node, DeltaRational>& min,
-                      const std::pair<Node, DeltaRational>& e);
-
- typedef ArithVariables::var_iterator var_iterator;
- var_iterator var_begin() const { return d_partialModel.var_begin(); }
- var_iterator var_end() const { return d_partialModel.var_end(); }
-
- NodeSet d_setupNodes;
-public:
-  bool isSetup(Node n) const {
-    return d_setupNodes.find(n) != d_setupNodes.end();
-  }
-  void markSetup(Node n){
-    Assert(!isSetup(n));
-    d_setupNodes.insert(n);
-  }
-private:
-  void setupVariable(const Variable& x);
-  void setupVariableList(const VarList& vl);
-  void setupPolynomial(const Polynomial& poly);
-public:
-  void setupAtom(TNode atom);
-private:
-  void cautiousSetupPolynomial(const Polynomial& p);
-
-  /**
-   * A superset of all of the assertions that currently are not the literal for
-   * their constraint do not match constraint literals. Not just the witnesses.
-   */
-  context::CDInsertHashMap<Node, ConstraintP>
-      d_assertionsThatDoNotMatchTheirLiterals;
-
-  /** Returns true if x is of type Integer. */
-  inline bool isInteger(ArithVar x) const {
-    return d_partialModel.isInteger(x);
-  }
-
-
-  /** Returns true if the variable was initially introduced as an auxiliary variable. */
-  inline bool isAuxiliaryVariable(ArithVar x) const{
-    return d_partialModel.isAuxiliary(x);
-  }
-
-  inline bool isIntegerInput(ArithVar x) const
-  {
-    return d_partialModel.isIntegerInput(x)
-           && d_preregisteredNodes.contains(d_partialModel.asNode(x));
-  }
-
-  /**
-   * On full effort checks (after determining LA(Q) satisfiability), we
-   * consider integer vars, but we make sure to do so fairly to avoid
-   * nontermination (although this isn't a guarantee).  To do it fairly,
-   * we consider variables in round-robin fashion.  This is the
-   * round-robin index.
-   */
-  ArithVar d_nextIntegerCheckVar;
-
-  /**
-   * Queue of Integer variables that are known to be equal to a constant.
-   */
-  context::CDQueue<ArithVar> d_constantIntegerVariables;
-
-  Node callDioSolver();
-  /**
-   * Produces lemmas of the form (or (>= f 0) (<= f 0)),
-   * where f is a plane that the diophantine solver is interested in.
-   *
-   * More precisely, produces lemmas of the form (or (>= lc -c) (<= lc -c))
-   * where lc is a linear combination of variables, c is a constant, and lc + c
-   * is the plane.
-   */
-  TrustNode dioCutting();
-
-  Comparison mkIntegerEqualityFromAssignment(ArithVar v);
-
-  /**
-   * List of all of the disequalities asserted in the current context that are not known
-   * to be satisfied.
-   */
-  context::CDQueue<ConstraintP> d_diseqQueue;
-
-  /**
-   * Constraints that have yet to be processed by proagation work list.
-   * All of the elements have type of LowerBound, UpperBound, or
-   * Equality.
-   *
-   * This is empty at the beginning of every check call.
-   *
-   * If head()->getType() == LowerBound or UpperBound,
-   * then d_cPL[1] is the previous constraint in d_partialModel for the
-   * corresponding bound.
-   * If head()->getType() == Equality,
-   * then d_cPL[1] is the previous lowerBound in d_partialModel,
-   * and d_cPL[2] is the previous upperBound in d_partialModel.
-   */
-  std::deque<ConstraintP> d_currentPropagationList;
-
-  context::CDQueue<ConstraintP> d_learnedBounds;
-
-  /**
-   * Contains all nodes that have been preregistered
-   */
-  context::CDHashSet<Node> d_preregisteredNodes;
-
-  /**
-   * Manages information about the assignment and upper and lower bounds on
-   * variables.
-   */
-  ArithVariables d_partialModel;
-
-  /** The set of variables in error in the partial model. */
-  ErrorSet d_errorSet;
-
-  /**
-   * The tableau for all of the constraints seen thus far in the system.
-   */
-  Tableau d_tableau;
-
-  /**
-   * Maintains the relationship between the PartialModel and the Tableau.
-   */
-  LinearEqualityModule d_linEq;
-
-  /**
-   * A Diophantine equation solver.  Accesses the tableau and partial
-   * model (each in a read-only fashion).
-   */
-  DioSolver d_diosolver;
-
-  /** Counts the number of notifyRestart() calls to the theory. */
-  uint32_t d_restartsCounter;
-
-  /**
-   * Every number of restarts equal to s_TABLEAU_RESET_PERIOD,
-   * the density of the tableau, d, is computed.
-   * If d >= s_TABLEAU_RESET_DENSITY * d_initialDensity, the tableau
-   * is set to d_initialTableau.
-   */
-  bool d_tableauSizeHasBeenModified;
-  double d_tableauResetDensity;
-  uint32_t d_tableauResetPeriod;
-  static constexpr uint32_t s_TABLEAU_RESET_INCREMENT = 5;
-
-  /** This is only used by simplex at the moment. */
-  context::CDList<std::pair<ConstraintCP, InferenceId>> d_conflicts;
-
-  /** This is only used by simplex at the moment. */
-  context::CDO<Node> d_blackBoxConflict;
-  /** For holding the proof of the above conflict node. */
-  context::CDO<std::shared_ptr<ProofNode>> d_blackBoxConflictPf;
-
-  bool isProofEnabled() const;
-
- public:
-  /**
-   * This adds the constraint a to the queue of conflicts in d_conflicts.
-   * Both a and ~a must have a proof.
-   */
-  void raiseConflict(ConstraintCP a, InferenceId id);
-
-  // inline void raiseConflict(const ConstraintCPVec& cv){
-  //   d_conflicts.push_back(cv);
-  // }
-
-  // void raiseConflict(ConstraintCP a, ConstraintCP b);
-  // void raiseConflict(ConstraintCP a, ConstraintCP b, ConstraintCP c);
-
-  /** This is a conflict that is magically known to hold. */
-  void raiseBlackBoxConflict(Node bb, std::shared_ptr<ProofNode> pf = nullptr);
-  /**
-   * Returns true iff a conflict has been raised. This method is public since
-   * it is needed by the ArithState class to know whether we are in conflict.
-   */
-  bool anyConflict() const;
-
- private:
-  inline bool conflictQueueEmpty() const {
-    return d_conflicts.empty();
-  }
-
-  /**
-   * Outputs the contents of d_conflicts onto d_out.
-   * The conditions of anyConflict() must hold.
-   */
-  void outputConflicts();
-
-  /**
-   * A copy of the tableau.
-   * This is equivalent  to the original tableau if d_tableauSizeHasBeenModified
-   * is false.
-   * The set of basic and non-basic variables may differ from d_tableau.
-   */
-  Tableau d_smallTableauCopy;
-
-  /**
-   * Returns true if all of the basic variables in the simplex queue of
-   * basic variables that violate their bounds in the current tableau
-   * are basic in d_smallTableauCopy.
-   *
-   * d_tableauSizeHasBeenModified must be false when calling this.
-   * Simplex's priority queue must be in collection mode.
-   */
-  bool safeToReset() const;
-
-  /** This keeps track of difference equalities. Mostly for sharing. */
-  ArithCongruenceManager d_congruenceManager;
-  context::CDO<bool> d_cmEnabled;
-
-  /** This implements the Simplex decision procedure. */
-  DualSimplexDecisionProcedure d_dualSimplex;
-  FCSimplexDecisionProcedure d_fcSimplex;
-  SumOfInfeasibilitiesSPD d_soiSimplex;
-  AttemptSolutionSDP d_attemptSolSimplex;
-
-  bool solveRealRelaxation(Theory::Effort effortLevel);
-
-  /* Returns true if this is heuristically a good time to try
-   * to solve the integers.
-   */
-  bool attemptSolveInteger(Theory::Effort effortLevel, bool emmmittedLemmaOrSplit);
-  bool replayLemmas(ApproximateSimplex* approx);
-  void solveInteger(Theory::Effort effortLevel);
-  bool safeToCallApprox() const;
-  SimplexDecisionProcedure& selectSimplex(bool pass1);
-  SimplexDecisionProcedure* d_pass1SDP;
-  SimplexDecisionProcedure* d_otherSDP;
-  /* Sets d_qflraStatus */
-  void importSolution(const ApproximateSimplex::Solution& solution);
-  bool solveRelaxationOrPanic(Theory::Effort effortLevel);
-  context::CDO<int> d_lastContextIntegerAttempted;
-  bool replayLog(ApproximateSimplex* approx);
-
-  class ModelException : public Exception {
-   public:
-    ModelException(TNode n, const char* msg);
-    ~ModelException() override;
-  };
-
-  /**
-   * Computes the delta rational value of a term from the current partial
-   * model. This returns the delta value assignment to the term if it is in the
-   * partial model. Otherwise, this is computed recursively for arithmetic terms
-   * from each subterm.
-   *
-   * This throws a DeltaRationalException if the value cannot be represented as
-   * a DeltaRational. This throws a ModelException if there is a term is not in
-   * the partial model and is not a theory of arithmetic term.
-   *
-   * precondition: The linear abstraction of the nodes must be satisfiable.
-   */
-  DeltaRational getDeltaValue(TNode term) const
-      /* throw(DeltaRationalException, ModelException) */;
- public:
-  TheoryArithPrivate(TheoryArith& containing, Env& env, BranchAndBound& bab);
-  ~TheoryArithPrivate();
-
-  //--------------------------------- initialization
-  /**
-   * Returns true if we need an equality engine, see
-   * Theory::needsEqualityEngine.
-   */
-  bool needsEqualityEngine(EeSetupInfo& esi);
-  /** finish initialize */
-  void finishInit();
-  //--------------------------------- end initialization
-
-  /**
-   * Does non-context dependent setup for a node connected to a theory.
-   */
-  void preRegisterTerm(TNode n);
-
-  void propagate(Theory::Effort e);
-  TrustNode explain(TNode n);
-
-  Rational deltaValueForTotalOrder() const;
-
-  bool collectModelInfo(TheoryModel* m);
-  /**
-   * Collect model values. This is the main method for extracting information
-   * about how to construct the model. This method relies on the caller for
-   * processing the map, which is done so that other modules (e.g. the
-   * non-linear extension) can modify arithModel before it is sent to the model.
-   *
-   * @param termSet The set of relevant terms
-   * @param arithModel Mapping from terms (of real type) to their values. The
-   * caller should assert equalities to the model for each entry in this map.
-   */
-  void collectModelValues(const std::set<Node>& termSet,
-                          std::map<Node, Node>& arithModel);
-
-  void shutdown(){ }
-
-  void presolve();
-  void notifyRestart();
-  Theory::PPAssertStatus ppAssert(TrustNode tin,
-                                  TrustSubstitutionMap& outSubstitutions);
-  void ppStaticLearn(TNode in, NodeBuilder& learned);
-
-  std::string identify() const { return std::string("TheoryArith"); }
-
-  EqualityStatus getEqualityStatus(TNode a, TNode b);
-
-  /** Called when n is notified as being a shared term with TheoryArith. */
-  void notifySharedTerm(TNode n);
-
-  Node getModelValue(TNode var);
-
-
-  std::pair<bool, Node> entailmentCheck(TNode lit, const ArithEntailmentCheckParameters& params, ArithEntailmentCheckSideEffects& out);
-
-  //--------------------------------- standard check
-  /** Pre-check, called before the fact queue of the theory is processed. */
-  bool preCheck(Theory::Effort level);
-  /** Pre-notify fact. */
-  void preNotifyFact(TNode atom, bool pol, TNode fact);
-  /**
-   * Post-check, called after the fact queue of the theory is processed. Returns
-   * true if a conflict or lemma was emitted.
-   */
-  bool postCheck(Theory::Effort level);
-  //--------------------------------- end standard check
-  /**
-   * Found non-linear? This returns true if this solver ever encountered
-   * any non-linear terms that were unhandled. Note that this class is not
-   * responsible for handling non-linear arithmetic. If the owner of this
-   * class does not handle non-linear arithmetic in another way, then
-   * setIncomplete should be called on the output channel of TheoryArith.
-   */
-  bool foundNonlinear() const;
-
-  /** get the proof checker of this theory */
-  ArithProofRuleChecker* getProofChecker();
-
- private:
-  /** The constant zero. */
-  DeltaRational d_DELTA_ZERO;
-
-  /** propagates an arithvar */
-  void propagateArithVar(bool upperbound, ArithVar var );
-
-  /**
-   * Using the simpleKind return the ArithVar associated with the assertion.
-   */
-  ArithVar determineArithVar(const Polynomial& p) const;
-  ArithVar determineArithVar(TNode assertion) const;
-
-  /**
-   * Splits the disequalities in d_diseq that are violated using lemmas on demand.
-   * returns true if any lemmas were issued.
-   * returns false if all disequalities are satisfied in the current model.
-   */
-  bool splitDisequalities();
-
-  /** A Difference variable is known to be 0.*/
-  void zeroDifferenceDetected(ArithVar x);
-
-
-  /**
-   * Looks for the next integer variable without an integer assignment in a
-   * round-robin fashion. Changes the value of d_nextIntegerCheckVar.
-   *
-   * This returns true if all integer variables have integer assignments.
-   * If this returns false, d_nextIntegerCheckVar does not have an integer
-   * assignment.
-   */
-  bool hasIntegerModel();
-
-  /**
-   * Looks for through the variables starting at d_nextIntegerCheckVar
-   * for the first integer variable that is between its upper and lower bounds
-   * that has a non-integer assignment.
-   *
-   * If assumeBounds is true, skip the check that the variable is in bounds.
-   *
-   * If there is no such variable, returns ARITHVAR_SENTINEL;
-   */
-  ArithVar nextIntegerViolation(bool assumeBounds) const;
-
-  /**
-   * Issues branches for non-auxiliary integer variables with non-integer assignments.
-   * Returns a cut for a lemma.
-   * If there is an integer model, this returns Node::null().
-   */
-  TrustNode roundRobinBranch();
-
-  bool proofsEnabled() const { return d_pnm; }
-
- public:
-  /**
-   * This requests a new unique ArithVar value for x.
-   * This also does initial (not context dependent) set up for a variable,
-   * except for setting up the initial.
-   *
-   * If aux is true, this is an auxiliary variable.
-   * If internal is true, x might not be unique up to a constant multiple.
-   */
-  ArithVar requestArithVar(TNode x, bool aux, bool internal);
-
-public:
-  const BoundsInfo& boundsInfo(ArithVar basic) const;
-
-
-private:
-  /** Initial (not context dependent) sets up for a variable.*/
-  void setupBasicValue(ArithVar x);
-
-  /** Initial (not context dependent) sets up for a new auxiliary variable.*/
-  void setupAuxiliary(TNode left);
-
-
-  /**
-   * Assert*(n, orig) takes an bound n that is implied by orig.
-   * and asserts that as a new bound if it is tighter than the current bound
-   * and updates the value of a basic variable if needed.
-   *
-   * orig must be a literal in the SAT solver so that it can be used for
-   * conflict analysis.
-   *
-   * x is the variable getting the new bound,
-   * c is the value of the new bound.
-   *
-   * If this new bound is in conflict with the other bound,
-   * a node describing this conflict is returned.
-   * If this new bound is not in conflict, Node::null() is returned.
-   */
-  bool AssertLower(ConstraintP constraint);
-  bool AssertUpper(ConstraintP constraint);
-  bool AssertEquality(ConstraintP constraint);
-  bool AssertDisequality(ConstraintP constraint);
-
-  /** Tracks the bounds that were updated in the current round. */
-  DenseSet d_updatedBounds;
-
-  /** Tracks the basic variables where propagation might be possible. */
-  DenseSet d_candidateBasics;
-  DenseSet d_candidateRows;
-
-  bool hasAnyUpdates() { return !d_updatedBounds.empty(); }
-  void clearUpdates();
-
-  void revertOutOfConflict();
-
-  void propagateCandidatesNew();
-  void dumpUpdatedBoundsToRows();
-  bool propagateCandidateRow(RowIndex rid);
-  bool propagateMightSucceed(ArithVar v, bool ub) const;
-  /** Attempt to perform a row propagation where there is at most 1 possible variable.*/
-  bool attemptSingleton(RowIndex ridx, bool rowUp);
-  /** Attempt to perform a row propagation where every variable is a potential candidate.*/
-  bool attemptFull(RowIndex ridx, bool rowUp);
-  bool tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, bool vUp, const DeltaRational& bound);
-  bool rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, ConstraintP bestImplied);
-  //void enqueueConstraints(std::vector<ConstraintCP>& out, Node n) const;
-  //ConstraintCPVec resolveOutPropagated(const ConstraintCPVec& v, const std::set<ConstraintCP>& propagated) const;
-  void resolveOutPropagated(std::vector<ConstraintCPVec>& confs, const std::set<ConstraintCP>& propagated) const;
-  void subsumption(std::vector<ConstraintCPVec>& confs) const;
-
-  Node cutToLiteral(ApproximateSimplex*  approx, const CutInfo& cut) const;
-  Node branchToNode(ApproximateSimplex* approx, const NodeLog& cut) const;
-
-  void propagateCandidates();
-  void propagateCandidate(ArithVar basic);
-  bool propagateCandidateBound(ArithVar basic, bool upperBound);
-
-  inline bool propagateCandidateLowerBound(ArithVar basic){
-    return propagateCandidateBound(basic, false);
-  }
-  inline bool propagateCandidateUpperBound(ArithVar basic){
-    return propagateCandidateBound(basic, true);
-  }
-
-  /**
-   * Performs a check to see if it is definitely true that setup can be avoided.
-   */
-  bool canSafelyAvoidEqualitySetup(TNode equality);
-
-  /**
-   * Handles the case splitting for check() for a new assertion.
-   * Returns a conflict if one was found.
-   * Returns Node::null if no conflict was found.
-   *
-   * @param assertion The assertion that was just popped from the fact queue
-   * of TheoryArith and given to this class via preNotifyFact.
-   */
-  ConstraintP constraintFromFactQueue(TNode assertion);
-  bool assertionCases(ConstraintP c);
-
-  /**
-   * Returns the basic variable with the shorted row containing a non-basic variable.
-   * If no such row exists, return ARITHVAR_SENTINEL.
-   */
-  ArithVar findShortestBasicRow(ArithVar variable);
-
-  /**
-   * Debugging only routine!
-   * Returns true iff every variable is consistent in the partial model.
-   */
-  bool entireStateIsConsistent(const std::string& locationHint);
-  bool unenqueuedVariablesAreConsistent();
-
-  bool isImpliedUpperBound(ArithVar var, Node exp);
-  bool isImpliedLowerBound(ArithVar var, Node exp);
-
-  void internalExplain(TNode n, NodeBuilder& explainBuilder);
-
-  void asVectors(const Polynomial& p,
-                 std::vector<Rational>& coeffs,
-                 std::vector<ArithVar>& variables);
-
-  /** Routine for debugging. Print the assertions the theory is aware of. */
-  void debugPrintAssertions(std::ostream& out) const;
-  /** Debugging only routine. Prints the model. */
-  void debugPrintModel(std::ostream& out) const;
-
-  bool done() const { return d_containing.done(); }
-  bool isLeaf(TNode x) const { return d_containing.isLeaf(x); }
-  TheoryId theoryOf(TNode x) const { return d_containing.theoryOf(x); }
-  void debugPrintFacts() const { d_containing.debugPrintFacts(); }
-  bool outputTrustedLemma(TrustNode lem, InferenceId id);
-  bool outputLemma(TNode lem, InferenceId id);
-  void outputTrustedConflict(TrustNode conf, InferenceId id);
-  void outputConflict(TNode lit, InferenceId id);
-  void outputPropagate(TNode lit);
-  void outputRestart();
-
-  inline bool isSatLiteral(TNode l) const {
-    return (d_containing.d_valuation).isSatLiteral(l);
-  }
-  inline Node getSatValue(TNode n) const {
-    return (d_containing.d_valuation).getSatValue(n);
-  }
-
-  /** Used for replaying approximate simplex */
-  context::CDQueue<TrustNode> d_approxCuts;
-  /** Also used for replaying approximate simplex. "approximate cuts temporary storage" */
-  std::vector<TrustNode> d_acTmp;
-
-  /** Counts the number of fullCheck calls to arithmetic. */
-  uint32_t d_fullCheckCounter;
-  std::vector<ArithVar> cutAllBounded() const;
-  TrustNode branchIntegerVariable(ArithVar x) const;
-  void branchVector(const std::vector<ArithVar>& lemmas);
-
-  context::CDO<unsigned> d_cutCount;
-  context::CDHashSet<ArithVar, std::hash<ArithVar>> d_cutInContext;
-
-  context::CDO<bool> d_likelyIntegerInfeasible;
-
-  context::CDO<bool> d_guessedCoeffSet;
-  ArithRatPairVec d_guessedCoeffs;
-
-
-  TreeLog* d_treeLog;
-  TreeLog& getTreeLog();
-
-
-  ArithVarVec d_replayVariables;
-  std::vector<ConstraintP> d_replayConstraints;
-  DenseMap<Rational> d_lhsTmp;
-
-  /* Approximate simpplex solvers are given a copy of their stats */
-  ApproximateStatistics* d_approxStats;
-  ApproximateStatistics& getApproxStats();
-  context::CDO<int32_t> d_attemptSolveIntTurnedOff;
-  void turnOffApproxFor(int32_t rounds);
-  bool getSolveIntegerResource();
-
-  void tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bl);
-  std::vector<ConstraintCPVec> replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth);
-
-  std::pair<ConstraintP, ArithVar> replayGetConstraint(const CutInfo& info);
-  std::pair<ConstraintP, ArithVar> replayGetConstraint(
-      ApproximateSimplex* approx, const NodeLog& nl);
-  std::pair<ConstraintP, ArithVar> replayGetConstraint(const DenseMap<Rational>& lhs, Kind k, const Rational& rhs, bool branch);
-
-  void replayAssert(ConstraintP c);
-
-  static ConstraintCP vectorToIntHoleConflict(const ConstraintCPVec& conflict);
-  static void intHoleConflictToVector(ConstraintCP conflicting, ConstraintCPVec& conflict);
-
-  // Returns true if the node contains a literal
-  // that is an arithmetic literal and is not a sat literal
-  // No caching is done so this should likely only
-  // be called carefully!
-  bool hasFreshArithLiteral(Node n) const;
-
-  int32_t d_dioSolveResources;
-  bool getDioCuttingResource();
-
-  uint32_t d_solveIntMaybeHelp, d_solveIntAttempts;
-
-  RationalVector d_farkasBuffer;
-
-  //---------------- during check
-  /** Whether there were new facts during preCheck */
-  bool d_newFacts;
-  /** The previous status, computed during preCheck */
-  Result::Status d_previousStatus;
-  //---------------- end during check
-
-  /** These fields are designed to be accessible to TheoryArith methods. */
-  class Statistics {
-  public:
-    IntStat d_statAssertUpperConflicts, d_statAssertLowerConflicts;
-
-    IntStat d_statUserVariables, d_statAuxiliaryVariables;
-    IntStat d_statDisequalitySplits;
-    IntStat d_statDisequalityConflicts;
-    TimerStat d_simplifyTimer;
-    TimerStat d_staticLearningTimer;
-
-    TimerStat d_presolveTime;
-
-    TimerStat d_newPropTime;
-
-    IntStat d_externalBranchAndBounds;
-
-    IntStat d_initialTableauSize;
-    IntStat d_currSetToSmaller;
-    IntStat d_smallerSetToCurr;
-    TimerStat d_restartTimer;
-
-    TimerStat d_boundComputationTime;
-    IntStat d_boundComputations, d_boundPropagations;
-
-    IntStat d_unknownChecks;
-    IntStat d_maxUnknownsInARow;
-    AverageStat d_avgUnknownsInARow;
-
-    IntStat d_revertsOnConflicts;
-    IntStat d_commitsOnConflicts;
-    IntStat d_nontrivialSatChecks;
-
-    IntStat d_replayLogRecCount,
-      d_replayLogRecConflictEscalation,
-      d_replayLogRecEarlyExit,
-      d_replayBranchCloseFailures,
-      d_replayLeafCloseFailures,
-      d_replayBranchSkips,
-      d_mirCutsAttempted,
-      d_gmiCutsAttempted,
-      d_branchCutsAttempted,
-      d_cutsReconstructed,
-      d_cutsReconstructionFailed,
-      d_cutsProven,
-      d_cutsProofFailed,
-      d_mipReplayLemmaCalls,
-      d_mipExternalCuts,
-      d_mipExternalBranch;
-
-    IntStat d_inSolveInteger,
-      d_branchesExhausted,
-      d_execExhausted,
-      d_pivotsExhausted,
-      d_panicBranches,
-      d_relaxCalls,
-      d_relaxLinFeas,
-      d_relaxLinFeasFailures,
-      d_relaxLinInfeas,
-      d_relaxLinInfeasFailures,
-      d_relaxLinExhausted,
-      d_relaxOthers;
-
-    IntStat d_applyRowsDeleted;
-    TimerStat d_replaySimplexTimer;
-
-    TimerStat d_replayLogTimer,
-      d_solveIntTimer,
-      d_solveRealRelaxTimer;
-
-    IntStat d_solveIntCalls,
-      d_solveStandardEffort;
-
-    IntStat d_approxDisabled;
-    IntStat d_replayAttemptFailed;
-
-    IntStat d_cutsRejectedDuringReplay;
-    IntStat d_cutsRejectedDuringLemmas;
-
-    HistogramStat<uint32_t> d_satPivots;
-    HistogramStat<uint32_t> d_unsatPivots;
-    HistogramStat<uint32_t> d_unknownPivots;
-
-    IntStat d_solveIntModelsAttempts;
-    IntStat d_solveIntModelsSuccessful;
-    TimerStat d_mipTimer;
-    TimerStat d_lpTimer;
-
-    IntStat d_mipProofsAttempted;
-    IntStat d_mipProofsSuccessful;
-
-    IntStat d_numBranchesFailed;
-
-    Statistics(StatisticsRegistry& reg, const std::string& name);
-  };
-
-
-  Statistics d_statistics;
-}; /* class TheoryArithPrivate */
-
-}  // namespace arith
-}  // namespace theory
-}  // namespace cvc5::internal
index 33b5f08f2fca8f0e88b2b8546baabb23a9e66413..e468ae0019cf8b93a4bef788f2be4f14f1be65c9 100644 (file)
@@ -18,9 +18,9 @@
 #include "expr/node_algorithm.h"
 #include "options/quantifiers_options.h"
 #include "theory/arith/arith_msum.h"
-#include "theory/arith/partial_model.h"
+#include "theory/arith/linear/partial_model.h"
 #include "theory/arith/theory_arith.h"
-#include "theory/arith/theory_arith_private.h"
+#include "theory/arith/linear/theory_arith_private.h"
 #include "theory/quantifiers/term_util.h"
 #include "theory/rewriter.h"
 #include "util/random.h"