Exemplo n.º 1
0
SequenceType::Ptr SubsequenceFN::staticType() const
{
    const SequenceType::Ptr opType(m_operands.first()->staticType());
    const Cardinality opCard(opType->cardinality());

    /* Optimization: we can do much stronger inference here. If the length is a
     * constant, we can constrain the range at least upwards of the
     * cardinality, for instance. */

    /* The subsequence(expr, 1, 1), add empty-sequence() to the static type.
     *
     * Note that we cannot do all these inferences before we've typechecked our
     * operands. The only known case of where our staticType() is called before
     * typeCheck() is through xmlpatternsview, although it wouldn't be
     * surprising if the more exotic paths can achieve that too.
     */
    if(m_hasTypeChecked &&
       m_operands.at(1)->isEvaluated()                                                      &&
       m_operands.count() == 3                                                              &&
       m_operands.at(2)->isEvaluated()                                                      &&
       m_operands.at(1)->as<Literal>()->item().as<Numeric>()->round()->toInteger() == 1     &&
       m_operands.at(2)->as<Literal>()->item().as<Numeric>()->round()->toInteger() == 1)
    {
        return makeGenericSequenceType(opType->itemType(),
                                       opCard.toWithoutMany());
    }
    else
    {
        return makeGenericSequenceType(opType->itemType(),
                                       opCard | Cardinality::zeroOrOne());
    }
}
Exemplo n.º 2
0
SequenceType::Ptr RemoveFN::staticType() const
{
    const SequenceType::Ptr opType(m_operands.first()->staticType());
    const Cardinality c(opType->cardinality());

    if(c.minimum() == 0)
        return makeGenericSequenceType(opType->itemType(), c);
    else
    {
        return makeGenericSequenceType(opType->itemType(),
                                       Cardinality::fromRange(c.minimum() - 1,
                                                              c.maximum()));
    }
}
Exemplo n.º 3
0
Expression::Ptr CountFN::compress(const StaticContext::Ptr &context)
{
    const Expression::Ptr me(FunctionCall::compress(context));
    if(me != this)
        return me;

    const Cardinality card(m_operands.first()->staticType()->cardinality());
    if(card.isExactlyOne())
        return wrapLiteral(CommonValues::IntegerOne, context, this);
    else if(card.isEmpty())
    {
        /* One might think that if the operand is (), that compress() would have
         * evaluated us and therefore this line never be reached, but "()" can
         * be combined with the DisableElimination flag. */
        return wrapLiteral(CommonValues::IntegerZero, context, this);
    }
    else if(card.isExact())
        return wrapLiteral(Integer::fromValue(card.minimum()), context, this);
    else
        return me;
}
Exemplo n.º 4
0
Expression::Ptr ForClause::typeCheck(const StaticContext::Ptr &context,
                                     const SequenceType::Ptr &reqType)
{
    const Expression::Ptr me(PairContainer::typeCheck(context, reqType));
    const Cardinality card(m_operand1->staticType()->cardinality());

    /* If our source is empty we will always evaluate to the empty sequence, so rewrite. */
    if(card.isEmpty())
        return EmptySequence::create(this, context);
    else
        return me;

    /* This breaks because  the variable references haven't rewritten themselves, so
     * they dangle. When this is fixed, evaluateSingleton can be removed. */
    /*
    else if(card->allowsMany())
        return me;
    else
        return m_operand2;
        */
}
Exemplo n.º 5
0
SequenceType::Ptr FunctionCall::staticType() const
{
    Q_ASSERT(m_signature);
    if(has(EmptynessFollowsChild))
    {
        if(m_operands.isEmpty())
        {
            /* This is a function which uses the context item when having no arguments. */
            return signature()->returnType();
        }
        const Cardinality card(m_operands.first()->staticType()->cardinality());
        if(card.allowsEmpty())
            return signature()->returnType();
        else
        {
            /* Remove empty. */
            return makeGenericSequenceType(signature()->returnType()->itemType(),
                                           card & Cardinality::oneOrMore());
        }
    }
    return signature()->returnType();
}
Expression::Ptr CardinalityVerifier::verifyCardinality(const Expression::Ptr &operand,
                                                       const Cardinality &requiredCard,
                                                       const StaticContext::Ptr &context,
                                                       const ReportContext::ErrorCode code)
{
    const Cardinality opCard(operand->staticType()->cardinality());

    if(requiredCard.isMatch(opCard))
        return operand;
    else if(requiredCard.canMatch(opCard))
        return Expression::Ptr(new CardinalityVerifier(operand, requiredCard, code));
    else if(context->compatModeEnabled() &&
            !opCard.isEmpty())
    {
        return GenericPredicate::createFirstItem(operand);
    }
    else
    {
        /* Sequences within this cardinality can never match. */
        context->error(wrongCardinality(requiredCard, opCard), code, operand.data());
        return operand;
    }
}
Exemplo n.º 7
0
Cardinality::CardinalityComparison Cardinality::compare(const Cardinality& c) const throw() {
  if(isUnknown() || c.isUnknown()) {
    return UNKNOWN;
  } else if(isLargeFinite()) {
    if(c.isLargeFinite()) {
      return UNKNOWN;
    } else if(c.isFinite()) {
        return GREATER;
    } else {
      Assert(c.isInfinite());
      return LESS;
    }
  } else if(c.isLargeFinite()) {
    if(isLargeFinite()) {
      return UNKNOWN;
    } else if(isFinite()) {
      return LESS;
    } else {
      Assert(isInfinite());
      return GREATER;
    }
  } else if(isInfinite()) {
    if(c.isFinite()) {
      return GREATER;
    } else {
      return d_card < c.d_card ? GREATER :
               (d_card == c.d_card ? EQUAL : LESS);
    }
  } else if(c.isInfinite()) {
    Assert(isFinite());
    return LESS;
  } else {
    Assert(isFinite() && !isLargeFinite());
    Assert(c.isFinite() && !c.isLargeFinite());
    return d_card < c.d_card ? LESS :
             (d_card == c.d_card ? EQUAL : GREATER);
  }

  Unreachable();
}
Exemplo n.º 8
0
void SharedTermsVisitor::visit(TNode current, TNode parent) {

  Debug("register") << "SharedTermsVisitor::visit(" << current << "," << parent << ")" << std::endl;
  if (Debug.isOn("register::internal")) {
    Debug("register::internal") << toString() << std::endl;
  }

  // Get the theories of the terms
  TheoryId currentTheoryId = Theory::theoryOf(current);
  TheoryId parentTheoryId  = Theory::theoryOf(parent);

#if 0
  bool useType = current != parent && currentTheoryId != parentTheoryId;
#else
  // Should we use the theory of the type
  bool useType = false;
  TheoryId typeTheoryId = THEORY_LAST;

  if (current != parent) {
    if (currentTheoryId != parentTheoryId) {
      // If enclosed by different theories it's shared -- in read(a, f(a)) f(a) should be shared with integers
      TypeNode type = current.getType();
      useType = true;
      typeTheoryId = Theory::theoryOf(type);
    } else {
      TypeNode type = current.getType();
      typeTheoryId = Theory::theoryOf(type);
      if (typeTheoryId != currentTheoryId) {
        if (options::finiteModelFind() && type.isSort()) {
          // We're looking for finite models
          useType = true;
        } else {
          Cardinality card = type.getCardinality();
          if (card.isFinite()) {
            useType = true;
          }
        }
      }
    }
  }
#endif

  Theory::Set visitedTheories = d_visited[current];
  Debug("register::internal") << "SharedTermsVisitor::visit(" << current << "," << parent << "): previously registered with " << Theory::setToString(visitedTheories) << std::endl;
  if (!Theory::setContains(currentTheoryId, visitedTheories)) {
    visitedTheories = Theory::setInsert(currentTheoryId, visitedTheories);
    Debug("register::internal") << "SharedTermsVisitor::visit(" << current << "," << parent << "): adding " << currentTheoryId << std::endl;
  }
  if (!Theory::setContains(parentTheoryId, visitedTheories)) {
    visitedTheories = Theory::setInsert(parentTheoryId, visitedTheories);
    Debug("register::internal") << "SharedTermsVisitor::visit(" << current << "," << parent << "): adding " << parentTheoryId << std::endl;
  }
  if (useType) {
    //////TheoryId typeTheoryId = Theory::theoryOf(current.getType());
    if (!Theory::setContains(typeTheoryId, visitedTheories)) {
      visitedTheories = Theory::setInsert(typeTheoryId, visitedTheories);
      Debug("register::internal") << "SharedTermsVisitor::visit(" << current << "," << parent << "): adding " << typeTheoryId << std::endl;
    }
  }
  Debug("register::internal") << "SharedTermsVisitor::visit(" << current << "," << parent << "): now registered with " << Theory::setToString(visitedTheories) << std::endl;

  // Record the new theories that we visited
  d_visited[current] = visitedTheories;

  // If there is more than two theories and a new one has been added notify the shared terms database
  if (Theory::setDifference(visitedTheories, Theory::setInsert(currentTheoryId))) {
    d_sharedTerms.addSharedTerm(d_atom, current, visitedTheories);
  }

  Assert(d_visited.find(current) != d_visited.end());
  Assert(alreadyVisited(current, parent));
}
Exemplo n.º 9
0
bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const {

  Debug("register::internal") << "SharedTermsVisitor::alreadyVisited(" << current << "," << parent << ")" << std::endl;

  if( ( parent.getKind() == kind::FORALL ||
        parent.getKind() == kind::EXISTS ||
        parent.getKind() == kind::REWRITE_RULE /*||
        parent.getKind() == kind::CARDINALITY_CONSTRAINT*/  ) &&
      current != parent ) {
    Debug("register::internal") << "quantifier:true" << std::endl;
    return true;
  }
  TNodeVisitedMap::const_iterator find = d_visited.find(current);

  // If node is not visited at all, just return false
  if (find == d_visited.end()) {
    Debug("register::internal") << "1:false" << std::endl;
    return false;
  }

  Theory::Set theories = (*find).second;

  TheoryId currentTheoryId = Theory::theoryOf(current);
  TheoryId parentTheoryId  = Theory::theoryOf(parent);

  // Should we use the theory of the type
#if 0
  bool useType = current != parent && currentTheoryId != parentTheoryId;
#else
  bool useType = false;
  TheoryId typeTheoryId = THEORY_LAST;

  if (current != parent) {
    if (currentTheoryId != parentTheoryId) {
      // If enclosed by different theories it's shared -- in read(a, f(a)) f(a) should be shared with integers
      TypeNode type = current.getType();
      useType = true;
      typeTheoryId = Theory::theoryOf(type);
    } else {
      TypeNode type = current.getType();
      typeTheoryId = Theory::theoryOf(type);
      if (typeTheoryId != currentTheoryId) {
        if (options::finiteModelFind() && type.isSort()) {
          // We're looking for finite models
          useType = true;
        } else {
          Cardinality card = type.getCardinality();
          if (card.isFinite()) {
            useType = true;
          }
        }
      }
    }
  }
#endif

  if (Theory::setContains(currentTheoryId, theories)) {
      if (Theory::setContains(parentTheoryId, theories)) {
        if (useType) {
          ////TheoryId typeTheoryId = Theory::theoryOf(current.getType());
          return Theory::setContains(typeTheoryId, theories);
        } else {
          return true;
        }
      } else {
        return false;
      }
  } else {
    return false;
  }
}
Exemplo n.º 10
0
void UnconstrainedSimplifier::processUnconstrained()
{
  TNodeSet::iterator it = d_unconstrained.begin(), iend = d_unconstrained.end();
  vector<TNode> workList;
  for ( ; it != iend; ++it) {
    workList.push_back(*it);
  }
  Node currentSub;
  TNode parent;
  bool swap;
  bool isSigned;
  bool strict;
  vector<TNode> delayQueueLeft;
  vector<Node> delayQueueRight;

  TNode current = workList.back();
  workList.pop_back();
  for (;;) {
    Assert(d_visitedOnce.find(current) != d_visitedOnce.end());
    parent = d_visitedOnce[current];
    if (!parent.isNull()) {
      swap = isSigned = strict = false;
      switch (parent.getKind()) {

        // If-then-else operator - any two unconstrained children makes the parent unconstrained
        case kind::ITE: {
          Assert(parent[0] == current || parent[1] == current || parent[2] == current);
          bool uCond = parent[0] == current || d_unconstrained.find(parent[0]) != d_unconstrained.end();
          bool uThen = parent[1] == current || d_unconstrained.find(parent[1]) != d_unconstrained.end();
          bool uElse = parent[2] == current || d_unconstrained.find(parent[2]) != d_unconstrained.end();
          if ((uCond && uThen) || (uCond && uElse) || (uThen && uElse)) {
            if (d_unconstrained.find(parent) == d_unconstrained.end() &&
                !d_substitutions.hasSubstitution(parent)) {
              ++d_numUnconstrainedElim;
              if (uThen) {
                if (parent[1] != current) {
                  if (parent[1].isVar()) {
                    currentSub = parent[1];
                  }
                  else {
                    Assert(d_substitutions.hasSubstitution(parent[1]));
                    currentSub = d_substitutions.apply(parent[1]);
                  }
                }
                else if (currentSub.isNull()) {
                  currentSub = current;
                }
              }
              else if (parent[2] != current) {
                if (parent[2].isVar()) {
                  currentSub = parent[2];
                }
                else {
                  Assert(d_substitutions.hasSubstitution(parent[2]));
                  currentSub = d_substitutions.apply(parent[2]);
                }
              }
              else if (currentSub.isNull()) {
                currentSub = current;
              }
              current = parent;
            }
            else {
              currentSub = Node();
            }
          }
          else if (uCond) {
            Cardinality card = parent.getType().getCardinality();
            if (card.isFinite() && !card.isLargeFinite() && card.getFiniteCardinality() == 2) {
              // Special case: condition is unconstrained, then and else are different, and total cardinality of the type is 2, then the result
              // is unconstrained
              Node test;
              if (parent.getType().isBoolean()) {
                test = Rewriter::rewrite(parent[1].iffNode(parent[2]));
              }
              else {
                test = Rewriter::rewrite(parent[1].eqNode(parent[2]));
              }
              if (test == NodeManager::currentNM()->mkConst<bool>(false)) {
                ++d_numUnconstrainedElim;
                if (currentSub.isNull()) {
                  currentSub = current;
                }
                currentSub = newUnconstrainedVar(parent.getType(), currentSub);
                current = parent;
              }
            }
          }
          break;
        }

        // Comparisons that return a different type - assuming domains are larger than 1, any
        // unconstrained child makes parent unconstrained as well
        case kind::EQUAL:
          if (parent[0].getType() != parent[1].getType()) {
            TNode other = (parent[0] == current) ? parent[1] : parent[0];
            if (current.getType().isSubtypeOf(other.getType())) {
              break;
            }
          }
          if( parent[0].getType().isDatatype() ){
            TypeNode tn = parent[0].getType();
            const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
            if( dt.isRecursiveSingleton( tn.toType() ) ){
              //domain size may be 1
              break;
            }
          }
        case kind::BITVECTOR_COMP:
        case kind::LT:
        case kind::LEQ:
        case kind::GT:
        case kind::GEQ:
        {
          if (d_unconstrained.find(parent) == d_unconstrained.end() &&
              !d_substitutions.hasSubstitution(parent)) {
            ++d_numUnconstrainedElim;
            Assert(parent[0] != parent[1] &&
                   (parent[0] == current || parent[1] == current));
            if (currentSub.isNull()) {
              currentSub = current;
            }
            currentSub = newUnconstrainedVar(parent.getType(), currentSub);
            current = parent;
          }
          else {
            currentSub = Node();
          }
          break;
        }

        // Unary operators that propagate unconstrainedness
        case kind::NOT:
        case kind::BITVECTOR_NOT:
        case kind::BITVECTOR_NEG:
        case kind::UMINUS:
          ++d_numUnconstrainedElim;
          Assert(parent[0] == current);
          if (currentSub.isNull()) {
            currentSub = current;
          }
          current = parent;
          break;

        // Unary operators that propagate unconstrainedness and return a different type
        case kind::BITVECTOR_EXTRACT:
          ++d_numUnconstrainedElim;
          Assert(parent[0] == current);
          if (currentSub.isNull()) {
            currentSub = current;
          }
          currentSub = newUnconstrainedVar(parent.getType(), currentSub);
          current = parent;
          break;

        // Operators returning same type requiring all children to be unconstrained
        case kind::AND:
        case kind::OR:
        case kind::IMPLIES:
        case kind::BITVECTOR_AND:
        case kind::BITVECTOR_OR:
        case kind::BITVECTOR_NAND:
        case kind::BITVECTOR_NOR:
        {
          bool allUnconstrained = true;
          for(TNode::iterator child_it = parent.begin(); child_it != parent.end(); ++child_it) {
            if (d_unconstrained.find(*child_it) == d_unconstrained.end()) {
              allUnconstrained = false;
              break;
            }
          }
          if (allUnconstrained) {
            if (d_unconstrained.find(parent) == d_unconstrained.end() &&
                !d_substitutions.hasSubstitution(parent)) {
              ++d_numUnconstrainedElim;
              if (currentSub.isNull()) {
                currentSub = current;
              }
              current = parent;
            }
            else {
              currentSub = Node();
            }
          }
        }
        break;

        // Require all children to be unconstrained and different
        case kind::BITVECTOR_SHL:
        case kind::BITVECTOR_LSHR:
        case kind::BITVECTOR_ASHR:
        case kind::BITVECTOR_UDIV_TOTAL:
        case kind::BITVECTOR_UREM_TOTAL:
        case kind::BITVECTOR_SDIV:
        case kind::BITVECTOR_SREM:
        case kind::BITVECTOR_SMOD: {
          bool allUnconstrained = true;
          bool allDifferent = true;
          for(TNode::iterator child_it = parent.begin(); child_it != parent.end(); ++child_it) {
            if (d_unconstrained.find(*child_it) == d_unconstrained.end()) {
              allUnconstrained = false;
              break;
            }
            for(TNode::iterator child_it2 = child_it + 1; child_it2 != parent.end(); ++child_it2) {
              if (*child_it == *child_it2) {
                allDifferent = false;
                break;
              }
            }
          }
          if (allUnconstrained && allDifferent) {
            if (d_unconstrained.find(parent) == d_unconstrained.end() &&
                !d_substitutions.hasSubstitution(parent)) {
              ++d_numUnconstrainedElim;
              if (currentSub.isNull()) {
                currentSub = current;
              }
              current = parent;
            }
            else {
              currentSub = Node();
            }
          }
          break;
        }

        // Requires all children to be unconstrained and different, and returns a different type
        case kind::BITVECTOR_CONCAT:
        {
          bool allUnconstrained = true;
          bool allDifferent = true;
          for(TNode::iterator child_it = parent.begin(); child_it != parent.end(); ++child_it) {
            if (d_unconstrained.find(*child_it) == d_unconstrained.end()) {
              allUnconstrained = false;
              break;
            }
            for(TNode::iterator child_it2 = child_it + 1; child_it2 != parent.end(); ++child_it2) {
              if (*child_it == *child_it2) {
                allDifferent = false;
                break;
              }
            }
          }
          if (allUnconstrained && allDifferent) {
            if (d_unconstrained.find(parent) == d_unconstrained.end() &&
                !d_substitutions.hasSubstitution(parent)) {
              ++d_numUnconstrainedElim;
              if (currentSub.isNull()) {
                currentSub = current;
              }
              currentSub = newUnconstrainedVar(parent.getType(), currentSub);
              current = parent;
            }
            else {
              currentSub = Node();
            }
          }
        }
        break;

        // N-ary operators returning same type requiring at least one child to be unconstrained
        case kind::PLUS:
        case kind::MINUS:
          if (current.getType().isInteger() &&
              !parent.getType().isInteger()) {
            break;
          }
        case kind::IFF:
        case kind::XOR:
        case kind::BITVECTOR_XOR:
        case kind::BITVECTOR_XNOR:
        case kind::BITVECTOR_PLUS:
        case kind::BITVECTOR_SUB:
          if (d_unconstrained.find(parent) == d_unconstrained.end() &&
              !d_substitutions.hasSubstitution(parent)) {
            ++d_numUnconstrainedElim;
            if (currentSub.isNull()) {
              currentSub = current;
            }
            current = parent;
          }
          else {
            currentSub = Node();
          }
          break;

        // Multiplication/division: must be non-integer and other operand must be non-zero
        case kind::MULT: {
        case kind::DIVISION:
          Assert(parent.getNumChildren() == 2);
          TNode other;
          if (parent[0] == current) {
            other = parent[1];
          }
          else {
            Assert(parent[1] == current);
            other = parent[0];
          }
          if (d_unconstrained.find(other) != d_unconstrained.end()) {
            if (d_unconstrained.find(parent) == d_unconstrained.end() &&
                !d_substitutions.hasSubstitution(parent)) {
              if (current.getType().isInteger() && other.getType().isInteger()) {
                Assert(parent.getKind() == kind::DIVISION || parent.getType().isInteger());
                if (parent.getKind() == kind::DIVISION) {
                  break;
                }
              }
              ++d_numUnconstrainedElim;
              if (currentSub.isNull()) {
                currentSub = current;
              }
              current = parent;
            }
            else {
              currentSub = Node();
            }
          }
          else {
            // if only the denominator of a division is unconstrained, can't set it to 0 so the result is not unconstrained
            if (parent.getKind() == kind::DIVISION && current == parent[1]) {
              break;
            }
            NodeManager* nm = NodeManager::currentNM();
            // if we are an integer, the only way we are unconstrained is if we are a MULT by -1
            if (current.getType().isInteger()) {
              // div/mult by 1 should have been simplified
              Assert(other != nm->mkConst<Rational>(1));
              if (other == nm->mkConst<Rational>(-1)) {
                // div by -1 should have been simplified
                Assert(parent.getKind() == kind::MULT);
                Assert(parent.getType().isInteger());
              }
              else {
                break;
              }
            }
            else {
              // TODO: could build ITE here
              Node test = other.eqNode(nm->mkConst<Rational>(0));
              if (Rewriter::rewrite(test) != nm->mkConst<bool>(false)) {
                break;
              }
            }
            ++d_numUnconstrainedElim;
            if (currentSub.isNull()) {
              currentSub = current;
            }
            current = parent;
          }
          break;
        }

        // Bitvector MULT - current must only appear once in the children:
        // all other children must be unconstrained or odd
        case kind::BITVECTOR_MULT:
        {
          bool found = false;
          bool done = false;
          for(TNode::iterator child_it = parent.begin(); child_it != parent.end(); ++child_it) {
            if ((*child_it) == current) {
              if (found) {
                done = true;
                break;
              }
              found = true;
              continue;
            }
            else if (d_unconstrained.find(*child_it) != d_unconstrained.end()) {
              continue;
            }
            else {
              NodeManager* nm = NodeManager::currentNM();
              Node extractOp = nm->mkConst<BitVectorExtract>(BitVectorExtract(0,0));
              vector<Node> children;
              children.push_back(*child_it);
              Node test = nm->mkNode(extractOp, children);
              BitVector one(1,unsigned(1));
              test = test.eqNode(nm->mkConst<BitVector>(one));
              if (Rewriter::rewrite(test) != nm->mkConst<bool>(true)) {
                done = true;
                break;
              }
            }
          }
          if (done) {
            break;
          }
          if (d_unconstrained.find(parent) == d_unconstrained.end() &&
              !d_substitutions.hasSubstitution(parent)) {
            ++d_numUnconstrainedElim;
            if (currentSub.isNull()) {
              currentSub = current;
            }
            current = parent;
          }
          else {
            currentSub = Node();
          }
          break;
        }

        // Uninterpreted function - if domain is infinite, no quantifiers are used, and any child is unconstrained, result is unconstrained
        case kind::APPLY_UF:
          if (d_logicInfo.isQuantified() || !current.getType().getCardinality().isInfinite()) {
            break;
          }
          if (d_unconstrained.find(parent) == d_unconstrained.end() &&
              !d_substitutions.hasSubstitution(parent)) {
            ++d_numUnconstrainedElim;
            if (currentSub.isNull()) {
              currentSub = current;
            }
            if (parent.getType() != current.getType()) {
              currentSub = newUnconstrainedVar(parent.getType(), currentSub);
            }
            current = parent;
          }
          else {
            currentSub = Node();
          }
          break;

        // Array select - if array is unconstrained, so is result
        case kind::SELECT:
          if (parent[0] == current) {
            ++d_numUnconstrainedElim;
            Assert(current.getType().isArray());
            if (currentSub.isNull()) {
              currentSub = current;
            }
            currentSub = newUnconstrainedVar(current.getType().getArrayConstituentType(), currentSub);
            current = parent;
          }
          break;

        // Array store - if both store and value are unconstrained, so is resulting store
        case kind::STORE:
          if (((parent[0] == current &&
                d_unconstrained.find(parent[2]) != d_unconstrained.end()) ||
               (parent[2] == current &&
                d_unconstrained.find(parent[0]) != d_unconstrained.end()))) {
            if (d_unconstrained.find(parent) == d_unconstrained.end() &&
                !d_substitutions.hasSubstitution(parent)) {
              ++d_numUnconstrainedElim;
              if (parent[0] != current) {
                if (parent[0].isVar()) {
                  currentSub = parent[0];
                }
                else {
                  Assert(d_substitutions.hasSubstitution(parent[0]));
                  currentSub = d_substitutions.apply(parent[0]);
                }
              }
              else if (currentSub.isNull()) {
                currentSub = current;
              }
              current = parent;
            }
            else {
              currentSub = Node();
            }
          }
          break;

        // Bit-vector comparisons: replace with new Boolean variable, but have
        // to also conjoin with a side condition as there is always one case
        // when the comparison is forced to be false
        case kind::BITVECTOR_ULT:
        case kind::BITVECTOR_UGE:
        case kind::BITVECTOR_UGT:
        case kind::BITVECTOR_ULE:
        case kind::BITVECTOR_SLT:
        case kind::BITVECTOR_SGE:
        case kind::BITVECTOR_SGT:
        case kind::BITVECTOR_SLE: {
          // Tuples over (signed, swap, strict).
          switch (parent.getKind()) {
            case kind::BITVECTOR_UGE:
              break;
            case kind::BITVECTOR_ULT:
              strict = true;
              break;
            case kind::BITVECTOR_ULE:
              swap = true;
              break;
            case kind::BITVECTOR_UGT:
              swap = true;
              strict = true;
              break;
            case kind::BITVECTOR_SGE:
              isSigned = true;
              break;
            case kind::BITVECTOR_SLT:
              isSigned = true;
              strict = true;
              break;
            case kind::BITVECTOR_SLE:
              isSigned = true;
              swap = true;
              break;
            case kind::BITVECTOR_SGT:
              isSigned = true;
              swap = true;
              strict = true;
              break;
            default:
              Unreachable();
          }
          TNode other;
          bool left = false;
          if (parent[0] == current) {
            other = parent[1];
            left = true;
          } else {
            Assert(parent[1] == current);
            other = parent[0];
          }
          if (d_unconstrained.find(other) != d_unconstrained.end()) {
            if (d_unconstrained.find(parent) == d_unconstrained.end() &&
                !d_substitutions.hasSubstitution(parent)) {
              ++d_numUnconstrainedElim;
              if (currentSub.isNull()) {
                currentSub = current;
              }
              currentSub = newUnconstrainedVar(parent.getType(), currentSub);
              current = parent;
            } else {
              currentSub = Node();
            }
          } else {
            unsigned size = current.getType().getBitVectorSize();
            BitVector bv =
                isSigned ? BitVector(size, Integer(1).multiplyByPow2(size - 1))
                         : BitVector(size, unsigned(0));
            if (swap == left) {
              bv = ~bv;
            }
            if (currentSub.isNull()) {
              currentSub = current;
            }
            currentSub = newUnconstrainedVar(parent.getType(), currentSub);
            current = parent;
            NodeManager* nm = NodeManager::currentNM();
            Node test =
                Rewriter::rewrite(other.eqNode(nm->mkConst<BitVector>(bv)));
            if (test == nm->mkConst<bool>(false)) {
              break;
            }
            if (strict) {
              currentSub = currentSub.andNode(test.notNode());
            } else {
              currentSub = currentSub.orNode(test);
            }
            // Delay adding this substitution - see comment at end of function
            delayQueueLeft.push_back(current);
            delayQueueRight.push_back(currentSub);
            currentSub = Node();
            parent = TNode();
          }
          break;
        }

        // Do nothing 
        case kind::BITVECTOR_SIGN_EXTEND:
        case kind::BITVECTOR_ZERO_EXTEND:
        case kind::BITVECTOR_REPEAT:
        case kind::BITVECTOR_ROTATE_LEFT:
        case kind::BITVECTOR_ROTATE_RIGHT:

        default:
          break;
      }
      if (current == parent && d_visited[parent] == 1) {
        d_unconstrained.insert(parent);
        continue;
      }
    }
    if (!currentSub.isNull()) {
      Assert(currentSub.isVar());
      d_substitutions.addSubstitution(current, currentSub, false);
    }
    if (workList.empty()) {
      break;
    }
    current = workList.back();
    currentSub = Node();
    workList.pop_back();
  }
  TNode left;
  Node right;
  // All substitutions except those arising from bitvector comparisons are
  // substitutions t -> x where x is a variable.  This allows us to build the
  // substitution very quickly (never invalidating the substitution cache).
  // Bitvector comparisons are more complicated and may require
  // back-substitution and cache-invalidation.  So we do these last.
  while (!delayQueueLeft.empty()) {
    left = delayQueueLeft.back();
    if (!d_substitutions.hasSubstitution(left)) {
      right = d_substitutions.apply(delayQueueRight.back());
      d_substitutions.addSubstitution(delayQueueLeft.back(), right);
    }
    delayQueueLeft.pop_back();
    delayQueueRight.pop_back();
  }
}