Merge from "cc" branch:
authorMorgan Deters <mdeters@gmail.com>
Tue, 17 Aug 2010 22:24:26 +0000 (22:24 +0000)
committerMorgan Deters <mdeters@gmail.com>
Tue, 17 Aug 2010 22:24:26 +0000 (22:24 +0000)
CongruenceClosure implementation; CongruenceClosure white-box test.

New UF theory implementation based on new CC module.  This one
supports predicates.  The two UF implementations exist in parallel
(they can be selected at runtime via the new command line option
"--uf").

Added type infrastructure for TUPLE.

Fixes to unit tests that failed in 16-August-2010 regressions.
Needed to instantiate TheoryEngine with an Options structure, and
explicitly call ->shutdown() on it before destruction (like the
SMTEngine does).

Fixed test makefiles to (1) perform all tests even in the presence of
failures, (2) give proper summaries of subdirectory tests
(e.g. regress0/uf and regress0/precedence)

Other minor changes.

48 files changed:
configure.ac
contrib/addsourcedir
src/context/cdmap.h
src/expr/expr_manager_template.cpp
src/expr/expr_manager_template.h
src/expr/node_builder.h
src/expr/node_manager.cpp
src/expr/node_manager.h
src/expr/type.cpp
src/expr/type.h
src/expr/type_node.cpp
src/expr/type_node.h
src/main/getopt.cpp
src/main/usage.h
src/smt/smt_engine.cpp
src/theory/builtin/kinds
src/theory/builtin/theory_builtin_type_rules.h
src/theory/theory_engine.h
src/theory/uf/Makefile.am
src/theory/uf/ecdata.cpp [deleted file]
src/theory/uf/ecdata.h [deleted file]
src/theory/uf/morgan/Makefile.am [new file with mode: 0644]
src/theory/uf/morgan/theory_uf_morgan.cpp [new file with mode: 0644]
src/theory/uf/morgan/theory_uf_morgan.h [new file with mode: 0644]
src/theory/uf/theory_uf.cpp [deleted file]
src/theory/uf/theory_uf.h
src/theory/uf/tim/Makefile.am [new file with mode: 0644]
src/theory/uf/tim/ecdata.cpp [new file with mode: 0644]
src/theory/uf/tim/ecdata.h [new file with mode: 0644]
src/theory/uf/tim/theory_uf_tim.cpp [new file with mode: 0644]
src/theory/uf/tim/theory_uf_tim.h [new file with mode: 0644]
src/util/congruence_closure.cpp [new file with mode: 0644]
src/util/congruence_closure.h
src/util/options.h
test/Makefile.am
test/regress/Makefile.am
test/regress/regress0/Makefile.am
test/regress/regress0/uf/Makefile.am
test/regress/regress1/Makefile.am
test/regress/regress2/Makefile.am
test/regress/regress3/Makefile.am
test/unit/Makefile.am
test/unit/expr/attribute_white.h
test/unit/theory/shared_term_manager_black.h
test/unit/theory/theory_engine_white.h
test/unit/theory/theory_uf_tim_white.h [new file with mode: 0644]
test/unit/theory/theory_uf_white.h [deleted file]
test/unit/util/congruence_closure_white.h [new file with mode: 0644]

index 21f2a9a8b962a330036ca051a03340caab582eed..c65dea9dc9ce933c984d12f8f5ffe2183c165e67 100644 (file)
@@ -76,9 +76,6 @@ AC_CANONICAL_TARGET
 if test "$enable_shared" = no -a "$user_specified_enable_or_disable_shared" = yes; then
   enable_static=yes
 fi
-if test "$enable_shared" = no -a "$enable_static" = yes; then
-  enable_static_binary=yes
-fi
 
 # Features requested by the user
 AC_MSG_CHECKING([for requested build profile])
index 190864469afe3d5598358cd7fe5ec0763f8560e1..ef6aedd156f70da3228554fc1c262f4f1b63c623 100644 (file)
@@ -68,14 +68,14 @@ EOF
     if expr "$srcdir" : src/parser >/dev/null; then
       definitions="    -D__BUILDING_CVC4PARSERLIB \\
 "
-      visibility=" -fvisibility=hidden"
+      visibility=' $(FLAG_VISIBILITY_HIDDEN)'
     elif expr "$srcdir" : src/main >/dev/null; then
       definitions=
       visibility=
     else
       definitions="    -D__BUILDING_CVC4LIB \\
 "
-      visibility=" -fvisibility=hidden"
+      visibility=' $(FLAG_VISIBILITY_HIDDEN)'
     fi
     clibname="lib${clibbase}.la"
     clibtarget="lib${clibbase}_la"
index c218d05f3beeb065930bdedb34c2e770d5774098..d9cc5b1a33754512e7637d6f6ff2c3cc98973a39 100644 (file)
@@ -247,9 +247,9 @@ public:
     return get();
   }
 
-  CDOmap& operator=(const Data& data) {
+  const Data& operator=(const Data& data) {
     set(data);
-    return *this;
+    return data;
   }
 
   CDOmap* next() const {
index 343f060e920c394baee0e74bedc1bcb01a5055b8..1eabc9f8ad4b8476b10b26956b3533445fe6764c 100644 (file)
@@ -253,6 +253,16 @@ FunctionType ExprManager::mkPredicateType(const std::vector<Type>& sorts) {
   return Type(d_nodeManager, new TypeNode(d_nodeManager->mkPredicateType(sortNodes)));
 }
 
+TupleType ExprManager::mkTupleType(const std::vector<Type>& types) {
+  Assert( types.size() >= 2 );
+  NodeManagerScope nms(d_nodeManager);
+  std::vector<TypeNode> typeNodes;
+  for (unsigned i = 0, i_end = types.size(); i < i_end; ++ i) {
+     typeNodes.push_back(*types[i].d_typeNode);
+  }
+  return Type(d_nodeManager, new TypeNode(d_nodeManager->mkTupleType(typeNodes)));
+}
+
 BitVectorType ExprManager::mkBitVectorType(unsigned size) const {
   NodeManagerScope nms(d_nodeManager);
   return Type(d_nodeManager, new TypeNode(d_nodeManager->mkBitVectorType(size)));
index 3b5b0e0f48b4f8bc06f6d92a58eecbcfba6a4d23..ab7aeace14a1264ec80ff0cdcbeba369d2a2e79d 100644 (file)
@@ -211,6 +211,13 @@ public:
    */
   FunctionType mkPredicateType(const std::vector<Type>& sorts);
 
+  /**
+   * Make a tuple type with types from
+   * <code>types[0..types.size()-1]</code>.  <code>types</code> must
+   * have at least 2 elements.
+   */
+  TupleType mkTupleType(const std::vector<Type>& types);
+
   /** Make a type representing a bit-vector of the given size */
   BitVectorType mkBitVectorType(unsigned size) const;
 
index 4021168423b683195d7d5b265d3354ef3607761d..4e4d69789277efcc011d70db7eead2dc32a638f3 100644 (file)
@@ -874,7 +874,7 @@ expr::NodeValue* NodeBuilder<nchild_thresh>::constructNV() {
     nv->d_rc = 0;
     setUsed();
     Debug("gc") << "creating node value " << nv
-                << " [" << nv->d_id << "]: " << nv->toString() << "\n";
+                << " [" << nv->d_id << "]: " << *nv << "\n";
     return nv;
   }
 
index fbfffe87d73518967b4a052879ff11f2d09d2db2..d017ad799d2c2b7690f1430e0b4309ed4be6648f 100644 (file)
@@ -205,6 +205,9 @@ TypeNode NodeManager::getType(TNode n, bool check)
     case kind::DISTINCT:
       typeNode = CVC4::theory::builtin::DistinctTypeRule::computeType(this, n, check);
       break;
+    case kind::TUPLE:
+      typeNode = CVC4::theory::builtin::TupleTypeRule::computeType(this, n, check);
+      break;
     case kind::CONST_BOOLEAN:
       typeNode = CVC4::theory::boolean::BooleanTypeRule::computeType(this, n, check);
       break;
index dcfb14b7a30135cbbb6945afcb2554851734f5bb..baa8de5aad55c47deb7db98fa76383ac57d43eac 100644 (file)
@@ -523,6 +523,16 @@ public:
    */
   inline TypeNode mkPredicateType(const std::vector<TypeNode>& sorts);
 
+  /**
+   * Make a tuple type with types from
+   * <code>types</code>. <code>types</code> must have at least two
+   * elements.
+   *
+   * @param types a vector of types
+   * @returns the tuple type (types[0], ..., types[n])
+   */
+  inline TypeNode mkTupleType(const std::vector<TypeNode>& types);
+
   /** Make the type of bitvectors of size <code>size</code> */
   inline TypeNode mkBitVectorType(unsigned size);
 
@@ -714,6 +724,15 @@ NodeManager::mkPredicateType(const std::vector<TypeNode>& sorts) {
   return mkTypeNode(kind::FUNCTION_TYPE, sortNodes);
 }
 
+inline TypeNode NodeManager::mkTupleType(const std::vector<TypeNode>& types) {
+  Assert(types.size() >= 2);
+  std::vector<TypeNode> typeNodes;
+  for (unsigned i = 0; i < types.size(); ++ i) {
+    typeNodes.push_back(types[i]);
+  }
+  return mkTypeNode(kind::TUPLE_TYPE, typeNodes);
+}
+
 inline TypeNode NodeManager::mkBitVectorType(unsigned size) {
   return TypeNode(mkTypeConst<BitVectorSize>(BitVectorSize(size)));
 }
index 225e5f9e0a7798e83158fefda807c87af406cfb5..b111e5604ca4905fc37ca280194f9da2bf232de8 100644 (file)
@@ -166,6 +166,19 @@ Type::operator FunctionType() const throw (AssertionException) {
   return FunctionType(*this);
 }
 
+/** Is this a tuple type? */
+bool Type::isTuple() const {
+  NodeManagerScope nms(d_nodeManager);
+  return d_typeNode->isTuple();
+}
+
+/** Cast to a tuple type */
+Type::operator TupleType() const throw (AssertionException) {
+  NodeManagerScope nms(d_nodeManager);
+  Assert(isTuple());
+  return TupleType(*this);
+}
+
 /** Is this an array type? */
 bool Type::isArray() const {
   NodeManagerScope nms(d_nodeManager);
@@ -222,6 +235,18 @@ Type FunctionType::getRangeType() const {
   return makeType(d_typeNode->getRangeType());
 }
 
+std::vector<Type> TupleType::getTypes() const {
+  NodeManagerScope nms(d_nodeManager);
+  std::vector<Type> types;
+  std::vector<TypeNode> typeNodes = d_typeNode->getTupleTypes();
+  std::vector<TypeNode>::iterator it = typeNodes.begin();
+  std::vector<TypeNode>::iterator it_end = typeNodes.end();
+  for(; it != it_end; ++ it) {
+    types.push_back(makeType(*it));
+  }
+  return types;
+}
+
 std::string SortType::getName() const {
   NodeManagerScope nms(d_nodeManager);
   return d_typeNode->getAttribute(expr::VarNameAttr());
@@ -257,6 +282,12 @@ FunctionType::FunctionType(const Type& t) throw (AssertionException)
   Assert(isFunction());
 }
 
+TupleType::TupleType(const Type& t) throw (AssertionException)
+: Type(t)
+{
+  Assert(isTuple());
+}
+
 ArrayType::ArrayType(const Type& t) throw (AssertionException)
 : Type(t)
 {
index be8a57be3d07d48ffe4e56e7c5326174272b1c2e..9ade5e6f5f76bdce12220b09ed1d6b19ba76b835 100644 (file)
@@ -43,6 +43,7 @@ class RealType;
 class BitVectorType;
 class ArrayType;
 class FunctionType;
+class TupleType;
 class KindType;
 class SortType;
 class Type;
@@ -183,7 +184,7 @@ public:
 
   /**
    * Is this a function type?
-   * @return true if the type is a Boolean type
+   * @return true if the type is a function type
    */
   bool isFunction() const;
 
@@ -201,8 +202,20 @@ public:
   operator FunctionType() const throw (AssertionException);
 
   /**
-   * Is this a function type?
-   * @return true if the type is a Boolean type
+   * Is this a tuple type?
+   * @return true if the type is a tuple type
+   */
+  bool isTuple() const;
+
+  /**
+   * Cast this type to a tuple type
+   * @return the TupleType
+   */
+  operator TupleType() const throw (AssertionException);
+
+  /**
+   * Is this an array type?
+   * @return true if the type is a array type
    */
   bool isArray() const;
 
@@ -295,7 +308,21 @@ public:
 };
 
 /**
- * Class encapsulating a function type.
+ * Class encapsulating a tuple type.
+ */
+class CVC4_PUBLIC TupleType : public Type {
+
+public:
+
+  /** Construct from the base type */
+  TupleType(const Type& type) throw (AssertionException);
+
+  /** Get the constituent types */
+  std::vector<Type> getTypes() const;
+};
+
+/**
+ * Class encapsulating an array type.
  */
 class CVC4_PUBLIC ArrayType : public Type {
 
index b32555b9dd726d27b59bc6518ee34318bb270ace..491734b35398a4784b263eb38f4139f642b0cdff 100644 (file)
@@ -63,7 +63,7 @@ bool TypeNode::isPredicate() const {
 std::vector<TypeNode> TypeNode::getArgTypes() const {
   Assert(isFunction());
   std::vector<TypeNode> args;
-  for (unsigned i = 0, i_end = getNumChildren() - 1; i < i_end; ++ i) {
+  for (unsigned i = 0, i_end = getNumChildren() - 1; i < i_end; ++i) {
     args.push_back((*this)[i]);
   }
   return args;
@@ -74,6 +74,20 @@ TypeNode TypeNode::getRangeType() const {
   return (*this)[getNumChildren()-1];
 }
 
+/** Is this a tuple type? */
+bool TypeNode::isTuple() const {
+  return getKind() == kind::TUPLE_TYPE;
+}
+
+/** Is this a tuple type? */
+std::vector<TypeNode> TypeNode::getTupleTypes() const {
+  Assert(isTuple());
+  std::vector<TypeNode> types;
+  for (unsigned i = 0, i_end = getNumChildren(); i < i_end; ++i) {
+    types.push_back((*this)[i]);
+  }
+  return types;
+}
 
 /** Is this a sort kind */
 bool TypeNode::isSort() const {
index 1d5f89c601ade3b88e045f99599a3c857119a76a..811eab54f03a213e62b9ca9ceabbd273f452e131 100644 (file)
@@ -333,6 +333,12 @@ public:
    */
   bool isPredicate() const;
 
+  /** Is this a tuple type? */
+  bool isTuple() const;
+
+  /** Get the constituent types of a tuple type */
+  std::vector<TypeNode> getTupleTypes() const;
+
   /** Is this a bit-vector type */
   bool isBitVector() const;
 
index ed196ac457458d31fcb93c2cebfb561288370a54..b15f4ae6646076d76e538447db6908f0ec79bf87 100644 (file)
@@ -69,7 +69,8 @@ enum OptionValue {
   SHOW_CONFIG,
   STRICT_PARSING,
   DEFAULT_EXPR_DEPTH,
-  PRINT_EXPR_TYPES
+  PRINT_EXPR_TYPES,
+  UF_THEORY
 };/* enum OptionValue */
 
 /**
@@ -115,6 +116,7 @@ static struct option cmdlineOptions[] = {
   { "strict-parsing", no_argument   , NULL, STRICT_PARSING },
   { "default-expr-depth", required_argument, NULL, DEFAULT_EXPR_DEPTH },
   { "print-expr-types", no_argument , NULL, PRINT_EXPR_TYPES },
+  { "uf"         , required_argument, NULL, UF_THEORY },
   { NULL         , no_argument      , NULL, '\0'        }
 };/* if you add things to the above, please remember to update usage.h! */
 
@@ -248,6 +250,24 @@ throw(OptionException) {
       }
       break;
 
+    case UF_THEORY:
+      {
+        if(!strcmp(optarg, "tim")) {
+          opts->uf_implementation = Options::TIM;
+        } else if(!strcmp(optarg, "morgan")) {
+          opts->uf_implementation = Options::MORGAN;
+        } else if(!strcmp(optarg, "help")) {
+          printf("UF implementations available:\n");
+          printf("tim\n");
+          printf("morgan\n");
+          exit(1);
+        } else {
+          throw OptionException(string("unknown language for --uf: `") +
+                                optarg + "'.  Try --uf help.");
+        }
+      }
+      break;
+
     case SHOW_CONFIG:
       fputs(Configuration::about().c_str(), stdout);
       printf("\n");
index 9fdc6a67b9b983572ab2641731fda10c859da676..2be5f092e0a42c732dee1ee5688ff2da72435d01 100644 (file)
@@ -45,7 +45,8 @@ CVC4 options:\n\
    --debug | -d           debugging for something (e.g. --debug arith), implies -t\n\
    --stats                give statistics on exit\n\
    --default-expr-depth=N print exprs to depth N (0 == default, -1 == no limit)\n\
-   --print-expr-types     print types with variables when printing exprs\n"
+   --print-expr-types     print types with variables when printing exprs\n\
+   --uf=morgan|tim        select uninterpreted function theory implementation\n"
 ;
 
 }/* CVC4::main namespace */
index 37f6f06576e20dbb287c0c993af5bfb5b6857665..cdb6597608b2de53e4e156c72cfa5ddacb71c0a6 100644 (file)
@@ -79,7 +79,7 @@ SmtEngine::SmtEngine(ExprManager* em, const Options* opts) throw () :
   d_decisionEngine = new DecisionEngine;
   // We have mutual dependancy here, so we add the prop engine to the theory
   // engine later (it is non-essential there)
-  d_theoryEngine = new TheoryEngine(d_ctxt);
+  d_theoryEngine = new TheoryEngine(d_ctxt, opts);
   d_propEngine = new PropEngine(opts, d_decisionEngine, d_theoryEngine, d_ctxt);
   d_theoryEngine->setPropEngine(d_propEngine);
 }
index dfa94a66de8f7548af39b53dda2e71d3e7d7a52c..d3b9d12fb5cc9c96c6b9c46bcd4e318830890853 100644 (file)
@@ -127,3 +127,4 @@ constant TYPE_CONSTANT \
     "expr/type_constant.h" \
     "basic types"
 operator FUNCTION_TYPE 2: "function type"
+operator TUPLE_TYPE 2: "tuple type"
index 354bf02bd6c4cf7f0f157ce92633556d044b34c6..42c9902e581f0bbe1fa9f135714c1a84b30406c0 100644 (file)
@@ -44,7 +44,7 @@ class EqualityTypeRule {
         std::stringstream ss;
         ss << "Types do not match in equation ";
         ss << "[" << lhsType << "<>" << rhsType << "]";
-        
+
         throw TypeCheckingExceptionPrivate(n, ss.str());
       }
 
@@ -54,7 +54,8 @@ class EqualityTypeRule {
     }
     return booleanType;
   }
-};
+};/* class EqualityTypeRule */
+
 
 class DistinctTypeRule {
 public:
@@ -71,7 +72,22 @@ public:
     }
     return nodeManager->booleanType();
   }
-};
+};/* class DistinctTypeRule */
+
+
+class TupleTypeRule {
+public:
+  inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) {
+    std::vector<TypeNode> types;
+    for(TNode::iterator child_it = n.begin(), child_it_end = n.end();
+        child_it != child_it_end;
+        ++child_it) {
+      types.push_back((*child_it).getType(check));
+    }
+    return nodeManager->mkTupleType(types);
+  }
+};/* class TupleTypeRule */
+
 
 }/* CVC4::theory::builtin namespace */
 }/* CVC4::theory namespace */
index 54204ae3fde657f15bebb1e80efdfdc47f3543a9..cc0e663fa427557882d45c6a96c72116ec6545c3 100644 (file)
 #include "theory/builtin/theory_builtin.h"
 #include "theory/booleans/theory_bool.h"
 #include "theory/uf/theory_uf.h"
+#include "theory/uf/tim/theory_uf_tim.h"
+#include "theory/uf/morgan/theory_uf_morgan.h"
 #include "theory/arith/theory_arith.h"
 #include "theory/arrays/theory_arrays.h"
 #include "theory/bv/theory_bv.h"
 
+#include "util/options.h"
 #include "util/stats.h"
 
 namespace CVC4 {
@@ -133,6 +136,12 @@ class TheoryEngine {
   theory::arrays::TheoryArrays* d_arrays;
   theory::bv::TheoryBV* d_bv;
 
+  /**
+   * Debugging flag to ensure that shutdown() is called before the
+   * destructor.
+   */
+  bool d_hasShutDown;
+
   /**
    * Check whether a node is in the pre-rewrite cache or not.
    */
@@ -193,16 +202,26 @@ public:
   /**
    * Construct a theory engine.
    */
-  TheoryEngine(context::Context* ctxt) :
+  TheoryEngine(context::Context* ctxt, const Options* opts) :
     d_propEngine(NULL),
     d_theoryOut(this, ctxt),
+    d_hasShutDown(false),
     d_statistics() {
 
     d_sharedTermManager = new SharedTermManager(this, ctxt);
 
     d_builtin = new theory::builtin::TheoryBuiltin(0, ctxt, d_theoryOut);
     d_bool = new theory::booleans::TheoryBool(1, ctxt, d_theoryOut);
-    d_uf = new theory::uf::TheoryUF(2, ctxt, d_theoryOut);
+    switch(opts->uf_implementation) {
+    case Options::TIM:
+      d_uf = new theory::uf::tim::TheoryUFTim(2, ctxt, d_theoryOut);
+      break;
+    case Options::MORGAN:
+      d_uf = new theory::uf::morgan::TheoryUFMorgan(2, ctxt, d_theoryOut);
+      break;
+    default:
+      Unhandled(opts->uf_implementation);
+    }
     d_arith = new theory::arith::TheoryArith(3, ctxt, d_theoryOut);
     d_arrays = new theory::arrays::TheoryArrays(4, ctxt, d_theoryOut);
     d_bv = new theory::bv::TheoryBV(5, ctxt, d_theoryOut);
@@ -222,12 +241,24 @@ public:
     d_theoryOfTable.registerTheory(d_bv);
   }
 
+  ~TheoryEngine() {
+    Assert(d_hasShutDown);
+
+    delete d_bv;
+    delete d_arrays;
+    delete d_arith;
+    delete d_uf;
+    delete d_bool;
+    delete d_builtin;
+
+    delete d_sharedTermManager;
+  }
+
   SharedTermManager* getSharedTermManager() {
     return d_sharedTermManager;
   }
 
-  void setPropEngine(prop::PropEngine* propEngine)
-  {
+  void setPropEngine(prop::PropEngine* propEngine) {
     Assert(d_propEngine == NULL);
     d_propEngine = propEngine;
   }
@@ -238,21 +269,17 @@ public:
    * ordering issues between PropEngine and Theory.
    */
   void shutdown() {
+    // Set this first; if a Theory shutdown() throws an exception,
+    // at least the destruction of the TheoryEngine won't confound
+    // matters.
+    d_hasShutDown = true;
+
     d_builtin->shutdown();
     d_bool->shutdown();
     d_uf->shutdown();
     d_arith->shutdown();
     d_arrays->shutdown();
     d_bv->shutdown();
-
-    delete d_bv;
-    delete d_arrays;
-    delete d_arith;
-    delete d_uf;
-    delete d_bool;
-    delete d_builtin;
-
-    delete d_sharedTermManager;
   }
 
   /**
index 4e399aeb7a515f95937e128378598fc59e2e8a95..85b41bdbec7f34b1a514d249d49aae7bbafb4d38 100644 (file)
@@ -6,10 +6,13 @@ AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN)
 noinst_LTLIBRARIES = libuf.la
 
 libuf_la_SOURCES = \
-       ecdata.h \
-       ecdata.cpp \
        theory_uf.h \
-       theory_uf.cpp \
        theory_uf_type_rules.h 
 
+libuf_la_LIBADD = \
+       @builddir@/tim/libuftim.la \
+       @builddir@/morgan/libufmorgan.la
+
+SUBDIRS = tim morgan
+
 EXTRA_DIST = kinds
diff --git a/src/theory/uf/ecdata.cpp b/src/theory/uf/ecdata.cpp
deleted file mode 100644 (file)
index 822f3a9..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*********************                                                        */
-/*! \file ecdata.cpp
- ** \verbatim
- ** Original author: taking
- ** Major contributors: mdeters
- ** Minor contributors (to current version): none
- ** This file is part of the CVC4 prototype.
- ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
- ** Courant Institute of Mathematical Sciences
- ** New York University
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief Implementation of equivalence class data for UF theory.
- **
- ** Implementation of equivalence class data for UF theory.  This is a
- ** context-dependent object.
- **/
-
-#include "theory/uf/ecdata.h"
-
-using namespace CVC4;
-using namespace context;
-using namespace theory;
-using namespace uf;
-
-ECData::ECData(Context * context, TNode n) :
-  ContextObj(context),
-  d_find(this),
-  d_rep(n),
-  d_watchListSize(0),
-  d_first(NULL),
-  d_last(NULL) {
-}
-
-bool ECData::isClassRep() {
-  return this == this->d_find;
-}
-
-void ECData::addPredecessor(TNode n) {
-  Assert(isClassRep());
-
-  makeCurrent();
-
-  Link * newPred = new(getCMM()) Link(getContext(), n, d_first);
-  d_first = newPred;
-  if(d_last == NULL) {
-    d_last = newPred;
-  }
-
-  ++d_watchListSize;
-}
-
-ContextObj* ECData::save(ContextMemoryManager* pCMM) {
-  return new(pCMM) ECData(*this);
-}
-
-void ECData::restore(ContextObj* pContextObj) {
-  ECData* data = (ECData*)pContextObj;
-  d_find = data->d_find;
-  d_first = data->d_first;
-  d_last = data->d_last;
-  d_rep = data->d_rep;
-  d_watchListSize = data->d_watchListSize;
-}
-
-Node ECData::getRep() {
-  return d_rep;
-}
-
-unsigned ECData::getWatchListSize() {
-  return d_watchListSize;
-}
-
-void ECData::setFind(ECData * ec) {
-  makeCurrent();
-  d_find = ec;
-}
-
-ECData* ECData::getFind() {
-  return d_find;
-}
-
-Link* ECData::getFirst() {
-  return d_first;
-}
-
-void ECData::takeOverDescendantWatchList(ECData* nslave, ECData* nmaster) {
-  Assert(nslave != nmaster);
-  Assert(nslave->getFind() == nmaster);
-
-  nmaster->makeCurrent();
-
-  nmaster->d_watchListSize += nslave->d_watchListSize;
-
-  if(nmaster->d_first == NULL) {
-    nmaster->d_first = nslave->d_first;
-    nmaster->d_last = nslave->d_last;
-  } else if(nslave->d_first != NULL) {
-    Link* currLast = nmaster->d_last;
-    currLast->d_next = nslave->d_first;
-    nmaster->d_last = nslave->d_last;
-  }
-}
diff --git a/src/theory/uf/ecdata.h b/src/theory/uf/ecdata.h
deleted file mode 100644 (file)
index bff0a67..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-/*********************                                                        */
-/*! \file ecdata.h
- ** \verbatim
- ** Original author: taking
- ** Major contributors: mdeters
- ** Minor contributors (to current version): none
- ** This file is part of the CVC4 prototype.
- ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
- ** Courant Institute of Mathematical Sciences
- ** New York University
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief Context dependent equivalence class datastructure for nodes.
- **
- ** Context dependent equivalence class datastructure for nodes.
- ** Currently keeps a context dependent watch list.
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__UF__ECDATA_H
-#define __CVC4__THEORY__UF__ECDATA_H
-
-#include "expr/node.h"
-#include "context/context.h"
-#include "context/cdo.h"
-#include "context/context_mm.h"
-
-namespace CVC4 {
-namespace theory {
-namespace uf {
-
-/**
- * Link is a context dependent linked list of nodes.
- * Link is intended to be allocated in a Context's memory manager.
- * The next pointer of the list is context dependent, but the node being
- * pointed to is fixed for the life of the Link.
- *
- * Clients of Link are intended not to modify the node that is being pointed
- * to in good faith.  This may change in the future.
- */
-struct Link {
-  /**
-   * Pointer to the next element in linked list.
-   * This is context dependent. 
-   */
-  context::CDO<Link*> d_next;
-
-  /**
-   * Link is supposed to be allocated in a region of a
-   * ContextMemoryManager.  In order to avoid having to decrement the
-   * ref count at deletion time, it is preferrable for the user of
-   * Link to maintain the invariant that data will survival for the
-   * entire scope of the TNode.
-   */
-  TNode d_data;
-
-  /**
-   * Creates a new Link w.r.t. a context for the node n.
-   * An optional parameter is to specify the next element in the link.
-   */
-  Link(context::Context* context, TNode n, Link* l = NULL) :
-    d_next(true, context, l),
-    d_data(n) {
-    Debug("context") << "Link: " << this
-                     << " so cdo is " << &d_next << std::endl;
-  }
-
-  /**
-   * Allocates a new Link in the region for the provided ContextMemoryManager.
-   * This allows for cheap cleanup on pop.
-   */
-  static void* operator new(size_t size, context::ContextMemoryManager* pCMM) {
-    return pCMM->newData(size);
-  }
-
-private:
-
-  /**
-   * The destructor isn't actually defined.  This declaration keeps
-   * the compiler from creating (wastefully) a default definition, and
-   * ensures that we get a link error if someone uses Link in a way
-   * that requires destruction.  Objects of class Link should always
-   * be allocated in a ContextMemoryManager, which doesn't call
-   * destructors.
-   */
-  ~Link() throw();
-
-  /**
-   * Just like the destructor, this is not defined.  This ensures no
-   * one tries to create a Link on the heap.
-   */
-  static void* operator new(size_t size);
-
-};/* struct Link */
-
-
-/**
- * ECData is a equivalence class object that is context dependent.
- * It is developed in order to support the congruence closure algorithm
- * in TheoryUF, and is not intended to be used outside of that package.
- *
- * ECData maintains:
- * - find pointer for the equivalence class (disjoint set forest)
- * - the node that represents the equivalence class.
- * - maintains a predecessorlist/watchlist
- *
- * ECData does not have support for the canonical find and union operators
- * for disjoint set forests.  Instead it only provides access to the find
- * pointer. The implementation of find is ccFind in TheoryUF.
- * union is broken into 2 phases:
- *  1) setting the find point with setFind
- *  2) taking over the watch list of the other node.
- * This is a technical requirement for the implementation of TheoryUF.
- * (See ccUnion in TheoryUF for more information.)
- *
- * The intended paradigm for iterating over the watch list of ec is:
- *      for(Link* i = ec->getFirst(); i != NULL; i = i->next );
- *
- * See also ECAttr() in theory_uf.h, and theory_uf.cpp where the codde that uses
- * ECData lives.
- */
-class ECData : public context::ContextObj {
-private:
-  /**
-   * This is the standard disjoint set forest find pointer.
-   *
-   * Why an ECData pointer instead of a node?
-   * This was chosen to be a ECData pointer in order to shortcut at least one
-   * table every time the find pointer is examined.
-   */
-  ECData* d_find;
-
-  /**
-   * This is pointer back to the node that represents this equivalence class.
-   *
-   * The following invariant should be maintained:
-   *  (n.getAttribute(ECAttr()))->rep == n
-   * i.e. rep is equal to the node that maps to the ECData using ECAttr.
-   *
-   * Tricky part: This needs to be a TNode, not a Node.
-   * Suppose that rep were a hard link.
-   * When a node n maps to an ECData via the ECAttr() there will be a hard
-   * link back to n in the ECData. The attribute does not do garbage collection
-   * until the node gets garbage collected, which does not happen until its
-   * ref count drops to 0. So because of this cycle neither the node and
-   * the ECData will never get garbage collected.
-   * So this needs to be a soft link.
-   */
-  TNode d_rep;
-
-  // Watch list data structures follow
-
-  /**
-   * Maintains watch list size for more efficient merging.
-   */
-  unsigned d_watchListSize;
-
-  /**
-   * Pointer to the beginning of the watchlist.
-   * This value is NULL iff the watch list is empty.
-   */
-  Link* d_first;
-
-  /**
-   * Pointer to the end of the watch-list.
-   * This is maintained in order to constant time list merging.
-   * (This does not give any asymptotic improve as this is currently always
-   * preceeded by an O(|watchlist|) operation.)
-   * This value is NULL iff the watch list is empty.
-   */
-  Link* d_last;
-
-  /** Context-dependent operation: save this ECData */
-  context::ContextObj* save(context::ContextMemoryManager* pCMM);
-
-  /** Context-dependent operation: restore this ECData */
-  void restore(context::ContextObj* pContextObj);
-
-public:
-  /**
-   * Returns true if this ECData object is the current representative of
-   * the equivalence class.
-   */
-  bool isClassRep();
-
-  /**
-   * Adds a node to the watch list of the equivalence class.  Does
-   * context-dependent memory allocation in the Context with which
-   * this ECData was created.
-   *
-   * @param n the node to be added.
-   * @pre isClassRep() == true
-   */
-  void addPredecessor(TNode n);
-
-  /**
-   * Creates a EQ with the representative n
-   * @param context the context to associate with this ecdata.
-   *   This is required as ECData is context dependent
-   * @param n the node that corresponds to this ECData
-   */
-  ECData(context::Context* context, TNode n);
-
-  /** Destructor for ECDatas */
-  ~ECData() {
-    Debug("ufgc") << "Calling ECData destructor" << std::endl;
-    destroy();
-  }
-
-  /**
-   * An ECData takes over the watch list of another ECData.
-   * This is the second step in the union operator for ECData.
-   * This should be called after nslave->setFind(nmaster);
-   * After this is done nslave's watch list should never be accessed by
-   * getLast() or getFirst()
-   */
-  static void takeOverDescendantWatchList(ECData * nslave, ECData * nmaster);
-
-  /**
-   * Returns the representative of this ECData.
-   */
-  Node getRep();
-
-  /**
-   * Returns the size of the equivalence class.
-   */
-  unsigned getWatchListSize();
-
-  /**
-   * Returns a pointer the first member of the watch list.
-   */
-  Link* getFirst();
-
-
-  /**
-   * Returns the find pointer of the ECData.
-   * If isClassRep(), then getFind() == this
-   */
-  ECData* getFind();
-
-  /**
-   * Sets the find pointer of the equivalence class to be another ECData object.
-   *
-   * @pre isClassRep() == true
-   * @pre ec->isClassRep() == true
-   * @post isClassRep() == false
-   * @post ec->isClassRep() == true
-   */
-  void setFind(ECData * ec);
-
-};/* class ECData */
-
-}/* CVC4::theory::uf namespace */
-}/* CVC4::theory namespace */
-}/* CVC4 namespace */
-
-#endif /* __CVC4__THEORY__UF__ECDATA_H */
diff --git a/src/theory/uf/morgan/Makefile.am b/src/theory/uf/morgan/Makefile.am
new file mode 100644 (file)
index 0000000..e9faa88
--- /dev/null
@@ -0,0 +1,12 @@
+AM_CPPFLAGS = \
+       -D__BUILDING_CVC4LIB \
+       -I@srcdir@/../../../include -I@srcdir@/../../.. -I@builddir@/../../..
+AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN)
+
+noinst_LTLIBRARIES = libufmorgan.la
+
+libufmorgan_la_SOURCES = \
+       theory_uf_morgan.h \
+       theory_uf_morgan.cpp
+
+EXTRA_DIST =
diff --git a/src/theory/uf/morgan/theory_uf_morgan.cpp b/src/theory/uf/morgan/theory_uf_morgan.cpp
new file mode 100644 (file)
index 0000000..a480a4d
--- /dev/null
@@ -0,0 +1,348 @@
+/*********************                                                        */
+/*! \file theory_uf_morgan.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: mdeters
+ ** Minor contributors (to current version): dejan
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Implementation of the theory of uninterpreted functions.
+ **
+ ** Implementation of the theory of uninterpreted functions.
+ **/
+
+#include "theory/uf/morgan/theory_uf_morgan.h"
+#include "expr/kind.h"
+#include "util/congruence_closure.h"
+
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::context;
+using namespace CVC4::theory;
+using namespace CVC4::theory::uf;
+using namespace CVC4::theory::uf::morgan;
+
+TheoryUFMorgan::TheoryUFMorgan(int id, Context* ctxt, OutputChannel& out) :
+  TheoryUF(id, ctxt, out),
+  d_assertions(ctxt),
+  d_ccChannel(this),
+  d_cc(ctxt, &d_ccChannel),
+  d_unionFind(ctxt),
+  d_disequalities(ctxt),
+  d_disequality(ctxt),
+  d_conflict(),
+  d_trueNode(),
+  d_falseNode() {
+  TypeNode boolType = NodeManager::currentNM()->booleanType();
+  d_trueNode = NodeManager::currentNM()->mkVar("TRUE_UF", boolType);
+  d_falseNode = NodeManager::currentNM()->mkVar("FALSE_UF", boolType);
+  d_cc.addTerm(d_trueNode);
+  d_cc.addTerm(d_falseNode);
+}
+
+TheoryUFMorgan::~TheoryUFMorgan() {
+}
+
+RewriteResponse TheoryUFMorgan::postRewrite(TNode n, bool topLevel) {
+  if(topLevel) {
+    Debug("uf") << "uf: begin rewrite(" << n << ")" << std::endl;
+    Node ret(n);
+    if(n.getKind() == EQUAL) {
+      if(n[0] == n[1]) {
+        ret = NodeManager::currentNM()->mkConst(true);
+      }
+    }
+    Debug("uf") << "uf: end rewrite(" << n << ") : " << ret << std::endl;
+    return RewriteComplete(ret);
+  } else {
+    return RewriteComplete(n);
+  }
+}
+
+void TheoryUFMorgan::preRegisterTerm(TNode n) {
+  Debug("uf") << "uf: preRegisterTerm(" << n << ")" << std::endl;
+}
+
+void TheoryUFMorgan::registerTerm(TNode n) {
+  Debug("uf") << "uf: registerTerm(" << n << ")" << std::endl;
+}
+
+Node TheoryUFMorgan::constructConflict(TNode diseq) {
+  Debug("uf") << "uf: begin constructConflict()" << std::endl;
+  Debug("uf") << "uf:   using diseq == " << diseq << std::endl;
+
+  Node explanation = d_cc.explain(diseq[0], diseq[1]);
+
+  NodeBuilder<> nb(kind::AND);
+  if(explanation.getKind() == kind::AND) {
+    for(Node::iterator i = explanation.begin();
+        i != explanation.end();
+        ++i) {
+      nb << *i;
+    }
+  } else {
+    Assert(explanation.getKind() == kind::EQUAL);
+    nb << explanation;
+  }
+  nb << diseq.notNode();
+
+  // by construction this should be true
+  Assert(nb.getNumChildren() > 1);
+
+  Node conflict = nb;
+  Debug("uf") << "conflict constructed : " << conflict << std::endl;
+
+  Debug("uf") << "uf: ending constructConflict()" << std::endl;
+
+  return conflict;
+}
+
+TNode TheoryUFMorgan::find(TNode a) {
+  UnionFind::iterator i = d_unionFind.find(a);
+  if(i == d_unionFind.end()) {
+    return a;
+  } else {
+    return d_unionFind[a] = find((*i).second);
+  }
+}
+
+// no path compression
+TNode TheoryUFMorgan::debugFind(TNode a) const {
+  UnionFind::iterator i = d_unionFind.find(a);
+  if(i == d_unionFind.end()) {
+    return a;
+  } else {
+    return debugFind((*i).second);
+  }
+}
+
+void TheoryUFMorgan::unionClasses(TNode a, TNode b) {
+  if(a == b) {
+    return;
+  }
+  Assert(d_unionFind.find(a) == d_unionFind.end());
+  Assert(d_unionFind.find(b) == d_unionFind.end());
+  d_unionFind[a] = b;
+}
+
+void TheoryUFMorgan::notifyCongruent(TNode a, TNode b) {
+  Debug("uf") << "uf: notified of merge " << a << std::endl
+              << "                  and " << b << std::endl;
+  if(!d_conflict.isNull()) {
+    // if already a conflict, we don't care
+    return;
+  }
+
+  // make "a" the one with shorter diseqList
+  DiseqLists::iterator deq_ia = d_disequalities.find(a);
+  DiseqLists::iterator deq_ib = d_disequalities.find(b);
+  if(deq_ia != d_disequalities.end()) {
+    if(deq_ib == d_disequalities.end() ||
+       (*deq_ia).second->size() > (*deq_ib).second->size()) {
+      TNode tmp = a;
+      a = b;
+      b = tmp;
+      Debug("uf") << "    swapping to make a shorter diseqList" << std::endl;
+    }
+  }
+  a = find(a);
+  b = find(b);
+  Debug("uf") << "uf: uf reps are " << a << std::endl
+              << "            and " << b << std::endl;
+  unionClasses(a, b);
+
+  DiseqLists::iterator deq_i = d_disequalities.find(a);
+  if(deq_i != d_disequalities.end()) {
+    // a set of other trees we are already disequal to
+    // (for the optimization below)
+    std::set<TNode> alreadyDiseqs;
+    DiseqLists::iterator deq_ib = d_disequalities.find(b);
+    if(deq_ib != d_disequalities.end()) {
+      DiseqList* deq = (*deq_i).second;
+      for(DiseqList::const_iterator j = deq->begin(); j != deq->end(); ++j) {
+        TNode deqn = *j;
+        TNode s = deqn[0];
+        TNode t = deqn[1];
+        TNode sp = find(s);
+        TNode tp = find(t);
+        Assert(sp == b || tp == b);
+        if(sp == b) {
+          alreadyDiseqs.insert(tp);
+        } else {
+          alreadyDiseqs.insert(sp);
+        }
+      }
+    }
+
+    DiseqList* deq = (*deq_i).second;
+    Debug("uf") << "a == " << a << std::endl;
+    Debug("uf") << "size of deq(a) is " << deq->size() << std::endl;
+    for(DiseqList::const_iterator j = deq->begin(); j != deq->end(); ++j) {
+      Debug("uf") << "  deq(a) ==> " << *j << std::endl;
+      TNode deqn = *j;
+      Assert(deqn.getKind() == kind::EQUAL);
+      TNode s = deqn[0];
+      TNode t = deqn[1];
+      Debug("uf") << "       s  ==> " << s << std::endl
+                  << "       t  ==> " << t << std::endl
+                  << "  find(s) ==> " << debugFind(s) << std::endl
+                  << "  find(t) ==> " << debugFind(t) << std::endl;
+      TNode sp = find(s);
+      TNode tp = find(t);
+      if(sp == tp) {
+        d_conflict = deqn;
+        return;
+      }
+      Assert(sp == b || tp == b);
+      // optimization: don't put redundant diseq's in the list
+      if(sp == b) {
+        if(alreadyDiseqs.find(tp) == alreadyDiseqs.end()) {
+          appendToDiseqList(b, deqn);
+          alreadyDiseqs.insert(tp);
+        }
+      } else {
+        if(alreadyDiseqs.find(sp) == alreadyDiseqs.end()) {
+          appendToDiseqList(b, deqn);
+          alreadyDiseqs.insert(sp);
+        }
+      }
+    }
+    Debug("uf") << "end" << std::endl;
+  }
+}
+
+void TheoryUFMorgan::appendToDiseqList(TNode of, TNode eq) {
+  Debug("uf") << "appending " << eq << std::endl
+              << "  to diseq list of " << of << std::endl;
+  Assert(eq.getKind() == kind::EQUAL);
+  Assert(of == debugFind(of));
+  DiseqLists::iterator deq_i = d_disequalities.find(of);
+  DiseqList* deq;
+  if(deq_i == d_disequalities.end()) {
+    deq = new(getContext()->getCMM()) DiseqList(true, getContext());
+    d_disequalities.insertDataFromContextMemory(of, deq);
+  } else {
+    deq = (*deq_i).second;
+  }
+  deq->push_back(eq);
+  Debug("uf") << "  size is now " << deq->size() << std::endl;
+}
+
+void TheoryUFMorgan::addDisequality(TNode eq) {
+  Assert(eq.getKind() == kind::EQUAL);
+
+  Node a = eq[0];
+  Node b = eq[1];
+
+  appendToDiseqList(find(a), eq);
+  appendToDiseqList(find(b), eq);
+}
+
+void TheoryUFMorgan::check(Effort level) {
+  Debug("uf") << "uf: begin check(" << level << ")" << std::endl;
+
+  while(!done()) {
+    Node assertion = get();
+
+    Debug("uf") << "uf check(): " << assertion << std::endl;
+
+    switch(assertion.getKind()) {
+    case EQUAL:
+      d_cc.addEquality(assertion);
+      if(!d_conflict.isNull()) {
+        Node conflict = constructConflict(d_conflict);
+        d_conflict = Node::null();
+        d_out->conflict(conflict, false);
+        return;
+      }
+      break;
+    case APPLY_UF:
+      { // predicate
+        Node eq = NodeManager::currentNM()->mkNode(kind::EQUAL, assertion, d_trueNode);
+        d_cc.addTerm(assertion);
+        d_cc.addEquality(eq);
+        Debug("uf") << "true == false ? " << (find(d_trueNode) == find(d_falseNode)) << std::endl;
+        Assert(find(d_trueNode) != find(d_falseNode));
+      }
+      break;
+    case NOT:
+      if(assertion[0].getKind() == kind::EQUAL) {
+        Node a = assertion[0][0];
+        Node b = assertion[0][1];
+
+        addDisequality(assertion[0]);
+        d_disequality.push_back(assertion[0]);
+
+        d_cc.addTerm(a);
+        d_cc.addTerm(b);
+
+        Debug("uf") << "       a  ==> " << a << std::endl
+                    << "       b  ==> " << b << std::endl
+                    << "  find(a) ==> " << debugFind(a) << std::endl
+                    << "  find(b) ==> " << debugFind(b) << std::endl;
+
+        // There are two ways to get a conflict here.
+        if(!d_conflict.isNull()) {
+          // We get a conflict this way if we weren't watching a, b
+          // before and we were just now notified (via
+          // notifyCongruent()) when we called addTerm() above that
+          // they are congruent.  We make this a separate case (even
+          // though the check in the "else if.." below would also
+          // catch it, so that we can clear out d_conflict.
+          Node conflict = constructConflict(d_conflict);
+          d_conflict = Node::null();
+          d_out->conflict(conflict, false);
+          return;
+        } else if(find(a) == find(b)) {
+          // We get a conflict this way if we WERE previously watching
+          // a, b and were notified previously (via notifyCongruent())
+          // that they were congruent.
+          Node conflict = constructConflict(assertion[0]);
+          d_out->conflict(conflict, false);
+          return;
+        }
+
+        // If we get this far, there should be nothing conflicting due
+        // to this disequality.
+        Assert(!d_cc.areCongruent(a, b));
+      } else {
+        Assert(assertion[0].getKind() == kind::APPLY_UF);
+        Node eq = NodeManager::currentNM()->mkNode(kind::EQUAL, assertion[0], d_falseNode);
+        d_cc.addTerm(assertion[0]);
+        d_cc.addEquality(eq);
+        Debug("uf") << "true == false ? " << (find(d_trueNode) == find(d_falseNode)) << std::endl;
+        Assert(find(d_trueNode) != find(d_falseNode));
+      }
+      break;
+    default:
+      Unhandled(assertion.getKind());
+    }
+  }
+  Debug("uf") << "uf check() done = " << (done() ? "true" : "false") << std::endl;
+
+  for(CDList<Node>::const_iterator diseqIter = d_disequality.begin();
+      diseqIter != d_disequality.end();
+      ++diseqIter) {
+
+    TNode left  = (*diseqIter)[0];
+    TNode right = (*diseqIter)[1];
+    Debug("uf") << "testing left: " << left << std::endl
+                << "       right: " << right << std::endl
+                << "     find(L): " << debugFind(left) << std::endl
+                << "     find(R): " << debugFind(right) << std::endl
+                << "     areCong: " << d_cc.areCongruent(left, right) << std::endl;
+    Assert((debugFind(left) == debugFind(right)) == d_cc.areCongruent(left, right));
+  }
+
+  Debug("uf") << "uf: end check(" << level << ")" << std::endl;
+}
+
+void TheoryUFMorgan::propagate(Effort level) {
+  Debug("uf") << "uf: begin propagate(" << level << ")" << std::endl;
+  Debug("uf") << "uf: end propagate(" << level << ")" << std::endl;
+}
diff --git a/src/theory/uf/morgan/theory_uf_morgan.h b/src/theory/uf/morgan/theory_uf_morgan.h
new file mode 100644 (file)
index 0000000..1a1ade2
--- /dev/null
@@ -0,0 +1,186 @@
+/*********************                                                        */
+/*! \file theory_uf_morgan.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: mdeters
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief This is a basic implementation of the Theory of Uninterpreted Functions
+ ** with Equality.
+ **
+ ** This is a basic implementation of the Theory of Uninterpreted Functions
+ ** with Equality.  It is based on the Nelson-Oppen algorithm given in
+ ** "Fast Decision Procedures Based on Congruence Closure"
+ **  (http://portal.acm.org/ft_gateway.cfm?id=322198&type=pdf)
+ ** This has been extended to work in a context-dependent way.
+ ** This interacts heavily with the data-structures given in ecdata.h .
+ **
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__UF__MORGAN__THEORY_UF_MORGAN_H
+#define __CVC4__THEORY__UF__MORGAN__THEORY_UF_MORGAN_H
+
+#include "expr/node.h"
+#include "expr/attribute.h"
+
+#include "theory/theory.h"
+#include "theory/uf/theory_uf.h"
+
+#include "context/context.h"
+#include "context/cdo.h"
+#include "context/cdlist.h"
+#include "util/congruence_closure.h"
+
+namespace CVC4 {
+namespace theory {
+namespace uf {
+namespace morgan {
+
+class TheoryUFMorgan : public TheoryUF {
+
+private:
+
+  class CongruenceChannel {
+    TheoryUFMorgan* d_uf;
+
+  public:
+    CongruenceChannel(TheoryUFMorgan* uf) : d_uf(uf) {}
+    void notifyCongruent(TNode a, TNode b) {
+      d_uf->notifyCongruent(a, b);
+    }
+  };/* class CongruenceChannel */
+  friend class CongruenceChannel;
+
+  /**
+   * List of all of the non-negated literals from the assertion queue.
+   * This is used only for conflict generation.
+   * This differs from pending as the program generates new equalities that
+   * are not in this list.
+   * This will probably be phased out in future version.
+   */
+  context::CDList<Node> d_assertions;
+
+  /**
+   * Our channel connected to the congruence closure module.
+   */
+  CongruenceChannel d_ccChannel;
+
+  /**
+   * Instance of the congruence closure module.
+   */
+  CongruenceClosure<CongruenceChannel> d_cc;
+
+  typedef context::CDMap<TNode, TNode, TNodeHashFunction> UnionFind;
+  UnionFind d_unionFind;
+
+  typedef context::CDList<Node> DiseqList;
+  typedef context::CDMap<Node, DiseqList*, NodeHashFunction> DiseqLists;
+
+  /** List of all disequalities this theory has seen. */
+  DiseqLists d_disequalities;
+
+  context::CDList<Node> d_disequality;
+
+  Node d_conflict;
+
+  Node d_trueNode, d_falseNode;
+
+public:
+
+  /** Constructs a new instance of TheoryUF w.r.t. the provided context.*/
+  TheoryUFMorgan(int id, context::Context* ctxt, OutputChannel& out);
+
+  /** Destructor for the TheoryUF object. */
+  ~TheoryUFMorgan();
+
+  /**
+   * Registers a previously unseen [in this context] node n.
+   * For TheoryUF, this sets up and maintains invaraints about
+   * equivalence class data-structures.
+   *
+   * Overloads a void registerTerm(TNode n); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  void registerTerm(TNode n);
+
+  /**
+   * Currently this does nothing.
+   *
+   * Overloads a void preRegisterTerm(TNode n); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  void preRegisterTerm(TNode n);
+
+  /**
+   * Checks whether the set of literals provided to the theory is consistent.
+   *
+   * If this is called at any effort level, it computes the congruence closure
+   * of all of the positive literals in the context.
+   *
+   * If this is called at full effort it checks if any of the negative literals
+   * are inconsistent with the congruence closure.
+   *
+   * Overloads  void check(Effort level); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  void check(Effort level);
+
+  /**
+   * Rewrites a node in the theory of uninterpreted functions.
+   * This is fairly basic and only ensures that atoms that are
+   * unsatisfiable or a valid are rewritten to false or true respectively.
+   */
+  RewriteResponse postRewrite(TNode n, bool topLevel);
+
+  /**
+   * Propagates theory literals.
+   *
+   * Overloads void propagate(Effort level); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  void propagate(Effort level);
+
+  /**
+   * Explains a previously reported conflict. Currently does nothing.
+   *
+   * Overloads void explain(TNode n, Effort level); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  void explain(TNode n, Effort level) {}
+
+  std::string identify() const { return std::string("TheoryUFMorgan"); }
+
+private:
+
+  /** Constructs a conflict from an inconsistent disequality. */
+  Node constructConflict(TNode diseq);
+
+  TNode find(TNode a);
+  TNode debugFind(TNode a) const;
+  void unionClasses(TNode a, TNode b);
+
+  void appendToDiseqList(TNode of, TNode eq);
+  void addDisequality(TNode eq);
+
+  /**
+   * Receives a notification from the congruence closure module that
+   * two nodes have been merged into the same congruence class.
+   */
+  void notifyCongruent(TNode a, TNode b);
+
+};/* class TheoryUFMorgan */
+
+}/* CVC4::theory::uf::morgan namespace */
+}/* CVC4::theory::uf namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__UF__MORGAN__THEORY_UF_MORGAN_H */
diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp
deleted file mode 100644 (file)
index 9060568..0000000
+++ /dev/null
@@ -1,327 +0,0 @@
-/*********************                                                        */
-/*! \file theory_uf.cpp
- ** \verbatim
- ** Original author: taking
- ** Major contributors: mdeters
- ** Minor contributors (to current version): dejan
- ** This file is part of the CVC4 prototype.
- ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
- ** Courant Institute of Mathematical Sciences
- ** New York University
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief Implementation of the theory of uninterpreted functions.
- **
- ** Implementation of the theory of uninterpreted functions.
- **/
-
-#include "theory/uf/theory_uf.h"
-#include "theory/uf/ecdata.h"
-#include "expr/kind.h"
-
-using namespace CVC4;
-using namespace CVC4::kind;
-using namespace CVC4::context;
-using namespace CVC4::theory;
-using namespace CVC4::theory::uf;
-
-TheoryUF::TheoryUF(int id, Context* c, OutputChannel& out) :
-  Theory(id, c, out),
-  d_assertions(c),
-  d_pending(c),
-  d_currentPendingIdx(c,0),
-  d_disequality(c),
-  d_registered(c) {
-}
-
-TheoryUF::~TheoryUF() {
-}
-
-Node TheoryUF::rewrite(TNode n){
-  Debug("uf") << "uf: begin rewrite(" << n << ")" << std::endl;
-  Node ret(n);
-  if(n.getKind() == EQUAL){
-    Assert(n.getNumChildren() == 2);
-    if(n[0] == n[1]) {
-      ret = NodeManager::currentNM()->mkConst(true);
-    }
-  }
-  Debug("uf") << "uf: end rewrite(" << n << ") : " << ret << std::endl;
-  return ret;
-}
-void TheoryUF::preRegisterTerm(TNode n) {
-  Debug("uf") << "uf: begin preRegisterTerm(" << n << ")" << std::endl;
-  Debug("uf") << "uf: end preRegisterTerm(" << n << ")" << std::endl;
-}
-
-void TheoryUF::registerTerm(TNode n) {
-
-  Debug("uf") << "uf: begin registerTerm(" << n << ")" << std::endl;
-
-  d_registered.push_back(n);
-
-  ECData* ecN;
-
-  if(n.getAttribute(ECAttr(), ecN)) {
-    /* registerTerm(n) is only called when a node has not been seen in the
-     * current context.  ECAttr() is not a context-dependent attribute.
-     * When n.hasAttribute(ECAttr(),...) is true on a registerTerm(n) call,
-     * then it must be the case that this attribute was created in a previous
-     * and no longer valid context. Because of this we have to reregister the
-     * predecessors lists.
-     * Also we do not have to worry about duplicates because all of the Link*
-     * setup before are removed when the context n was setup in was popped out
-     * of. All we are going to do here are sanity checks.
-     */
-
-    /*
-     * Consider the following chain of events:
-     * 1) registerTerm(n) is called on node n where n : f(m) in context level X,
-     * 2) A new ECData is created on the heap, ecN,
-     * 3) n is added to the predessecor list of m in context level X,
-     * 4) We pop out of X,
-     * 5) n is removed from the predessecor list of m because this is context
-     *    dependent, the Link* will be destroyed and pointers to the Link
-     *    structs in the ECData objects will be updated.
-     * 6) registerTerm(n) is called on node n in context level Y,
-     * 7) If n.hasAttribute(ECAttr(), &ecN), then ecN is still around,
-     *    but the predecessor list is not
-     *
-     * The above assumes that the code is working correctly.
-     */
-    Assert(ecN->getFirst() == NULL,
-           "Equivalence class data exists for the node being registered.  "
-           "Expected getFirst() == NULL.  "
-           "This data is either already in use or was not properly maintained "
-           "during backtracking");
-    /*Assert(ecN->getLast() == NULL,
-           "Equivalence class data exists for the node being registered.  "
-           "Expected getLast() == NULL.  "
-           "This data is either already in use or was not properly maintained "
-           "during backtracking.");*/
-    Assert(ecN->isClassRep(),
-           "Equivalence class data exists for the node being registered.  "
-           "Expected isClassRep() to be true.  "
-           "This data is either already in use or was not properly maintained "
-           "during backtracking");
-    Assert(ecN->getWatchListSize() == 0,
-           "Equivalence class data exists for the node being registered.  "
-           "Expected getWatchListSize() == 0.  "
-           "This data is either already in use or was not properly maintained "
-           "during backtracking");
-  } else {
-    //The attribute does not exist, so it is created and set
-    ecN = new (true) ECData(getContext(), n);
-    n.setAttribute(ECAttr(), ecN);
-  }
-
-  /* If the node is an APPLY_UF, we need to add it to the predecessor list
-   * of its children.
-   */
-  if(n.getKind() == APPLY_UF) {
-    TNode::iterator cIter = n.begin();
-
-    for(; cIter != n.end(); ++cIter) {
-      TNode child = *cIter;
-
-      /* Because this can be called after nodes have been merged, we need
-       * to lookup the representative in the UnionFind datastructure.
-       */
-      ECData* ecChild = ccFind(child.getAttribute(ECAttr()));
-
-      /* Because this can be called after nodes have been merged we may need
-       * to be merged with other predecessors of the equivalence class.
-       */
-      for(Link* Px = ecChild->getFirst(); Px != NULL; Px = Px->d_next ) {
-        if(equiv(n, Px->d_data)) {
-          Node pend = n.eqNode(Px->d_data);
-          d_pending.push_back(pend);
-        }
-      }
-
-      ecChild->addPredecessor(n);
-    }
-  }
-  Debug("uf") << "uf: end registerTerm(" << n << ")" << std::endl;
-
-}
-
-bool TheoryUF::sameCongruenceClass(TNode x, TNode y) {
-  return
-    ccFind(x.getAttribute(ECAttr())) ==
-    ccFind(y.getAttribute(ECAttr()));
-}
-
-bool TheoryUF::equiv(TNode x, TNode y) {
-  Assert(x.getKind() == kind::APPLY_UF);
-  Assert(y.getKind() == kind::APPLY_UF);
-
-  if(x.getNumChildren() != y.getNumChildren()) {
-    return false;
-  }
-
-  if(x.getOperator() != y.getOperator()) {
-    return false;
-  }
-
-  // intentionally don't look at operator
-
-  TNode::iterator xIter = x.begin();
-  TNode::iterator yIter = y.begin();
-
-  while(xIter != x.end()) {
-
-    if(!sameCongruenceClass(*xIter, *yIter)) {
-      return false;
-    }
-
-    ++xIter;
-    ++yIter;
-  }
-  return true;
-}
-
-/* This is a very basic, but *obviously correct* find implementation
- * of the classic find algorithm.
- * TODO after we have done some more testing:
- * 1) Add path compression.  This is dependent on changes to ccUnion as
- *    many better algorithms use eager path compression.
- * 2) Elminate recursion.
- */
-ECData* TheoryUF::ccFind(ECData * x) {
-  if(x->getFind() == x) {
-    return x;
-  } else {
-    return ccFind(x->getFind());
-  }
-  /* Slightly better Find w/ path compression and no recursion*/
-  /*
-    ECData* start;
-    ECData* next = x;
-    while(x != x->getFind()) x=x->getRep();
-    while( (start = next) != x) {
-      next = start->getFind();
-      start->setFind(x);
-    }
-    return x;
-  */
-}
-
-void TheoryUF::ccUnion(ECData* ecX, ECData* ecY) {
-  ECData* nslave;
-  ECData* nmaster;
-
-  if(ecX->getWatchListSize() <= ecY->getWatchListSize()) {
-    nslave = ecX;
-    nmaster = ecY;
-  } else {
-    nslave = ecY;
-    nmaster = ecX;
-  }
-
-  nslave->setFind(nmaster);
-
-  for(Link* Px = nmaster->getFirst(); Px != NULL; Px = Px->d_next ) {
-    for(Link* Py = nslave->getFirst(); Py != NULL; Py = Py->d_next ) {
-      if(equiv(Px->d_data,Py->d_data)) {
-        Node pendingEq = (Px->d_data).eqNode(Py->d_data);
-        d_pending.push_back(pendingEq);
-      }
-    }
-  }
-
-  ECData::takeOverDescendantWatchList(nslave, nmaster);
-}
-
-void TheoryUF::merge() {
-  while(d_currentPendingIdx < d_pending.size() ) {
-    Node assertion = d_pending[d_currentPendingIdx];
-    d_currentPendingIdx = d_currentPendingIdx + 1;
-
-    TNode x = assertion[0];
-    TNode y = assertion[1];
-
-    ECData* tmpX = x.getAttribute(ECAttr());
-    ECData* tmpY = y.getAttribute(ECAttr());
-
-    ECData* ecX = ccFind(tmpX);
-    ECData* ecY = ccFind(tmpY);
-    if(ecX == ecY)
-      continue;
-
-    Debug("uf") << "merging equivalence classes for " << std::endl;
-    Debug("uf") << "left equivalence class :" << (ecX->getRep()) << std::endl;
-    Debug("uf") << "right equivalence class :" << (ecY->getRep()) << std::endl;
-    Debug("uf") << std::endl;
-
-    ccUnion(ecX, ecY);
-  }
-}
-
-Node TheoryUF::constructConflict(TNode diseq) {
-  Debug("uf") << "uf: begin constructConflict()" << std::endl;
-
-  NodeBuilder<> nb(kind::AND);
-  nb << diseq;
-  for(unsigned i = 0; i < d_assertions.size(); ++i) {
-    nb << d_assertions[i];
-  }
-
-  Assert(nb.getNumChildren() > 0);
-  Node conflict = nb.getNumChildren() == 1 ? nb[0] : nb;
-
-  Debug("uf") << "conflict constructed : " << conflict << std::endl;
-
-  Debug("uf") << "uf: ending constructConflict()" << std::endl;
-
-  return conflict;
-}
-
-void TheoryUF::check(Effort level) {
-
-  Debug("uf") << "uf: begin check(" << level << ")" << std::endl;
-
-  while(!done()) {
-    Node assertion = get();
-    Debug("uf") << "TheoryUF::check(): " << assertion << std::endl;
-
-    switch(assertion.getKind()) {
-    case EQUAL:
-      d_assertions.push_back(assertion);
-      d_pending.push_back(assertion);
-      merge();
-      break;
-    case NOT:
-      d_disequality.push_back(assertion[0]);
-      break;
-    default:
-      Unhandled(assertion.getKind());
-    }
-
-    Debug("uf") << "TheoryUF::check(): done = " << (done() ? "true" : "false") << std::endl;
-  }
-
-  //Make sure all outstanding merges are completed.
-  if(d_currentPendingIdx < d_pending.size()) {
-    merge();
-  }
-
-  if(standardEffortOrMore(level)) {
-    for(CDList<Node>::const_iterator diseqIter = d_disequality.begin();
-        diseqIter != d_disequality.end();
-        ++diseqIter) {
-
-      TNode left  = (*diseqIter)[0];
-      TNode right = (*diseqIter)[1];
-      if(sameCongruenceClass(left, right)) {
-        Node remakeNeq = (*diseqIter).notNode();
-        Node conflict = constructConflict(remakeNeq);
-        d_out->conflict(conflict, false);
-        return;
-      }
-    }
-  }
-
-  Debug("uf") << "uf: end check(" << level << ")" << std::endl;
-}
index f0be0668abb601dfce6d28043a9117843516bf8a..f745cf402b43a624094bc0a9e51c92031f3da375 100644 (file)
@@ -1,8 +1,8 @@
 /*********************                                                        */
 /*! \file theory_uf.h
  ** \verbatim
- ** Original author: taking
- ** Major contributors: mdeters
+ ** Original author: mdeters
+ ** Major contributors: none
  ** Minor contributors (to current version): none
  ** This file is part of the CVC4 prototype.
  ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
  ** See the file COPYING in the top-level source directory for licensing
  ** information.\endverbatim
  **
- ** \brief This is a basic implementation of the Theory of Uninterpreted Functions
- ** with Equality.
- **
- ** This is a basic implementation of the Theory of Uninterpreted Functions
- ** with Equality.  It is based on the Nelson-Oppen algorithm given in
- ** "Fast Decision Procedures Based on Congruence Closure"
- **  (http://portal.acm.org/ft_gateway.cfm?id=322198&type=pdf)
- ** This has been extended to work in a context-dependent way.
- ** This interacts heavily with the data-structures given in ecdata.h .
+ ** \brief This is the interface to TheoryUF implementations
  **
+ ** This is the interface to TheoryUF implementations.  All
+ ** implementations of TheoryUF should inherit from this class.
  **/
 
 #include "cvc4_private.h"
 #include "theory/theory.h"
 
 #include "context/context.h"
-#include "context/cdo.h"
-#include "context/cdlist.h"
-#include "theory/uf/ecdata.h"
 
 namespace CVC4 {
 namespace theory {
 namespace uf {
 
 class TheoryUF : public Theory {
-
-private:
-
-  /**
-   * List of all of the non-negated literals from the assertion queue.
-   * This is used only for conflict generation.
-   * This differs from pending as the program generates new equalities that
-   * are not in this list.
-   * This will probably be phased out in future version.
-   */
-  context::CDList<Node> d_assertions;
-
-  /**
-   * List of pending equivalence class merges.
-   *
-   * Tricky part:
-   * Must keep a hard link because new equality terms are created and appended
-   * to this list.
-   */
-  context::CDList<Node> d_pending;
-
-  /** Index of the next pending equality to merge. */
-  context::CDO<unsigned> d_currentPendingIdx;
-
-  /** List of all disequalities this theory has seen. */
-  context::CDList<Node> d_disequality;
-
-  /**
-   * List of all of the terms that are registered in the current context.
-   * When registerTerm is called on a term we want to guarentee that there
-   * is a hard link to the term for the duration of the context in which
-   * register term is called.
-   * This invariant is enough for us to use soft links where we want is the
-   * current implementation as well as making ECAttr() not context dependent.
-   * Soft links used both in ECData, and Link.
-   */
-  context::CDList<Node> d_registered;
-
 public:
 
   /** Constructs a new instance of TheoryUF w.r.t. the provided context.*/
-  TheoryUF(int id, context::Context* c, OutputChannel& out);
-
-  /** Destructor for the TheoryUF object. */
-  ~TheoryUF();
-
-
-
-  /**
-   * Registers a previously unseen [in this context] node n.
-   * For TheoryUF, this sets up and maintains invaraints about
-   * equivalence class data-structures.
-   *
-   * Overloads a void registerTerm(TNode n); from theory.h.
-   * See theory/theory.h for more information about this method.
-   */
-  void registerTerm(TNode n);
-
-  /**
-   * Currently this does nothing.
-   *
-   * Overloads a void preRegisterTerm(TNode n); from theory.h.
-   * See theory/theory.h for more information about this method.
-   */
-  void preRegisterTerm(TNode n);
-
-  /**
-   * Checks whether the set of literals provided to the theory is consistent.
-   *
-   * If this is called at any effort level, it computes the congruence closure
-   * of all of the positive literals in the context.
-   *
-   * If this is called at full effort it checks if any of the negative literals
-   * are inconsistent with the congruence closure.
-   *
-   * Overloads  void check(Effort level); from theory.h.
-   * See theory/theory.h for more information about this method.
-   */
-  void check(Effort level);
-
-
-  /**
-   * Rewrites a node in the theory of uninterpreted functions.
-   * This is fairly basic and only ensures that atoms that are
-   * unsatisfiable or a valid are rewritten to false or true respectively.
-   */
-  Node rewrite(TNode n);
-
-  /**
-   * Plug in old rewrite to the new (pre,post)rewrite interface.
-   */
-  RewriteResponse postRewrite(TNode n, bool topLevel) {
-    return RewriteComplete(topLevel ? rewrite(n) : Node(n));
-  }
-
-  /**
-   * Propagates theory literals. Currently does nothing.
-   *
-   * Overloads void propagate(Effort level); from theory.h.
-   * See theory/theory.h for more information about this method.
-   */
-  void propagate(Effort level) {}
-
-  /**
-   * Explains a previously reported conflict. Currently does nothing.
-   *
-   * Overloads void explain(TNode n, Effort level); from theory.h.
-   * See theory/theory.h for more information about this method.
-   */
-  void explain(TNode n, Effort level) {}
-
-  std::string identify() const { return std::string("TheoryUF"); }
-
-private:
-  /**
-   * Checks whether 2 nodes are already in the same equivalence class tree.
-   * This should only be used internally, and it should only be called when
-   * the only thing done with the equivalence classes is an equality check.
-   *
-   * @returns true iff ccFind(x) == ccFind(y);
-   */
-  bool sameCongruenceClass(TNode x, TNode y);
-
-  /**
-   * Checks whether Node x and Node y are currently congruent
-   * using the equivalence class data structures.
-   * @returns true iff
-   *    |x| = n = |y| and
-   *    x.getOperator() == y.getOperator() and
-   *    forall 1 <= i < n : ccFind(x[i]) == ccFind(y[i])
-   */
-  bool equiv(TNode x, TNode y);
-
-  /**
-   * Merges 2 equivalence classes, checks wether any predecessors need to
-   * be set equal to complete congruence closure.
-   * The class with the smaller class size will be merged.
-   * @pre ecX->isClassRep()
-   * @pre ecY->isClassRep()
-   */
-  void ccUnion(ECData* ecX, ECData* ecY);
-
-  /**
-   * Returns the representative of the equivalence class.
-   * May modify the find pointers associated with equivalence classes.
-   */
-  ECData* ccFind(ECData* x);
-
-  /** Performs Congruence Closure to reflect the new additions to d_pending. */
-  void merge();
-
-  /** Constructs a conflict from an inconsistent disequality. */
-  Node constructConflict(TNode diseq);
+  TheoryUF(int id, context::Context* ctxt, OutputChannel& out)
+    : Theory(id, ctxt, out) {}
 
 };/* class TheoryUF */
 
-
-/**
- * Cleanup function for ECData. This will be used for called whenever
- * a ECAttr is being destructed.
- */
-struct ECCleanupStrategy {
-  static void cleanup(ECData* ec) {
-    Debug("ufgc") << "cleaning up ECData " << ec << "\n";
-    ec->deleteSelf();
-  }
-};/* struct ECCleanupStrategy */
-
-/** Unique name to use for constructing ECAttr. */
-struct ECAttrTag {};
-
-/**
- * ECAttr is the attribute that maps a node to an equivalence class.
- */
-typedef expr::Attribute<ECAttrTag, ECData*, ECCleanupStrategy> ECAttr;
-
 }/* CVC4::theory::uf namespace */
 }/* CVC4::theory namespace */
 }/* CVC4 namespace */
diff --git a/src/theory/uf/tim/Makefile.am b/src/theory/uf/tim/Makefile.am
new file mode 100644 (file)
index 0000000..6477838
--- /dev/null
@@ -0,0 +1,14 @@
+AM_CPPFLAGS = \
+       -D__BUILDING_CVC4LIB \
+       -I@srcdir@/../../../include -I@srcdir@/../../.. -I@builddir@/../../..
+AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN)
+
+noinst_LTLIBRARIES = libuftim.la
+
+libuftim_la_SOURCES = \
+       ecdata.h \
+       ecdata.cpp \
+       theory_uf_tim.h \
+       theory_uf_tim.cpp
+
+EXTRA_DIST =
diff --git a/src/theory/uf/tim/ecdata.cpp b/src/theory/uf/tim/ecdata.cpp
new file mode 100644 (file)
index 0000000..52a110f
--- /dev/null
@@ -0,0 +1,105 @@
+/*********************                                                        */
+/*! \file ecdata.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: mdeters
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Implementation of equivalence class data for UF theory.
+ **
+ ** Implementation of equivalence class data for UF theory.  This is a
+ ** context-dependent object.
+ **/
+
+#include "theory/uf/tim/ecdata.h"
+
+using namespace CVC4;
+using namespace CVC4::context;
+using namespace CVC4::theory;
+using namespace CVC4::theory::uf;
+using namespace CVC4::theory::uf::tim;
+
+ECData::ECData(Context * context, TNode n) :
+  ContextObj(context),
+  d_find(this),
+  d_rep(n),
+  d_watchListSize(0),
+  d_first(NULL),
+  d_last(NULL) {
+}
+
+bool ECData::isClassRep() {
+  return this == this->d_find;
+}
+
+void ECData::addPredecessor(TNode n) {
+  Assert(isClassRep());
+
+  makeCurrent();
+
+  Link * newPred = new(getCMM()) Link(getContext(), n, d_first);
+  d_first = newPred;
+  if(d_last == NULL) {
+    d_last = newPred;
+  }
+
+  ++d_watchListSize;
+}
+
+ContextObj* ECData::save(ContextMemoryManager* pCMM) {
+  return new(pCMM) ECData(*this);
+}
+
+void ECData::restore(ContextObj* pContextObj) {
+  ECData* data = (ECData*)pContextObj;
+  d_find = data->d_find;
+  d_first = data->d_first;
+  d_last = data->d_last;
+  d_rep = data->d_rep;
+  d_watchListSize = data->d_watchListSize;
+}
+
+Node ECData::getRep() {
+  return d_rep;
+}
+
+unsigned ECData::getWatchListSize() {
+  return d_watchListSize;
+}
+
+void ECData::setFind(ECData * ec) {
+  makeCurrent();
+  d_find = ec;
+}
+
+ECData* ECData::getFind() {
+  return d_find;
+}
+
+Link* ECData::getFirst() {
+  return d_first;
+}
+
+void ECData::takeOverDescendantWatchList(ECData* nslave, ECData* nmaster) {
+  Assert(nslave != nmaster);
+  Assert(nslave->getFind() == nmaster);
+
+  nmaster->makeCurrent();
+
+  nmaster->d_watchListSize += nslave->d_watchListSize;
+
+  if(nmaster->d_first == NULL) {
+    nmaster->d_first = nslave->d_first;
+    nmaster->d_last = nslave->d_last;
+  } else if(nslave->d_first != NULL) {
+    Link* currLast = nmaster->d_last;
+    currLast->d_next = nslave->d_first;
+    nmaster->d_last = nslave->d_last;
+  }
+}
diff --git a/src/theory/uf/tim/ecdata.h b/src/theory/uf/tim/ecdata.h
new file mode 100644 (file)
index 0000000..5e72f00
--- /dev/null
@@ -0,0 +1,261 @@
+/*********************                                                        */
+/*! \file ecdata.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: mdeters
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Context dependent equivalence class datastructure for nodes.
+ **
+ ** Context dependent equivalence class datastructure for nodes.
+ ** Currently keeps a context dependent watch list.
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__UF__TIM__ECDATA_H
+#define __CVC4__THEORY__UF__TIM__ECDATA_H
+
+#include "expr/node.h"
+#include "context/context.h"
+#include "context/cdo.h"
+#include "context/context_mm.h"
+
+namespace CVC4 {
+namespace theory {
+namespace uf {
+namespace tim {
+
+/**
+ * Link is a context dependent linked list of nodes.
+ * Link is intended to be allocated in a Context's memory manager.
+ * The next pointer of the list is context dependent, but the node being
+ * pointed to is fixed for the life of the Link.
+ *
+ * Clients of Link are intended not to modify the node that is being pointed
+ * to in good faith.  This may change in the future.
+ */
+struct Link {
+  /**
+   * Pointer to the next element in linked list.
+   * This is context dependent. 
+   */
+  context::CDO<Link*> d_next;
+
+  /**
+   * Link is supposed to be allocated in a region of a
+   * ContextMemoryManager.  In order to avoid having to decrement the
+   * ref count at deletion time, it is preferrable for the user of
+   * Link to maintain the invariant that data will survival for the
+   * entire scope of the TNode.
+   */
+  TNode d_data;
+
+  /**
+   * Creates a new Link w.r.t. a context for the node n.
+   * An optional parameter is to specify the next element in the link.
+   */
+  Link(context::Context* context, TNode n, Link* l = NULL) :
+    d_next(true, context, l),
+    d_data(n) {
+    Debug("context") << "Link: " << this
+                     << " so cdo is " << &d_next << std::endl;
+  }
+
+  /**
+   * Allocates a new Link in the region for the provided ContextMemoryManager.
+   * This allows for cheap cleanup on pop.
+   */
+  static void* operator new(size_t size, context::ContextMemoryManager* pCMM) {
+    return pCMM->newData(size);
+  }
+
+private:
+
+  /**
+   * The destructor isn't actually defined.  This declaration keeps
+   * the compiler from creating (wastefully) a default definition, and
+   * ensures that we get a link error if someone uses Link in a way
+   * that requires destruction.  Objects of class Link should always
+   * be allocated in a ContextMemoryManager, which doesn't call
+   * destructors.
+   */
+  ~Link() throw();
+
+  /**
+   * Just like the destructor, this is not defined.  This ensures no
+   * one tries to create a Link on the heap.
+   */
+  static void* operator new(size_t size);
+
+};/* struct Link */
+
+
+/**
+ * ECData is a equivalence class object that is context dependent.
+ * It is developed in order to support the congruence closure algorithm
+ * in TheoryUF, and is not intended to be used outside of that package.
+ *
+ * ECData maintains:
+ * - find pointer for the equivalence class (disjoint set forest)
+ * - the node that represents the equivalence class.
+ * - maintains a predecessorlist/watchlist
+ *
+ * ECData does not have support for the canonical find and union operators
+ * for disjoint set forests.  Instead it only provides access to the find
+ * pointer. The implementation of find is ccFind in TheoryUF.
+ * union is broken into 2 phases:
+ *  1) setting the find point with setFind
+ *  2) taking over the watch list of the other node.
+ * This is a technical requirement for the implementation of TheoryUF.
+ * (See ccUnion in TheoryUF for more information.)
+ *
+ * The intended paradigm for iterating over the watch list of ec is:
+ *      for(Link* i = ec->getFirst(); i != NULL; i = i->next );
+ *
+ * See also ECAttr() in theory_uf.h, and theory_uf.cpp where the codde that uses
+ * ECData lives.
+ */
+class ECData : public context::ContextObj {
+private:
+  /**
+   * This is the standard disjoint set forest find pointer.
+   *
+   * Why an ECData pointer instead of a node?
+   * This was chosen to be a ECData pointer in order to shortcut at least one
+   * table every time the find pointer is examined.
+   */
+  ECData* d_find;
+
+  /**
+   * This is pointer back to the node that represents this equivalence class.
+   *
+   * The following invariant should be maintained:
+   *  (n.getAttribute(ECAttr()))->rep == n
+   * i.e. rep is equal to the node that maps to the ECData using ECAttr.
+   *
+   * Tricky part: This needs to be a TNode, not a Node.
+   * Suppose that rep were a hard link.
+   * When a node n maps to an ECData via the ECAttr() there will be a hard
+   * link back to n in the ECData. The attribute does not do garbage collection
+   * until the node gets garbage collected, which does not happen until its
+   * ref count drops to 0. So because of this cycle neither the node and
+   * the ECData will never get garbage collected.
+   * So this needs to be a soft link.
+   */
+  TNode d_rep;
+
+  // Watch list data structures follow
+
+  /**
+   * Maintains watch list size for more efficient merging.
+   */
+  unsigned d_watchListSize;
+
+  /**
+   * Pointer to the beginning of the watchlist.
+   * This value is NULL iff the watch list is empty.
+   */
+  Link* d_first;
+
+  /**
+   * Pointer to the end of the watch-list.
+   * This is maintained in order to constant time list merging.
+   * (This does not give any asymptotic improve as this is currently always
+   * preceeded by an O(|watchlist|) operation.)
+   * This value is NULL iff the watch list is empty.
+   */
+  Link* d_last;
+
+  /** Context-dependent operation: save this ECData */
+  context::ContextObj* save(context::ContextMemoryManager* pCMM);
+
+  /** Context-dependent operation: restore this ECData */
+  void restore(context::ContextObj* pContextObj);
+
+public:
+  /**
+   * Returns true if this ECData object is the current representative of
+   * the equivalence class.
+   */
+  bool isClassRep();
+
+  /**
+   * Adds a node to the watch list of the equivalence class.  Does
+   * context-dependent memory allocation in the Context with which
+   * this ECData was created.
+   *
+   * @param n the node to be added.
+   * @pre isClassRep() == true
+   */
+  void addPredecessor(TNode n);
+
+  /**
+   * Creates a EQ with the representative n
+   * @param context the context to associate with this ecdata.
+   *   This is required as ECData is context dependent
+   * @param n the node that corresponds to this ECData
+   */
+  ECData(context::Context* context, TNode n);
+
+  /** Destructor for ECDatas */
+  ~ECData() {
+    Debug("ufgc") << "Calling ECData destructor" << std::endl;
+    destroy();
+  }
+
+  /**
+   * An ECData takes over the watch list of another ECData.
+   * This is the second step in the union operator for ECData.
+   * This should be called after nslave->setFind(nmaster);
+   * After this is done nslave's watch list should never be accessed by
+   * getLast() or getFirst()
+   */
+  static void takeOverDescendantWatchList(ECData * nslave, ECData * nmaster);
+
+  /**
+   * Returns the representative of this ECData.
+   */
+  Node getRep();
+
+  /**
+   * Returns the size of the equivalence class.
+   */
+  unsigned getWatchListSize();
+
+  /**
+   * Returns a pointer the first member of the watch list.
+   */
+  Link* getFirst();
+
+
+  /**
+   * Returns the find pointer of the ECData.
+   * If isClassRep(), then getFind() == this
+   */
+  ECData* getFind();
+
+  /**
+   * Sets the find pointer of the equivalence class to be another ECData object.
+   *
+   * @pre isClassRep() == true
+   * @pre ec->isClassRep() == true
+   * @post isClassRep() == false
+   * @post ec->isClassRep() == true
+   */
+  void setFind(ECData * ec);
+
+};/* class ECData */
+
+}/* CVC4::theory::uf::tim namespace */
+}/* CVC4::theory::uf namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__UF__TIM__ECDATA_H */
diff --git a/src/theory/uf/tim/theory_uf_tim.cpp b/src/theory/uf/tim/theory_uf_tim.cpp
new file mode 100644 (file)
index 0000000..ccc37a2
--- /dev/null
@@ -0,0 +1,332 @@
+/*********************                                                        */
+/*! \file theory_uf_tim.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: mdeters
+ ** Minor contributors (to current version): dejan
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Implementation of the theory of uninterpreted functions.
+ **
+ ** Implementation of the theory of uninterpreted functions.
+ **/
+
+#include "theory/uf/tim/theory_uf_tim.h"
+#include "theory/uf/tim/ecdata.h"
+#include "expr/kind.h"
+
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::context;
+using namespace CVC4::theory;
+using namespace CVC4::theory::uf;
+using namespace CVC4::theory::uf::tim;
+
+TheoryUFTim::TheoryUFTim(int id, Context* c, OutputChannel& out) :
+  TheoryUF(id, c, out),
+  d_assertions(c),
+  d_pending(c),
+  d_currentPendingIdx(c,0),
+  d_disequality(c),
+  d_registered(c) {
+}
+
+TheoryUFTim::~TheoryUFTim() {
+}
+
+Node TheoryUFTim::rewrite(TNode n){
+  Debug("uf") << "uf: begin rewrite(" << n << ")" << std::endl;
+  Node ret(n);
+  if(n.getKind() == EQUAL){
+    Assert(n.getNumChildren() == 2);
+    if(n[0] == n[1]) {
+      ret = NodeManager::currentNM()->mkConst(true);
+    }
+  }
+  Debug("uf") << "uf: end rewrite(" << n << ") : " << ret << std::endl;
+  return ret;
+}
+void TheoryUFTim::preRegisterTerm(TNode n) {
+  Debug("uf") << "uf: begin preRegisterTerm(" << n << ")" << std::endl;
+  Debug("uf") << "uf: end preRegisterTerm(" << n << ")" << std::endl;
+}
+
+void TheoryUFTim::registerTerm(TNode n) {
+
+  Debug("uf") << "uf: begin registerTerm(" << n << ")" << std::endl;
+
+  d_registered.push_back(n);
+
+  ECData* ecN;
+
+  if(n.getAttribute(ECAttr(), ecN)) {
+    /* registerTerm(n) is only called when a node has not been seen in the
+     * current context.  ECAttr() is not a context-dependent attribute.
+     * When n.hasAttribute(ECAttr(),...) is true on a registerTerm(n) call,
+     * then it must be the case that this attribute was created in a previous
+     * and no longer valid context. Because of this we have to reregister the
+     * predecessors lists.
+     * Also we do not have to worry about duplicates because all of the Link*
+     * setup before are removed when the context n was setup in was popped out
+     * of. All we are going to do here are sanity checks.
+     */
+
+    /*
+     * Consider the following chain of events:
+     * 1) registerTerm(n) is called on node n where n : f(m) in context level X,
+     * 2) A new ECData is created on the heap, ecN,
+     * 3) n is added to the predessecor list of m in context level X,
+     * 4) We pop out of X,
+     * 5) n is removed from the predessecor list of m because this is context
+     *    dependent, the Link* will be destroyed and pointers to the Link
+     *    structs in the ECData objects will be updated.
+     * 6) registerTerm(n) is called on node n in context level Y,
+     * 7) If n.hasAttribute(ECAttr(), &ecN), then ecN is still around,
+     *    but the predecessor list is not
+     *
+     * The above assumes that the code is working correctly.
+     */
+    Assert(ecN->getFirst() == NULL,
+           "Equivalence class data exists for the node being registered.  "
+           "Expected getFirst() == NULL.  "
+           "This data is either already in use or was not properly maintained "
+           "during backtracking");
+    /*Assert(ecN->getLast() == NULL,
+           "Equivalence class data exists for the node being registered.  "
+           "Expected getLast() == NULL.  "
+           "This data is either already in use or was not properly maintained "
+           "during backtracking.");*/
+    Assert(ecN->isClassRep(),
+           "Equivalence class data exists for the node being registered.  "
+           "Expected isClassRep() to be true.  "
+           "This data is either already in use or was not properly maintained "
+           "during backtracking");
+    Assert(ecN->getWatchListSize() == 0,
+           "Equivalence class data exists for the node being registered.  "
+           "Expected getWatchListSize() == 0.  "
+           "This data is either already in use or was not properly maintained "
+           "during backtracking");
+  } else {
+    //The attribute does not exist, so it is created and set
+    ecN = new (true) ECData(getContext(), n);
+    n.setAttribute(ECAttr(), ecN);
+  }
+
+  /* If the node is an APPLY_UF, we need to add it to the predecessor list
+   * of its children.
+   */
+  if(n.getKind() == APPLY_UF) {
+    TNode::iterator cIter = n.begin();
+
+    for(; cIter != n.end(); ++cIter) {
+      TNode child = *cIter;
+
+      /* Because this can be called after nodes have been merged, we need
+       * to lookup the representative in the UnionFind datastructure.
+       */
+      ECData* ecChild = ccFind(child.getAttribute(ECAttr()));
+
+      /* Because this can be called after nodes have been merged we may need
+       * to be merged with other predecessors of the equivalence class.
+       */
+      for(Link* Px = ecChild->getFirst(); Px != NULL; Px = Px->d_next ) {
+        if(equiv(n, Px->d_data)) {
+          Node pend = n.eqNode(Px->d_data);
+          d_pending.push_back(pend);
+        }
+      }
+
+      ecChild->addPredecessor(n);
+    }
+  }
+  Debug("uf") << "uf: end registerTerm(" << n << ")" << std::endl;
+
+}
+
+bool TheoryUFTim::sameCongruenceClass(TNode x, TNode y) {
+  return
+    ccFind(x.getAttribute(ECAttr())) ==
+    ccFind(y.getAttribute(ECAttr()));
+}
+
+bool TheoryUFTim::equiv(TNode x, TNode y) {
+  Assert(x.getKind() == kind::APPLY_UF);
+  Assert(y.getKind() == kind::APPLY_UF);
+
+  if(x.getNumChildren() != y.getNumChildren()) {
+    return false;
+  }
+
+  if(x.getOperator() != y.getOperator()) {
+    return false;
+  }
+
+  // intentionally don't look at operator
+
+  TNode::iterator xIter = x.begin();
+  TNode::iterator yIter = y.begin();
+
+  while(xIter != x.end()) {
+
+    if(!sameCongruenceClass(*xIter, *yIter)) {
+      return false;
+    }
+
+    ++xIter;
+    ++yIter;
+  }
+  return true;
+}
+
+/* This is a very basic, but *obviously correct* find implementation
+ * of the classic find algorithm.
+ * TODO after we have done some more testing:
+ * 1) Add path compression.  This is dependent on changes to ccUnion as
+ *    many better algorithms use eager path compression.
+ * 2) Elminate recursion.
+ */
+ECData* TheoryUFTim::ccFind(ECData * x) {
+  if(x->getFind() == x) {
+    return x;
+  } else {
+    return ccFind(x->getFind());
+  }
+  /* Slightly better Find w/ path compression and no recursion*/
+  /*
+    ECData* start;
+    ECData* next = x;
+    while(x != x->getFind()) x=x->getRep();
+    while( (start = next) != x) {
+      next = start->getFind();
+      start->setFind(x);
+    }
+    return x;
+  */
+}
+
+void TheoryUFTim::ccUnion(ECData* ecX, ECData* ecY) {
+  ECData* nslave;
+  ECData* nmaster;
+
+  if(ecX->getWatchListSize() <= ecY->getWatchListSize()) {
+    nslave = ecX;
+    nmaster = ecY;
+  } else {
+    nslave = ecY;
+    nmaster = ecX;
+  }
+
+  nslave->setFind(nmaster);
+
+  for(Link* Px = nmaster->getFirst(); Px != NULL; Px = Px->d_next ) {
+    for(Link* Py = nslave->getFirst(); Py != NULL; Py = Py->d_next ) {
+      if(equiv(Px->d_data,Py->d_data)) {
+        Node pendingEq = (Px->d_data).eqNode(Py->d_data);
+        d_pending.push_back(pendingEq);
+      }
+    }
+  }
+
+  ECData::takeOverDescendantWatchList(nslave, nmaster);
+}
+
+void TheoryUFTim::merge() {
+  while(d_currentPendingIdx < d_pending.size() ) {
+    Node assertion = d_pending[d_currentPendingIdx];
+    d_currentPendingIdx = d_currentPendingIdx + 1;
+
+    TNode x = assertion[0];
+    TNode y = assertion[1];
+
+    ECData* tmpX = x.getAttribute(ECAttr());
+    ECData* tmpY = y.getAttribute(ECAttr());
+
+    ECData* ecX = ccFind(tmpX);
+    ECData* ecY = ccFind(tmpY);
+    if(ecX == ecY)
+      continue;
+
+    Debug("uf") << "merging equivalence classes for " << std::endl;
+    Debug("uf") << "left equivalence class :" << (ecX->getRep()) << std::endl;
+    Debug("uf") << "right equivalence class :" << (ecY->getRep()) << std::endl;
+    Debug("uf") << std::endl;
+
+    ccUnion(ecX, ecY);
+  }
+}
+
+Node TheoryUFTim::constructConflict(TNode diseq) {
+  Debug("uf") << "uf: begin constructConflict()" << std::endl;
+
+  NodeBuilder<> nb(kind::AND);
+  nb << diseq;
+  for(unsigned i = 0; i < d_assertions.size(); ++i) {
+    nb << d_assertions[i];
+  }
+
+  Assert(nb.getNumChildren() > 0);
+  Node conflict = nb.getNumChildren() == 1 ? nb[0] : nb;
+
+  Debug("uf") << "conflict constructed : " << conflict << std::endl;
+
+  Debug("uf") << "uf: ending constructConflict()" << std::endl;
+
+  return conflict;
+}
+
+void TheoryUFTim::check(Effort level) {
+
+  Debug("uf") << "uf: begin check(" << level << ")" << std::endl;
+
+  while(!done()) {
+    Node assertion = get();
+    Debug("uf") << "TheoryUFTim::check(): " << assertion << std::endl;
+
+    switch(assertion.getKind()) {
+    case EQUAL:
+      d_assertions.push_back(assertion);
+      d_pending.push_back(assertion);
+      merge();
+      break;
+    case NOT:
+      Assert(assertion[0].getKind() == EQUAL,
+             "predicates not supported in this UF implementation");
+      d_disequality.push_back(assertion[0]);
+      break;
+    case APPLY_UF:
+      Unhandled("predicates not supported in this UF implementation");
+    default:
+      Unhandled(assertion.getKind());
+    }
+
+    Debug("uf") << "TheoryUFTim::check(): done = " << (done() ? "true" : "false") << std::endl;
+  }
+
+  //Make sure all outstanding merges are completed.
+  if(d_currentPendingIdx < d_pending.size()) {
+    merge();
+  }
+
+  if(standardEffortOrMore(level)) {
+    for(CDList<Node>::const_iterator diseqIter = d_disequality.begin();
+        diseqIter != d_disequality.end();
+        ++diseqIter) {
+
+      TNode left  = (*diseqIter)[0];
+      TNode right = (*diseqIter)[1];
+      if(sameCongruenceClass(left, right)) {
+        Node remakeNeq = (*diseqIter).notNode();
+        Node conflict = constructConflict(remakeNeq);
+        d_out->conflict(conflict, false);
+        return;
+      }
+    }
+  }
+
+  Debug("uf") << "uf: end check(" << level << ")" << std::endl;
+}
diff --git a/src/theory/uf/tim/theory_uf_tim.h b/src/theory/uf/tim/theory_uf_tim.h
new file mode 100644 (file)
index 0000000..1591cc0
--- /dev/null
@@ -0,0 +1,229 @@
+/*********************                                                        */
+/*! \file theory_uf.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: mdeters
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief This is a basic implementation of the Theory of Uninterpreted Functions
+ ** with Equality.
+ **
+ ** This is a basic implementation of the Theory of Uninterpreted Functions
+ ** with Equality.  It is based on the Nelson-Oppen algorithm given in
+ ** "Fast Decision Procedures Based on Congruence Closure"
+ **  (http://portal.acm.org/ft_gateway.cfm?id=322198&type=pdf)
+ ** This has been extended to work in a context-dependent way.
+ ** This interacts heavily with the data-structures given in ecdata.h .
+ **
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__UF__TIM__THEORY_UF_TIM_H
+#define __CVC4__THEORY__UF__TIM__THEORY_UF_TIM_H
+
+#include "expr/node.h"
+#include "expr/attribute.h"
+
+#include "theory/theory.h"
+
+#include "context/context.h"
+#include "context/cdo.h"
+#include "context/cdlist.h"
+#include "theory/uf/theory_uf.h"
+#include "theory/uf/tim/ecdata.h"
+
+namespace CVC4 {
+namespace theory {
+namespace uf {
+namespace tim {
+
+class TheoryUFTim : public TheoryUF {
+
+private:
+
+  /**
+   * List of all of the non-negated literals from the assertion queue.
+   * This is used only for conflict generation.
+   * This differs from pending as the program generates new equalities that
+   * are not in this list.
+   * This will probably be phased out in future version.
+   */
+  context::CDList<Node> d_assertions;
+
+  /**
+   * List of pending equivalence class merges.
+   *
+   * Tricky part:
+   * Must keep a hard link because new equality terms are created and appended
+   * to this list.
+   */
+  context::CDList<Node> d_pending;
+
+  /** Index of the next pending equality to merge. */
+  context::CDO<unsigned> d_currentPendingIdx;
+
+  /** List of all disequalities this theory has seen. */
+  context::CDList<Node> d_disequality;
+
+  /**
+   * List of all of the terms that are registered in the current context.
+   * When registerTerm is called on a term we want to guarentee that there
+   * is a hard link to the term for the duration of the context in which
+   * register term is called.
+   * This invariant is enough for us to use soft links where we want is the
+   * current implementation as well as making ECAttr() not context dependent.
+   * Soft links used both in ECData, and Link.
+   */
+  context::CDList<Node> d_registered;
+
+public:
+
+  /** Constructs a new instance of TheoryUF w.r.t. the provided context.*/
+  TheoryUFTim(int id, context::Context* c, OutputChannel& out);
+
+  /** Destructor for the TheoryUF object. */
+  ~TheoryUFTim();
+
+
+
+  /**
+   * Registers a previously unseen [in this context] node n.
+   * For TheoryUF, this sets up and maintains invaraints about
+   * equivalence class data-structures.
+   *
+   * Overloads a void registerTerm(TNode n); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  void registerTerm(TNode n);
+
+  /**
+   * Currently this does nothing.
+   *
+   * Overloads a void preRegisterTerm(TNode n); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  void preRegisterTerm(TNode n);
+
+  /**
+   * Checks whether the set of literals provided to the theory is consistent.
+   *
+   * If this is called at any effort level, it computes the congruence closure
+   * of all of the positive literals in the context.
+   *
+   * If this is called at full effort it checks if any of the negative literals
+   * are inconsistent with the congruence closure.
+   *
+   * Overloads  void check(Effort level); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  void check(Effort level);
+
+
+  /**
+   * Rewrites a node in the theory of uninterpreted functions.
+   * This is fairly basic and only ensures that atoms that are
+   * unsatisfiable or a valid are rewritten to false or true respectively.
+   */
+  Node rewrite(TNode n);
+
+  /**
+   * Plug in old rewrite to the new (pre,post)rewrite interface.
+   */
+  RewriteResponse postRewrite(TNode n, bool topLevel) {
+    return RewriteComplete(topLevel ? rewrite(n) : Node(n));
+  }
+
+  /**
+   * Propagates theory literals. Currently does nothing.
+   *
+   * Overloads void propagate(Effort level); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  void propagate(Effort level) {}
+
+  /**
+   * Explains a previously reported conflict. Currently does nothing.
+   *
+   * Overloads void explain(TNode n, Effort level); from theory.h.
+   * See theory/theory.h for more information about this method.
+   */
+  void explain(TNode n, Effort level) {}
+
+  std::string identify() const { return std::string("TheoryUFTim"); }
+
+private:
+  /**
+   * Checks whether 2 nodes are already in the same equivalence class tree.
+   * This should only be used internally, and it should only be called when
+   * the only thing done with the equivalence classes is an equality check.
+   *
+   * @returns true iff ccFind(x) == ccFind(y);
+   */
+  bool sameCongruenceClass(TNode x, TNode y);
+
+  /**
+   * Checks whether Node x and Node y are currently congruent
+   * using the equivalence class data structures.
+   * @returns true iff
+   *    |x| = n = |y| and
+   *    x.getOperator() == y.getOperator() and
+   *    forall 1 <= i < n : ccFind(x[i]) == ccFind(y[i])
+   */
+  bool equiv(TNode x, TNode y);
+
+  /**
+   * Merges 2 equivalence classes, checks wether any predecessors need to
+   * be set equal to complete congruence closure.
+   * The class with the smaller class size will be merged.
+   * @pre ecX->isClassRep()
+   * @pre ecY->isClassRep()
+   */
+  void ccUnion(ECData* ecX, ECData* ecY);
+
+  /**
+   * Returns the representative of the equivalence class.
+   * May modify the find pointers associated with equivalence classes.
+   */
+  ECData* ccFind(ECData* x);
+
+  /** Performs Congruence Closure to reflect the new additions to d_pending. */
+  void merge();
+
+  /** Constructs a conflict from an inconsistent disequality. */
+  Node constructConflict(TNode diseq);
+
+};/* class TheoryUFTim */
+
+
+/**
+ * Cleanup function for ECData. This will be used for called whenever
+ * a ECAttr is being destructed.
+ */
+struct ECCleanupStrategy {
+  static void cleanup(ECData* ec) {
+    Debug("ufgc") << "cleaning up ECData " << ec << "\n";
+    ec->deleteSelf();
+  }
+};/* struct ECCleanupStrategy */
+
+/** Unique name to use for constructing ECAttr. */
+struct ECAttrTag {};
+
+/**
+ * ECAttr is the attribute that maps a node to an equivalence class.
+ */
+typedef expr::Attribute<ECAttrTag, ECData*, ECCleanupStrategy> ECAttr;
+
+}/* CVC4::theory::uf::tim namespace */
+}/* CVC4::theory::uf namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__UF__TIM__THEORY_UF_TIM_H */
diff --git a/src/util/congruence_closure.cpp b/src/util/congruence_closure.cpp
new file mode 100644 (file)
index 0000000..82e6584
--- /dev/null
@@ -0,0 +1,33 @@
+/*********************                                                        */
+/*! \file congruence_closure.cpp
+ ** \verbatim
+ ** Original author: mdeters
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief The congruence closure module implementation
+ **
+ ** The congruence closure module implementation.
+ **/
+
+#include "util/congruence_closure.h"
+#include "util/Assert.h"
+
+#include <string>
+#include <list>
+#include <algorithm>
+#include <utility>
+#include <ext/hash_map>
+
+using namespace std;
+
+namespace CVC4 {
+
+
+}/* CVC4 namespace */
index 1727b3b301e0f133848656b866daf7f9c7505b3d..058f9c1930fb103c404def32df283f533f3201ce 100644 (file)
 #define __CVC4__UTIL__CONGRUENCE_CLOSURE_H
 
 #include <list>
-#include <algorithm>
-#include <utility>
 #include <ext/hash_map>
+#include <ext/hash_set>
+#include <sstream>
 
 #include "expr/node_manager.h"
 #include "expr/node.h"
+#include "context/context.h"
 #include "context/cdmap.h"
+#include "context/cdset.h"
+#include "context/cdlist.h"
+#include "util/exception.h"
 
 namespace CVC4 {
 
+template <class OutputChannel>
+class CongruenceClosure;
+
+template <class OutputChannel>
+std::ostream& operator<<(std::ostream& out,
+                         const CongruenceClosure<OutputChannel>& cc);
+
+class CVC4_PUBLIC CongruenceClosureException : public Exception {
+public:
+  inline CongruenceClosureException(std::string msg) :
+    Exception(std::string("Congruence closure exception: ") + msg) {}
+  inline CongruenceClosureException(const char* msg) :
+    Exception(std::string("Congruence closure exception: ") + msg) {}
+};/* class CongruenceClosureException */
+
+
 /**
  * Congruence closure module for CVC4.
  *
@@ -43,67 +63,206 @@ namespace CVC4 {
  * OutputChannel is a template argument (it's probably a thin layer,
  * and we want to avoid a virtual call hierarchy in this case, and
  * enable aggressive inlining).  It need only implement one method,
- * merge():
+ * notifyCongruent():
  *
  *   class MyOutputChannel {
  *   public:
- *     void merge(TNode a, TNode b) {
+ *     void notifyCongruent(TNode a, TNode b) {
  *       // CongruenceClosure is notifying us that "a" is now the EC
  *       // representative for "b" in this context.  After a pop, "a"
  *       // will be its own representative again.  Note that "a" and
  *       // "b" might never have been added with addTerm().  However,
  *       // something in their equivalence class was (for which a
- *       // previous merge() would have notified you of their EC
- *       // representative, which is now "a" or "b").
+ *       // previous notifyCongruent() would have notified you of
+ *       // their EC representative, which is now "a" or "b").
  *       //
- *       // This call is made immediately after the merge is done, and
- *       // while other pending merges may be on the queue.  In particular,
- *       // any exception thrown from this function will immediately
- *       // exit the CongruenceClosure call.  A subsequent call to
- *       // doPendingMerges() is necessary to continue merging;
- *       // doPendingMerges() is automatically done at the very beginning
- *       // of every call, including find() and areCongruent(), to ensure
- *       // consistency.  However, if the context pops, the pending merges
- *       // up to that level are cleared.
+ *       // This call is made while the merge is being done.  If you
+ *       // throw any exception here, you'll immediately exit the
+ *       // CongruenceClosure call and will miss whatever pending
+ *       // merges were being performed, leaving the CongruenceClosure
+ *       // module in a bad state.  Therefore if you throw, you should
+ *       // only do so if you are going to backjump, re-establishing a
+ *       // good (former) state.  Keep this in mind if a
+ *       // CVC4::Interrupted that *doesn't* lead to a backjump can
+ *       // interrupt you.
  *     }
  *   };
  */
 template <class OutputChannel>
 class CongruenceClosure {
+  /** The context */
+  context::Context* d_context;
+
   /** The output channel */
   OutputChannel* d_out;
 
+  // typedef all of these so that iterators are easy to define
+  typedef context::CDMap<Node, Node, NodeHashFunction> RepresentativeMap;
+  typedef context::CDList<Node> ClassList;
+  typedef context::CDMap<Node, ClassList*, NodeHashFunction> ClassLists;
+  typedef context::CDList<Node> UseList;
+  typedef context::CDMap<TNode, UseList*, TNodeHashFunction> UseLists;
+  typedef context::CDMap<Node, Node, NodeHashFunction> LookupMap;
+
+  typedef context::CDMap<TNode, Node, NodeHashFunction> EqMap;
+
+  typedef context::CDMap<Node, Node, NodeHashFunction> ProofMap;
+  typedef context::CDMap<Node, Node, NodeHashFunction> ProofLabel;
+
+  RepresentativeMap d_representative;
+  ClassLists d_classList;
+  UseLists d_useList;
+  LookupMap d_lookup;
+
+  EqMap d_eqMap;
+
+  ProofMap d_proof;
+  ProofLabel d_proofLabel;
+
+  ProofMap d_proofRewrite;
+
+  /**
+   * The set of terms we care about (i.e. those that have been given
+   * us with addTerm() and their representatives).
+   */
+  typedef context::CDSet<Node, NodeHashFunction> CareSet;
+  CareSet d_careSet;
+
 public:
   /** Construct a congruence closure module instance */
   CongruenceClosure(context::Context* ctxt, OutputChannel* out)
-    throw(AssertionException);
+    throw(AssertionException) :
+    d_context(ctxt),
+    d_out(out),
+    d_representative(ctxt),
+    d_classList(ctxt),
+    d_useList(ctxt),
+    d_lookup(ctxt),
+    d_eqMap(ctxt),
+    d_proof(ctxt),
+    d_proofLabel(ctxt),
+    d_proofRewrite(ctxt),
+    d_careSet(ctxt) {
+  }
 
   /**
-   * Add a term into CC consideration.
+   * Add a term into CC consideration.  This is context-dependent.
+   * Calls OutputChannel::notifyCongruent(), so it can throw anything
+   * that that function can.
    */
-  void addTerm(TNode trm) throw(AssertionException);
+  void addTerm(TNode trm);
 
   /**
-   * Add an equality literal eq into CC consideration.
+   * Add an equality literal eq into CC consideration.  This is
+   * context-dependent.  Calls OutputChannel::notifyCongruent()
+   * indirectly, so it can throw anything that that function can.
    */
-  void addEquality(TNode eq) throw(AssertionException);
+  void addEquality(TNode inputEq) {
+    Debug("cc") << "CC addEquality[" << d_context->getLevel() << "]: " << inputEq << std::endl;
+    Assert(inputEq.getKind() == kind::EQUAL);
+    NodeBuilder<> eqb(kind::EQUAL);
+    if(inputEq[1].getKind() == kind::APPLY_UF &&
+       inputEq[0].getKind() != kind::APPLY_UF) {
+      eqb << flatten(inputEq[1]) << inputEq[0];
+    } else {
+      eqb << flatten(inputEq[0]) << replace(flatten(inputEq[1]));
+    }
+    Node eq = eqb;
+    addEq(eq, inputEq);
+  }
+  void addEq(TNode eq, TNode inputEq);
+
+  Node flatten(TNode t) {
+    if(t.getKind() == kind::APPLY_UF) {
+      NodeBuilder<> appb(kind::APPLY_UF);
+      appb << replace(flatten(t.getOperator()));
+      for(TNode::iterator i = t.begin(); i != t.end(); ++i) {
+        appb << replace(flatten(*i));
+      }
+      return Node(appb);
+    } else {
+      return t;
+    }
+  }
+
+  Node replace(TNode t) {
+    if(t.getKind() == kind::APPLY_UF) {
+      EqMap::iterator i = d_eqMap.find(t);
+      if(i == d_eqMap.end()) {
+        Node v = NodeManager::currentNM()->mkSkolem(t.getType());
+        addEq(NodeManager::currentNM()->mkNode(kind::EQUAL, t, v), TNode::null());
+        d_eqMap.insert(t, v);
+        return v;
+      } else {
+        return (*i).second;
+      }
+    } else {
+      return t;
+    }
+  }
+
+  TNode proofRewrite(TNode pfStep) {
+    ProofMap::const_iterator i = d_proofRewrite.find(pfStep);
+    if(i == d_proofRewrite.end()) {
+      return pfStep;
+    } else {
+      return (*i).second;
+    }
+  }
 
   /**
-   * Inquire whether two expressions are congruent.
+   * Inquire whether two expressions are congruent in the current
+   * context.
    */
-  bool areCongruent(TNode a, TNode b) throw(AssertionException);
+  inline bool areCongruent(TNode a, TNode b) const throw(AssertionException) {
+    Debug("cc") << "CC areCongruent? " << a << "  ==  " << b << std::endl;
+    Debug("cc") << "  a  " << a << std::endl;
+    Debug("cc") << "  a' " << normalize(a) << std::endl;
+    Debug("cc") << "  b  " << b << std::endl;
+    Debug("cc") << "  b' " << normalize(b) << std::endl;
+
+    Node ap = find(a), bp = find(b);
+
+    // areCongruent() works fine as just find(a) == find(b) _except_
+    // for terms not appearing in equalities.  For example, let's say
+    // you have unary f and binary g, h, and
+    //
+    //   a == f(b) ; f(a) == b ; g == h
+    //
+    // it's clear that h(f(a),a) == g(b,a), but it's not in the
+    // union-find even if you addTerm() on those two.
+    //
+    // we implement areCongruent() to handle more general
+    // queries---i.e., to check for new congruences---but shortcut a
+    // cheap & common case
+    //
+    return ap == bp || normalize(ap) == normalize(bp);
+  }
 
   /**
-   * Find the EC representative for a term t
+   * Find the EC representative for a term t in the current context.
    */
-  TNode find(TNode t) throw(AssertionException);
+  inline TNode find(TNode t) const throw(AssertionException) {
+    context::CDMap<Node, Node, NodeHashFunction>::iterator i =
+      d_representative.find(t);
+    return (i == d_representative.end()) ? t : TNode((*i).second);
+  }
+
+  void explainAlongPath(TNode a, TNode c, std::list<std::pair<Node, Node> >& pending, __gnu_cxx::hash_map<Node, Node, NodeHashFunction>& unionFind, std::list<Node>& pf)
+    throw(AssertionException);
+
+  Node highestNode(TNode a, __gnu_cxx::hash_map<Node, Node, NodeHashFunction>& unionFind)
+    throw(AssertionException);
+
+  Node nearestCommonAncestor(TNode a, TNode b)
+    throw(AssertionException);
 
   /**
-   * Request an explanation for why a and b are in the same EC.
-   * Throws a CongruenceClosureException if they aren't in the same
-   * EC.
+   * Request an explanation for why a and b are in the same EC in the
+   * current context.  Throws a CongruenceClosureException if they
+   * aren't in the same EC.
    */
-  Node explain(TNode a, TNode b)
+  Node explain(Node a, Node b)
     throw(CongruenceClosureException, AssertionException);
 
   /**
@@ -111,38 +270,641 @@ public:
    * are in the same EC.  Throws a CongruenceClosureException if they
    * aren't in the same EC.
    */
-  Node explain(TNode eq)
-    throw(CongruenceClosureException, AssertionException);
+  inline Node explain(TNode eq)
+    throw(CongruenceClosureException, AssertionException) {
+    Assert(eq.getKind() == kind::EQUAL);
+    return explain(eq[0], eq[1]);
+  }
+
+private:
+
+  friend std::ostream& operator<< <>(std::ostream& out,
+                                     const CongruenceClosure<OutputChannel>& cc);
 
   /**
-   * Request that any pending merges (canceled with an
-   * exception thrown from OutputChannel::merge()) be
-   * performed and the OutputChannel notified.
+   * Internal propagation of information.  Propagation tends to
+   * cascade (this implementation uses an iterative "pending"
+   * worklist), and "seed" is the "seeding" propagation to do (the
+   * first one).  Calls OutputChannel::notifyCongruent() indirectly,
+   * so it can throw anything that that function can.
    */
-  void doPendingMerges() throw(AssertionException);
-
-private:
+  void propagate(TNode seed);
 
   /**
-   * Internal propagation of information.
+   * Internal lookup mapping from tuples to equalities.
    */
-  void propagate();
+  inline TNode lookup(TNode a) const {
+    context::CDMap<Node, Node, NodeHashFunction>::iterator i = d_lookup.find(a);
+    if(i == d_lookup.end()) {
+      return TNode::null();
+    } else {
+      TNode l = (*i).second;
+      Assert(l.getKind() == kind::EQUAL);
+      return l;
+    }
+  }
 
   /**
    * Internal lookup mapping.
    */
-  TNode lookup(TNode a);
+  inline void setLookup(TNode a, TNode b) {
+    Assert(a.getKind() == kind::TUPLE);
+    Assert(b.getKind() == kind::EQUAL);
+    d_lookup[a] = b;
+  }
 
   /**
-   * Internal lookup mapping.
+   * Given an apply (f a1 a2...), build a TUPLE expression
+   * (f', a1', a2', ...) suitable for a lookup() key.
    */
-  void setLookup(TNode a, TNode b);
+  Node buildRepresentativesOfApply(TNode apply, Kind kindToBuild = kind::TUPLE)
+    throw(AssertionException);
+
+  /**
+   * Append equality "eq" to uselist of "of".
+   */
+  inline void appendToUseList(TNode of, TNode eq) {
+    Debug("cc") << "adding " << eq << " to use list of " << of << std::endl;
+    Assert(eq.getKind() == kind::EQUAL);
+    Assert(of == find(of));
+    UseLists::iterator usei = d_useList.find(of);
+    UseList* ul;
+    if(usei == d_useList.end()) {
+      ul = new(d_context->getCMM()) UseList(true, d_context);
+      d_useList.insertDataFromContextMemory(of, ul);
+    } else {
+      ul = (*usei).second;
+    }
+    ul->push_back(eq);
+  }
+
+  /**
+   * Merge equivalence class ec1 under ec2.  ec1's new union-find
+   * representative becomes ec2.  Calls
+   * OutputChannel::notifyCongruent(), so it can throw anything that
+   * that function can.
+   */
+  void merge(TNode ec1, TNode ec2);
+  void mergeProof(TNode a, TNode b, TNode e);
 
   /**
    * Internal normalization.
    */
-  Node normalize(TNode t);
-};
+  Node normalize(TNode t) const throw(AssertionException);
+
+};/* class CongruenceClosure */
+
+
+template <class OutputChannel>
+void CongruenceClosure<OutputChannel>::addTerm(TNode t) {
+  Node trm = replace(flatten(t));
+  Node trmp = find(trm);
+
+  Debug("cc") << "CC addTerm [" << d_careSet.size() << "] " << d_careSet.contains(t) << ": " << t << std::endl
+              << "           [" << d_careSet.size() << "] " << d_careSet.contains(trm) << ": " << trm << std::endl
+              << "           [" << d_careSet.size() << "] " << d_careSet.contains(trmp) << ": " << trmp << std::endl;
+
+  if(t != trm && !d_careSet.contains(t)) {
+    // we take care to only notify our client once of congruences
+    d_out->notifyCongruent(t, trm);
+    d_careSet.insert(t);
+  }
+
+  if(!d_careSet.contains(trm)) {
+    if(trm != trmp) {
+      // if part of an equivalence class headed by another node,
+      // notify the client of this merge that's already been
+      // performed..
+      d_out->notifyCongruent(trm, trmp);
+    }
+
+    // add its representative to the care set
+    d_careSet.insert(trmp);
+  }
+}
+
+
+template <class OutputChannel>
+void CongruenceClosure<OutputChannel>::addEq(TNode eq, TNode inputEq) {
+  d_proofRewrite[eq] = inputEq;
+
+  Debug("cc") << "CC addEq[" << d_context->getLevel() << "]: " << eq << std::endl;
+  Assert(eq.getKind() == kind::EQUAL);
+  Assert(eq[1].getKind() != kind::APPLY_UF);
+  if(areCongruent(eq[0], eq[1])) {
+    Debug("cc") << "CC -- redundant, ignoring...\n";
+    return;
+  }
+
+  TNode s = eq[0], t = eq[1];
+
+  Assert(s != t);
+
+  Debug("cc:detail") << "CC        " << s << " == " << t << std::endl;
+
+  // change from paper: do this whether or not s, t are applications
+  Debug("cc:detail") << "CC        propagating the eq" << std::endl;
+
+  if(s.getKind() != kind::APPLY_UF) {
+    // s, t are constants
+    propagate(eq);
+  } else {
+    // s is an apply, t is a constant
+    Node ap = buildRepresentativesOfApply(s);
+
+    Debug("cc:detail") << "CC rewrLHS " << "op_and_args_a == " << t << std::endl;
+    Debug("cc") << "CC ap is   " << ap << std::endl;
+
+    TNode l = lookup(ap);
+    Debug("cc:detail") << "CC lookup(ap): " << l << std::endl;
+    if(!l.isNull()) {
+      // ensure a hard Node link exists to this during the call
+      Node pending = NodeManager::currentNM()->mkNode(kind::TUPLE, eq, l);
+      Debug("cc:detail") << "pending1 " << pending << std::endl;
+      propagate(pending);
+    } else {
+      Debug("cc") << "CC lookup(ap) setting to " << eq << std::endl;
+      setLookup(ap, eq);
+      for(Node::iterator i = ap.begin(); i != ap.end(); ++i) {
+        appendToUseList(*i, eq);
+      }
+    }
+  }
+
+  Debug("cc") << *this;
+}/* addEq() */
+
+
+template <class OutputChannel>
+Node CongruenceClosure<OutputChannel>::buildRepresentativesOfApply(TNode apply,
+                                                              Kind kindToBuild)
+  throw(AssertionException) {
+  Assert(apply.getKind() == kind::APPLY_UF);
+  NodeBuilder<> argspb(kindToBuild);
+  argspb << find(apply.getOperator());
+  for(TNode::iterator i = apply.begin(); i != apply.end(); ++i) {
+    argspb << find(*i);
+  }
+  return argspb;
+}/* buildRepresentativesOfApply() */
+
+
+template <class OutputChannel>
+void CongruenceClosure<OutputChannel>::propagate(TNode seed) {
+  Debug("cc:detail") << "=== doing a round of propagation ===" << std::endl
+              << "the \"seed\" propagation is: " << seed << std::endl;
+
+  std::list<Node> pending;
+
+  Debug("cc") << "seed propagation with: " << seed << std::endl;
+  pending.push_back(seed);
+  do { // while(!pending.empty())
+    Node e = pending.front();
+    pending.pop_front();
+
+    Debug("cc") << "=== top of propagate loop ===" << std::endl;
+    Debug("cc") << "=== e is " << e << " ===" << std::endl;
+
+    TNode a, b;
+    if(e.getKind() == kind::EQUAL) {
+      // e is an equality a = b (a, b are constants)
+
+      a = e[0];
+      b = e[1];
+
+      Debug("cc:detail") << "propagate equality: " << a << " == " << b << std::endl;
+    } else {
+      // e is a tuple ( apply f A... = a , apply f B... = b )
+
+      Debug("cc") << "propagate tuple: " << e << "\n";
+
+      Assert(e.getKind() == kind::TUPLE);
+
+      Assert(e.getNumChildren() == 2);
+      Assert(e[0].getKind() == kind::EQUAL);
+      Assert(e[1].getKind() == kind::EQUAL);
+
+      // tuple is (eq, lookup)
+      a = e[0][1];
+      b = e[1][1];
+
+      Assert(a.getKind() != kind::APPLY_UF);
+      Assert(b.getKind() != kind::APPLY_UF);
+
+      Debug("cc") << "                 ( " << a << " , " << b << " )" << std::endl;
+    }
+
+    Debug("cc:detail") << "=====at start=====" << std::endl
+                << "a          :" << a << std::endl
+                << "NORMALIZE a:" << normalize(a) << std::endl
+                << "b          :" << b << std::endl
+                << "NORMALIZE b:" << normalize(b) << std::endl
+                << "alreadyCongruent?:" << areCongruent(a,b) << std::endl;
+
+    // change from paper: need to normalize() here since in our
+    // implementation, a and b can be applications
+    Node ap = find(a), bp = find(b);
+    if(ap != bp) {
+
+      Debug("cc:detail") << "EC[a] == " << ap << std::endl
+                         << "EC[b] == " << bp << std::endl;
+
+      { // w.l.o.g., |classList ap| <= |classList bp|
+        ClassLists::iterator cl_api = d_classList.find(ap);
+        ClassLists::iterator cl_bpi = d_classList.find(bp);
+        unsigned sizeA = (cl_api == d_classList.end() ? 0 : (*cl_api).second->size());
+        unsigned sizeB = (cl_bpi == d_classList.end() ? 0 : (*cl_bpi).second->size());
+        Debug("cc") << "sizeA == " << sizeA << "  sizeB == " << sizeB << std::endl;
+        if(sizeA > sizeB) {
+          Debug("cc") << "swapping..\n";
+          TNode tmp = ap; ap = bp; bp = tmp;
+          tmp = a; a = b; b = tmp;
+        }
+      }
+
+      { // class list handling
+        ClassLists::iterator cl_bpi = d_classList.find(bp);
+        ClassList* cl_bp;
+        if(cl_bpi == d_classList.end()) {
+          cl_bp = new(d_context->getCMM()) ClassList(true, d_context);
+          d_classList.insertDataFromContextMemory(bp, cl_bp);
+          Debug("cc:detail") << "CC in prop alloc classlist for " << bp << std::endl;
+        } else {
+          cl_bp = (*cl_bpi).second;
+        }
+        // we don't store 'ap' in its own class list; so process it here
+        Debug("cc:detail") << "calling mergeproof/merge1 " << ap
+                           << "   AND   " << bp << std::endl;
+        mergeProof(a, b, e);
+        merge(ap, bp);
+
+        Debug("cc") << " adding ap == " << ap << std::endl
+                    << "   to class list of " << bp << std::endl;
+        cl_bp->push_back(ap);
+        ClassLists::const_iterator cli = d_classList.find(ap);
+        if(cli != d_classList.end()) {
+          const ClassList* cl = (*cli).second;
+          for(ClassList::const_iterator i = cl->begin();
+              i != cl->end();
+              ++i) {
+            TNode c = *i;
+            Debug("cc") << "c is " << c << "\n"
+                        << " from cl of " << ap << std::endl;
+            Debug("cc") << " it's find ptr is: " << find(c) << std::endl;
+            Assert(find(c) == ap);
+            Debug("cc:detail") << "calling merge2 " << c << bp << std::endl;
+            merge(c, bp);
+            // move c from classList(ap) to classlist(bp);
+            //i = cl.erase(i);// FIXME do we need to?
+            Debug("cc") << " adding c to class list of " << bp << std::endl;
+            cl_bp->push_back(c);
+          }
+        }
+        Debug("cc:detail") << "bottom\n";
+      }
+
+      { // use list handling
+        Debug("cc:detail") << "ap is " << ap << std::endl;
+        Debug("cc:detail") << "find(ap) is " << find(ap) << std::endl;
+        Debug("cc:detail") << "CC in prop go through useList of " << ap << std::endl;
+        UseLists::iterator usei = d_useList.find(ap);
+        if(usei != d_useList.end()) {
+          UseList* ul = (*usei).second;
+          //for( f(c1,c2)=c in UseList(ap) )
+          for(UseList::const_iterator i = ul->begin();
+              i != ul->end();
+              ++i) {
+            TNode eq = *i;
+            Debug("cc") << "CC  -- useList: " << eq << std::endl;
+            Assert(eq.getKind() == kind::EQUAL);
+            // change from paper
+            // use list elts can have form (apply c..) = x  OR  x = (apply c..)
+            Assert(eq[0].getKind() == kind::APPLY_UF || eq[1].getKind() == kind::APPLY_UF);
+            // do for each side that is an application
+            for(int side = 0; side <= 1; ++side) {
+              if(eq[side].getKind() != kind::APPLY_UF) {
+                continue;
+              }
+
+              Node cp = buildRepresentativesOfApply(eq[side]);
+
+              // if lookup(c1',c2') is some f(d1,d2)=d then
+              TNode n = lookup(cp);
+
+              Debug("cc:detail") << "CC     -- c' is " << cp << std::endl;
+
+              if(!n.isNull()) {
+                Debug("cc:detail") << "CC     -- lookup(c') is " << n << std::endl;
+                // add (f(c1,c2)=c,f(d1,d2)=d) to pending
+                Node tuple = NodeManager::currentNM()->mkNode(kind::TUPLE, eq, n);
+                Debug("cc") << "CC add tuple to pending: " << tuple << std::endl;
+                pending.push_back(tuple);
+                // remove f(c1,c2)=c from UseList(ap)
+                Debug("cc:detail") << "supposed to remove " << eq << std::endl
+                                   << "  from UseList of " << ap << std::endl;
+                //i = ul.erase(i);// FIXME do we need to?
+              } else {
+                Debug("cc") << "CC     -- lookup(c') is null" << std::endl;
+                Debug("cc") << "CC     -- setlookup(c') to " << eq << std::endl;
+                // set lookup(c1',c2') to f(c1,c2)=c
+                setLookup(cp, eq);
+                // move f(c1,c2)=c from UseList(ap) to UseList(b')
+                //i = ul.erase(i);// FIXME do we need to remove from UseList(ap) ?
+                appendToUseList(bp, eq);
+              }
+            }
+          }
+        }
+      }/* use lists */
+      Debug("cc:detail") << "CC in prop done with useList of " << ap << std::endl;
+    } else {
+      Debug("cc:detail") << "CCs the same ( == " << ap << "), do nothing."
+                  << std::endl;
+    }
+
+    Debug("cc") << "=====at end=====" << std::endl
+                << "a          :" << a << std::endl
+                << "NORMALIZE a:" << normalize(a) << std::endl
+                << "b          :" << b << std::endl
+                << "NORMALIZE b:" << normalize(b) << std::endl
+                << "alreadyCongruent?:" << areCongruent(a,b) << std::endl;
+    Assert(areCongruent(a, b));
+  } while(!pending.empty());
+}/* propagate() */
+
+
+template <class OutputChannel>
+void CongruenceClosure<OutputChannel>::merge(TNode ec1, TNode ec2) {
+  /*
+  Debug("cc:detail") << "  -- merging " << ec1
+                     << (d_careSet.find(ec1) == d_careSet.end() ?
+                         " -- NOT in care set" : " -- IN CARE SET") << std::endl
+                     << "         and " << ec2
+                     << (d_careSet.find(ec2) == d_careSet.end() ?
+                         " -- NOT in care set" : " -- IN CARE SET") << std::endl;
+  */
+
+  Debug("cc") << "CC setting rep of " << ec1 << std::endl;
+  Debug("cc") << "CC             to " << ec2 << std::endl;
+
+  /* can now be applications
+  Assert(ec1.getKind() != kind::APPLY_UF);
+  Assert(ec2.getKind() != kind::APPLY_UF);
+  */
+
+  Assert(find(ec1) != ec2);
+  //Assert(find(ec1) == ec1);
+  Assert(find(ec2) == ec2);
+
+  d_representative[ec1] = ec2;
+
+  if(d_careSet.find(ec1) != d_careSet.end()) {
+    d_careSet.insert(ec2);
+    d_out->notifyCongruent(ec1, ec2);
+  }
+}/* merge() */
+
+
+template <class OutputChannel>
+void CongruenceClosure<OutputChannel>::mergeProof(TNode a, TNode b, TNode e) {
+  Debug("cc") << "  -- merge-proofing " << a << "\n"
+              << "                and " << b << "\n"
+              << "               with " << e << "\n";
+
+  // proof forest gets a -> b labeled with e
+
+  // first reverse all the edges in proof forest to root of this proof tree
+  Debug("cc") << "CC PROOF reversing proof tree\n";
+  // c and p are child and parent in (old) proof tree
+  Node c = a, p = d_proof[a], edgePf = d_proofLabel[a];
+  // when we hit null p, we're at the (former) root
+  Debug("cc") << "CC PROOF start at c == " << c << std::endl
+              << "                  p == " << p << std::endl
+              << "             edgePf == " << edgePf << std::endl;
+  while(!p.isNull()) {
+    // in proof forest,
+    // we have edge   c --edgePf-> p
+    // turn it into   c <-edgePf-- p
+
+    Node pParSave = d_proof[p];
+    Node pLabelSave = d_proofLabel[p];
+    d_proof[p] = c;
+    d_proofLabel[p] = edgePf;
+    c = p;
+    p = pParSave;
+    edgePf = pLabelSave;
+    Debug("cc") << "CC PROOF now   at c == " << c << std::endl
+                << "                  p == " << p << std::endl
+                << "             edgePf == " << edgePf << std::endl;
+  }
+
+  // add an edge from a to b
+  d_proof[a] = b;
+  d_proofLabel[a] = e;
+}/* mergeProof() */
+
+
+template <class OutputChannel>
+Node CongruenceClosure<OutputChannel>::normalize(TNode t) const
+  throw(AssertionException) {
+  Debug("cc:detail") << "normalize " << t << std::endl;
+  if(t.getKind() != kind::APPLY_UF) {// t is a constant
+    t = find(t);
+    Debug("cc:detail") << "  find " << t << std::endl;
+    return t;
+  } else {// t is an apply
+    NodeBuilder<> apb(kind::TUPLE);
+    TNode op = t.getOperator();
+    Node n = normalize(op);
+    apb << n;
+    bool allConstants = (n.getKind() != kind::APPLY_UF);
+    for(TNode::iterator i = t.begin(); i != t.end(); ++i) {
+      TNode c = *i;
+      n = normalize(c);
+      apb << n;
+      allConstants = (allConstants && n.getKind() != kind::APPLY_UF);
+    }
+
+    Node ap = apb;
+    Debug("cc:detail") << "  got ap " << ap << std::endl;
+
+    Node theLookup = lookup(ap);
+    if(allConstants && !theLookup.isNull()) {
+      Assert(theLookup.getKind() == kind::EQUAL);
+      Assert(theLookup[0].getKind() == kind::APPLY_UF);
+      Assert(theLookup[1].getKind() != kind::APPLY_UF);
+      return find(theLookup[1]);
+    } else {
+      NodeBuilder<> fa(kind::APPLY_UF);
+      for(Node::iterator i = ap.begin(); i != ap.end(); ++i) {
+        fa << *i;
+      }
+      // ensure a hard Node link exists during the call
+      Node n = fa;
+      return n;
+    }
+  }
+}/* normalize() */
+
+
+template <class OutputChannel>
+Node CongruenceClosure<OutputChannel>::highestNode(TNode a, __gnu_cxx::hash_map<Node, Node, NodeHashFunction>& unionFind)
+  throw(AssertionException) {
+  __gnu_cxx::hash_map<Node, Node, NodeHashFunction>::iterator i = unionFind.find(a);
+  if(i == unionFind.end()) {
+    return a;
+  } else {
+    return unionFind[a] = highestNode((*i).second, unionFind);
+  }
+}
+
+
+template <class OutputChannel>
+void CongruenceClosure<OutputChannel>::explainAlongPath(TNode a, TNode c, std::list<std::pair<Node, Node> >& pending, __gnu_cxx::hash_map<Node, Node, NodeHashFunction>& unionFind, std::list<Node>& pf)
+  throw(AssertionException) {
+
+  a = highestNode(a, unionFind);
+  Assert(c == highestNode(c, unionFind));
+
+  while(a != c) {
+    Node b = d_proof[a];
+    Node e = d_proofLabel[a];
+    if(e.getKind() == kind::EQUAL) {
+      pf.push_back(e);
+    } else {
+      Assert(e.getKind() == kind::TUPLE);
+      pf.push_back(e[0]);
+      pf.push_back(e[1]);
+      Assert(e[0][0].getKind() == kind::APPLY_UF);
+      Assert(e[0][1].getKind() != kind::APPLY_UF);
+      Assert(e[1][0].getKind() == kind::APPLY_UF);
+      Assert(e[1][1].getKind() != kind::APPLY_UF);
+      Assert(e[0][0].getNumChildren() == e[1][0].getNumChildren());
+      pending.push_back(std::make_pair(e[0][0].getOperator(), e[1][0].getOperator()));
+      for(int i = 0, nc = e[0][0].getNumChildren(); i < nc; ++i) {
+        pending.push_back(std::make_pair(e[0][0][i], e[1][0][i]));
+      }
+    }
+    unionFind[a] = b;
+    a = highestNode(b, unionFind);
+  }
+}/* explainAlongPath() */
+
+
+template <class OutputChannel>
+Node CongruenceClosure<OutputChannel>::nearestCommonAncestor(TNode a, TNode b)
+  throw(AssertionException) {
+  Assert(find(a) == find(b));
+  return find(a); // FIXME
+}/* nearestCommonAncestor() */
+
+
+template <class OutputChannel>
+Node CongruenceClosure<OutputChannel>::explain(Node a, Node b)
+  throw(CongruenceClosureException, AssertionException) {
+
+  Assert(a != b);
+
+  if(!areCongruent(a, b)) {
+    throw CongruenceClosureException("asked to explain() congruence of nodes "
+                                     "that aren't congruent");
+  }
+
+  if(a.getKind() == kind::APPLY_UF) {
+    a = replace(flatten(a));
+  }
+  if(b.getKind() == kind::APPLY_UF) {
+    b = replace(flatten(b));
+  }
+
+  std::list<std::pair<Node, Node> > pending;
+  __gnu_cxx::hash_map<Node, Node, NodeHashFunction> unionFind;
+  std::list<Node> terms;
+
+  pending.push_back(std::make_pair(a, b));
+
+  Debug("cc") << "CC EXPLAINING " << a << " == " << b << std::endl;
+
+  do {// while(!pending.empty())
+    std::pair<Node, Node> eq = pending.front();
+    pending.pop_front();
+
+    a = eq.first;
+    b = eq.second;
+
+    Node c = nearestCommonAncestor(a, b);
+
+    explainAlongPath(a, c, pending, unionFind, terms);
+    explainAlongPath(b, c, pending, unionFind, terms);
+  } while(!pending.empty());
+
+  Debug("cc") << "CC EXPLAIN final proof has size " << terms.size() << std::endl;
+
+  NodeBuilder<> pf(kind::AND);
+  while(!terms.empty()) {
+    Node p = terms.front();
+    terms.pop_front();
+    Debug("cc") << "CC EXPLAIN " << p << std::endl;
+    p = proofRewrite(p);
+    Debug("cc") << "   rewrite " << p << std::endl;
+    if(!p.isNull()) {
+      pf << p;
+    }
+  }
+
+  Debug("cc") << "CC EXPLAIN done" << std::endl;
+
+  Assert(pf.getNumChildren() > 0);
+
+  if(pf.getNumChildren() == 1) {
+    return pf[0];
+  } else {
+    return pf;
+  }
+}/* explain() */
+
+
+template <class OutputChannel>
+std::ostream& operator<<(std::ostream& out,
+                         const CongruenceClosure<OutputChannel>& cc) {
+  out << "==============================================" << std::endl;
+
+  out << "Representatives:" << std::endl;
+  for(typename CongruenceClosure<OutputChannel>::RepresentativeMap::const_iterator i = cc.d_representative.begin(); i != cc.d_representative.end(); ++i) {
+    out << "  " << (*i).first << " => " << (*i).second << std::endl;
+  }
+
+  out << "ClassLists:" << std::endl;
+  for(typename CongruenceClosure<OutputChannel>::ClassLists::const_iterator i = cc.d_classList.begin(); i != cc.d_classList.end(); ++i) {
+    if(cc.find((*i).first) == (*i).first) {
+      out << "  " << (*i).first << " =>" << std::endl;
+      for(typename CongruenceClosure<OutputChannel>::ClassList::const_iterator j = (*i).second->begin(); j != (*i).second->end(); ++j) {
+        out << "      " << *j << std::endl;
+      }
+    }
+  }
+
+  out << "UseLists:" << std::endl;
+  for(typename CongruenceClosure<OutputChannel>::UseLists::const_iterator i = cc.d_useList.begin(); i != cc.d_useList.end(); ++i) {
+    if(cc.find((*i).first) == (*i).first) {
+      out << "  " << (*i).first << " =>" << std::endl;
+      for(typename CongruenceClosure<OutputChannel>::UseList::const_iterator j = (*i).second->begin(); j != (*i).second->end(); ++j) {
+        out << "      " << *j << std::endl;
+      }
+    }
+  }
+
+  out << "Lookup:" << std::endl;
+  for(typename CongruenceClosure<OutputChannel>::LookupMap::const_iterator i = cc.d_lookup.begin(); i != cc.d_lookup.end(); ++i) {
+    TNode n = (*i).second;
+    out << "  " << (*i).first << " => " << n << std::endl;
+  }
+
+  out << "==============================================" << std::endl;
+
+  return out;
+}
+
 
 }/* CVC4 namespace */
 
index 7fcf35f0064568d9db256f2327e4bad458584072..c79bc93b1f2bcbc93108e5c4984a4de41bf4b943 100644 (file)
@@ -47,6 +47,12 @@ struct CVC4_PUBLIC Options {
   /** The input language */
   parser::InputLanguage lang;
 
+  /** Enumeration of UF implementation choices */
+  typedef enum { TIM, MORGAN } UfImplementation;
+
+  /** Which implementation of uninterpreted function theory to use */
+  UfImplementation uf_implementation;
+
   /** Should we exit after parsing? */
   bool parseOnly;
 
@@ -65,6 +71,7 @@ struct CVC4_PUBLIC Options {
               err(0),
               verbosity(0),
               lang(parser::LANG_AUTO),
+              uf_implementation(MORGAN),
               parseOnly(false),
               semanticChecks(true),
               memoryMap(false),
@@ -72,6 +79,21 @@ struct CVC4_PUBLIC Options {
   {}
 };/* struct Options */
 
+inline std::ostream& operator<<(std::ostream& out, Options::UfImplementation uf) {
+  switch(uf) {
+  case Options::TIM:
+    out << "TIM";
+    break;
+  case Options::MORGAN:
+    out << "MORGAN";
+    break;
+  default:
+    out << "UfImplementation:UNKNOWN![" << unsigned(uf) << "]";
+  }
+
+  return out;
+}
+
 }/* CVC4 namespace */
 
 #endif /* __CVC4__OPTIONS_H */
index 2b79c50458b2c452aceeae304223b3c532242ac1..501f85090bc4949ea3c0e6ee1ec326bba238748c 100644 (file)
@@ -26,7 +26,7 @@ test "X$(AM_COLOR_TESTS)" != Xno \
   blu='\e[1;34m'; \
   std='\e[m'; \
 }
-subdirs_to_check = unit system regress/regress0 regress/regress1 regress/regress2 regress/regress3
+subdirs_to_check = unit system regress/regress0 regress/regress0/uf regress/regress0/precedence regress/regress1 regress/regress2 regress/regress3
 check-recursive: check-pre
 .PHONY: check-pre
 check-pre:
index a3808b7519d8f6173d0dd173555c77b705cd3cc6..80e8da387c7f4327d29f4ff98bc1e814520a21f4 100644 (file)
@@ -1,6 +1,8 @@
 SUBDIRS = regress0
 DIST_SUBDIRS = regress0 regress1 regress2 regress3
 
+MAKEFLAGS = -k
+
 export VERBOSE = 1
 
 .PHONY: regress0 regress1 regress2 regress3
index 09dc52ce406f453a781c96fc4cda0b8573a31904..7f732b17fdee43e1926883413bb193465ab42e22 100644 (file)
@@ -1,6 +1,7 @@
 SUBDIRS = . precedence uf
 
 TESTS_ENVIRONMENT = @srcdir@/../run_regression @top_builddir@/src/main/cvc4
+MAKEFLAGS = -k
 
 # These are run for all build profiles.
 # If a test shouldn't be run in e.g. competition mode,
index bf516107e8f451c3dba2fb0c6496cde1a6811d0c..452024309468cca19f2877b6c413672576c17b26 100644 (file)
@@ -16,9 +16,9 @@ TESTS =       \
        euf_simp11.smt \
        euf_simp12.smt \
        euf_simp13.smt \
+       eq_diamond1.smt \
        dead_dnd002.smt \
        iso_brn001.smt \
-       SEQ032_size2.smt \
        simple.01.cvc \
        simple.02.cvc \
        simple.03.cvc \
index abc4efcc7459c6ab21816934c700de2ec20b91f0..4e4a42a31af79c500407637d74f7f939f1b506c3 100644 (file)
@@ -1,6 +1,7 @@
 SUBDIRS = .
 
 TESTS_ENVIRONMENT = @srcdir@/../run_regression @top_builddir@/src/main/cvc4
+MAKEFLAGS = -k
 
 # These are run for all build profiles.
 # If a test shouldn't be run in e.g. competition mode,
index 51d1458597a9c3d41ccd31841dc908c34035156b..1651865fdcc85896ae4491303671bd6458da0022 100644 (file)
@@ -1,6 +1,7 @@
 SUBDIRS = .
 
 TESTS_ENVIRONMENT = @srcdir@/../run_regression @top_builddir@/src/main/cvc4
+MAKEFLAGS = -k
 
 # These are run for all build profiles.
 # If a test shouldn't be run in e.g. competition mode,
index 81fb8616e9646d094beeecee3c7570050552a073..744ec57751a3e65bb37eff77f97a3d5b657a7c2e 100644 (file)
@@ -1,6 +1,7 @@
 SUBDIRS = .
 
 TESTS_ENVIRONMENT = @srcdir@/../run_regression @top_builddir@/src/main/cvc4
+MAKEFLAGS = -k
 
 # These are run for all build profiles.
 # If a test shouldn't be run in e.g. competition mode,
index f25862b54dca7689ab1422e3c9e27023f2d4235b..eb920e6c5a05b29153414d323930289b7c3197d4 100644 (file)
@@ -3,7 +3,7 @@ UNIT_TESTS = \
        theory/shared_term_manager_black \
        theory/theory_engine_white \
        theory/theory_black \
-       theory/theory_uf_white \
+       theory/theory_uf_tim_white \
        theory/theory_arith_white \
        expr/expr_public \
         expr/expr_manager_public \
@@ -29,6 +29,7 @@ UNIT_TESTS = \
        util/assert_white \
        util/bitvector_black \
        util/configuration_black \
+       util/congruence_closure_white \
        util/output_black \
        util/exception_black \
        util/integer_black \
@@ -182,13 +183,13 @@ no-cxxtest-available:
        else \
                echo; \
                echo "ERROR:"; \
-               echo "ERROR: You cannot make dist in this build directory, you do not have CxxTest."; \
-               echo "ERROR: The tests should be generated for the user and included in the tarball,"; \
+               echo "ERROR: You cannot make dist in this build directory, you do not have CxxTest."; \
+               echo "ERROR: The tests should be generated for the user and included in the tarball,"; \
                echo "ERROR: otherwise they'll be required to have CxxTest just to test the standard"; \
                echo "ERROR: distribution built correctly."; \
                echo "ERROR: If you really want to do this, append the following to your make command."; \
                echo "ERROR:"; \
-               echo "ERROR:     I_REALLY_WANT_TO_BUILD_CVC4_DIST_WITHOUT_TESTS=1"; \
+               echo "ERROR:     I_REALLY_WANT_TO_BUILD_CVC4_DIST_WITHOUT_TESTS=1"; \
                echo "ERROR:"; \
                echo; \
                exit 1; \
index e18aeb975a25da8feb531018ae9705f11b159440..ea1f40eac50f0e66e817675f051953e40c45b660 100644 (file)
@@ -104,8 +104,8 @@ public:
     TS_ASSERT_DIFFERS(VarNameAttr::s_id, TestStringAttr2::s_id);
     TS_ASSERT_DIFFERS(TestStringAttr1::s_id, TestStringAttr2::s_id);
 
-    lastId = attr::LastAttributeId<void*, false>::s_id;
-    TS_ASSERT_LESS_THAN(theory::uf::ECAttr::s_id, lastId);
+    //lastId = attr::LastAttributeId<void*, false>::s_id;
+    //TS_ASSERT_LESS_THAN(theory::uf::ECAttr::s_id, lastId);
 
     lastId = attr::LastAttributeId<bool, false>::s_id;
     TS_ASSERT_LESS_THAN(theory::Theory::PreRegisteredAttr::s_id, lastId);
index 4393d99cd060c809fe0ba2fc7bb55fddb76e91f2..def2f70498523fa61af6156a1ae4b6563dd1209a 100644 (file)
@@ -31,6 +31,7 @@
 #include "context/context.h"
 #include "util/rational.h"
 #include "util/integer.h"
+#include "util/options.h"
 #include "util/Assert.h"
 
 using namespace CVC4;
@@ -50,6 +51,7 @@ class SharedTermManagerBlack : public CxxTest::TestSuite {
   NodeManager* d_nm;
   NodeManagerScope* d_scope;
   TheoryEngine* d_theoryEngine;
+  Options d_options;
 
 public:
 
@@ -59,11 +61,11 @@ public:
     d_nm = new NodeManager(d_ctxt);
     d_scope = new NodeManagerScope(d_nm);
 
-    d_theoryEngine = new TheoryEngine(d_ctxt);
-
+    d_theoryEngine = new TheoryEngine(d_ctxt, &d_options);
   }
 
   void tearDown() {
+    d_theoryEngine->shutdown();
     delete d_theoryEngine;
 
     delete d_scope;
index ead879336285fa23d7edc7b74f27b4fb5dae6452..1ec52314811e7a9521140c322f894b4d01d52367 100644 (file)
@@ -35,6 +35,7 @@
 #include "context/context.h"
 #include "util/rational.h"
 #include "util/integer.h"
+#include "util/options.h"
 #include "util/Assert.h"
 
 using namespace CVC4;
@@ -214,6 +215,7 @@ class TheoryEngineWhite : public CxxTest::TestSuite {
   FakeOutputChannel *d_nullChannel;
   FakeTheory *d_builtin, *d_bool, *d_uf, *d_arith, *d_arrays, *d_bv;
   TheoryEngine* d_theoryEngine;
+  Options d_options;
 
 public:
 
@@ -234,7 +236,7 @@ public:
     d_bv = new FakeTheory(d_ctxt, *d_nullChannel, "BV");
 
     // create the TheoryEngine
-    d_theoryEngine = new TheoryEngine(d_ctxt);
+    d_theoryEngine = new TheoryEngine(d_ctxt, &d_options);
 
     // insert our fake versions into the TheoryEngine's theoryOf table
     d_theoryEngine->d_theoryOfTable.
@@ -254,6 +256,7 @@ public:
   }
 
   void tearDown() {
+    d_theoryEngine->shutdown();
     delete d_theoryEngine;
 
     delete d_bv;
diff --git a/test/unit/theory/theory_uf_tim_white.h b/test/unit/theory/theory_uf_tim_white.h
new file mode 100644 (file)
index 0000000..aec9992
--- /dev/null
@@ -0,0 +1,252 @@
+/*********************                                                        */
+/*! \file theory_uf_white.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: mdeters
+ ** Minor contributors (to current version): dejan
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief White box testing of CVC4::theory::uf::tim::TheoryUFTim.
+ **
+ ** White box testing of CVC4::theory::uf::tim::TheoryUFTim.
+ **/
+
+#include <cxxtest/TestSuite.h>
+
+#include "theory/theory.h"
+#include "theory/uf/theory_uf.h"
+#include "theory/uf/tim/theory_uf_tim.h"
+#include "expr/node.h"
+#include "expr/node_manager.h"
+#include "context/context.h"
+
+#include "theory/theory_test_utils.h"
+
+#include <vector>
+
+using namespace CVC4;
+using namespace CVC4::theory;
+using namespace CVC4::theory::uf;
+using namespace CVC4::theory::uf::tim;
+using namespace CVC4::expr;
+using namespace CVC4::context;
+
+using namespace std;
+
+
+class TheoryUFTimWhite : public CxxTest::TestSuite {
+
+  Context* d_ctxt;
+  NodeManager* d_nm;
+  NodeManagerScope* d_scope;
+
+  TestOutputChannel d_outputChannel;
+  Theory::Effort d_level;
+
+  TheoryUFTim* d_euf;
+
+  TypeNode* d_booleanType;
+
+public:
+
+  TheoryUFTimWhite() : d_level(Theory::FULL_EFFORT) {}
+
+  void setUp() {
+    d_ctxt = new Context;
+    d_nm = new NodeManager(d_ctxt);
+    d_scope = new NodeManagerScope(d_nm);
+    d_outputChannel.clear();
+    d_euf = new TheoryUFTim(0, d_ctxt, d_outputChannel);
+
+    d_booleanType = new TypeNode(d_nm->booleanType());
+  }
+
+  void tearDown() {
+    delete d_booleanType;
+    delete d_euf;
+    d_outputChannel.clear();
+    delete d_scope;
+    delete d_nm;
+    delete d_ctxt;
+  }
+
+  void testPushPopSimple() {
+    TypeNode t = d_nm->mkSort();
+    Node x = d_nm->mkVar(t);
+    Node x_eq_x = x.eqNode(x);
+
+    d_ctxt->push();
+    d_ctxt->pop();
+  }
+
+// FIXME: This is broken because of moving registration into theory_engine @CB
+// //   void testPushPopChain() {
+// //     Node x = d_nm->mkVar(*d_booleanType);
+// //     Node f = d_nm->mkVar(*d_booleanType);
+// //     Node f_x = d_nm->mkNode(kind::APPLY_UF, f, x);
+// //     Node f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_x);
+// //     Node f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_x);
+// //     Node f_f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_f_x);
+// //     Node f_f_f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_f_f_x);
+
+// //     Node f3_x_eq_x = f_f_f_x.eqNode(x);
+// //     Node f5_x_eq_x = f_f_f_f_f_x.eqNode(x);
+// //     Node f1_x_neq_x = f_x.eqNode(x).notNode();
+
+// //     Node expectedConflict = d_nm->mkNode(kind::AND,
+// //                                          f1_x_neq_x,
+// //                                          f3_x_eq_x,
+// //                                          f5_x_eq_x
+// //                                          );
+
+// //     d_euf->assertFact( f3_x_eq_x );
+// //     d_euf->assertFact( f1_x_neq_x );
+// //     d_euf->check(d_level);
+// //     d_ctxt->push();
+
+// //     d_euf->assertFact( f5_x_eq_x );
+// //     d_euf->check(d_level);
+
+// //     TS_ASSERT_EQUALS(1u, d_outputChannel.getNumCalls());
+// //     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(0));
+// //     Node realConflict = d_outputChannel.getIthNode(0);
+// //     TS_ASSERT_EQUALS(expectedConflict, realConflict);
+
+// //     d_ctxt->pop();
+// //     d_euf->check(d_level);
+
+// //     //Test that no additional calls to the output channel occurred.
+// //     TS_ASSERT_EQUALS(1u, d_outputChannel.getNumCalls());
+
+// //     d_euf->assertFact( f5_x_eq_x );
+
+// //     d_euf->check(d_level);
+
+// //     TS_ASSERT_EQUALS(2u, d_outputChannel.getNumCalls());
+// //     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(0));
+// //     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(1));
+// //     Node  firstConflict = d_outputChannel.getIthNode(0);
+// //     Node secondConflict = d_outputChannel.getIthNode(1);
+// //     TS_ASSERT_EQUALS(expectedConflict,  firstConflict);
+// //     TS_ASSERT_EQUALS(expectedConflict, secondConflict);
+
+// //   }
+
+
+
+//   /* test that {f(f(x)) == x, f(f(f(x))) != f(x)} is inconsistent */
+//   void testSimpleChain() {
+//     Node x = d_nm->mkVar(*d_booleanType);
+//     Node f = d_nm->mkVar(*d_booleanType);
+//     Node f_x = d_nm->mkNode(kind::APPLY_UF, f, x);
+//     Node f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_x);
+//     Node f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_x);
+
+//     Node f_f_x_eq_x = f_f_x.eqNode(x);
+//     Node f_f_f_x_neq_f_x = (f_f_f_x.eqNode(f_x)).notNode();
+
+//     Node expectedConflict = f_f_f_x_neq_f_x.andNode(f_f_x_eq_x);
+
+//     d_euf->assertFact(f_f_x_eq_x);
+//     d_euf->assertFact(f_f_f_x_neq_f_x);
+//     d_euf->check(d_level);
+
+//     TS_ASSERT_EQUALS(1u, d_outputChannel.getNumCalls());
+//     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(0));
+
+//     Node realConflict = d_outputChannel.getIthNode(0);
+//     TS_ASSERT_EQUALS(expectedConflict, realConflict);
+
+//   }
+
+  /* test that !(x == x) is inconsistent */
+//   void testSelfInconsistent() {
+//     Node x = d_nm->mkVar(*d_booleanType);
+//     Node x_neq_x = (x.eqNode(x)).notNode();
+
+//     d_euf->assertFact(x_neq_x);
+//     d_euf->check(d_level);
+
+//     TS_ASSERT_EQUALS(1u, d_outputChannel.getNumCalls());
+//     TS_ASSERT_EQUALS(x_neq_x, d_outputChannel.getIthNode(0));
+//     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(0));
+//   }
+
+//   /* test that (x == x) is consistent */
+//   void testSelfConsistent() {
+//     Node x = d_nm->mkVar(*d_booleanType);
+//     Node x_eq_x = x.eqNode(x);
+
+//     d_euf->assertFact(x_eq_x);
+//     d_euf->check(d_level);
+
+//     TS_ASSERT_EQUALS(0u, d_outputChannel.getNumCalls());
+//   }
+
+
+//   /* test that
+//      {f(f(f(x))) == x,
+//       f(f(f(f(f(x))))) = x,
+//       f(x) != x
+//      } is inconsistent */
+//   void testChain() {
+//     Node x = d_nm->mkVar(*d_booleanType);
+//     Node f = d_nm->mkVar(*d_booleanType);
+//     Node f_x = d_nm->mkNode(kind::APPLY_UF, f, x);
+//     Node f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_x);
+//     Node f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_x);
+//     Node f_f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_f_x);
+//     Node f_f_f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_f_f_x);
+
+//     Node f3_x_eq_x = f_f_f_x.eqNode(x);
+//     Node f5_x_eq_x = f_f_f_f_f_x.eqNode(x);
+//     Node f1_x_neq_x = f_x.eqNode(x).notNode();
+
+//     Node expectedConflict = d_nm->mkNode(kind::AND,
+//                                          f1_x_neq_x,
+//                                          f3_x_eq_x,
+//                                          f5_x_eq_x
+//                                          );
+
+//     d_euf->assertFact( f3_x_eq_x );
+//     d_euf->assertFact( f5_x_eq_x );
+//     d_euf->assertFact( f1_x_neq_x );
+//     d_euf->check(d_level);
+
+//     TS_ASSERT_EQUALS(1u, d_outputChannel.getNumCalls());
+//     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(0));
+//     Node realConflict = d_outputChannel.getIthNode(0);
+//     TS_ASSERT_EQUALS(expectedConflict, realConflict);
+//   }
+
+
+//   void testPushPopA() {
+//     Node x = d_nm->mkVar(*d_booleanType);
+//     Node x_eq_x = x.eqNode(x);
+
+//     d_ctxt->push();
+//     d_euf->assertFact( x_eq_x );
+//     d_euf->check(d_level);
+//     d_ctxt->pop();
+//     d_euf->check(d_level);
+//   }
+
+//   void testPushPopB() {
+//     Node x = d_nm->mkVar(*d_booleanType);
+//     Node f = d_nm->mkVar(*d_booleanType);
+//     Node f_x = d_nm->mkNode(kind::APPLY_UF, f, x);
+//     Node f_x_eq_x = f_x.eqNode(x);
+
+//     d_euf->assertFact( f_x_eq_x );
+//     d_ctxt->push();
+//     d_euf->check(d_level);
+//     d_ctxt->pop();
+//     d_euf->check(d_level);
+//   }
+
+};
diff --git a/test/unit/theory/theory_uf_white.h b/test/unit/theory/theory_uf_white.h
deleted file mode 100644 (file)
index f273de3..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*********************                                                        */
-/*! \file theory_uf_white.h
- ** \verbatim
- ** Original author: taking
- ** Major contributors: mdeters
- ** Minor contributors (to current version): dejan
- ** This file is part of the CVC4 prototype.
- ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
- ** Courant Institute of Mathematical Sciences
- ** New York University
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief White box testing of CVC4::theory::uf::TheoryUF.
- **
- ** White box testing of CVC4::theory::uf::TheoryUF.
- **/
-
-#include <cxxtest/TestSuite.h>
-
-#include "theory/theory.h"
-#include "theory/uf/theory_uf.h"
-#include "expr/node.h"
-#include "expr/node_manager.h"
-#include "context/context.h"
-
-#include "theory/theory_test_utils.h"
-
-#include <vector>
-
-using namespace CVC4;
-using namespace CVC4::theory;
-using namespace CVC4::theory::uf;
-using namespace CVC4::expr;
-using namespace CVC4::context;
-
-using namespace std;
-
-
-class TheoryUFWhite : public CxxTest::TestSuite {
-
-  Context* d_ctxt;
-  NodeManager* d_nm;
-  NodeManagerScope* d_scope;
-
-  TestOutputChannel d_outputChannel;
-  Theory::Effort d_level;
-
-  TheoryUF* d_euf;
-
-  TypeNode* d_booleanType;
-
-public:
-
-  TheoryUFWhite() : d_level(Theory::FULL_EFFORT) {}
-
-  void setUp() {
-    d_ctxt = new Context;
-    d_nm = new NodeManager(d_ctxt);
-    d_scope = new NodeManagerScope(d_nm);
-    d_outputChannel.clear();
-    d_euf = new TheoryUF(0, d_ctxt, d_outputChannel);
-
-    d_booleanType = new TypeNode(d_nm->booleanType());
-  }
-
-  void tearDown() {
-    delete d_booleanType;
-    delete d_euf;
-    d_outputChannel.clear();
-    delete d_scope;
-    delete d_nm;
-    delete d_ctxt;
-  }
-
-  void testPushPopSimple() {
-    TypeNode t = d_nm->mkSort();
-    Node x = d_nm->mkVar(t);
-    Node x_eq_x = x.eqNode(x);
-
-    d_ctxt->push();
-    d_ctxt->pop();
-  }
-
-// FIXME: This is broken because of moving registration into theory_engine @CB
-// //   void testPushPopChain() {
-// //     Node x = d_nm->mkVar(*d_booleanType);
-// //     Node f = d_nm->mkVar(*d_booleanType);
-// //     Node f_x = d_nm->mkNode(kind::APPLY_UF, f, x);
-// //     Node f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_x);
-// //     Node f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_x);
-// //     Node f_f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_f_x);
-// //     Node f_f_f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_f_f_x);
-
-// //     Node f3_x_eq_x = f_f_f_x.eqNode(x);
-// //     Node f5_x_eq_x = f_f_f_f_f_x.eqNode(x);
-// //     Node f1_x_neq_x = f_x.eqNode(x).notNode();
-
-// //     Node expectedConflict = d_nm->mkNode(kind::AND,
-// //                                          f1_x_neq_x,
-// //                                          f3_x_eq_x,
-// //                                          f5_x_eq_x
-// //                                          );
-
-// //     d_euf->assertFact( f3_x_eq_x );
-// //     d_euf->assertFact( f1_x_neq_x );
-// //     d_euf->check(d_level);
-// //     d_ctxt->push();
-
-// //     d_euf->assertFact( f5_x_eq_x );
-// //     d_euf->check(d_level);
-
-// //     TS_ASSERT_EQUALS(1u, d_outputChannel.getNumCalls());
-// //     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(0));
-// //     Node realConflict = d_outputChannel.getIthNode(0);
-// //     TS_ASSERT_EQUALS(expectedConflict, realConflict);
-
-// //     d_ctxt->pop();
-// //     d_euf->check(d_level);
-
-// //     //Test that no additional calls to the output channel occurred.
-// //     TS_ASSERT_EQUALS(1u, d_outputChannel.getNumCalls());
-
-// //     d_euf->assertFact( f5_x_eq_x );
-
-// //     d_euf->check(d_level);
-
-// //     TS_ASSERT_EQUALS(2u, d_outputChannel.getNumCalls());
-// //     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(0));
-// //     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(1));
-// //     Node  firstConflict = d_outputChannel.getIthNode(0);
-// //     Node secondConflict = d_outputChannel.getIthNode(1);
-// //     TS_ASSERT_EQUALS(expectedConflict,  firstConflict);
-// //     TS_ASSERT_EQUALS(expectedConflict, secondConflict);
-
-// //   }
-
-
-
-//   /* test that {f(f(x)) == x, f(f(f(x))) != f(x)} is inconsistent */
-//   void testSimpleChain() {
-//     Node x = d_nm->mkVar(*d_booleanType);
-//     Node f = d_nm->mkVar(*d_booleanType);
-//     Node f_x = d_nm->mkNode(kind::APPLY_UF, f, x);
-//     Node f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_x);
-//     Node f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_x);
-
-//     Node f_f_x_eq_x = f_f_x.eqNode(x);
-//     Node f_f_f_x_neq_f_x = (f_f_f_x.eqNode(f_x)).notNode();
-
-//     Node expectedConflict = f_f_f_x_neq_f_x.andNode(f_f_x_eq_x);
-
-//     d_euf->assertFact(f_f_x_eq_x);
-//     d_euf->assertFact(f_f_f_x_neq_f_x);
-//     d_euf->check(d_level);
-
-//     TS_ASSERT_EQUALS(1u, d_outputChannel.getNumCalls());
-//     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(0));
-
-//     Node realConflict = d_outputChannel.getIthNode(0);
-//     TS_ASSERT_EQUALS(expectedConflict, realConflict);
-
-//   }
-
-  /* test that !(x == x) is inconsistent */
-//   void testSelfInconsistent() {
-//     Node x = d_nm->mkVar(*d_booleanType);
-//     Node x_neq_x = (x.eqNode(x)).notNode();
-
-//     d_euf->assertFact(x_neq_x);
-//     d_euf->check(d_level);
-
-//     TS_ASSERT_EQUALS(1u, d_outputChannel.getNumCalls());
-//     TS_ASSERT_EQUALS(x_neq_x, d_outputChannel.getIthNode(0));
-//     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(0));
-//   }
-
-//   /* test that (x == x) is consistent */
-//   void testSelfConsistent() {
-//     Node x = d_nm->mkVar(*d_booleanType);
-//     Node x_eq_x = x.eqNode(x);
-
-//     d_euf->assertFact(x_eq_x);
-//     d_euf->check(d_level);
-
-//     TS_ASSERT_EQUALS(0u, d_outputChannel.getNumCalls());
-//   }
-
-
-//   /* test that
-//      {f(f(f(x))) == x,
-//       f(f(f(f(f(x))))) = x,
-//       f(x) != x
-//      } is inconsistent */
-//   void testChain() {
-//     Node x = d_nm->mkVar(*d_booleanType);
-//     Node f = d_nm->mkVar(*d_booleanType);
-//     Node f_x = d_nm->mkNode(kind::APPLY_UF, f, x);
-//     Node f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_x);
-//     Node f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_x);
-//     Node f_f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_f_x);
-//     Node f_f_f_f_f_x = d_nm->mkNode(kind::APPLY_UF, f, f_f_f_f_x);
-
-//     Node f3_x_eq_x = f_f_f_x.eqNode(x);
-//     Node f5_x_eq_x = f_f_f_f_f_x.eqNode(x);
-//     Node f1_x_neq_x = f_x.eqNode(x).notNode();
-
-//     Node expectedConflict = d_nm->mkNode(kind::AND,
-//                                          f1_x_neq_x,
-//                                          f3_x_eq_x,
-//                                          f5_x_eq_x
-//                                          );
-
-//     d_euf->assertFact( f3_x_eq_x );
-//     d_euf->assertFact( f5_x_eq_x );
-//     d_euf->assertFact( f1_x_neq_x );
-//     d_euf->check(d_level);
-
-//     TS_ASSERT_EQUALS(1u, d_outputChannel.getNumCalls());
-//     TS_ASSERT_EQUALS(CONFLICT, d_outputChannel.getIthCallType(0));
-//     Node realConflict = d_outputChannel.getIthNode(0);
-//     TS_ASSERT_EQUALS(expectedConflict, realConflict);
-//   }
-
-
-//   void testPushPopA() {
-//     Node x = d_nm->mkVar(*d_booleanType);
-//     Node x_eq_x = x.eqNode(x);
-
-//     d_ctxt->push();
-//     d_euf->assertFact( x_eq_x );
-//     d_euf->check(d_level);
-//     d_ctxt->pop();
-//     d_euf->check(d_level);
-//   }
-
-//   void testPushPopB() {
-//     Node x = d_nm->mkVar(*d_booleanType);
-//     Node f = d_nm->mkVar(*d_booleanType);
-//     Node f_x = d_nm->mkNode(kind::APPLY_UF, f, x);
-//     Node f_x_eq_x = f_x.eqNode(x);
-
-//     d_euf->assertFact( f_x_eq_x );
-//     d_ctxt->push();
-//     d_euf->check(d_level);
-//     d_ctxt->pop();
-//     d_euf->check(d_level);
-//   }
-
-};
diff --git a/test/unit/util/congruence_closure_white.h b/test/unit/util/congruence_closure_white.h
new file mode 100644 (file)
index 0000000..273f9cf
--- /dev/null
@@ -0,0 +1,454 @@
+/*********************                                                        */
+/*! \file congruence_closure_white.h
+ ** \verbatim
+ ** Original author: mdeters
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief White box testing of CVC4::CongruenceClosure.
+ **
+ ** White box testing of CVC4::CongruenceClosure.
+ **/
+
+#include <cxxtest/TestSuite.h>
+#include <sstream>
+
+#include "context/context.h"
+#include "context/cdset.h"
+#include "context/cdmap.h"
+#include "expr/node.h"
+#include "expr/kind.h"
+#include "expr/node_manager.h"
+#include "util/congruence_closure.h"
+
+
+using namespace CVC4;
+using namespace CVC4::context;
+using namespace std;
+
+
+struct MyOutputChannel {
+  CDSet<Node, NodeHashFunction> d_notifications;
+  CDMap<Node, Node, NodeHashFunction> d_equivalences;
+  NodeManager* d_nm;
+
+  MyOutputChannel(Context* ctxt, NodeManager* nm) :
+    d_notifications(ctxt),
+    d_equivalences(ctxt),
+    d_nm(nm) {
+  }
+
+  bool saw(TNode a, TNode b) {
+    return d_notifications.contains(d_nm->mkNode(kind::EQUAL, a, b)) ||
+      d_notifications.contains(d_nm->mkNode(kind::EQUAL, b, a));
+  }
+
+  TNode find(TNode n) {
+    CDMap<Node, Node, NodeHashFunction>::iterator i = d_equivalences.find(n);
+    if(i == d_equivalences.end()) {
+      return n;
+    } else {
+      // lazy path compression
+      TNode p = (*i).second; // parent
+      TNode f = d_equivalences[n] = find(p); // compress
+      return f;
+    }
+  }
+
+  bool areCongruent(TNode a, TNode b) {
+    Debug("cc") << "=== a is  " << a << std::endl
+                << "=== a' is " << find(a) << std::endl
+                << "=== b is  " << b << std::endl
+                << "=== b' is " << find(b) << std::endl;
+
+    return find(a) == find(b);
+  }
+
+  void notifyCongruent(TNode a, TNode b) {
+    Debug("cc") << "======OI!  I've been notified of: "
+                << a << " == " << b << std::endl;
+
+    Node eq = d_nm->mkNode(kind::EQUAL, a, b);
+    Node eqRev = d_nm->mkNode(kind::EQUAL, b, a);
+
+    TS_ASSERT(!d_notifications.contains(eq));
+    TS_ASSERT(!d_notifications.contains(eqRev));
+
+    d_notifications.insert(eq);
+
+    d_equivalences.insert(a, b);
+  }
+};/* class MyOutputChannel */
+
+
+class CongruenceClosureWhite : public CxxTest::TestSuite {
+  Context* d_context;
+  NodeManager* d_nm;
+  NodeManagerScope* d_scope;
+  MyOutputChannel* d_out;
+  CongruenceClosure<MyOutputChannel>* d_cc;
+
+  TypeNode U;
+  Node a, f, fa, ffa, fffa, ffffa, b, fb, ffb, fffb, ffffb;
+  Node g, gab, gba, gfafb, gfbfa, gfaa, gbfb;
+  Node h, hab, hba, hfaa;
+  Node a_eq_b, fa_eq_b, a_eq_fb, fa_eq_fb, h_eq_g;
+public:
+
+  void setUp() {
+    d_context = new Context;
+    d_nm = new NodeManager(d_context);
+    d_scope = new NodeManagerScope(d_nm);
+    d_out = new MyOutputChannel(d_context, d_nm);
+    d_cc = new CongruenceClosure<MyOutputChannel>(d_context, d_out);
+
+    U = d_nm->mkSort("U");
+
+    f = d_nm->mkVar("f", d_nm->mkFunctionType(U, U));
+    a = d_nm->mkVar("a", U);
+    fa = d_nm->mkNode(kind::APPLY_UF, f, a);
+    ffa = d_nm->mkNode(kind::APPLY_UF, f, fa);
+    fffa = d_nm->mkNode(kind::APPLY_UF, f, ffa);
+    ffffa = d_nm->mkNode(kind::APPLY_UF, f, fffa);
+    b = d_nm->mkVar("b", U);
+    fb = d_nm->mkNode(kind::APPLY_UF, f, b);
+    ffb = d_nm->mkNode(kind::APPLY_UF, f, fb);
+    fffb = d_nm->mkNode(kind::APPLY_UF, f, ffb);
+    ffffb = d_nm->mkNode(kind::APPLY_UF, f, fffb);
+    vector<TypeNode> args(2, U);
+    g = d_nm->mkVar("g", d_nm->mkFunctionType(args, U));
+    gab = d_nm->mkNode(kind::APPLY_UF, g, a, b);
+    gfafb = d_nm->mkNode(kind::APPLY_UF, g, fa, fb);
+    gba = d_nm->mkNode(kind::APPLY_UF, g, b, a);
+    gfbfa = d_nm->mkNode(kind::APPLY_UF, g, fb, fa);
+    gfaa = d_nm->mkNode(kind::APPLY_UF, g, fa, a);
+    gbfb = d_nm->mkNode(kind::APPLY_UF, g, b, fb);
+    h = d_nm->mkVar("h", d_nm->mkFunctionType(args, U));
+    hab = d_nm->mkNode(kind::APPLY_UF, h, a, b);
+    hba = d_nm->mkNode(kind::APPLY_UF, h, b, a);
+    hfaa = d_nm->mkNode(kind::APPLY_UF, h, fa, a);
+
+    a_eq_b = d_nm->mkNode(kind::EQUAL, a, b);
+    fa_eq_b = d_nm->mkNode(kind::EQUAL, fa, b);
+    a_eq_fb = d_nm->mkNode(kind::EQUAL, a, fb);
+    fa_eq_fb = d_nm->mkNode(kind::EQUAL, fa, fb);
+
+    h_eq_g = d_nm->mkNode(kind::EQUAL, h, g);
+  }
+
+  void tearDown() {
+    try {
+      f = a = fa = ffa = fffa = ffffa = Node::null();
+      b = fb = ffb = fffb = ffffb = Node::null();
+      g = gab = gba = gfafb = gfbfa = gfaa = gbfb = Node::null();
+      h = hab = hba = hfaa = Node::null();
+      a_eq_b = fa_eq_b = a_eq_fb = fa_eq_fb = h_eq_g = Node::null();
+      U = TypeNode::null();
+
+      delete d_cc;
+      delete d_out;
+      delete d_scope;
+      delete d_nm;
+      delete d_context;
+
+    } catch(Exception& e) {
+      Warning() << std::endl << e << std::endl;
+      throw;
+    }
+  }
+
+  void testSimple() {
+    //Debug.on("cc");
+    // add terms, then add equalities
+
+    d_cc->addTerm(fa);
+    d_cc->addTerm(fb);
+
+    d_cc->addEquality(a_eq_b);
+
+    TS_ASSERT(d_out->areCongruent(fa, fb));
+    TS_ASSERT(d_cc->areCongruent(fa, fb));
+
+    TS_ASSERT(!d_out->areCongruent(a, b));
+    TS_ASSERT(d_cc->areCongruent(a, b));
+  }
+
+  void testSimpleReverse() {
+    // add equalities, then add terms; should get the same as
+    // testSimple()
+
+    d_cc->addEquality(a_eq_b);
+
+    d_cc->addTerm(fa);
+    d_cc->addTerm(fb);
+
+    TS_ASSERT(d_out->areCongruent(fa, fb));
+    TS_ASSERT(d_cc->areCongruent(fa, fb));
+
+    TS_ASSERT(!d_out->areCongruent(a, b));
+    TS_ASSERT(d_cc->areCongruent(a, b));
+  }
+
+  void testSimpleContextual() {
+    TS_ASSERT(!d_out->areCongruent(fa, fb));
+    TS_ASSERT(!d_cc->areCongruent(fa, fb));
+
+    TS_ASSERT(!d_out->areCongruent(a, b));
+    TS_ASSERT(!d_cc->areCongruent(a, b));
+
+    {
+      d_context->push();
+
+      d_cc->addTerm(fa);
+      d_cc->addTerm(fb);
+
+      TS_ASSERT(!d_out->areCongruent(fa, fb));
+      TS_ASSERT(!d_cc->areCongruent(fa, fb));
+
+      TS_ASSERT(!d_out->areCongruent(a, b));
+      TS_ASSERT(!d_cc->areCongruent(a, b));
+
+      {
+        d_context->push();
+
+        d_cc->addEquality(a_eq_b);
+
+        TS_ASSERT(d_out->areCongruent(fa, fb));
+        TS_ASSERT(d_cc->areCongruent(fa, fb));
+
+        TS_ASSERT(!d_out->areCongruent(a, b));
+        TS_ASSERT(d_cc->areCongruent(a, b));
+
+        d_context->pop();
+      }
+
+      TS_ASSERT(!d_out->areCongruent(fa, fb));
+      TS_ASSERT(!d_cc->areCongruent(fa, fb));
+
+      TS_ASSERT(!d_out->areCongruent(a, b));
+      TS_ASSERT(!d_cc->areCongruent(a, b));
+
+      d_context->pop();
+    }
+
+    TS_ASSERT(!d_out->areCongruent(fa, fb));
+    TS_ASSERT(!d_cc->areCongruent(fa, fb));
+
+    TS_ASSERT(!d_out->areCongruent(a, b));
+    TS_ASSERT(!d_cc->areCongruent(a, b));
+  }
+
+  void testSimpleContextualReverse() {
+    TS_ASSERT(!d_out->areCongruent(fa, fb));
+    TS_ASSERT(!d_cc->areCongruent(fa, fb));
+
+    TS_ASSERT(!d_out->areCongruent(a, b));
+    TS_ASSERT(!d_cc->areCongruent(a, b));
+
+    {
+      d_context->push();
+
+      d_cc->addEquality(a_eq_b);
+
+      TS_ASSERT(!d_out->areCongruent(fa, fb));
+      TS_ASSERT(d_cc->areCongruent(fa, fb));
+
+      TS_ASSERT(!d_out->areCongruent(a, b));
+      TS_ASSERT(d_cc->areCongruent(a, b));
+
+      {
+        d_context->push();
+
+        d_cc->addTerm(fa);
+        d_cc->addTerm(fb);
+
+        TS_ASSERT(d_out->areCongruent(fa, fb));
+        TS_ASSERT(d_cc->areCongruent(fa, fb));
+
+        TS_ASSERT(!d_out->areCongruent(a, b));
+        TS_ASSERT(d_cc->areCongruent(a, b));
+
+        d_context->pop();
+      }
+
+      TS_ASSERT(!d_out->areCongruent(fa, fb));
+      TS_ASSERT(d_cc->areCongruent(fa, fb));
+
+      TS_ASSERT(!d_out->areCongruent(a, b));
+      TS_ASSERT(d_cc->areCongruent(a, b));
+
+      d_context->pop();
+    }
+
+    TS_ASSERT(!d_out->areCongruent(fa, fb));
+    TS_ASSERT(!d_cc->areCongruent(fa, fb));
+
+    TS_ASSERT(!d_out->areCongruent(a, b));
+    TS_ASSERT(!d_cc->areCongruent(a, b));
+  }
+
+  void test_f_of_f_of_something() {
+    d_cc->addTerm(ffa);
+    d_cc->addTerm(ffb);
+
+    d_cc->addEquality(a_eq_b);
+
+    TS_ASSERT(d_out->areCongruent(ffa, ffb));
+    TS_ASSERT(d_cc->areCongruent(ffa, ffb));
+
+    TS_ASSERT(!d_out->areCongruent(fa, fb));
+    TS_ASSERT(d_cc->areCongruent(fa, fb));
+
+    TS_ASSERT(!d_out->areCongruent(a, b));
+    TS_ASSERT(d_cc->areCongruent(a, b));
+  }
+
+  void test_f4_of_something() {
+    d_cc->addTerm(ffffa);
+    d_cc->addTerm(ffffb);
+
+    d_cc->addEquality(a_eq_b);
+
+    TS_ASSERT(d_out->areCongruent(ffffa, ffffb));
+    TS_ASSERT(d_cc->areCongruent(ffffa, ffffb));
+
+    TS_ASSERT(!d_out->areCongruent(fffa, fffb));
+    TS_ASSERT(d_cc->areCongruent(fffa, fffb));
+
+    TS_ASSERT(!d_out->areCongruent(ffa, ffb));
+    TS_ASSERT(d_cc->areCongruent(ffa, ffb));
+
+    TS_ASSERT(!d_out->areCongruent(fa, fb));
+    TS_ASSERT(d_cc->areCongruent(fa, fb));
+
+    TS_ASSERT(!d_out->areCongruent(a, b));
+    TS_ASSERT(d_cc->areCongruent(a, b));
+  }
+
+  void testSimpleBinaryFunction() {
+    d_cc->addTerm(gab);
+    d_cc->addTerm(gba);
+
+    d_cc->addEquality(a_eq_b);
+
+    TS_ASSERT(d_out->areCongruent(gab, gba));
+    TS_ASSERT(d_cc->areCongruent(gab, gba));
+  }
+
+  void testSimpleBinaryFunction2() {
+
+    //Debug.on("cc");
+    try {
+
+    d_cc->addTerm(gfafb);
+    d_cc->addTerm(gba);
+    d_cc->addTerm(hfaa);
+
+    d_cc->addEquality(a_eq_fb);
+    d_cc->addEquality(fa_eq_b);
+    d_cc->addEquality(h_eq_g);
+
+    TS_ASSERT(d_cc->areCongruent(a, fb));
+    TS_ASSERT(d_cc->areCongruent(b, fa));
+    TS_ASSERT(d_cc->areCongruent(fb, ffa));
+
+    Debug("cc") << "\n\n\n"
+                << "a norm:     " << d_cc->normalize(a) << std::endl
+                << "fb norm:    " << d_cc->normalize(fb) << std::endl
+                << "b norm:     " << d_cc->normalize(b) << std::endl
+                << "fa norm:    " << d_cc->normalize(fa) << std::endl
+                << "ffa norm:   " << d_cc->normalize(ffa) << std::endl
+                << "ffffa norm  " << d_cc->normalize(ffffa) << std::endl
+                << "ffffb norm  " << d_cc->normalize(ffffb) << std::endl
+                << "gba norm:   " << d_cc->normalize(gba) << std::endl
+                << "gfaa norm:  " << d_cc->normalize(gfaa) << std::endl
+                << "gbfb norm:  " << d_cc->normalize(gbfb) << std::endl
+                << "gfafb norm: " << d_cc->normalize(gfafb) << std::endl
+                << "hab norm:   " << d_cc->normalize(hab) << std::endl
+                << "hba norm:   " << d_cc->normalize(hba) << std::endl
+                << "hfaa norm:  " << d_cc->normalize(hfaa) << std::endl;
+
+    TS_ASSERT(d_out->areCongruent(gfafb, gba));
+    TS_ASSERT(d_cc->areCongruent(b, fa));
+    TS_ASSERT(d_cc->areCongruent(gfafb, hba));
+    TS_ASSERT(d_cc->areCongruent(gfafb, gba));
+    TS_ASSERT(d_cc->areCongruent(hba, gba));
+    TS_ASSERT(d_cc->areCongruent(hfaa, hba));
+    TS_ASSERT(d_out->areCongruent(hfaa, gba));// fails due to problem with care lists
+    TS_ASSERT(d_cc->areCongruent(hfaa, gba));
+
+    } catch(Exception e) {
+      cout << "\n\n" << e << "\n\n";
+      throw;
+    }
+  }
+
+  void testUF() {
+    //Debug.on("cc");
+    try{
+    Node c_0 = d_nm->mkVar("c_0", U);
+    Node c_1 = d_nm->mkVar("c_1", U);
+    Node c_2 = d_nm->mkVar("c_2", U);
+    Node c2 = d_nm->mkVar("c2", U);
+    Node c3 = d_nm->mkVar("c3", U);
+    Node c4 = d_nm->mkVar("c4", U);
+    Node c5 = d_nm->mkVar("c5", U);
+    Node c6 = d_nm->mkVar("c6", U);
+    Node c7 = d_nm->mkVar("c7", U);
+    Node c8 = d_nm->mkVar("c8", U);
+    Node c9 = d_nm->mkVar("c9", U);
+    vector<TypeNode> args2U(2, U);
+    Node f1 = d_nm->mkVar("f1", d_nm->mkFunctionType(args2U, U));
+
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,d_nm->mkNode(kind::APPLY_UF, f1,c_0,c_0),c_0),d_nm->mkNode(kind::APPLY_UF, f1,c_0,d_nm->mkNode(kind::APPLY_UF, f1,c_0,c_0))));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,d_nm->mkNode(kind::APPLY_UF, f1,c_0,c_0),c_2),d_nm->mkNode(kind::APPLY_UF, f1,c_0,d_nm->mkNode(kind::APPLY_UF, f1,c_0,c_2))));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,d_nm->mkNode(kind::APPLY_UF, f1,c_0,c_1),c_1),d_nm->mkNode(kind::APPLY_UF, f1,c_0,d_nm->mkNode(kind::APPLY_UF, f1,c_1,c_1))));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,d_nm->mkNode(kind::APPLY_UF, f1,c_0,c_1),c_2),d_nm->mkNode(kind::APPLY_UF, f1,c_0,d_nm->mkNode(kind::APPLY_UF, f1,c_1,c_2))));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,d_nm->mkNode(kind::APPLY_UF, f1,c_2,c_1),c_2),d_nm->mkNode(kind::APPLY_UF, f1,c_2,d_nm->mkNode(kind::APPLY_UF, f1,c_1,c_2))));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,d_nm->mkNode(kind::APPLY_UF, f1,c_2,c_2),c_0),d_nm->mkNode(kind::APPLY_UF, f1,c_2,d_nm->mkNode(kind::APPLY_UF, f1,c_2,c_0))));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,d_nm->mkNode(kind::APPLY_UF, f1,c_2,c_2),c_1),d_nm->mkNode(kind::APPLY_UF, f1,c_2,d_nm->mkNode(kind::APPLY_UF, f1,c_2,c_1))));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,c_0,d_nm->mkNode(kind::APPLY_UF, f1,c_2,d_nm->mkNode(kind::APPLY_UF, f1,c_2,d_nm->mkNode(kind::APPLY_UF, f1,c_2,c_0)))),d_nm->mkNode(kind::APPLY_UF, f1,c_2,d_nm->mkNode(kind::APPLY_UF, f1,c_0,d_nm->mkNode(kind::APPLY_UF, f1,c_2,d_nm->mkNode(kind::APPLY_UF, f1,c_0,c_2))))));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,c2,c8),d_nm->mkNode(kind::APPLY_UF, f1,c4,c9)));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, c9,c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, c8,c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, c4,c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, c2,c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, c5,c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, c7,c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, c3,c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, c6,c_1));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,c_2,c_2),c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,c_2,c_1),c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,c_2,c_0),c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,c_1,c_2),c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,c_1,c_1),c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,c_1,c_0),c_1));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,c_0,c_1),c_0));
+    d_cc->addEquality(d_nm->mkNode(kind::EQUAL, d_nm->mkNode(kind::APPLY_UF, f1,c_0,c_0),c_0));
+    }catch(Exception &e) { cout << e << "\n"; throw e; }
+  }
+
+  void testUF2() {
+    Node f = d_nm->mkVar("f", d_nm->mkFunctionType(U, U));
+    Node x = d_nm->mkVar("x", U);
+    Node y = d_nm->mkVar("y", U);
+    Node z = d_nm->mkVar("z", U);
+    Node ffx = d_nm->mkNode(kind::APPLY_UF, f, d_nm->mkNode(kind::APPLY_UF, f, x));
+    Node fffx = d_nm->mkNode(kind::APPLY_UF, f, ffx);
+    Node ffffx = d_nm->mkNode(kind::APPLY_UF, f, fffx);
+    Node ffx_eq_fffx = ffx.eqNode(fffx);
+    Node ffx_eq_y = ffx.eqNode(y);
+    Node ffffx_eq_z = ffffx.eqNode(z);
+
+    d_cc->addEquality(ffx_eq_fffx);
+    d_cc->addEquality(ffx_eq_y);
+    d_cc->addEquality(ffffx_eq_z);
+  }
+
+};/* class CongruenceClosureWhite */