Beispiel #1
0
//
// Subroutine of explain
// A step of explanation for x and y
//
void Egraph::expExplainAlongPath ( Enode * x, Enode * y )
{
    Enode * v  = expHighestNode( x );
    Enode * to = expHighestNode( y );

    while ( v != to )
    {
        Enode * p = v->getExpParent( );
        assert( p != NULL );
        Enode * r = v->getExpReason( );

        // If it is not a congruence edge
        if ( r != NULL )
        {
            if ( !isDup1( r ) )
            {
                assert( r->isTerm( ) );
                explanation.push_back( r );
                storeDup1( r );
            }
        }
        // Otherwise it is a congruence edge
        // This means that the edge is linking nodes
        // like (v)f(a1,...,an) (p)f(b1,...,bn), and that
        // a1,...,an = b1,...bn. For each pair ai,bi
        // we have therefore to compute the reasons
        else
        {
            assert( v->getCar( ) == p->getCar( ) );
            assert( v->getArity( ) == p->getArity( ) );
            expEnqueueArguments( v, p );
        }

#ifdef PRODUCE_PROOF
        if ( config.produce_inter > 0
                && config.logic != QF_AX )
        {
            cgraph.addCNode( v );
            cgraph.addCNode( p );
            cgraph.addCEdge( v, p, r );
        }
#endif

        expUnion( v, p );
        v = expHighestNode( p );
    }
}
Beispiel #2
0
Enode *
ExpandITEs::doit( Enode * formula )
{
  assert( formula );
  list< Enode * > new_clauses;
  vector< Enode * > unprocessed_enodes;
  egraph.initDupMap1( );

  unprocessed_enodes.push_back( formula );
  //
  // Visit the DAG of the formula from the leaves to the root
  //
  while( !unprocessed_enodes.empty( ) )
  {
    Enode * enode = unprocessed_enodes.back( );
    //
    // Skip if the node has already been processed before
    //
    if ( egraph.valDupMap1( enode ) != NULL )
    {
      unprocessed_enodes.pop_back( );
      continue;
    }

    bool unprocessed_children = false;
    Enode * arg_list;
    for ( arg_list = enode->getCdr( ) ;
          arg_list != egraph.enil ;
          arg_list = arg_list->getCdr( ) )
    {
      Enode * arg = arg_list->getCar( );

      assert( arg->isTerm( ) );
      //
      // Push only if it is unprocessed
      //
      if ( egraph.valDupMap1( arg ) == NULL )
      {
        unprocessed_enodes.push_back( arg );
        unprocessed_children = true;
      }
    }
    //
    // SKip if unprocessed_children
    //
    if ( unprocessed_children )
      continue;

    unprocessed_enodes.pop_back( );
    Enode * result = NULL;
    //
    // At this point, every child has been processed
    //
    char def_name[ 32 ];

    if ( enode->isIte( ) )
    {
      //
      // Retrieve arguments
      //
      Enode * i = egraph.valDupMap1( enode->get1st( ) );
      Enode * t = egraph.valDupMap1( enode->get2nd( ) );
      Enode * e = egraph.valDupMap1( enode->get3rd( ) );
      Enode * not_i = egraph.mkNot( egraph.cons( i ) );
      //
      // Generate variable symbol
      //
      sprintf( def_name, ITE_STR, enode->getId( ) );
      Snode * sort = enode->getLastSort( );
      egraph.newSymbol( def_name, sort );
      //
      // Generate placeholder
      //
      result = egraph.mkVar( def_name );
      //
      // Generate additional clauses
      //
      Enode * eq_then = egraph.mkEq( egraph.cons( result
                                   , egraph.cons( t ) ) );
      Enode * eq_else = egraph.mkEq( egraph.cons( result
                                   , egraph.cons( e ) ) );
      new_clauses.push_back( egraph.mkOr( egraph.cons( not_i
                                        , egraph.cons( eq_then ) ) ) );
      new_clauses.push_back( egraph.mkOr( egraph.cons( i
                                        , egraph.cons( eq_else ) ) ) );
    }
    else
    {
      result = egraph.copyEnodeEtypeTermWithCache( enode );
    }

    assert( result );
    assert( egraph.valDupMap1( enode ) == NULL );
    egraph.storeDupMap1( enode, result );
  }

  Enode * new_formula = egraph.valDupMap1( formula );
  assert( new_formula );
  egraph.doneDupMap1( );

  new_clauses.push_back( new_formula );

  return egraph.mkAnd( egraph.cons( new_clauses ) );
}
Beispiel #3
0
void Egraph::gatherInterfaceTerms( Enode * e )
{
  assert( config.sat_lazy_dtc != 0 );
  assert( config.logic == QF_UFIDL
       || config.logic == QF_UFLRA );

  assert( e );

  if ( config.verbosity > 2 )
    cerr << "# Egraph::Gathering interface terms" << endl;

  vector< Enode * > unprocessed_enodes;
  initDup1( );

  unprocessed_enodes.push_back( e );
  //
  // Visit the DAG of the term from the leaves to the root
  //
  while( !unprocessed_enodes.empty( ) )
  {
    Enode * enode = unprocessed_enodes.back( );
    //
    // Skip if the node has already been processed before
    //
    if ( isDup1( enode ) )
    {
      unprocessed_enodes.pop_back( );
      continue;
    }

    bool unprocessed_children = false;
    Enode * arg_list;
    for ( arg_list = enode->getCdr( ) ;
          arg_list != enil ;
          arg_list = arg_list->getCdr( ) )
    {
      Enode * arg = arg_list->getCar( );
      assert( arg->isTerm( ) );
      //
      // Push only if it is unprocessed
      //
      if ( !isDup1( arg ) )
      {
        unprocessed_enodes.push_back( arg );
        unprocessed_children = true;
      }
    }
    //
    // SKip if unprocessed_children
    //
    if ( unprocessed_children )
      continue;

    unprocessed_enodes.pop_back( );
    //
    // At this point, every child has been processed
    //
    if ( enode->isUFOp( ) )
    {
      // Retrieve arguments
      for ( Enode * arg_list = enode->getCdr( )
          ; !arg_list->isEnil( )
          ; arg_list = arg_list->getCdr( ) )
      {
        Enode * arg = arg_list->getCar( );
        // This is for sure an interface term
        if ( ( arg->isArithmeticOp( )
            || arg->isConstant( ) )
          && interface_terms_cache.insert( arg ).second )
        {
          interface_terms.push_back( arg );
          if ( config.verbosity > 2 )
            cerr << "# Egraph::Added interface term: " << arg << endl;
        }
        // We add this variable to the potential
        // interface terms or to interface terms if
        // already seen in LA
        else if ( arg->isVar( ) || arg->isConstant( ) )
        {
          if ( it_la.find( arg ) == it_la.end( ) )
            it_uf.insert( arg );
          else if ( interface_terms_cache.insert( arg ).second )
          {
            interface_terms.push_back( arg );
            if ( config.verbosity > 2 )
              cerr << "# Egraph::Added interface term: " << arg << endl;
          }
        }
      }
    }

    if ( enode->isArithmeticOp( )
      && !isRootUF( enode ) )
    {
      // Retrieve arguments
      for ( Enode * arg_list = enode->getCdr( )
          ; !arg_list->isEnil( )
          ; arg_list = arg_list->getCdr( ) )
      {
        Enode * arg = arg_list->getCar( );
        // This is for sure an interface term
        if ( arg->isUFOp( )
          && interface_terms_cache.insert( arg ).second )
        {
          interface_terms.push_back( arg );
          if ( config.verbosity > 2 )
            cerr << "# Egraph::Added interface term: " << arg << endl;
        }
        // We add this variable to the potential
        // interface terms or to interface terms if
        // already seen in UF
        else if ( arg->isVar( ) || arg->isConstant( ) )
        {
          if ( it_uf.find( arg ) == it_uf.end( ) )
            it_la.insert( arg );
          else if ( interface_terms_cache.insert( arg ).second )
          {
            interface_terms.push_back( arg );
            if ( config.verbosity > 2 )
              cerr << "# Egraph::Added interface term: " << arg << endl;
          }
        }
      }
    }

    assert( !isDup1( enode ) );
    storeDup1( enode );
  }

  doneDup1( );
}
Beispiel #4
0
Enode * Egraph::canonizeDTC( Enode * formula, bool split_eqs )
{
  assert( config.sat_lazy_dtc != 0 );
  assert( config.logic == QF_UFLRA
       || config.logic == QF_UFIDL );

  list< Enode * > dtc_axioms;
  vector< Enode * > unprocessed_enodes;
  initDupMap1( );

  unprocessed_enodes.push_back( formula );
  //
  // Visit the DAG of the formula from the leaves to the root
  //
  while( !unprocessed_enodes.empty( ) )
  {
    Enode * enode = unprocessed_enodes.back( );
    //
    // Skip if the node has already been processed before
    //
    if ( valDupMap1( enode ) != NULL )
    {
      unprocessed_enodes.pop_back( );
      continue;
    }

    bool unprocessed_children = false;
    Enode * arg_list;
    for ( arg_list = enode->getCdr( )
        ; arg_list != enil
        ; arg_list = arg_list->getCdr( ) )
    {
      Enode * arg = arg_list->getCar( );
      assert( arg->isTerm( ) );
      //
      // Push only if it is unprocessed
      //
      if ( valDupMap1( arg ) == NULL )
      {
        unprocessed_enodes.push_back( arg );
        unprocessed_children = true;
      }
    }
    //
    // SKip if unprocessed_children
    //
    if ( unprocessed_children )
      continue;

    unprocessed_enodes.pop_back( );
    Enode * result = NULL;
    //
    // Replace arithmetic atoms with canonized version
    //
    if (  enode->isTAtom( )
      && !enode->isUp( ) )
    {
      // No need to do anything if node is purely UF
      if ( isRootUF( enode ) )
      {
        if ( config.verbosity > 2 )
          cerr << "# Egraph::Skipping canonization of " << enode << " as it's root is purely UF" << endl;
        result = enode;
      }
      else
      {
        LAExpression a( enode );
        result = a.toEnode( *this );
#ifdef PRODUCE_PROOF
        const uint64_t partitions = getIPartitions( enode );
        assert( partitions != 0 );
        setIPartitions( result, partitions );
#endif

        if ( split_eqs && result->isEq( ) )
        {
#ifdef PRODUCE_PROOF
          if ( config.produce_inter > 0 )
            opensmt_error2( "can't compute interpolant for equalities at the moment ", enode );
#endif
          LAExpression aa( enode );
          Enode * e = aa.toEnode( *this );
#ifdef PRODUCE_PROOF
          assert( partitions != 0 );
          setIPartitions( e, partitions );
#endif
          Enode * lhs = e->get1st( );
          Enode * rhs = e->get2nd( );
          Enode * leq = mkLeq( cons( lhs, cons( rhs ) ) );
          LAExpression b( leq );
          leq = b.toEnode( *this );
#ifdef PRODUCE_PROOF
          assert( partitions != 0 );
          setIPartitions( leq, partitions );
#endif
          Enode * geq = mkGeq( cons( lhs, cons( rhs ) ) );
          LAExpression c( geq );
          geq = c.toEnode( *this );
#ifdef PRODUCE_PROOF
          assert( partitions != 0 );
          setIPartitions( geq, partitions );
#endif
          Enode * not_e = mkNot( cons( enode ) );
          Enode * not_l = mkNot( cons( leq ) );
          Enode * not_g = mkNot( cons( geq ) );
          // Add clause ( !x=y v x<=y )
          Enode * c1 = mkOr( cons( not_e
                           , cons( leq ) ) );
          // Add clause ( !x=y v x>=y )
          Enode * c2 = mkOr( cons( not_e
                           , cons( geq ) ) );
          // Add clause ( x=y v !x>=y v !x<=y )
          Enode * c3 = mkOr( cons( enode
                           , cons( not_l
                           , cons( not_g ) ) ) );
          // Add conjunction of clauses
          Enode * ax = mkAnd( cons( c1
                            , cons( c2
                            , cons( c3 ) ) ) );

          dtc_axioms.push_back( ax );
          result = enode;
        }
      }
    }
    //
    // If nothing have been done copy and simplify
    //
    if ( result == NULL )
      result = copyEnodeEtypeTermWithCache( enode );

    assert( valDupMap1( enode ) == NULL );
    storeDupMap1( enode, result );
#ifdef PRODUCE_PROOF
    if ( config.produce_inter > 0 )
    {
      // Setting partitions for result
      setIPartitions( result, getIPartitions( enode ) );
      // Setting partitions for negation as well occ if atom
      if ( result->hasSortBool( ) )
      {
        setIPartitions( mkNot( cons( result ) )
                      , getIPartitions( enode ) );
      }
    }
#endif
  }

  Enode * new_formula = valDupMap1( formula );
  assert( new_formula );
  doneDupMap1( );

  if ( !dtc_axioms.empty( ) )
  {
    dtc_axioms.push_back( new_formula );
    new_formula = mkAnd( cons( dtc_axioms ) );
  }

  return new_formula;
}
Beispiel #5
0
bool Egraph::isPureUF( Enode * e )
{
  assert( config.sat_lazy_dtc != 0 );
  assert( config.logic == QF_UFIDL
       || config.logic == QF_UFLRA );

  assert( e );
  vector< Enode * > unprocessed_enodes;
  initDup1( );

  unprocessed_enodes.push_back( e );
  //
  // Visit the DAG of the term from the leaves to the root
  //
  while( !unprocessed_enodes.empty( ) )
  {
    Enode * enode = unprocessed_enodes.back( );
    //
    // Skip if the node has already been processed before
    //
    if ( isDup1( enode ) )
    {
      unprocessed_enodes.pop_back( );
      continue;
    }

    bool unprocessed_children = false;
    Enode * arg_list;
    for ( arg_list = enode->getCdr( ) ;
          arg_list != enil ;
          arg_list = arg_list->getCdr( ) )
    {
      Enode * arg = arg_list->getCar( );
      assert( arg->isTerm( ) );
      //
      // Push only if it is unprocessed
      //
      if ( !isDup1( arg ) )
      {
        unprocessed_enodes.push_back( arg );
        unprocessed_children = true;
      }
    }
    //
    // SKip if unprocessed_children
    //
    if ( unprocessed_children )
      continue;

    unprocessed_enodes.pop_back( );

    //
    // At this point, every child has been processed
    //
    if ( enode->isArithmeticOp( ) )
    {
      doneDup1( );
      return false;
    }

    assert( !isDup1( enode ) );
    storeDup1( enode );
  }

  doneDup1( );
  return true;
}
Beispiel #6
0
//
// Performs the actual cnfization
//
bool Tseitin::cnfize( Enode * formula, map< enodeid_t, Enode * > & cnf_cache )
{
  (void)cnf_cache;
  assert( formula );
  assert( !formula->isAnd( ) );

  Enode * arg_def = egraph.valDupMap1( formula );
  if ( arg_def != NULL )
  {
    vector< Enode * > clause;
    clause.push_back( arg_def );
#ifdef PRODUCE_PROOF
    if ( config.produce_inter > 0 )
      return solver.addSMTClause( clause, egraph.getIPartitions( formula ) );
#endif
    return solver.addSMTClause( clause );
  }

  vector< Enode * > unprocessed_enodes;       // Stack for unprocessed enodes
  unprocessed_enodes.push_back( formula );    // formula needs to be processed
  //
  // Visit the DAG of the formula from the leaves to the root
  //
  while( !unprocessed_enodes.empty( ) )
  {
    Enode * enode = unprocessed_enodes.back( );
    //
    // Skip if the node has already been processed before
    //
    if ( egraph.valDupMap1( enode ) != NULL )
    {
      unprocessed_enodes.pop_back( );
      continue;
    }

    bool unprocessed_children = false;
    Enode * arg_list;
    for ( arg_list = enode->getCdr( ) ;
          arg_list != egraph.enil ;
          arg_list = arg_list->getCdr( ) )
    {
      Enode * arg = arg_list->getCar( );

      assert( arg->isTerm( ) );
      //
      // Push only if it is an unprocessed boolean operator
      //
      if ( enode->isBooleanOperator( )
        && egraph.valDupMap1( arg ) == NULL )
      {
        unprocessed_enodes.push_back( arg );
        unprocessed_children = true;
      }
      //
      // If it is an atom (either boolean or theory) just
      // store it in the cache
      //
      else if ( arg->isAtom( ) )
      {
        egraph.storeDupMap1( arg, arg );
      }
    }
    //
    // SKip if unprocessed_children
    //
    if ( unprocessed_children )
      continue;

    unprocessed_enodes.pop_back( );
    Enode * result = NULL;
    //
    // At this point, every child has been processed
    //
    //
    // Do the actual cnfization, according to the node type
    //
    char def_name[ 32 ];

    if ( enode->isLit( ) )
    {
      result = enode;
    }
    else if ( enode->isNot( ) )
    {
      Enode * arg_def = egraph.valDupMap1( enode->get1st( ) );
      assert( arg_def );
      result = egraph.mkNot( egraph.cons( arg_def ) ); // Toggle the literal
    }
    else
    {
      Enode * arg_def = NULL;
      Enode * new_arg_list = egraph.copyEnodeEtypeListWithCache( enode->getCdr( ) );
      //
      // If the enode is not top-level it needs a definition
      //
      if ( formula != enode )
      {
        sprintf( def_name, CNF_STR, formula->getId( ), enode->getId( ) );
        egraph.newSymbol( def_name, sstore.mkBool( ) );
        arg_def = egraph.mkVar( def_name );
#ifdef PRODUCE_PROOF
        if ( config.produce_inter > 0 )
        {
          // Tag Positive and negative literals
          egraph.tagIFormula( arg_def
                            , egraph.getIPartitions( enode ) );
          egraph.tagIFormula( egraph.mkNot( egraph.cons( arg_def ) )
                            , egraph.getIPartitions( enode ) );
        }
#endif
      }
#ifdef PRODUCE_PROOF
      uint64_t partitions = 0;
      if ( config.produce_inter > 0 )
      {
        partitions = egraph.getIPartitions( enode );
        assert( partitions != 0 );
      }
#endif
      //
      // Handle boolean operators
      //
      if ( enode->isAnd( ) )
        cnfizeAnd( new_arg_list, arg_def
#ifdef PRODUCE_PROOF
        , partitions
#endif
        );
      else if ( enode->isOr( ) )
        cnfizeOr( new_arg_list, arg_def
#ifdef PRODUCE_PROOF
        , partitions
#endif
        );
      else if ( enode->isIff( ) )
        cnfizeIff( new_arg_list, arg_def
#ifdef PRODUCE_PROOF
        , partitions
#endif
        );
      else if ( enode->isXor( ) )
        cnfizeXor( new_arg_list, arg_def
#ifdef PRODUCE_PROOF
        , partitions
#endif
        );
      else
      {
        opensmt_error2( "operator not handled ", enode->getCar( ) );
      }

      if ( arg_def != NULL )
        result = arg_def;
    }

    assert( egraph.valDupMap1( enode ) == NULL );
    egraph.storeDupMap1( enode, result );
  }

  if ( formula->isNot( ) )
  {
    // Retrieve definition of argument
    Enode * arg_def = egraph.valDupMap1( formula->get1st( ) );
    assert( arg_def );
    vector< Enode * > clause;
    clause.push_back( toggleLit( arg_def ) );
#ifdef PRODUCE_PROOF
    if ( config.produce_inter > 0 )
      return solver.addSMTClause( clause, egraph.getIPartitions( formula ) );
#endif
    return solver.addSMTClause( clause );
  }

  return true;
}
Beispiel #7
0
//
// Rewrite formula with maximum arity for operators
//
Enode * Cnfizer::rewriteMaxArity( Enode * formula, map< enodeid_t, int > & enodeid_to_incoming_edges )
{
  assert( formula );

  vector< Enode * > unprocessed_enodes;       // Stack for unprocessed enodes
  unprocessed_enodes.push_back( formula );    // formula needs to be processed
  map< enodeid_t, Enode * > cache;            // Cache of processed nodes
  //
  // Visit the DAG of the formula from the leaves to the root
  //
  while( !unprocessed_enodes.empty( ) )
  {
    Enode * enode = unprocessed_enodes.back( );
    //
    // Skip if the node has already been processed before
    //
    if ( cache.find( enode->getId( ) ) != cache.end( ) )
    {
      unprocessed_enodes.pop_back( );
      continue;
    }

    bool unprocessed_children = false;
    Enode * arg_list;
    for ( arg_list = enode->getCdr( ) ;
          arg_list != egraph.enil ;
          arg_list = arg_list->getCdr( ) )
    {
      Enode * arg = arg_list->getCar( );

      assert( arg->isTerm( ) );
      //
      // Push only if it is an unprocessed boolean operator
      //
      if ( arg->isBooleanOperator( )
        && cache.find( arg->getId( ) ) == cache.end( ) )
      {
        unprocessed_enodes.push_back( arg );
        unprocessed_children = true;
      }
      //
      // If it is an atom (either boolean or theory) just
      // store it in the cache
      //
      else if ( arg->isAtom( ) )
      {
        cache.insert( make_pair( arg->getId( ), arg ) );
      }
    }
    //
    // SKip if unprocessed_children
    //
    if ( unprocessed_children )
      continue;

    unprocessed_enodes.pop_back( );
    Enode * result = NULL;
    //
    // At this point, every child has been processed
    //
    assert ( enode->isBooleanOperator( ) );

    if ( enode->isAnd( )
      || enode->isOr ( ) )
    {
      assert( enode->isAnd( ) || enode->isOr( ) );
      //
      // Construct the new lists for the operators
      //
      result = mergeEnodeArgs( enode, cache, enodeid_to_incoming_edges );
    }
    else
    {
      result = enode;
    }

    assert( result );
    assert( cache.find( enode->getId( ) ) == cache.end( ) );
    cache[ enode->getId( ) ] = result;
  }

  Enode * top_enode = cache[ formula->getId( ) ];
  return top_enode;
}
Beispiel #8
0
//
// Ackermann related routines
//
void
Egraph::retrieveFunctionApplications( Enode * formula )
{
  assert( formula );
  vector< Enode * > unprocessed_enodes;
  initDup1( );

  unprocessed_enodes.push_back( formula );

  //
  // Visit the DAG of the formula from the leaves to the root
  //
  while( !unprocessed_enodes.empty( ) )
  {
    Enode * enode = unprocessed_enodes.back( );
    // 
    // Skip if the node has already been processed before
    //
    if ( isDup1( enode ) )
    {
      unprocessed_enodes.pop_back( );
      continue;
    }

    bool unprocessed_children = false;
    Enode * arg_list;
    for ( arg_list = enode->getCdr( ) ; 
	  arg_list != enil ; 
	  arg_list = arg_list->getCdr( ) )
    {
      Enode * arg = arg_list->getCar( );

      assert( arg->isTerm( ) );
      //
      // Push only if it is unprocessed
      //
      if ( !isDup1( arg ) )
      {
	unprocessed_enodes.push_back( arg );
	unprocessed_children = true;
      }
    }
    //
    // SKip if unprocessed_children
    //
    if ( unprocessed_children )
      continue;

    unprocessed_enodes.pop_back( );                      
    //
    // At this point, every child has been processed
    //
    if ( enode->isUf( ) || enode->isUp( ) )
    {
      if ( uf_to_appl_cache[ enode->getCar( ) ].insert( enode ).second )
      {
	uf_to_appl[ enode->getCar( ) ].push_back( enode );
	undo_stack_oper.push_back( ACK_APPL );
	undo_stack_term.push_back( enode );
      }
    }

    assert( !isDup1( enode ) );
    storeDup1( enode );
  }

  doneDup1( );
}
Beispiel #9
0
void Egraph::getInterfaceVars( Enode * e, set< Enode * > & iv )
{
  assert( config.produce_inter != 0 );
  assert( config.sat_lazy_dtc != 0 );
  assert( config.logic == QF_UFIDL
       || config.logic == QF_UFLRA );

  assert( e );

  vector< Enode * > unprocessed_enodes;
  initDup1( );

  unprocessed_enodes.push_back( e );
  //
  // Visit the DAG of the term from the leaves to the root
  //
  while( !unprocessed_enodes.empty( ) )
  {
    Enode * enode = unprocessed_enodes.back( );
    // 
    // Skip if the node has already been processed before
    //
    if ( isDup1( enode ) )
    {
      unprocessed_enodes.pop_back( );
      continue;
    }

    bool unprocessed_children = false;
    Enode * arg_list;
    for ( arg_list = enode->getCdr( ) ; 
	  arg_list != enil ; 
	  arg_list = arg_list->getCdr( ) )
    {
      Enode * arg = arg_list->getCar( );
      assert( arg->isTerm( ) );
      //
      // Push only if it is unprocessed
      //
      if ( !isDup1( arg ) )
      {
	unprocessed_enodes.push_back( arg );
	unprocessed_children = true;
      }
    }
    //
    // SKip if unprocessed_children
    //
    if ( unprocessed_children )
      continue;

    unprocessed_enodes.pop_back( );                      

    if ( enode->isVar( )
      && interface_terms_cache.find( enode ) != interface_terms_cache.end( ) )
      iv.insert( enode );

    assert( !isDup1( enode ) );
    storeDup1( enode );
  }

  doneDup1( );
}