/********************************************************************
 * AUTHORS: Vijay Ganesh
 *
 * BEGIN DATE: November, 2005
 *
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
********************************************************************/

#include "stp/Simplifier/BVSolver.h"
#include "stp/AST/AST.h"
#include "stp/STPManager/STPManager.h"

// This file contains the implementation of member functions of
// bvsolver class, which represents the bitvector arithmetic linear
// solver. Please also refer the STP's CAV 2007 paper for the
// complete description of the linear solver algorithm
//
// The bitvector solver is a partial solver, i.e. it does not solve
// for all variables in the system of equations. it is
// best-effort. it relies on the SAT solver to be complete.
//
// The BVSolver assumes that the input equations are normalized, and
// have liketerms combined etc. It won't fail if the input isn't
// normalised. It just won't be able to simplify things, for example
// it cant simplify (3*3)*x = y.
//
// 0. Traverse top-down over the input DAG, looking for a conjunction
// 0. of equations. if you find one, then for each equation in the
// 0. conjunction, do the following steps.
//
// 1. check for Linearity of the input equation
//
// 2. Solve for a "chosen" variable. The variable should occur
// 2. exactly once and must have an odd coeff. Refer STP's CAV 2007
// 2. paper for actual solving procedure
//
// 4. Outside the solver, Substitute and Re-normalize the input DAG
namespace stp
{
const bool flatten_ands = true;
const bool debug_bvsolver = false;

// The simplify functions can increase the size of the DAG,
// so we have the option to disable simplifications.
ASTNode BVSolver::simplifyNode(const ASTNode n)
{
  if (!simplify)
    return n;

  if (n.GetType() == BOOLEAN_TYPE)
    return _simp->SimplifyFormula(n, false, NULL);
  else
    return _simp->SimplifyTerm(n);
}

// check the solver map for 'key'. If key is present, then return the
// value by reference in the argument 'output'
bool BVSolver::CheckAlreadySolvedMap(const ASTNode& key, ASTNode& output)
{
  if (key == ASTTrue || key == ASTFalse)
  {
    output = key;
    return true;
  }

  ASTNodeMap::const_iterator it;
  if ((it = FormulasAlreadySolvedMap.find(key)) !=
      FormulasAlreadySolvedMap.end())
  {
    output = it->second;
    return true;
  }
  return false;
} // CheckAlreadySolvedMap()

void BVSolver::UpdateAlreadySolvedMap(const ASTNode& key, const ASTNode& value)
{
  assert(key.GetType() == BOOLEAN_TYPE);
  FormulasAlreadySolvedMap[key] = value;
}

// accepts an even number "in", and returns the location of
// the lowest bit that is turned on in that number.
void BVSolver::SplitEven_into_Oddnum_PowerOf2(const ASTNode& in,
                                              unsigned int& number_shifts)
{
  assert(BVCONST == in.GetKind() && !_simp->BVConstIsOdd(in));

  // location of the least significant bit turned on.
  for (number_shifts = 0;
       number_shifts < in.GetValueWidth() &&
       !CONSTANTBV::BitVector_bit_test(in.GetBVConst(), number_shifts);
       number_shifts++)
  {
  };
  assert(number_shifts > 0); // shouldn't be odd.
}

// chooses a variable in the lhs and returns the chosen variable
ASTNode BVSolver::ChooseMonom(const ASTNode& eq, ASTNode& modifiedlhs,
                              ASTNodeSet& checked)
{
  assert(EQ == eq.GetKind());
  assert(BVPLUS == eq[0].GetKind() || BVPLUS == eq[1].GetKind());

  // Unfortunately, the bvsolver is written to expect nodes in the
  // reverse order to how the simplifying node factory produces them.
  // That is, the simplifying node factory sorts by arithless, i.e.
  // with constants or symbols on the left.
  const bool lhsIsPlus = (BVPLUS == eq[0].GetKind());
  const ASTNode& lhs = lhsIsPlus ? eq[0] : eq[1];
  const ASTNode& rhs = lhsIsPlus ? eq[1] : eq[0];

  assert(BVPLUS == lhs.GetKind());

  // collect all the vars in the lhs and rhs
  vars.getSymbol(eq);

  // handle BVPLUS case
  ASTVec c = FlattenKind(BVPLUS, lhs.GetChildren(),50);
  ASTVec o;
  ASTNode outmonom = ASTUndefined;
  bool chosen_symbol = false;

  // choose variables with no coeffs
  for (ASTVec::const_iterator it = c.begin(), itend = c.end(); it != itend;
       it++)
  {
    const ASTNode& monom = *it;
    if ((SYMBOL == monom.GetKind() && !chosen_symbol &&
         checked.find(monom) == checked.end() &&
         !vars.VarSeenInTerm(monom, rhs)) ||
        (BVUMINUS == monom.GetKind() && SYMBOL == monom[0].GetKind() &&
         !chosen_symbol && checked.find(monom[0]) == checked.end() &&
         !vars.VarSeenInTerm(monom[0], rhs)))
    {
      // Look through all the children of the BVPLUS and check
      // that the variable appears in none of them.

      ASTNode var = (SYMBOL == monom.GetKind()) ? monom : monom[0];
      bool duplicated = false;
      for (ASTVec::const_iterator it2 = c.begin(); it2 != itend; it2++)
      {
        if (it2 == it)
          continue;
        if (vars.VarSeenInTerm(var, *it2))
        {
          duplicated = true;
          break;
        }
      }
      if (!duplicated)
      {
        outmonom = monom; // nb. monom not var.
        chosen_symbol = true;
        checked.insert(var);
      }
      else
        o.push_back(monom);
    }
    else
      o.push_back(monom);
  }

  // try to choose only odd coeffed variables first
  if (!chosen_symbol)
  {
    ASTNode zero = _bm->CreateZeroConst(32);

    o.clear();
    for (ASTVec::const_iterator it = c.begin(), itend = c.end(); it != itend;
         it++)
    {
      const ASTNode& monom = *it;
      ASTNode var = (BVMULT == monom.GetKind()) ? monom[1] : ASTUndefined;

      if (BVMULT == monom.GetKind() && BVCONST == monom[0].GetKind() &&
          _simp->BVConstIsOdd(monom[0]) && !chosen_symbol &&
          checked.find(var) == checked.end() &&
          ((SYMBOL == var.GetKind() && !vars.VarSeenInTerm(var, rhs)) ||
           (BVEXTRACT == var.GetKind() && SYMBOL == var[0].GetKind() &&
            BVCONST == var[1].GetKind() && zero == var[2] &&
            !vars.VarSeenInTerm(var[0], rhs))))
      {
        // monom[0] is odd.
        outmonom = monom;
        chosen_symbol = true;
        checked.insert(var);
      }
      else if (!chosen_symbol && monom.GetKind() == BVEXTRACT &&
               SYMBOL == monom[0].GetKind() && BVCONST == monom[1].GetKind() &&
               zero == monom[2] && checked.find(monom[0]) == checked.end() &&
               !vars.VarSeenInTerm(monom[0], rhs))
      {
        outmonom = monom;
        chosen_symbol = true;
        checked.insert(monom[0]);
      }
      else if (!chosen_symbol && monom.GetKind() == BVUMINUS &&
               monom[0].GetKind() == BVEXTRACT &&
               SYMBOL == monom[0][0].GetKind() &&
               BVCONST == monom[0][1].GetKind() && zero == monom[0][2] &&
               checked.find(monom[0][0]) == checked.end() &&
               !vars.VarSeenInTerm(monom[0][0], rhs))
      {
        outmonom = monom;
        chosen_symbol = true;
        checked.insert(monom[0][0]);
      }
      else

      {
        o.push_back(monom);
      }
    }
  }

  modifiedlhs =
      (o.size() > 1) ? _bm->CreateTerm(BVPLUS, lhs.GetValueWidth(), o) : o[0];

  if (debug_bvsolver)
  {
    std::cerr << "Initial:" << eq;
    std::cerr << "Chosen Monomial:" << outmonom;
    std::cerr << "Output LHS:" << modifiedlhs;
  }

  // can be SYMBOL or (BVUMINUS SYMBOL) or (BVMULT ODD_BVCONST SYMBOL) or
  // (BVMULT ODD_BVCONST (EXTRACT SYMBOL BV_CONST ZERO)) or
  // BVUMINUS (EXTRACT SYMBOL BV_CONST ZERO) or
  // (EXTRACT SYMBOL BV_CONST ZERO)
  return outmonom;
}

// Manipulate the lhs and rhs to get just a variable on the lhs (if possible).
// Then add to the
// substitution map.
ASTNode BVSolver::substitute(const ASTNode& eq, const ASTNode& lhs,
                             const ASTNode& rhs, const bool single)
{

  ASTNode output;

  switch (lhs.GetKind())
  {
    case SYMBOL:
    {
      // input is of the form x = rhs first make sure that the lhs
      // symbol does not occur on the rhs or that it has not been
      // solved for
      if (!single && vars.VarSeenInTerm(lhs, rhs))
      {
        // found the lhs in the rhs. Abort!
        return eq;
      }

      if (!_simp->UpdateSolverMap(lhs, rhs))
      {
        return eq;
      }

      output = ASTTrue;
      break;
    }

    case BVEXTRACT:
    {
      const ASTNode zero = _bm->CreateZeroConst(32);

      if (!(SYMBOL == lhs[0].GetKind() && BVCONST == lhs[1].GetKind() &&
            zero == lhs[2] && !vars.VarSeenInTerm(lhs[0], rhs)))
      {
        return eq;
      }

      if (vars.VarSeenInTerm(lhs[0], rhs))
      {
        return eq;
      }

      if (!_simp->UpdateSolverMap(lhs, rhs))
      {
        return eq;
      }

      if (lhs[0].GetValueWidth() != lhs.GetValueWidth())
      {
        // if the extract of x[i:0] = t is entered into the solvermap,
        // then also add another entry for x = x1@t
        ASTNode var = lhs[0];
        ASTNode newvar = _bm->CreateFreshVariable(
            0, var.GetValueWidth() - lhs.GetValueWidth(), "v_solver");
        newvar = _bm->CreateTerm(BVCONCAT, var.GetValueWidth(), newvar, rhs);
        assert(BVTypeCheck(newvar));
        _simp->UpdateSolverMap(var, newvar);
      }
      else
        _simp->UpdateSolverMap(lhs[0], rhs);
      output = ASTTrue;
      break;
    }
    case BVMULT:
    {
      // the input is of the form a*x = t. If 'a' is odd, then compute
      // its multiplicative inverse a^-1, multiply 't' with it, and
      // update the solver map
      if (BVCONST != lhs[0].GetKind())
      {
        return eq;
      }

      if (!(SYMBOL == lhs[1].GetKind() ||
            (BVEXTRACT == lhs[1].GetKind() && SYMBOL == lhs[1][0].GetKind())))
      {
        return eq;
      }

      bool ChosenVar_Is_Extract = (BVEXTRACT == lhs[1].GetKind());

      // if coeff is even, then we know that all the coeffs in the eqn
      // are even. Simply return the eqn
      if (!_simp->BVConstIsOdd(lhs[0]))
      {
        return eq;
      }

      ASTNode a = _simp->MultiplicativeInverse(lhs[0]);
      ASTNode chosenvar = ChosenVar_Is_Extract ? lhs[1][0] : lhs[1];
      ASTNode chosenvar_value =
          simplifyNode(_bm->CreateTerm(BVMULT, rhs.GetValueWidth(), a, rhs));

      // if chosenvar is seen in chosenvar_value then abort
      if (vars.VarSeenInTerm(chosenvar, chosenvar_value))
      {
        // abort solving
        return eq;
      }

      // It fails if it's a full-bitwidth extract. These are rare, and won't be
      // present after simplification. So ignore them for now.
      if (ChosenVar_Is_Extract && lhs[0].GetValueWidth() == lhs.GetValueWidth())
        return eq;

      // found a variable to solve
      chosenvar = lhs[1];
      if (!_simp->UpdateSolverMap(chosenvar, chosenvar_value))
      {
        return eq;
      }

      if (ChosenVar_Is_Extract)
      {
        const ASTNode& var = lhs[1][0];

        ASTNode newvar = _bm->CreateFreshVariable(
            0, var.GetValueWidth() - lhs[1].GetValueWidth(), "v_solver");
        newvar = _bm->CreateTerm(BVCONCAT, var.GetValueWidth(), newvar,
                                 chosenvar_value);
        assert(BVTypeCheck(newvar));
        _simp->UpdateSolverMap(var, newvar);
      }
      output = ASTTrue;
      break;
    }
    default:
      output = eq;
      break;
  }
  return output;
}

// solver function which solves for variables with odd coefficient
ASTNode BVSolver::BVSolve_Odd(const ASTNode& input)
{
  ASTNode eq = input;
  //std::cerr << "Input to BVSolve_Odd()" << eq << std::endl;

  if (EQ != eq.GetKind())
  {
    return input;
  }

  ASTNode output = input;

  // get the lhs and the rhs, and case-split on the lhs kind
  ASTNode lhs = eq[0];
  ASTNode rhs = eq[1];

  if (((BVCONST == lhs.GetKind()) &&
       (BVCONST != rhs.GetKind())) || // if only one side is a constant, it
                                      // should be on the RHS.
      ((SYMBOL == rhs.GetKind()) &&
       (SYMBOL != lhs.GetKind())) // If there is only one variable. It should be
                                  // on the left.
      )
  {
    lhs = eq[1];
    rhs = eq[0];
    //std::cerr << "Two sides are:" << lhs << " --- SIDE -- " << rhs << std::endl;
    eq = _bm->CreateNode(EQ, lhs, rhs); // If "return eq" is called, return the
                                        // arguments in the correct order.
    assert(BVTypeCheck(eq));
    //std::cerr << "Flipped equation around, it's now: " << eq << std::endl;
  }

  if (CheckAlreadySolvedMap(eq, output))
  {
    return output;
  }

  // ChooseMonom makes sure that the the LHS is not contained on the RHS, so we
  // set this "single" to true in the branch that runs chooseMonomial.
  bool single = false;

  if (BVPLUS == lhs.GetKind())
  {

    ASTNodeSet checked;
    do
    {
      ASTNode chosen_monom = ASTUndefined;
      ASTNode leftover_lhs;

      // choose monom makes sure that it gets only those vars that
      // occur exactly once in lhs and rhs put together
      chosen_monom = ChooseMonom(eq, leftover_lhs, checked);
      if (chosen_monom == ASTUndefined)
      {
        // no monomial was chosen
        return eq;
      }

      // if control is here then it means that a monom was chosen
      //
      // construct:  rhs - (lhs without the chosen monom)
      unsigned int len = lhs.GetValueWidth();
      leftover_lhs = simplifyNode(_bm->CreateTerm(BVUMINUS, len, leftover_lhs));
      assert(BVTypeCheck(leftover_lhs));
      rhs = simplifyNode(_bm->CreateTerm(BVPLUS, len, rhs, leftover_lhs));
      assert(BVTypeCheck(rhs));
      lhs = chosen_monom;
      single = true;
      // This tries to substitute it.. But it might not work, in which case it
      // will return eq.
      output = substitute(eq, lhs, rhs, single);
    } while (output == eq);
  }

  else if (BVUMINUS == lhs.GetKind())
  {
    // equation is of the form (-lhs0) = rhs
    ASTNode lhs0 = lhs[0];
    rhs = simplifyNode(_bm->CreateTerm(BVUMINUS, rhs.GetValueWidth(), rhs));
    assert(BVTypeCheck(rhs));
    lhs = lhs0;
    output = substitute(eq, lhs, rhs, single);
  }
  else
    output = substitute(eq, lhs, rhs, single);

  UpdateAlreadySolvedMap(input, output);
  return output;
}

// The toplevel bvsolver(). Checks if the formula has already been
// solved. If not, the solver() is invoked. If yes, then simply drop
// the formula
ASTNode BVSolver::TopLevelBVSolve(const ASTNode& _input,
                                  const bool enable_simplify)
{
  assert(_bm->UserFlags.wordlevel_solve_flag);
  ASTNode input = _input;
  simplify = enable_simplify;

  ASTNode output = input;
  if (CheckAlreadySolvedMap(input, output))
  {
    // output is TRUE. The formula is thus dropped
    return output;
  }

  Kind k = input.GetKind();

  if (!(EQ == k || AND == k))
  {
    return input;
  }

  if (flatten_ands && AND == k)
  {
    ASTVec c = FlattenKind(AND, input.GetChildren());
    input = _bm->CreateNode(AND, c);
    k = input.GetKind();

    // When flattening simplifications will be applied to the node, potentially
    // changing it's type:
    // (AND x (ANY (not x) y)) gives us FALSE.
    if (!(EQ == k || AND == k))
      return input;

    if (CheckAlreadySolvedMap(input, output))
    {
      // output is TRUE. The formula is thus dropped
      return output;
    }
  }

  _bm->GetRunTimes()->start(RunTimes::BVSolver);
  ASTVec o;
  ASTVec c;
  if (EQ == k)
    c.push_back(input);
  else
    c = input.GetChildren();

  ASTVec eveneqns;
  bool any_solved = false;
  ASTNodeMap cache;
  for (ASTVec::iterator it = c.begin(), itend = c.end(); it != itend; it++)
  {
    /*
      Calling applySubstitutionMapUntilArrays makes the required substitutions.
      For instance, if
      first was : v = x,
      then if the next formula is: x = v
      calling applySubstitutionMapUntilArrays on the second formula will convert
      it into "true", avoiding a cycle.

      The problem with this is that applySubstitutionMapUntilArrays() doesn't
      normally simplify into array
      operations. So given something like :
      a = b
      b = write(A,a,a)

      It will replace (a=b) with true, and store (a=b) in the solverMap. Then it
      will
      store b = write(A,a,a) in the solver map. Which is wrong. What it should
      do is
      rewrite a=b through the second expression, giving:
      b = write(A,b,b),
      which shouldn't be simplified.
    */

    ASTNode aaa =
        (any_solved && EQ == it->GetKind())
            ? simplifyNode(_simp->applySubstitutionMapUntilArrays(*it,cache))
            : *it;

    if (ASTFalse == aaa)
    {
      _bm->GetRunTimes()->stop(RunTimes::BVSolver);
      return ASTFalse; // shortcut. It's unsatisfiable.
    }
    aaa = BVSolve_Odd(aaa);

    bool even = false;
    aaa = CheckEvenEqn(aaa, even);
    if (even)
    {
      eveneqns.push_back(aaa);
    }
    else
    {
      if (ASTTrue != aaa)
      {
        o.push_back(aaa);
      }
    }
    if (ASTTrue == aaa)
    {
      any_solved = true;
      cache.clear();
    }
  }

  ASTNode evens;
  if (eveneqns.size() > 0)
  {
    // if there is a system of even equations then solve them
    evens =
        (eveneqns.size() > 1) ? _bm->CreateNode(AND, eveneqns) : eveneqns[0];
    // evens = _simp->SimplifyFormula(evens,false);
    evens = BVSolve_Even(evens);
    _bm->ASTNodeStats("Printing after evensolver:", evens);
  }
  else
  {
    evens = ASTTrue;
  }
  output = (o.size() > 0) ? ((o.size() > 1) ? nf->CreateNode(AND, o) : o[0])
                          : ASTTrue;
  if (evens != ASTTrue)
    output = nf->CreateNode(AND, output, evens);

  _bm->GetRunTimes()->stop(RunTimes::BVSolver);

  // Imagine in the last conjunct A is replaced by B. But there could
  // be variable A's in the first conjunct. This gets rid of 'em.
  if (_simp->hasUnappliedSubstitutions())
  {
    output = _simp->applySubstitutionMap(output);
    _simp->haveAppliedSubstitutionMap();
  }

  UpdateAlreadySolvedMap(_input, output);
  return output;
}

ASTNode BVSolver::CheckEvenEqn(const ASTNode& input, bool& evenflag)
{
  ASTNode eq = input;

  //std::cerr << "Input to BVSolve_Odd()" << eq << std::endl;
  if (EQ != eq.GetKind())
  {
    evenflag = false;
    return eq;
  }

  const ASTNode zero = _bm->CreateZeroConst(eq[0].GetValueWidth());

  // lhs must be a BVPLUS, and rhs must be a BVCONST
  const bool lhsIsPlus = (BVPLUS == eq[0].GetKind());
  ASTNode lhs = lhsIsPlus ? eq[0] : eq[1];
  ASTNode rhs = lhsIsPlus ? eq[1] : eq[0];
  //std::cerr << "LHS:" << lhs << " RHS:" << rhs << std::endl;

  if (BVPLUS != lhs.GetKind() || zero != rhs)
  {
    evenflag = false;
    return eq;
  }
  //Now, LHS is BVPLUS and RHS is ZERO

  const ASTVec& lhs_c = lhs.GetChildren();
  ASTNode savetheconst = rhs;
  for (ASTVec::const_iterator it = lhs_c.begin(), itend = lhs_c.end();
       it != itend; it++)
  {
    const ASTNode aaa = *it;
    const Kind itk = aaa.GetKind();
    //std::cout << "AT node:" << aaa << " (kind: " << itk << " )" << std::endl;

    if (BVCONST == itk)
    {
      // Can't decide if there are more than 1 constants
      // TODO fix logic so we can decide
      if (savetheconst != rhs)
      {
        evenflag = false;
        return eq;
      }

      // check later if the constant is even or not
      savetheconst = aaa;
      continue;
    }

    if (!(BVMULT == itk && BVCONST == aaa[0].GetKind() &&
          SYMBOL == aaa[1].GetKind() && !_simp->BVConstIsOdd(aaa[0])))
    {
      // If the monomials of the lhs are NOT of the form 'a*x' where
      //'a' is even, then return the false
      evenflag = false;
      return eq;
    }
  }

  // if control is here then it means that all coeffs are even. the
  // only remaining thing is to check if the constant is even or not
  if (_simp->BVConstIsOdd(savetheconst))
  {
    // the constant turned out to be odd. we have UNSAT eqn
    evenflag = false;
    return ASTFalse;
  }

  // all is clear. the eqn in even, through and through
  evenflag = true;
  return eq;
}

// solve an eqn whose monomials have only even coefficients
ASTNode BVSolver::BVSolve_Even(const ASTNode& input)
{

  if (!(EQ == input.GetKind() || AND == input.GetKind()))
  {
    return input;
  }

  ASTNode output;
  if (CheckAlreadySolvedMap(input, output))
  {
    return output;
  }

  ASTVec input_c;
  if (EQ == input.GetKind())
  {
    input_c.push_back(input);
  }
  else
  {
    input_c = input.GetChildren();
  }

  // power_of_2 holds the exponent of 2 in the coeff
  unsigned int power_of_2 = 0;
  // we need this additional variable to find the lowest power of 2
  unsigned int power_of_2_lowest = ~0;
  // the monom which has the least power of 2 in the coeff
  // ASTNode monom_with_best_coeff;
  for (ASTVec::iterator jt = input_c.begin(), jtend = input_c.end();
       jt != jtend; jt++)
  {
    ASTNode eq = *jt;
    assert(EQ == eq.GetKind());

    const bool lhsIsPlus = (BVPLUS == eq[0].GetKind());
    ASTNode lhs = lhsIsPlus ? eq[0] : eq[1];
    ASTNode rhs = lhsIsPlus ? eq[1] : eq[0];

    ASTNode zero = _bm->CreateZeroConst(rhs.GetValueWidth());
    // lhs must be a BVPLUS, and rhs must be a BVCONST
    if (!(BVPLUS == lhs.GetKind() && zero == rhs))
    {
      return input;
    }

    const ASTVec& lhs_c = lhs.GetChildren();
    for (ASTVec::const_iterator it = lhs_c.begin(), itend = lhs_c.end();
         it != itend; it++)
    {
      const ASTNode aaa = *it;
      const Kind itk = aaa.GetKind();
      if (!(BVCONST == itk && !_simp->BVConstIsOdd(aaa)) &&
          !(BVMULT == itk && BVCONST == aaa[0].GetKind() &&
            SYMBOL == aaa[1].GetKind() && !_simp->BVConstIsOdd(aaa[0])))
      {
        // If the monomials of the lhs are NOT of the form 'a*x' or 'a'
        // where 'a' is even, then return the eqn
        return input;
      }

      // we are gauranteed that if control is here then the monomial is
      // of the form 'a*x' or 'a', where 'a' is even
      ASTNode coeff = (BVCONST == itk) ? aaa : aaa[0];
      SplitEven_into_Oddnum_PowerOf2(coeff, power_of_2);
      if (power_of_2 < power_of_2_lowest)
      {
        power_of_2_lowest = power_of_2;
        // monom_with_best_coeff = aaa;
      }
      power_of_2 = 0;
    }
  }

  // get the exponent
  power_of_2 = power_of_2_lowest;
  assert(power_of_2 > 0);

  // if control is here, we are gauranteed that we have chosen a
  // monomial with fewest powers of 2
  ASTVec formula_out;
  for (ASTVec::iterator jt = input_c.begin(), jtend = input_c.end();
       jt != jtend; jt++)
  {
    ASTNode eq = *jt;

    // Want the plus on the lhs.
    const bool lhsIsPlus = (BVPLUS == eq[0].GetKind());
    ASTNode lhs = lhsIsPlus ? eq[0] : eq[1];
    ASTNode rhs = lhsIsPlus ? eq[1] : eq[0];

    ASTNode zero = _bm->CreateZeroConst(rhs.GetValueWidth());
    // lhs must be a BVPLUS, and rhs must be a BVCONST
    if (!(BVPLUS == lhs.GetKind() && zero == rhs))
    {
      return input;
    }

    unsigned len = lhs.GetValueWidth();
    ASTNode hi = _bm->CreateBVConst(32, len - 1);
    ASTNode low = _bm->CreateBVConst(32, len - power_of_2);
    ASTNode low_minus_one = _bm->CreateBVConst(32, len - power_of_2 - 1);
    ASTNode low_zero = _bm->CreateZeroConst(32);
    unsigned newlen = len - power_of_2;
    ASTNode two_const = _bm->CreateTwoConst(len);

    unsigned count = power_of_2;
    ASTNode two = two_const;
    while (--count)
    {
      two =
          _simp->BVConstEvaluator(_bm->CreateTerm(BVMULT, len, two_const, two));
    }
    const ASTVec& lhs_c = lhs.GetChildren();
    ASTVec lhs_out;
    for (ASTVec::const_iterator it = lhs_c.begin(), itend = lhs_c.end();
         it != itend; it++)
    {
      ASTNode aaa = *it;
      const Kind itk = aaa.GetKind();
      if (BVCONST == itk)
      {
        aaa = _simp->BVConstEvaluator(_bm->CreateTerm(BVDIV, len, aaa, two));
        aaa = _simp->BVConstEvaluator(
            _bm->CreateTerm(BVEXTRACT, newlen, aaa, low_minus_one, low_zero));
      }
      else
      {
        // it must be of the form a*x
        ASTNode coeff =
            _simp->BVConstEvaluator(_bm->CreateTerm(BVDIV, len, aaa[0], two));
        coeff = _simp->BVConstEvaluator(
            _bm->CreateTerm(BVEXTRACT, newlen, coeff, low_minus_one, low_zero));
        ASTNode lower_x = simplifyNode(_bm->CreateTerm(
            BVEXTRACT, newlen, aaa[1], low_minus_one, low_zero));
        aaa = _bm->CreateTerm(BVMULT, newlen, coeff, lower_x);
      }
      lhs_out.push_back(aaa);
    }
    rhs = _bm->CreateZeroConst(newlen);
    lhs = _bm->CreateTerm(BVPLUS, newlen, lhs_out);
    formula_out.push_back(_simp->CreateSimplifiedEQ(lhs, rhs));
  }

  output = (formula_out.size() > 0)
               ? (formula_out.size() > 1) ? _bm->CreateNode(AND, formula_out)
                                          : formula_out[0]
               : ASTTrue;

  UpdateAlreadySolvedMap(input, output);
  return output;
}

} // end of namespace stp
