diff --git a/.gitignore b/.gitignore index 454577180..765f3720a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,29 @@ /llvm-seahorn/ /seahorn-llvm/ /gtags.files + +.mypy_cache +.vscode +.sync +.tags +.tags1 +.ipynb_checkpoints +terraform.tfstate +terraform.tfstate.backup* +tools/deep_bench/windows/files/ice.zip +tools/deep_bench/windows/files/mcmc.zip +tools/deep_bench/**/.terraform +tools/deep_bench/**/playbook.retry +tools/deep_bench/**/playbook.retry +tools/deep_bench/windows/clusterjobs.log +tools/deep_bench/windows/out-*.tar.gz + +# Benchmarks + +bench/ +bench_adt/ +bench_horn_adt/ +bench_sim/ + +# .idea files +cmake-build-debug/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 22b92c6a3..dce3185d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,24 +1,22 @@ cmake_minimum_required(VERSION 2.8.11) -project (SeaHorn) -set (SeaHorn_VERSION_MAJOR 0) -set (SeaHorn_VERSION_MINOR 1) -set (SeaHorn_VERSION_PATCH 0) -set (SeaHorn_VERSION_TWEAK "rc3") +project (FreqHorn) +set (FreqHorn_VERSION_MAJOR 0) +set (FreqHorn_VERSION_MINOR 5) if (NOT PACKAGE_VERSION) set(PACKAGE_VERSION - "${SeaHorn_VERSION_MAJOR}.${SeaHorn_VERSION_MINOR}.${SeaHorn_VERSION_PATCH}") - if (DEFINED SeaHorn_VERSION_TWEAK) - set (PACKAGE_VERSION "${PACKAGE_VERSION}-${SeaHorn_VERSION_TWEAK}") + "${FreqHorn_VERSION_MAJOR}.${FreqHorn_VERSION_MINOR}.${FreqHorn_VERSION_PATCH}") + if (DEFINED FreqHorn_VERSION_TWEAK) + set (PACKAGE_VERSION "${PACKAGE_VERSION}-${FreqHorn_VERSION_TWEAK}") endif() - set (SeaHorn_VERSION_INFO ${PACKAGE_VERSION}) + set (FreqHorn_VERSION_INFO ${PACKAGE_VERSION}) endif() if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR ) message (FATAL_ERROR - "In-source builds are not allowed. Please clean your source tree and try again.") + "In-source builds are not allowed. Please clean your source tree and try again.") endif() enable_testing() @@ -26,10 +24,10 @@ include (CTest) # Add path for custom modules list (APPEND CMAKE_MODULE_PATH - "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -option (SEAHORN_STATIC_EXE "Static executable." OFF) +option (FreqHorn_STATIC_EXE "Static executable." OFF) set (CUSTOM_BOOST_ROOT "" CACHE PATH "Path to custom boost installation.") if (CUSTOM_BOOST_ROOT) @@ -46,7 +44,7 @@ set (BOOST_COMPONENTS system) if (UNIT_TESTS) set (BOOST_COMPONENTS ${BOOST_COMPONENTS} unit_test_framework) endif() - find_package (Boost 1.55 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) +find_package (Boost 1.71 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) if (Boost_FOUND) include_directories (${Boost_INCLUDE_DIRS}) endif () @@ -54,8 +52,8 @@ endif () include(ExternalProject) set_property(DIRECTORY PROPERTY EP_STEP_TARGETS configure build test) -set (Z3_TAG "origin/spacer3" CACHE STRING "Z3 git tag to use") -set (Z3_REPO "https://bitbucket.org/spacer/code.git" CACHE STRING "Z3 repo") +set (Z3_TAG "z3-4.8.17" CACHE STRING "Z3 git tag to use") +set (Z3_REPO "https://github.com/Z3Prover/z3.git" CACHE STRING "Z3 repo") if (CMAKE_BUILD_TYPE STREQUAL "Debug") set (Z3_DEBUG "-d") else() @@ -63,24 +61,25 @@ else() endif() ExternalProject_Add(z3 - GIT_REPOSITORY ${Z3_REPO} - GIT_TAG ${Z3_TAG} - BUILD_IN_SOURCE 1 - INSTALL_DIR ${CMAKE_BINARY_DIR}/run - CONFIGURE_COMMAND env CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} - ./configure -p -b build --staticlib ${Z3_DEBUG} - BUILD_COMMAND make -j3 -C build - INSTALL_COMMAND make -C build install - COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_LIST_FILE} - LOG_CONFIGURE 1 - LOG_INSTALL 1 - LOG_BUILD 1) - -find_package(Z3 4.3.2) + GIT_REPOSITORY ${Z3_REPO} + GIT_TAG ${Z3_TAG} + BUILD_IN_SOURCE 1 + INSTALL_DIR ${CMAKE_BINARY_DIR}/run + CONFIGURE_COMMAND env CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} + ./configure -p -b build --staticlib ${Z3_DEBUG} + BUILD_COMMAND make -j3 -C build + INSTALL_COMMAND make -C build install + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_LIST_FILE} + LOG_CONFIGURE 1 + LOG_INSTALL 1 + LOG_BUILD 1) + +find_package(Z3 4.8.17) if (NOT Z3_FOUND) ExternalProject_Get_Property (z3 INSTALL_DIR) set(Z3_ROOT ${INSTALL_DIR} CACHE PATH "Forced location of Z3" FORCE) message(WARNING "No Z3 found. Run \n\tcmake --build . && cmake ${CMAKE_SOURCE_DIR}") + return() else() set_target_properties(z3 PROPERTIES EXCLUDE_FROM_ALL ON) include_directories(${Z3_INCLUDE_DIR}) @@ -89,86 +88,28 @@ else() install (PROGRAMS ${Z3_EXECUTABLE} DESTINATION bin) endif() -ExternalProject_Add (llvm - SVN_REPOSITORY http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_360/final/ - SOURCE_DIR ${CMAKE_SOURCE_DIR}/ext/llvm - INSTALL_DIR ${CMAKE_BINARY_DIR}/run - CMAKE_ARGS - -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} - -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} - -DCMAKE_INSTALL_PREFIX:PATH= - -DLLVM_TARGETS_TO_BUILD:STRING=X86 -DWITH_POLY:BOOL=OFF - -DLLVM_ENABLE_PEDANTIC=OFF - -DLLVM_ENABLE_PIC=ON -DLLVM_REQUIRES_RTTI:BOOL=TRUE - TEST_AFTER_INSTALL 1 - TEST_COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_LIST_FILE} - LOG_CONFIGURE 1 - LOG_BUILD 1 - LOG_INSTALL 1) - -find_package (LLVM 3.6.0 CONFIG NO_DEFAULT_PATH) -if (NOT LLVM_FOUND) - ExternalProject_Get_Property (llvm INSTALL_DIR) - set (LLVM_ROOT ${INSTALL_DIR}) - set (LLVM_DIR ${LLVM_ROOT}/share/llvm/cmake CACHE PATH - "Forced location of LLVM cmake config" FORCE) - message (WARNING "No llvm found. Run \n\tcmake --build . && cmake ${CMAKE_SOURCE_DIR}") - return() -else() - set_target_properties(llvm PROPERTIES EXCLUDE_FROM_ALL ON) - - message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") - message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") - - # We incorporate the CMake features provided by LLVM: - list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") - include(AddLLVM) - include(HandleLLVMOptions) - set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) - set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib) - - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_CXXFLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LLVM_LDFLAGS}") - - set(LLVM_REQUIRES_RTTI TRUE) - set(LLVM_REQUIRES_EH TRUE) - - include_directories(${LLVM_INCLUDE_DIRS}) - link_directories(${LLVM_LIBRARY_DIRS}) - add_definitions(${LLVM_DEFINITIONS}) - -endif() - - install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.md DESTINATION .) # install all the licenses install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/license.txt - DESTINATION share/doc/seahorn - RENAME seahorn_license.txt) + DESTINATION share/doc/FreqHorn + RENAME FreqHorn_license.txt) if (EXISTS z3-prefix/src/z3/LICENSE.txt) install ( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/LICENSE.txt - DESTINATION share/doc/seahorn - RENAME z3_license.txt) -endif() - -if (EXISTS ${CMAKE_CURRENT_BINARY_DIR}/llvm-prefix/src/llvm-build/bin/clang-3.6) - install ( - PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/llvm-prefix/src/llvm-build/bin/clang-3.6 - DESTINATION bin) + FILES + ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/LICENSE.txt + DESTINATION share/doc/FreqHorn + RENAME z3_license.txt) endif() if (EXISTS ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build) file(GLOB z3py - ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build/*.py? - ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build/*.py? - ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build/*.so - ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build/*.dylib - ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build/*.dll - ) + ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build/*.py? + ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build/*.py? + ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build/*.so + ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build/*.dylib + ${CMAKE_CURRENT_BINARY_DIR}/z3-prefix/src/z3/build/*.dll + ) install(FILES ${z3py} DESTINATION lib/z3py) endif() @@ -182,31 +123,41 @@ else() endif() find_package(OpenMP) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") +if (OPENMP_FOUND) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp") +endif() + +find_package(Armadillo) +if (ARMADILLO_FOUND) + add_definitions(-DHAVE_ARMADILLO) + include_directories(${ARMADILLO_INCLUDE_DIR}) +endif() install(DIRECTORY include/ - DESTINATION include - FILES_MATCHING - PATTERN "*.hpp" - PATTERN "*.hh" - PATTERN "*.h" - PATTERN ".svn" EXCLUDE - ) + DESTINATION include + FILES_MATCHING + PATTERN "*.hpp" + PATTERN "*.hh" + PATTERN "*.h" + PATTERN ".svn" EXCLUDE + ) install(DIRECTORY ${CMAKE_BINARY_DIR}/include/ - DESTINATION include - FILES_MATCHING - PATTERN "*.hpp" - PATTERN "*.hh" - PATTERN "*.h" - PATTERN "CMakeFiles" EXCLUDE - PATTERN ".svn" EXCLUDE - ) + DESTINATION include + FILES_MATCHING + PATTERN "*.hpp" + PATTERN "*.hh" + PATTERN "*.h" + PATTERN "CMakeFiles" EXCLUDE + PATTERN ".svn" EXCLUDE + ) set(CMAKE_CXX_EXTENSIONS ON) add_definitions(-Wno-redeclared-class-member -Wno-sometimes-uninitialized) add_definitions(-Wno-covered-switch-default) add_definitions(-Wno-inconsistent-missing-override) +set(CMAKE_CXX_STANDARD 11) if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") add_definitions( -Wno-unused-local-typedefs) endif () @@ -217,7 +168,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include) ### add our include directories to the front, overriding directories ### specified by external packages. include_directories(BEFORE - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_BINARY_DIR}/include) + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include) add_subdirectory(tools) diff --git a/README.md b/README.md index 2e88b4ccc..a1b0db593 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,34 @@ -#About# - -Skolemizer for AE-formulas in LIA/LRA based on the SeaHorn verification framework and the Z3 SMT solver. This is the main computational engine used in the Incremental Model Checking (LPAR'15, CAV'16) and in the Program Synthesis from Assume-Guarantee contracts (preprint). - -#Installation# - -* `cd aeval ; mkdir build ; cd build` -* `cmake ../` -* `make` to build dependencies (Z3 and LLVM) -* `make` to build AE-VAL - -The binary of AE-VAL can be found in `build/tools/aeval/` - +TG-nonlin +======== + +Maximizing Branch Coverage with Constrained Horn Clauses +for Solidity Language + +Installation +============ + +Assumes preinstalled Boost (e.g., 1.75.0) and Gmp (e.g. 10.4.0) packages. + +* `git clone https://github.com/izlatkin/aeval` +* `cd aeval` +* `git checkout tg-nonlin` +* `mkdir build ; cd build` +* `cmake ../` +* `cmake --build . && cmake {PATH_TO_REPO}/aeval` +* `make` (again) to build TG + +The binary of TG-nonlin can be found at `build/tools/nonlin/tgnonlin`. +Note that TG-nonlin comes with its own version of Z3 + +HowTo +========== +`./tools/nonlin/tgnonlin file.smt2` +generated raw tests dumped to `testgen.txt` file + +Benchmarks +========== + +Collection of the Solidity files +https://github.com/leonardoalt/cav_2022_artifact/tree/main/regression +sol files should be encoded to smt2 format (see: https://github.com/izlatkin/solidity_testgen) + diff --git a/include/ae/AeValSolver.hpp b/include/ae/AeValSolver.hpp index 19a0cb49f..219199d5b 100644 --- a/include/ae/AeValSolver.hpp +++ b/include/ae/AeValSolver.hpp @@ -7,665 +7,861 @@ using namespace std; using namespace boost; +using namespace expr::op::bind; namespace ufo { - /** engine to solve validity of \forall-\exists formulas and synthesize Skolem relation */ - - class AeValSolver { - private: - - Expr s; - Expr t; - ExprSet v; // existentially quantified vars - ExprVector stVars; - - ExprVector tConjs; - ExprSet usedConjs; - ExprMap defMap; - ExprSet conflictVars; - - ExprFactory &efac; - EZ3 z3; - ZSolver smt; - SMTUtils u; - - unsigned partitioning_size; - ExprVector projections; - ExprVector instantiations; - ExprVector interpolants; - vector skolMaps; - vector someEvals; - Expr skolSkope; - - bool debug; - unsigned fresh_var_ind; - - public: - - AeValSolver (Expr _s, Expr _t, ExprSet &_v) : - s(_s), t(_t), v(_v), - efac(s->getFactory()), - z3(efac), - smt (z3), - u(efac), - fresh_var_ind(0), - partitioning_size(0), - debug(0) - { - filter (boolop::land(s,t), bind::IsConst (), back_inserter (stVars)); - getConj(t, tConjs); - for (auto &exp: v) { - Expr definition = getDefinitionFormulaFromT(exp); - defMap[exp] = definition; - } - } - - /** - * Decide validity of \forall s => \exists v . t - */ - boost::tribool solve () - { - smt.reset(); - smt.assertExpr (s); - if (!smt.solve ()) { - outs() << "\nE.v.: -; Iter.: 0; Result: valid\n\n"; - return false; - } - if (v.size () == 0) - { - smt.assertExpr (boolop::lneg (t)); - boost::tribool res = smt.solve (); - outs() << "\nE.v.: 0; Iter.: 0; Result: " << (res? "invalid" : "valid") << "\n\n"; - return res; - } - - smt.push (); - smt.assertExpr (t); - - boost::tribool res = true; - - while (smt.solve ()) - { - outs() << "."; - outs().flush (); - ZSolver::Model m = smt.getModel(); - - if (debug && false) + /** engine to solve validity of \forall-\exists formulas and synthesize Skolem relation */ + + class AeValSolver { + private: + + Expr s; + Expr t; + ExprSet v; // existentially quantified vars + ExprVector stVars; + + ExprSet tConjs; + ExprSet usedConjs; + ExprMap defMap; + ExprSet conflictVars; + + ExprFactory &efac; + EZ3 z3; + ZSolver smt; + SMTUtils u; + + unsigned partitioning_size; + ExprVector projections; + ExprVector instantiations; + ExprVector interpolants; + vector skolMaps; + vector someEvals; + Expr skolSkope; + + bool debug; + unsigned fresh_var_ind; + + public: + + AeValSolver (Expr _s, Expr _t, ExprSet &_v) : + s(_s), t(_t), v(_v), + efac(s->getFactory()), + z3(efac), + smt (z3), + u(efac), + fresh_var_ind(0), + partitioning_size(0), + debug(0) { - outs() << "\nmodel " << partitioning_size << ":\n"; - for (auto &exp: stVars) - { - if (exp != m.eval(exp)) - outs() << "[" << *exp << "=" << *m.eval(exp) << "],"; - } - outs() <<"\n"; + filter (boolop::land(s,t), bind::IsConst (), back_inserter (stVars)); + getConj(t, tConjs); + for (auto &exp: v) { + Expr definition = getDefinitionFormulaFromT(exp); + defMap[exp] = definition; + } } - getMBPandSkolem(m); - - smt.pop(); - smt.assertExpr(boolop::lneg(projections[partitioning_size++])); - if (!smt.solve()) { res = false; break; } - - smt.push(); - smt.assertExpr (t); - } - - outs() << "\nE.v.: " << v.size() << "; Iter.: " << partitioning_size - << "; Result: " << (res? "invalid" : "valid") << "\n\n"; - - return res; - } - - /** - * Extract MBP and local Skolem - */ - void getMBPandSkolem(ZSolver::Model &m) - { - Expr pr = t; - ExprMap substsMap; - ExprMap modelMap; - for (auto &exp: v) - { - ExprMap map; - pr = z3_qe_model_project_skolem (z3, m, exp, pr, map); - getLocalSkolems(m, exp, map, substsMap, modelMap, pr); - } - - someEvals.push_back(modelMap); - skolMaps.push_back(substsMap); - projections.push_back(pr); - } - - /** - * Compute local skolems based on the model - */ - void getLocalSkolems(ZSolver::Model &m, Expr exp, - ExprMap &map, ExprMap &substsMap, ExprMap &modelMap, Expr& mbp) - { - if (map.size() > 0){ - ExprSet substs; - for (auto &e: map){ - Expr ef = e.first; - Expr es = e.second; - - if (debug) outs() << "subst: " << *ef << " <--> " << *es << "\n"; - - if (isOpX(es)){ - substs.insert(ineqNegReverter(ef)); - } else if (isOpX(es)){ - if (isOpX(ef)){ - ef = ef->arg(0); - } else { - ef = ineqNegReverter(mk(ef)); + /** + * Decide validity of \forall s => \exists v . t + */ + boost::tribool solve () + { + smt.reset(); + smt.assertExpr (s); + if (!smt.solve ()) { + if (debug) outs() << "\nE.v.: -; Iter.: 0; Result: valid\n\n"; + return false; } - substs.insert(ef); - } else { - if (es == mbp) substs.insert(ineqNegReverter(ef)); - else if (!(isOp(ef) && isOp(es)) && - !(isOp(ef) && isOp(es))){ - substs.insert(mk(ineqNegReverter(ef), ineqNegReverter(es))); + if (v.size () == 0) + { + smt.assertExpr (boolop::lneg (t)); + boost::tribool res = smt.solve (); + if (debug) outs() << "\nE.v.: 0; Iter.: 0; Result: " << (res? "invalid" : "valid") << "\n\n"; + return res; } - } - } - if (substs.size() == 0) outs() << "WARNING: subst is empty for " << *exp << "\n"; - substsMap[exp] = conjoin(substs, efac); - } - else if (m.eval(exp) != exp){ - if (debug) outs () << "model: " << *exp << " <--> " << *m.eval(exp) << "\n"; - modelMap[exp] = mk(exp, m.eval(exp)); - } - } - - /** - * Global Skolem function from MBPs and local ones - */ - Expr getSimpleSkolemFunction() - { - if (partitioning_size == 0){ - outs() << "WARNING: Skolem can be arbitrary\n"; - return mk(efac); - } - - skolSkope = mk(efac); - - for (int i = 0; i < partitioning_size; i++) - { - ExprSet skoledvars; - ExprMap substsMap; - for (auto &exp: v) { - - Expr exp2 = skolMaps[i][exp]; - - if (exp2 != NULL) - { - // GF: todo simplif (?) - exp2 = getAssignmentForVar(exp, exp2); - } - else if (defMap[exp] != NULL) - { - // GF: todo simplif (?) - exp2 = defMap[exp]; - } - else if (someEvals[i][exp] != NULL) - { - exp2 = someEvals[i][exp]->right(); - } - else - { - exp2 = getDefaultAssignment(exp); - } - - if (debug) outs() << "compiling skolem [pt1]: " << *exp << " -- > " << *exp2 << "\n"; - - substsMap[exp] = exp2; + + smt.push (); + smt.assertExpr (t); + + boost::tribool res = true; + + while (smt.solve ()) + { + if (debug) { + outs() << "."; + outs().flush (); + } + ZSolver::Model m = smt.getModel(); + + if (debug) + { + outs() << "\nmodel " << partitioning_size << ":\n"; + for (auto &exp: stVars) + { + if (exp != m.eval(exp)) + outs() << "[" << *exp << "=" << *m.eval(exp) << "],"; + } + outs() <<"\n"; + } + + getMBPandSkolem(m); + + smt.pop(); + smt.assertExpr(boolop::lneg(projections[partitioning_size++])); + if (!smt.solve()) { res = false; break; } + + smt.push(); + smt.assertExpr (t); + } + + if (debug) outs() << "\nE.v.: " << v.size() << "; Iter.: " << partitioning_size + << "; Result: " << (res? "invalid" : "valid") << "\n\n"; + + return res; } - - // get rid of inter-dependencies cascadically: - - ExprVector cnjs; - - for (auto &exp: v) { - refreshMapEntry(substsMap, exp); - cnjs.push_back(mk(exp, substsMap[exp])); - if (debug) outs() << "compiling skolem [pt2]: " << *exp << " <-----> " << *substsMap[exp]<<"\n"; + + /** + * Extract MBP and local Skolem + */ + void getMBPandSkolem(ZSolver::Model &m) + { + Expr pr = t; + ExprMap substsMap; + ExprMap modelMap; + for (auto &exp: v) + { + ExprMap map; + ExprSet lits; + u.getTrueLiterals(pr, m, lits); +// pr = z3_qe_model_project_skolem (z3, m, exp, pr, map); + pr = z3_qe_model_project_skolem (z3, m, exp, conjoin(lits, efac), map); + if (m.eval(exp) != exp) modelMap[exp] = mk(exp, m.eval(exp)); + for (auto it = lits.begin(); it != lits.end(); ){ + if (contains(*it, exp)) ++it; + else it = lits.erase(it); + } + substsMap[exp] = conjoin(lits, efac); + //TODO: Not sure if makes sense: + getLocalSkolems(m, exp, map, substsMap, modelMap, pr); + } + + someEvals.push_back(modelMap); + skolMaps.push_back(substsMap); + projections.push_back(pr); } - - instantiations.push_back(conjoin(cnjs, efac)); - if (debug) outs() << "Sanity check [" < (s,mk (projections[i], instantiations[i])), t) << "\n"; - } - Expr sk = mk(efac); - - for (int i = partitioning_size - 1; i >= 0; i--){ - if (isOpX(projections[i]) && isOpX(sk)) sk = instantiations[i]; - else sk = mk(projections[i], instantiations[i], sk); - } - - Expr skol = simplifiedAnd(skolSkope, sk); - - if (true) outs() << "Sanity check: " << u.isImplies(mk(s, skol), t) << "\n"; - - return skol; - } - - /** - * Valid Subset of S (if overall AE-formula is invalid) - */ - Expr getValidSubset() - { - if (partitioning_size == 0){ - outs() << "WARNING: Trivial valid subset (equal to False) due to 0 iterations\n"; - } - return mk(s, disjoin(projections, efac)); - } - - /** - * Mine the structure of T to get what was assigned to a variable - */ - Expr getDefinitionFormulaFromT(Expr var) - { - Expr def; - for (auto & cnj : tConjs) - { - // get equality (unique per variable) - if (std::find(std::begin(usedConjs), - std::end (usedConjs), cnj) != std::end(usedConjs)) continue; - - if (isOpX(cnj) ) + + /** + * Legacy code, for old Z3 + * Compute local skolems based on the model + */ + void getLocalSkolems(ZSolver::Model &m, Expr exp, + ExprMap &map, ExprMap &substsMap, ExprMap &modelMap, Expr& mbp) { - if (var == cnj->left()) - { - def = cnj->right(); - usedConjs.insert(cnj); - break; - } - else if (var == cnj->right()) - { - def = cnj->left(); - usedConjs.insert(cnj); - break; - } + if (map.size() > 0){ + ExprSet substs; + for (auto &e: map){ + Expr ef = e.first; + Expr es = e.second; + + if (debug) outs() << "subst: " << *ef << " <--> " << *es << "\n"; + + if (isOpX(es)){ + substs.insert(ineqNegReverter(ef)); + } else if (isOpX(es)){ + if (isOpX(ef)){ + ef = ef->arg(0); + } else { + ef = ineqNegReverter(mk(ef)); + } + substs.insert(ef); + } else { + if (es == mbp) substs.insert(ineqNegReverter(ef)); + else if (!(isOp(ef) && isOp(es)) && + !(isOp(ef) && isOp(es))){ + substs.insert(mk(ineqNegReverter(ef), ineqNegReverter(es))); + } + } + } + if (substs.size() == 0) outs() << "WARNING: subst is empty for " << *exp << "\n"; + substsMap[exp] = conjoin(substs, efac); + } + else if (m.eval(exp) != exp){ + if (debug) outs () << "model: " << *exp << " <--> " << *m.eval(exp) << "\n"; + modelMap[exp] = mk(exp, m.eval(exp)); + } } - } - return def; - } - - /** - * Self explanatory - */ - void GetSymbolicMax(ExprVector vec, Expr& curMax) - { - curMax = vec[0]; - for (int i = 1; i < vec.size(); i++){ - if (u.isEquiv(mk(curMax, vec[i]), mk(efac))){ - curMax = vec[i]; - } else if (u.isEquiv(mk(curMax, vec[i]), mk(efac))){ - // curMax is OK - } else { - string ind = lexical_cast (fresh_var_ind++); - string varName = "_aeval_tmp_max_" + ind; - Expr realVarName = mkTerm (varName, efac); - Expr realVar = bind::realConst(realVarName); - - skolSkope = simplifiedAnd(skolSkope, - mk(realVar, mk(mk(curMax, vec[i]), vec[i], curMax))); - curMax = realVar; + + /** + * Global Skolem function from MBPs and local ones + */ + Expr getSimpleSkolemFunction() + { + if (partitioning_size == 0){ + if (debug) outs() << "WARNING: Skolem can be arbitrary\n"; + return mk(efac); + } + + skolSkope = mk(efac); + + for (int i = 0; i < partitioning_size; i++) + { + ExprSet skoledvars; + ExprMap substsMap; + for (auto &exp: v) { + + Expr exp2 = skolMaps[i][exp]; + + if (exp2 != NULL && !isOpX(exp2)) + { + // GF: todo simplif (?) + exp2 = getAssignmentForVar(exp, exp2); + } + else if (defMap[exp] != NULL) + { + // GF: todo simplif (?) + exp2 = defMap[exp]; + } + else if (someEvals[i][exp] != NULL) + { + exp2 = someEvals[i][exp]->right(); + } + else + { + exp2 = getDefaultAssignment(exp); + } + + if (debug) outs() << "compiling skolem [pt1]: " << *exp << " -- > " << *exp2 << "\n"; + + substsMap[exp] = exp2; + } + + // get rid of inter-dependencies cascadically: + + ExprVector cnjs; + + for (auto &exp: v) { + refreshMapEntry(substsMap, exp); + cnjs.push_back(mk(exp, substsMap[exp])); + if (debug) outs() << "compiling skolem [pt2]: " << *exp << " <-----> " << *substsMap[exp]<<"\n"; + } + + instantiations.push_back(conjoin(cnjs, efac)); + if (debug) outs() << "Sanity check [" < (s,mk (projections[i], instantiations[i])), t) << "\n"; + } + Expr sk = mk(efac); + + for (int i = partitioning_size - 1; i >= 0; i--){ + if (isOpX(projections[i]) && isOpX(sk)) sk = instantiations[i]; + else sk = mk(projections[i], instantiations[i], sk); + } + + Expr skol = simplifiedAnd(skolSkope, sk); + + if (false) outs() << "Sanity check: " << (bool)u.implies(mk(s, skol), t) << "\n"; + + return skol; } - } - } - - /** - * Self explanatory - */ - void GetSymbolicMin(ExprVector vec, Expr& curMin) - { - curMin = vec[0]; - for (int i = 1; i < vec.size(); i++){ - if (u.isEquiv(mk(curMin, vec[i]), mk(efac))){ - curMin = vec[i]; - } else if (u.isEquiv(mk(curMin, vec[i]), mk(efac))){ - // curMin is OK - } else { - Expr eqRhs; - string ind = lexical_cast (fresh_var_ind++); - string varName = "_aeval_tmp_min_" + ind; - Expr realVarName = mkTerm (varName, efac); - Expr realVar = bind::realConst(realVarName); - eqRhs = mk(mk(curMin, vec[i]), vec[i], curMin); - skolSkope = simplifiedAnd(skolSkope, mk(realVar, eqRhs)); - curMin = realVar; + + /** + * Valid Subset of S (if overall AE-formula is invalid) + */ + Expr getValidSubset(bool compact = true) + { + if (partitioning_size == 0){ + if (debug) outs() << "WARNING: Trivial valid subset (equal to False) due to 0 iterations\n"; + return mk(efac); + } + + Expr prs; + if (compact) + { + ExprSet all; + vector pprs; + + for (auto & a : projections) + { + ExprSet tmp; + getConj(a, tmp); + pprs.push_back(tmp); + all.insert(tmp.begin(), tmp.end()); + } + + ExprSet common; + + for (auto & a : all) + { + bool everywhere = true; + vector pprsTmp = pprs; + for (auto & p : pprsTmp) + { + bool found = false; + for (auto it = p.begin(); it != p.end(); ++it) + { + if (*it == a) { + found = true; + p.erase(it); + break; + } + } + if (!found) + { + everywhere = false; + break; + } + } + if (everywhere) + { + pprs = pprsTmp; + if (!isOpX(a)) common.insert(a); + } + } + + ExprSet cnjs; + for (auto & p : pprs) + { + cnjs.insert(conjoin(p, efac)); + } + + if (!cnjs.empty()) + { + Expr tmp = simplifyBool(disjoin(cnjs, efac)); + if (!isOpX(tmp)) common.insert(tmp); + } + prs = conjoin(common, efac); + } + else + { + prs = disjoin(projections, efac); + } + return simplifyBool(mk(s, prs)); } - } - } - - /** - * Weird thing, never happens in the experiments - */ - void GetSymbolicNeg(ExprVector vec, Expr& lower, Expr& upper, Expr& candidate) - { - // TODO: maybe buggy in LIA, due to a naive shrinking of the segment; - - for (int i = 0; i < vec.size(); i++){ - - ExprVector forLower; - forLower.push_back(lower); - forLower.push_back(vec[i]); - Expr updLower; - GetSymbolicMax(forLower, updLower); - - ExprVector forUpper; - forUpper.push_back(upper); - forUpper.push_back(vec[i]); - Expr updUpper; - GetSymbolicMin(forUpper, updUpper); - - // TODO: do optimizations - - // first, try to see if there are any concrete values for updLower and updUpper - if (updLower == updUpper) { - upper = updUpper; + + /** + * Mine the structure of T to get what was assigned to a variable + */ + Expr getDefinitionFormulaFromT(Expr var) + { + Expr def; + for (auto & cnj : tConjs) + { + // get equality (unique per variable) + if (std::find(std::begin(usedConjs), + std::end (usedConjs), cnj) != std::end(usedConjs)) continue; + + if (isOpX(cnj) ) + { + if (var == cnj->left()) + { + def = cnj->right(); + usedConjs.insert(cnj); + break; + } + else if (var == cnj->right()) + { + def = cnj->left(); + usedConjs.insert(cnj); + break; + } + } + } + return def; } - else if (upper != updUpper) { - // second, force the symbolic value for upper - upper = mk (mk(updLower, updUpper), updUpper, upper); + + /** + * Self explanatory + */ + void GetSymbolicMax(ExprVector vec, Expr& curMax) + { + curMax = vec[0]; + for (int i = 1; i < vec.size(); i++){ + if (u.isEquiv(mk(curMax, vec[i]), mk(efac))){ + curMax = vec[i]; + } else if (u.isEquiv(mk(curMax, vec[i]), mk(efac))){ + // curMax is OK + } else { + string ind = lexical_cast (fresh_var_ind++); + string varName = "_aeval_tmp_max_" + ind; + Expr realVarName = mkTerm (varName, efac); + Expr realVar = bind::realConst(realVarName); + + skolSkope = simplifiedAnd(skolSkope, + mk(realVar, mk(mk(curMax, vec[i]), vec[i], curMax))); + curMax = realVar; + } + } } - - candidate = mk
(mk(lower, upper), mkTerm (mpq_class (2), efac)); - } - } - - /** - * Aux - */ - void pushVecRedund(ExprVector& vec, Expr a) - { - bool tmp = true; - for (auto& b: vec){ - if (a == b) { - tmp = false; - } else if (lexical_cast(a) == lexical_cast(b)){ - tmp = false; + + /** + * Self explanatory + */ + void GetSymbolicMin(ExprVector vec, Expr& curMin) + { + curMin = vec[0]; + for (int i = 1; i < vec.size(); i++){ + if (u.isEquiv(mk(curMin, vec[i]), mk(efac))){ + curMin = vec[i]; + } else if (u.isEquiv(mk(curMin, vec[i]), mk(efac))){ + // curMin is OK + } else { + Expr eqRhs; + string ind = lexical_cast (fresh_var_ind++); + string varName = "_aeval_tmp_min_" + ind; + Expr realVarName = mkTerm (varName, efac); + Expr realVar = bind::realConst(realVarName); + eqRhs = mk(mk(curMin, vec[i]), vec[i], curMin); + skolSkope = simplifiedAnd(skolSkope, mk(realVar, eqRhs)); + curMin = realVar; + } + } } - } - if (tmp) vec.push_back(a); - } - - /** - * Based on type - */ - Expr getDefaultAssignment(Expr var) - { - if (bind::isBoolConst(var)) return mk(efac); - if (bind::isIntConst(var)) return mkTerm (mpz_class (0), efac); - else // that is, isRealConst(var) == true - return mkTerm (mpq_class (0), efac); - } - - /** - * Return "e + c" - */ - Expr getPlusConst(Expr e, bool isInt, int c) - { - if (isOpX(e) && isInt) - return mkTerm (mpz_class (c + boost::lexical_cast (e)), efac); - - Expr ce = isInt ? mkTerm (mpz_class (c), efac) : mkTerm (mpq_class (c), efac); - return mk(e, ce); - } - - /** - * Extract function from relation - */ - Expr getAssignmentForVar(Expr var, Expr exp) - { - if (debug) outs () << "getAssignmentForVar " << *var << " in " << *exp << "\n"; - - bool isInt = bind::isIntConst(var); - - if (isOp(exp)) - { - if (!bind::isBoolConst(var) && var != exp->left()) - exp = ineqReverter(ineqMover(exp, var)); - // TODO: write a similar simplifier fo booleans - - assert (var == exp->left()); - - if (isOpX(exp) || isOpX(exp) || isOpX(exp)){ - if (exp->left() == exp->right()) return getDefaultAssignment(var); - return exp->right(); + /** + * Weird thing, never happens in the experiments + */ + void GetSymbolicNeg(ExprVector vec, Expr& lower, Expr& upper, Expr& candidate) + { + // TODO: maybe buggy in LIA, due to a naive shrinking of the segment; + + for (int i = 0; i < vec.size(); i++){ + + ExprVector forLower; + forLower.push_back(lower); + forLower.push_back(vec[i]); + Expr updLower; + GetSymbolicMax(forLower, updLower); + + ExprVector forUpper; + forUpper.push_back(upper); + forUpper.push_back(vec[i]); + Expr updUpper; + GetSymbolicMin(forUpper, updUpper); + + // TODO: do optimizations + + // first, try to see if there are any concrete values for updLower and updUpper + if (updLower == updUpper) { + upper = updUpper; + } + else if (upper != updUpper) { + // second, force the symbolic value for upper + upper = mk (mk(updLower, updUpper), updUpper, upper); + } + + candidate = mk
(mk(lower, upper), mkTerm (mpq_class (2), efac)); + } } - else if (isOpX(exp)){ - return getPlusConst (exp->right(), isInt, -1); + + /** + * Aux + */ + void pushVecRedund(ExprVector& vec, Expr a) + { + bool tmp = true; + for (auto& b: vec){ + if (a == b) { + tmp = false; + } else if (lexical_cast(a) == lexical_cast(b)){ + tmp = false; + } + } + if (tmp) vec.push_back(a); } - else if (isOpX(exp)){ - return getPlusConst (exp->right(), isInt, 1); + + /** + * Based on type + */ + Expr getDefaultAssignment(Expr var) + { + if (bind::isBoolConst(var)) return mk(efac); + if (bind::isIntConst(var)) return mkMPZ(0, efac); + else // that is, isRealConst(var) == true + return mkTerm (mpq_class (0), efac); } - else assert(0); - } - else if (isOpX(exp)){ - if (isOpX(exp->left())) { - return getPlusConst (getAssignmentForVar(var, exp->left()), isInt, 1); + + /** + * Return "e + c" + */ + Expr getPlusConst(Expr e, bool isInt, cpp_int c) + { + if (isOpX(e) && isInt) + return mkMPZ(c + boost::lexical_cast (e), efac); + + Expr ce = isInt ? mkMPZ(c, efac) : + mkTerm (mpq_class (lexical_cast(c)), efac); + return mk(e, ce); } - } - else if (isOpX(exp)){ - - exp = u.numericUnderapprox(exp); // try to see if there are only numerals - - if (isOpX(exp)) return exp->right(); - - bool incomplete = false; - - // split constraints - - ExprVector conjLT; - ExprVector conjGT; - ExprVector conjNEG; - ExprVector conjEG; - for (auto it = exp->args_begin(), end = exp->args_end(); it != end; ++it){ - if (isOpX(*it)){ - if (var == (*it)->left()) { - pushVecRedund(conjEG, (*it)->right()); - } else { - incomplete = true; - } - } - else if (isOpX(*it) || isOpX(*it)){ - if (var == (*it)->left()) { - pushVecRedund(conjLT, (*it)->right()); - } else { - incomplete = true; + + /** + * Extract function from relation + */ + Expr getAssignmentForVar(Expr var, Expr exp) + { + exp = simplifyArithmConjunctions(exp); + if (debug) outs () << "getAssignmentForVar " << *var << " in " << *exp << "\n"; + + bool isInt = bind::isIntConst(var); + + if (isOp(exp)) + { + // TODO: write a similar simplifier fo booleans + if (!bind::isBoolConst(var) && var != exp->left()) + exp = ineqMover(exp, var); + + if (var != exp->left()) exp = ineqReverter(exp); + + assert (var == exp->left()); + + if (isOpX(exp) || isOpX(exp) || isOpX(exp)){ + if (exp->left() == exp->right()) return getDefaultAssignment(var); + return exp->right(); + } + else if (isOpX(exp)){ + return getPlusConst (exp->right(), isInt, -1); + } + else if (isOpX(exp)){ + return getPlusConst (exp->right(), isInt, 1); + } + else if (isOpX(exp)){ + return getPlusConst (exp->right(), isInt, 1); + } + else assert(0); } - } - else if (isOpX(*it) || isOpX(*it)){ - if (var == (*it)->left()) { - pushVecRedund(conjGT, (*it)->right()); - } else { - incomplete = true; + else if (isOpX(exp)){ + if (isOpX(exp->left())) { + return getPlusConst (getAssignmentForVar(var, exp->left()), isInt, 1); + } } - } else if (isOpX(*it)){ - Expr negated = (*it)->left(); - - if (isOpX(negated)){ - - if (var == negated->left()) { - pushVecRedund(conjNEG, negated->right()); - } else { - incomplete = true; - } + else if (isOpX(exp)){ + + exp = u.numericUnderapprox(exp); // try to see if there are only numerals + + if (isOpX(exp)) return exp->right(); + + bool incomplete = false; + + // split constraints + + ExprVector conjLT; + ExprVector conjGT; + ExprVector conjNEG; + ExprVector conjEG; + for (auto it = exp->args_begin(), end = exp->args_end(); it != end; ++it){ + Expr norm = ineqSimplifier(var, *it); + if (isOpX(norm)){ + if (var == (norm)->left()) { + pushVecRedund(conjEG, (norm)->right()); + } else { + incomplete = true; + } + } + else if (isOpX(norm) || isOpX(norm)){ + if (var == (norm)->left()) { + pushVecRedund(conjLT, (norm)->right()); + } else { + incomplete = true; + } + } + else if (isOpX(norm) || isOpX(norm)){ + if (var == (norm)->left()) { + pushVecRedund(conjGT, (norm)->right()); + } else { + incomplete = true; + } + } else if (isOpX(norm)){ + Expr negated = (norm)->left(); + + if (isOpX(negated)){ + + if (var == negated->left()) { + pushVecRedund(conjNEG, negated->right()); + } else { + incomplete = true; + } + } + } + } + + // get the assignment (if exists) + + if (conjEG.size() > 0) return *(conjEG.begin()); // GF: maybe try to find the best of them + + if (incomplete) outs() << "WARNING: Some Skolem constraints unsupported\n"; + + // get symbolic max and min + + Expr extraDefsMax = mk(efac); + Expr curMax; + if (conjGT.size() > 1){ + GetSymbolicMax(conjGT, curMax); + } else if (conjGT.size() == 1){ + curMax = conjGT[0]; + } + + Expr extraDefsMin = mk(efac); + Expr curMin; + + if (conjLT.size() > 1){ + GetSymbolicMin(conjLT, curMin); + } else if (conjLT.size() == 1){ + curMin = conjLT[0]; + } + + // get value in the middle of max and min + + if (conjNEG.size() == 0){ + if (conjLT.size() > 0 && conjGT.size() > 0){ + return mk
(mk(curMin, curMax), mkTerm (mpq_class (2), efac)); + } else { + if (conjLT.size() == 0){ + return getPlusConst (curMax, isInt, 1); + } else { + return getPlusConst (curMin, isInt, -1); + } + } + } + + // here and later, we get conjNEG.size() > 0 + + if (conjLT.size() > 0 && conjGT.size() == 0) { + conjNEG.push_back(curMin); + GetSymbolicMin(conjNEG, curMin); + return getPlusConst (curMin, isInt, -1); + } + + if (conjLT.size() == 0 && conjGT.size() > 0) { + conjNEG.push_back(curMax); + GetSymbolicMax(conjNEG, curMax); + return getPlusConst (curMax, isInt, 1); + } + + if (conjLT.size() == 0 && conjGT.size() == 0) { + GetSymbolicMax(conjNEG, curMax); + return getPlusConst (curMax, isInt, 1); + } + + // now, both conjLT and conjGT are non-empty + Expr curMid; + GetSymbolicNeg(conjNEG, curMax, curMin, curMid); + return curMid; } - } - } - - // get the assignment (if exists) - - if (conjEG.size() > 0) return *(conjEG.begin()); // GF: maybe try to find the best of them - - if (incomplete) outs() << "WARNING: Some Skolem constraints unsupported\n"; - - // get symbolic max and min - - Expr extraDefsMax = mk(efac); - Expr curMax; - if (conjGT.size() > 1){ - GetSymbolicMax(conjGT, curMax); - } else if (conjGT.size() == 1){ - curMax = conjGT[0]; + return exp; } - - Expr extraDefsMin = mk(efac); - Expr curMin; - - if (conjLT.size() > 1){ - GetSymbolicMin(conjLT, curMin); - } else if (conjLT.size() == 1){ - curMin = conjLT[0]; + + /** + * Check if there are bounded cycles (at most lvl steps) in the map + */ + bool findCycles(ExprMap &m, Expr var, Expr var2, int lvl=3) + { + Expr entr = m[var]; + if (entr == NULL) return false; + + ExprSet all; + filter (entr, bind::IsConst (), inserter (all, all.begin())); + + if (!emptyIntersect(var2, all)) return true; + + bool res = false; + if (lvl > 0) for (auto& exp: all) res |= findCycles(m, exp, var2, lvl-1); + + return res; } - - // get value in the middle of max and min - - if (conjNEG.size() == 0){ - if (conjLT.size() > 0 && conjGT.size() > 0){ - return mk
(mk(curMin, curMax), mkTerm (mpq_class (2), efac)); - } else { - if (conjLT.size() == 0){ - return getPlusConst (curMax, isInt, 1); - } else { - return getPlusConst (curMin, isInt, -1); + + /** + * Unfolding/simplifying of the map with definitions / substitutions + */ + void refreshMapEntry (ExprMap &m, Expr var) + { + if (debug && false) outs() << "refreshMapEntry for " << *var << "\n"; + + Expr entr = m[var]; + if (std::find(std::begin(conflictVars), std::end (conflictVars), var) != std::end(conflictVars)) + { + entr = defMap[var]; + conflictVars.erase(var); } - } - } - - // here and later, we get conjNEG.size() > 0 - - if (conjLT.size() > 0 && conjGT.size() == 0) { - conjNEG.push_back(curMin); - GetSymbolicMin(conjNEG, curMin); - return getPlusConst (curMin, isInt, -1); + + if (entr == NULL) return; + + if (conflictVars.empty() && findCycles(m, var, var, 1)) + { + // FIXME: it does not find all cycles unfortunately + if (debug) outs () << "cycle found for " << *var << "\n"; + conflictVars.insert(var); + } + + ExprSet skv; + filter (entr, bind::IsConst (), inserter (skv, skv.begin())); + + for (auto& exp2: skv) { + refreshMapEntry(m, exp2); + entr = simplifyBool (u.simplifyITE (replaceAll(entr, exp2, m[exp2]))); + } + + m[var] = u.numericUnderapprox(mk(var, entr))->right(); } - - if (conjLT.size() == 0 && conjGT.size() > 0) { - conjNEG.push_back(curMax); - GetSymbolicMax(conjNEG, curMax); - return getPlusConst (curMax, isInt, 1); + + /** + * Actually, just print it to cmd in the smt-lib2 format + */ + void serialize_formula(Expr form) + { + smt.reset(); + smt.assertExpr(form); + + string errorInfo; + + if (errorInfo.empty ()) + { + smt.toSmtLib (outs()); + outs().flush (); + } } - - if (conjLT.size() == 0 && conjGT.size() == 0) { - GetSymbolicMax(conjNEG, curMax); - return getPlusConst (curMax, isInt, 1); + }; + + inline static bool qeUnsupported (Expr e) + { + if (containsOp(e)) return true; + if (containsOp(e)) return true; + if (containsOp
(e)) return true; + return isNonlinear(e); + } + + inline static Expr coreQE(Expr fla, ExprSet& vars) + { + if (!emptyIntersect(fla, vars) && + !containsOp(fla) && !containsOp(fla) && !qeUnsupported(fla)) + { + AeValSolver ae(mk(fla->getFactory()), fla, vars); // exists quantified . formula + if (ae.solve()) return ae.getValidSubset(); + else return mk(fla->getFactory()); } - - // now, both conjLT and conjGT are non-empty - Expr curMid; - GetSymbolicNeg(conjNEG, curMax, curMin, curMid); - return curMid; - } - return exp; + return fla; + }; + + inline static Expr coreQE(Expr fla, ExprVector& vars) + { + ExprSet varsSet; + for (auto & v : vars) varsSet.insert(v); + return coreQE(fla, varsSet); } - - /** - * Check if there are bounded cycles (at most lvl steps) in the map - */ - bool findCycles(ExprMap &m, Expr var, Expr var2, int lvl=3) + + //TODO: there is a diff with adt-chc, but Idk if there is smth missing from adt-chc + template static Expr eliminateQuantifiers(Expr fla, Range& qVars, bool doArithm = true) { - Expr entr = m[var]; - if (entr == NULL) return false; - - ExprSet all; - filter (entr, bind::IsConst (), inserter (all, all.begin())); - - if (!emptyIntersect(var2, all)) return true; - - bool res = false; - if (lvl > 0) for (auto& exp: all) res |= findCycles(m, exp, var2, lvl-1); - - return res; + if (qVars.size() == 0) return fla; + ExprSet dsjs, newDsjs; + getDisj(fla, dsjs); + if (dsjs.size() > 1) + { + for (auto & d : dsjs) newDsjs.insert(eliminateQuantifiers(d, qVars)); + return disjoin(newDsjs, fla->getFactory()); + } + + ExprSet hardVars; + filter (fla, bind::IsConst (), inserter(hardVars, hardVars.begin())); + minusSets(hardVars, qVars); + ExprSet cnjs; + getConj(fla, cnjs); + constantPropagation(hardVars, cnjs, doArithm); + Expr tmp = simpEquivClasses(hardVars, cnjs, fla->getFactory()); + tmp = simpleQE(tmp, qVars); + return coreQE(tmp, qVars); } - - /** - * Unfolding/simplifying of the map with definitions / substitutions - */ - void refreshMapEntry (ExprMap &m, Expr var) + + template static Expr eliminateQuantifiersExceptForVars(Expr fla, Range const & qVars, bool doArithm = true) { - if (debug && false) outs() << "refreshMapEntry for " << *var << "\n"; - - Expr entr = m[var]; - if (std::find(std::begin(conflictVars), std::end (conflictVars), var) != std::end(conflictVars)) + if (qVars.size() == 0) return fla; + ExprSet dsjs, newDsjs; + getDisj(fla, dsjs); + if (dsjs.size() > 1) { - entr = defMap[var]; - conflictVars.erase(var); - } - - if (entr == NULL) return; - - if (conflictVars.empty() && findCycles(m, var, var, 1)) - { - // FIXME: it does not find all cycles unfortunately - if (debug) outs () << "cycle found for " << *var << "\n"; - conflictVars.insert(var); - } - - ExprSet skv; - filter (entr, bind::IsConst (), inserter (skv, skv.begin())); - - for (auto& exp2: skv) { - refreshMapEntry(m, exp2); - entr = simplifyBool (u.simplifyITE (replaceAll(entr, exp2, m[exp2]))); + for (auto & d : dsjs) newDsjs.insert(eliminateQuantifiersExceptForVars(d, qVars)); + return disjoin(newDsjs, fla->getFactory()); } - - m[var] = u.numericUnderapprox(mk(var, entr))->right(); + + ExprSet hardVars; + filter (fla, bind::IsConst (), inserter(hardVars, hardVars.begin())); + minusSets(hardVars, qVars); + ExprSet cnjs; + getConj(fla, cnjs); + constantPropagation(hardVars, cnjs, doArithm); + Expr tmp = simpEquivClasses(qVars, cnjs, fla->getFactory()); + tmp = simpleQE(tmp, hardVars); + return coreQE(tmp, hardVars); } - - /** - * Actually, just print it to cmd in the smt-lib2 format - */ - void serialize_formula(Expr form) + + template static Expr eliminateQuantifiersRepl(Expr fla, Range& vars) { - smt.reset(); - smt.assertExpr(form); - - string errorInfo; - - if (errorInfo.empty ()) - { - smt.toSmtLib (outs()); - outs().flush (); - } + ExprFactory &efac = fla->getFactory(); + SMTUtils u(efac); + ExprSet complex; + findComplexNumerics(fla, complex); + ExprMap repls; + ExprSet varsCond; varsCond.insert(vars.begin(), vars.end()); + for (auto & a : complex) + { + Expr repl = bind::intConst(mkTerm + ("__repl_" + lexical_cast(repls.size()), efac)); + repls[a] = repl; + for (auto & v : vars) if (contains(a, v)) varsCond.erase(v); + } + Expr condTmp = replaceAll(fla, repls); + Expr tmp = eliminateQuantifiers(condTmp, varsCond); + tmp = replaceAllRev(tmp, repls); + return eliminateQuantifiers(tmp, vars); + } + + inline static Expr keepQuantifiers(Expr fla, ExprVector& vars) + { + ExprSet varsSet; + filter (fla, bind::IsConst (), inserter(varsSet, varsSet.begin())); + minusSets(varsSet, vars); + return eliminateQuantifiers(fla, varsSet); } - }; - - /** - * Simple wrapper - */ - inline void aeSolveAndSkolemize(Expr s, Expr t) - { - ExprSet s_vars; - ExprSet t_vars; - - filter (s, bind::IsConst (), inserter (s_vars, s_vars.begin())); - filter (t, bind::IsConst (), inserter (t_vars, t_vars.begin())); - - ExprSet t_quantified = minusSets(t_vars, s_vars); - - outs() << "S: " << *s << "\n"; - outs() << "T: \\exists "; - for (auto &a: t_quantified) outs() << *a << ", "; - outs() << *t << "\n"; - - AeValSolver ae(s, t, t_quantified); - - Expr res; - if (ae.solve()){ - res = ae.getValidSubset(); - outs() << "\nvalid subset:\n"; - } else { - res = ae.getSimpleSkolemFunction(); - outs() << "\nextracted skolem:\n"; + + inline static Expr keepQuantifiersRepl(Expr fla, ExprVector& vars) + { + ExprSet varsSet; + filter (fla, bind::IsConst (), inserter(varsSet, varsSet.begin())); + minusSets(varsSet, vars); + return eliminateQuantifiersRepl(fla, varsSet); + } + + inline static Expr abduce (Expr goal, Expr assm) + { + ExprFactory &efac = goal->getFactory(); + SMTUtils u(efac); + ExprSet complex; + findComplexNumerics(assm, complex); + findComplexNumerics(goal, complex); + ExprMap repls; + ExprMap replsRev; + for (auto & a : complex) + { + Expr repl = bind::intConst(mkTerm + ("__repl_" + lexical_cast(repls.size()), efac)); + repls[a] = repl; + replsRev[repl] = a; + } + Expr goalTmp = replaceAll(goal, repls); + Expr assmTmp = replaceAll(assm, repls); + + ExprSet vars; + filter (assmTmp, bind::IsConst (), inserter(vars, vars.begin())); + Expr tmp = mkNeg(eliminateQuantifiers(mkNeg(mk(assmTmp, goalTmp)), vars)); + tmp = replaceAll(tmp, replsRev); + + if (isOpX(tmp)) return NULL; // abduction unsuccessful + + // sanity check: + if (!u.implies(mk(tmp, assm), goal)) + { + errs () << "WARNING: abduction fail: "<< * mk(tmp, assm) << " does not imply " << *goal << "\n"; + return NULL; + } + return tmp; } - - ae.serialize_formula(res); - }; } #endif diff --git a/include/ae/ExprSimpl.hpp b/include/ae/ExprSimpl.hpp index d237f08c4..193be8f88 100644 --- a/include/ae/ExprSimpl.hpp +++ b/include/ae/ExprSimpl.hpp @@ -3,22 +3,372 @@ #include #include "ufo/Smt/EZ3.hh" +#include "ufo/Expr.hpp" using namespace std; +using namespace expr::op::bind; using namespace boost; +using namespace boost::multiprecision; + namespace ufo { - /** + template static int getVarIndex(T e, R& vec) + { + int i = 0; + for (auto v = vec.begin(); v != vec.end(); ++v, ++i) + if (*v == e) return i; + return -1; + } + + template static bool contains(R& v, T e) { + return find(v.begin(), v.end(), e) != v.end(); + } + + template static void unique_push_back(T e, vector& v) { + if (!contains(v, e)) v.push_back(e); + } + + template static Expr conjoin(Range& conjs, ExprFactory &efac){ + return + (conjs.size() == 0) ? mk(efac) : + (conjs.size() == 1) ? *conjs.begin() : + mknary(conjs); + } + + template static Expr disjoin(Range& disjs, ExprFactory &efac){ + return + (disjs.size() == 0) ? mk(efac) : + (disjs.size() == 1) ? *disjs.begin() : + mknary(disjs); + } + + template static Expr mkplus(Range& terms, ExprFactory &efac){ + return + (terms.size() == 0) ? mkMPZ (0, efac) : + (terms.size() == 1) ? *terms.begin() : + mknary(terms); + } + + template static Expr mkmult(Range& terms, ExprFactory &efac){ + return + (terms.size() == 0) ? mkMPZ (1, efac) : + (terms.size() == 1) ? *terms.begin() : + mknary(terms); + } + + template static Expr mkbv(Range& terms, ExprFactory &efac){ + return + (terms.size() == 0) ? mkMPZ (1, efac) : + (terms.size() == 1) ? *terms.begin() : + mknary(terms); + } + + template static bool emptyIntersect(Range1& av, Range2& bv){ + for (auto &var1: av){ + for (auto &var2: bv) if (var1 == var2) return false; + } + return true; + } + + template static bool emptyIntersect(Expr a, Range& bv){ + ExprVector av; + filter (a, IsConst (), inserter(av, av.begin())); + return emptyIntersect(av, bv); + } + + inline static bool emptyIntersect(Expr a, Expr b){ + ExprVector bv; + filter (b, IsConst (), inserter(bv, bv.begin())); + return emptyIntersect(a, bv); + } + + // if at the end disjs is empty, then a == true + inline static void getConj (Expr a, ExprSet &conjs) + { + if (isOpX(a)) return; + if (isOpX(a)){ + conjs.clear(); + conjs.insert(a); + return; + } else if (isOpX(a)){ + for (unsigned i = 0; i < a->arity(); i++){ + getConj(a->arg(i), conjs); + } + } else { + conjs.insert(a); + } + } + + // if at the end disjs is empty, then a == false + inline static void getDisj (Expr a, ExprSet &disjs) + { + if (isOpX(a)) return; + if (isOpX(a)){ + disjs.clear(); + disjs.insert(a); + return; + } else if (isOpX(a)){ + for (unsigned i = 0; i < a->arity(); i++){ + getDisj(a->arg(i), disjs); + } + } else { + disjs.insert(a); + } + } + + // rewrites v1 to contain v1 \ v2 + template static void minusSets (Range1& v1, Range2& v2){ + for (auto it = v1.begin(); it != v1.end(); ){ + if (find(v2.begin(), v2.end(), *it) != v2.end()) + it = v1.erase(it); + else ++it; + } + } + + // rewrites v1 to contain only v2 + template static void keepOnly(Range1& v1, Range2& v2){ + for (auto it = v1.begin(); it != v1.end(); ){ + if (find(v2.begin(), v2.end(), *it) == v2.end()) + it = v1.erase(it); + else ++it; + } + } + + // is v1 a subset of v2? + template static bool isSubset(Range1& v1, Range2& v2){ + for (auto it = v1.begin(); it != v1.end(); ++it) + if (find(v2.begin(), v2.end(), *it) == v2.end()) + return false; + return true; + } + + inline static void distribDisjoin(ExprSet& dsj1, ExprSet& dsj2, ExprSet& comm) + { + for (auto it1 = dsj1.begin(); it1 != dsj1.end(); ) + { + bool found = false; + for (auto it2 = dsj2.begin(); it2 != dsj2.end(); ) + { + if (*it1 == *it2) + { + found = true; + comm.insert(*it1); + it1 = dsj1.erase(it1); + it2 = dsj2.erase(it2); + break; + } + else ++it2; + } + if (!found) ++it1; + } + } + + inline static Expr distribDisjoin(Expr d1, Expr d2) + { + auto & efac = d1->getFactory(); + ExprSet dsj1, dsj2, comm; + getConj(d1, dsj1); + getConj(d2, dsj2); + distribDisjoin (dsj1, dsj2, comm); + comm.insert(mk(conjoin(dsj1, efac), conjoin(dsj2, efac))); + return conjoin(comm, d1->getFactory()); + } + + template static Expr distribDisjoin(T& d, ExprFactory &efac) + { + if (d.size() <= 1) return disjoin(d, efac); + + ExprSet comm; + vector dsjs; + dsjs.push_back(ExprSet()); + auto it = d.begin(); + getConj(*it, dsjs.back()); + comm = dsjs.back(); + for (it = std::next(it); it != d.end(); ++it) + { + ExprSet updComm, tmp; + dsjs.push_back(ExprSet()); + getConj(*it, dsjs.back()); + tmp = dsjs.back(); + distribDisjoin (comm, tmp, updComm); + comm = updComm; + } + + ExprSet toDisj; + for (auto a : dsjs) + { + minusSets(a, comm); + toDisj.insert(conjoin(a, efac)); + } + comm.insert(disjoin(toDisj, efac)); + return conjoin(comm, efac); + } + + Expr projectVar(Expr fla, Expr var) + { + ExprSet cnjs; + getConj(fla, cnjs); + for (auto c = cnjs.begin(); c != cnjs.end(); ) + { + if (contains(*c, var)) ++c; + else c = cnjs.erase(c); + } + return conjoin(cnjs, fla->getFactory()); + } + + inline static void getArrInds (Expr a, ExprSet &inds) + { + if ((isOpX(a->right()) && !containsOp(a->right())) + inds.insert(a->right()); + + for (unsigned i = 0; i < a->arity(); i++) + getArrInds(a->arg(i), inds); + } + + inline static void getITEs (Expr a, ExprVector &ites) + { + if (isOpX(a)){ + ites.push_back(a); + } else { + for (unsigned i = 0; i < a->arity(); i++) + getITEs(a->arg(i), ites); + } + } + + inline static bool isBoolean(Expr a) + { + Expr t = typeOf(a); + if (t == NULL) return false; + return isOpX(t); + } + + inline static bool isNumeric(Expr a) + { + Expr t = typeOf(a); + if (t == NULL) return false; + return isOpX(t); + } + + inline static bool isReal(Expr a) + { + Expr t = typeOf(a); + if (t == NULL) return false; + return isOpX(t); + } + + inline static bool isArray(Expr a) + { + Expr t = typeOf(a); + if (t == NULL) return false; + return isOpX(t); + } + + inline static bool isBV(Expr a) + { + Expr t = typeOf(a); + if (t == NULL) return false; + return isOpX(t); + } + + inline static bool isNumericEq(Expr a) + { + return isOpX(a) && isNumeric(a->left()); + } + + inline static bool isNumericConst(Expr e) + { + return isOpX(e) || isOpX(e); + } + + inline static bool isValue(Expr a) + { + return (isNumericConst(a) || isOpX(a) || isOpX(a)); + } + + inline static unsigned containsNum (Expr a, Expr b) + { + if (a == b) return 1; + + unsigned res = 0; + for (unsigned i = 0; i < a->arity(); i++) + res = res + containsNum(a->arg(i), b); + return res; + } + + inline static void findComplexNumerics (Expr a, ExprSet &terms) + { + if (isIntConst(a) || isOpX(a) || isOp(a)) return; + if (isNumeric(a) && !isOpX(a)) + { + bool hasNoNumeric = false; + for (unsigned i = 0; i < a->arity(); i++) + if (!isNumeric(a->arg(i))) hasNoNumeric = true; + if (hasNoNumeric) + { + terms.insert(a); + return; + } + } + for (unsigned i = 0; i < a->arity(); i++) + findComplexNumerics(a->arg(i), terms); + } + + inline static void getArrIneqs (Expr a, ExprSet &ineqs) + { + if (isOp(a) && containsOp(e)) + return extractVal(e->left(), e->right()); + return NULL; + } + + inline static bool evalSelectEq(Expr e) + { + // sound only if returns true + if (isOpX(e)) + { + Expr l = evalSelect(e->left()); + Expr r = evalSelect(e->right()); + if (l == NULL) l = e->left(); + if (r == NULL) r = e->right(); + return (l == r); + } + return false; + } + + struct SimplifyArrExpr + { + SimplifyArrExpr () {}; + + Expr operator() (Expr exp) + { + // GF: to enhance + + if (isOpX(exp)) + { + if (isOpX(exp->left()) && + exp->last() == exp->left()->right()) + return exp->left(); + return mk(duplChainOfStores(exp->left(), exp->right()), + exp->right(), exp->last()); + } + + if (isOpX(s->left(), c); + if (isOpX(s) && isOpX(typeOf(s)->last())) + return mk( + mk(mk(c, b), s->last()), + mk(mk(c, b), mk(r, l->right()), l->last())); + if (isOpX(r) && l == r->left()) + return simplifyArr(mk(mk(l) && isOpX(l->left()) && + r == l->left()->last()) + return mk( + mk(l->right(), l->left()->right()), + mk(mk(r) && isOpX(r->left()) && + l == r->left()->last()) + return mk( + mk(r->right(), r->left()->right()), + mk(mk(t))) + { + int indl = getVarIndex(t->left(), vars); + int indr = getVarIndex(t->right(), vars); + + Expr key = t; + if (indl >= 0) key = replaceAll(key, t->left(), vars[indl]); + if (indr >= 0) key = replaceAll(key, t->right(), vars[indr]); + + if (isOpX(t->left()) && lexical_cast(t->left()) == 0) + return mkMPZ (0, t->getFactory()); + + if (extraVars[key] == NULL) + { + Expr new_name = mkTerm ("__e__" + to_string(extraVars.size()), t->getFactory()); + Expr var = intConst(new_name); + extraVars[key] = var; + } + return extraVars[key]; + } + return t; + } + }; + + inline static Expr findNonlinAndRewrite (Expr exp, ExprVector& vars, ExprMap& extraVars, bool arrays = false) + { + RW mu(new FindNonlinAndRewrite(vars, extraVars, arrays)); + return dagVisit (mu, exp); + } + + struct FindNonlin : public std::unary_function + { + bool found; + + FindNonlin () : found (false) {} + + VisitAction operator() (Expr exp) + { + if (found) + { + found = true; + return VisitAction::skipKids (); + } + else if (isOpX(exp) || isOpX(exp) || isOpX
(exp) || isOpX(exp)) + { + int v = 0; + for (unsigned j = 0; j < exp->arity(); j++) + { + Expr q = exp->arg(j); + if (isIntConst(q)) v++; // GF: a simple counter, to extend + } + + if (v > 1) + { + found = true; + return VisitAction::skipKids (); + } + } + return VisitAction::doKids (); + } + }; + + inline bool findNonlin (Expr e1) + { + FindNonlin fn; + dagVisit (fn, e1); + return fn.found; + } + + inline static Expr simplifiedAnd (Expr a, Expr b){ + ExprSet conjs; + getConj(a, conjs); + getConj(b, conjs); + return conjoin(conjs, a->getFactory()); + } + + inline int intersectSize(ExprVector& a, ExprVector& b){ + ExprSet c; + for (auto &var: a) + if (find(b.begin(), b.end(), var) != b.end()) c.insert(var); + return c.size(); + } + + Expr projectITE(Expr ite, Expr var) + { + if (isOpX(ite)) + { + return mk(ite->arg(0), projectITE(ite->arg(1), var), projectITE(ite->arg(2), var)); + } + else + { + ExprSet cnjs; + getConj(ite, cnjs); + for (auto & a : cnjs) + { + if (a->left() == var) return a->right(); + else if (a->right() == var) return a->left(); + } + + return NULL; + } + } + + inline static void productAnd (ExprSet& as, ExprSet& bs, ExprSet& ps) + { + for (auto &a : as) + { + for (auto &b : bs) + { + Expr orredExpr = simplifyArithmDisjunctions(mk(a, b), false); + if (!isOpX(orredExpr)) + ps.insert(orredExpr); + } + } + } + + // ab \/ cde \/ f => + // (a \/ c \/ f) /\ (a \/ d \/ f) /\ (a \/ e \/ f) /\ + // (b \/ c \/ f) /\ (b \/ d \/ f) /\ (b \/ e \/ f) + inline static Expr rewriteOrAnd(Expr exp, bool approx = false) + { + int maxConjs = 0; + ExprSet disjs; + getDisj(exp, disjs); + if (disjs.size() <= 1) + return disjoin(disjs, exp->getFactory()); + + vector dconjs; + for (auto &a : disjs) + { + ExprSet conjs; + getConj(a, conjs); + dconjs.push_back(conjs); + if (maxConjs < conjs.size()) maxConjs = conjs.size(); + } + + if (disjs.size() > 3 && maxConjs > 3) + { + approx = true; + } + + if (approx) + { + ExprSet newDisjs; + for (auto &d : dconjs) + for (auto &c : d) + newDisjs.insert(c); + return disjoin(newDisjs, exp->getFactory()); + } + + ExprSet older; + productAnd(dconjs[0], dconjs[1], older); + + ExprSet newer = older; + for (int i = 2; i < disjs.size(); i++) + { + newer.clear(); + productAnd(dconjs[i], older, newer); + older = newer; + } + return conjoin (newer, exp->getFactory()); + } + + inline static Expr cloneVar(Expr var, Expr new_name) // ... and give a new_name to the clone + { + if (isOpX(var)) + return replaceAll(var, var->left()->left(), new_name); + + Expr t = typeOf(var); + if (isOpX(t)) + return intConst(new_name); + else if (isOpX(t)) + return realConst(new_name); + else if (isOpX(t)) + return boolConst(new_name); + else if (isOpX(t)) + return mkConst(new_name, mk (t->left(), t->right())); + + else return NULL; + } + + inline static void cloneVector(ExprVector& src, ExprVector& dst, string new_prefix) + { + assert (dst.empty()); + for (int i = 0; i < src.size(); i++) + { + Expr new_name = mkTerm (new_prefix + lexical_cast(src[i]), src[i]->getFactory()); + dst.push_back(cloneVar(src[i], new_name)); + } + } + + inline static bool isBool(Expr e) + { + return typeOf(e) == mk(e->getFactory()); + } + + inline Expr rewriteDivConstraints(Expr fla) + { + // heuristic for the divisibility constraints + assert (isOp(fla)); + ExprVector plusOpsLeft; + ExprVector plusOpsRight; + getAddTerm(fla->left(), plusOpsLeft); + getAddTerm(fla->right(), plusOpsRight); + Expr lhs = NULL; + for (auto & a : plusOpsRight) plusOpsLeft.push_back(additiveInverse(a)); + plusOpsRight.clear(); + for (auto it1 = plusOpsLeft.begin(); it1 != plusOpsLeft.end(); ) + { + if (isOpX(*it1)) + { + lhs = *it1; + plusOpsLeft.erase(it1); + for (auto & a : plusOpsLeft) plusOpsRight.push_back(additiveInverse(a)); + break; + } + if (isOpX(*it1) && isOpX((*it1)->left())) + { + lhs = (*it1)->left(); + plusOpsLeft.erase(it1); + for (auto & a : plusOpsLeft) plusOpsRight.push_back(a); + break; + } + ++it1; + } + + if (lhs == NULL) return fla; + Expr rhs = mkplus(plusOpsRight, fla->getFactory()); + + if (isOpX(fla)) + return mk(mk(lhs->left(), mk(lhs->right(), rhs)), + mk(mk (mk(lhs->right(), rhs), lhs->right()), lhs->left())); + else if (isOpX(fla)) + return mk(mk(mk(lhs->right(), rhs), lhs->left()), + mk(lhs->left(), mk (mk(lhs->right(), rhs), lhs->right()))); + return fla; + } + + inline Expr rewriteModConstraints(Expr fla) + { + // heuristic for the divisibility constraints + assert (isOp(fla)); + ExprVector plusOpsLeft; + ExprVector plusOpsRight; + getAddTerm(fla->left(), plusOpsLeft); + getAddTerm(fla->right(), plusOpsRight); + Expr lhs = NULL; + for (auto & a : plusOpsRight) plusOpsLeft.push_back(additiveInverse(a)); + plusOpsRight.clear(); + cpp_int c1, c2; + for (auto it1 = plusOpsLeft.begin(); it1 != plusOpsLeft.end(); ) + { + if (isOpX(*it1)) + { + Expr d = simplifyArithm((*it1)->last()); + if (isNumericConst(d)) + { + lhs = replaceAll(*it1, (*it1)->last(), d); + c1 = lexical_cast(d); + plusOpsLeft.erase(it1); + for (auto & a : plusOpsLeft) plusOpsRight.push_back(additiveInverse(a)); + plusOpsLeft.clear(); + break; + } + } + ++it1; + } + if (!plusOpsLeft.empty() || lhs == NULL) return fla; + + Expr rhs = mkplus(plusOpsRight, fla->getFactory()); + rhs = simplifyArithm(rhs); + if (isNumericConst(rhs)) c2 = lexical_cast(rhs); + else return fla; + + ExprSet dsjs; + for (auto i = 0; i < c1; i++) + if (evaluateCmpConsts(fla, i, c2)) + dsjs.insert(mk(lhs, mkMPZ(i, fla->getFactory()))); + + fla = disjoin(dsjs, fla->getFactory()); + return fla; + } + + inline static Expr convertToGEandGT(Expr fla) + { + Expr lhs = fla->left(); + Expr rhs = fla->right(); + + if (isOpX(fla)) return mkNeg(convertToGEandGT(lhs)); + + if (isOpX(fla)) return mk(rhs, lhs); + + if (isOpX(fla)) return mk(rhs, lhs); + + if (isOpX(fla)) + { + if (isBool(lhs)) return + mk(mk(lhs, rhs), + mk(mkNeg(lhs), mkNeg(rhs))); + else if (isNumeric(lhs)) { + // heuristic for the divisibility constraints + if (isOpX(rhs) || isOpX(lhs)) { + return rewriteDivConstraints(fla); + } + else + return mk(mk(lhs, rhs), mk(rhs, lhs)); + } + else return fla; + } + + if (isOpX(fla)) + { + if (isBool(lhs)) return + mk(mk(lhs, mkNeg(rhs)), + mk(mkNeg(lhs), rhs)); + else if (isNumeric(lhs)) { + if (isOpX(rhs) || isOpX(lhs)) { + return rewriteDivConstraints(fla); + } + else return mk( + mk(lhs, rhs), mk(rhs, lhs)); + } + else return fla; + } + + if (isOpX(fla) || isOpX(fla)) + { + ExprSet args; + for (int i = 0; i < fla->arity(); i++){ + args.insert(convertToGEandGT(fla->arg(i))); + } + + return isOpX(fla) ? conjoin (args, fla->getFactory()) : + disjoin (args, fla->getFactory()); + } + return fla; + } + + /* find expressions of type expr = arrayVar in e and store it in output */ + inline static void getArrayEqualExprs(Expr e, Expr arrayVar, ExprVector & output) + { + if (e->arity() == 1) { + return; + + } else if (e->arity() == 2) { + Expr lhs = e->left(); + Expr rhs = e->right(); + if (lhs == arrayVar) { + output.push_back(rhs); + return; + + } else if (rhs == arrayVar) { + output.push_back(lhs); + return; + } + } + + for (int i = 0; i < e->arity(); i++) { + getArrayEqualExprs(e->arg(i), arrayVar, output); + } + } + + /* find all expressions in e of type expr = arrayVar */ + /* and replace it by STORE(expr, itr, val) = arrayVar*/ + inline static Expr propagateStore(Expr e, Expr itr, Expr val, Expr arrayVar) + { + Expr retExpr = e; + ExprVector exprvec; + getArrayEqualExprs(e, arrayVar, exprvec); + for (auto & ev : exprvec) + retExpr = replaceAll(retExpr, ev, mk(ev, itr, val)); + return retExpr; + } + + struct ITElifter + { + ITElifter () {}; + + Expr operator() (Expr exp) + { + // currently, can lift only one ITE + if (isOpX(exp) || isOp(exp)) + { + ExprVector vars1; + ExprVector vars2; + Expr cond = NULL; + int i = 0; + if (isOpX(exp)) + { + vars1.push_back(exp->arg(0)); + vars2.push_back(exp->arg(0)); + i = 1; + } + for (; i < exp->arity(); i++) + { + if (isOpX(exp->arg(i)) && cond == NULL) + { + cond = exp->arg(i)->arg(0); + vars1.push_back(exp->arg(i)->arg(1)); + vars2.push_back(exp->arg(i)->arg(2)); + } + else + { + vars1.push_back(exp->arg(i)); + vars2.push_back(exp->arg(i)); + } + } + if (cond == NULL) return exp; + + if (isOpX(exp)) + return mk(cond, mknary(vars1), mknary(vars2)); + else + // isOp(exp) here; thus vars1.size() == vars2.size() == 2 + return mk(cond, reBuildCmp(exp, vars1[0], vars1[1]), reBuildCmp(exp, vars2[0], vars2[1])); + } + return exp; + } + }; + + inline static Expr liftITEs (Expr exp) + { + RW rw(new ITElifter()); + return dagVisit (rw, exp); + } + + inline static Expr unfoldITE(Expr term) + { + if (isOpX(term) && isBool (term->last())) + { + Expr iteCond = unfoldITE (term->arg(0)); + Expr iteC1 = unfoldITE (term->arg(1)); + Expr iteC2 = unfoldITE (term->arg(2)); + + return mk( mk(iteCond, iteC1), + mk(mkNeg(iteCond), iteC2)); + } + else if (isOpX(term)) + { + return mkNeg(unfoldITE(term->last())); + } + else if (isOpX(term) || isOpX(term)) + { + ExprSet args; + for (int i = 0; i < term->arity(); i++){ + args.insert(unfoldITE(term->arg(i))); + } + return isOpX(term) ? conjoin (args, term->getFactory()) : + disjoin (args, term->getFactory()); + } + else if (isOp(term)) + { + Expr lhs = term->arg(0); + Expr rhs = term->arg(1); + + if (isOpX(rhs)) + { + Expr iteCond = unfoldITE (rhs->arg(0)); + Expr iteC1 = rhs->arg(1); + Expr iteC2 = rhs->arg(2); + + Expr newCmp1 = unfoldITE (reBuildCmp(term, lhs, iteC1)); + Expr newCmp2 = unfoldITE (reBuildCmp(term, lhs, iteC2)); + + Expr transformed = mk( mk(iteCond, newCmp1), + mk(mkNeg(iteCond), newCmp2)); + + // outs () << " [1b] ---> " << *term << "\n"; + // outs () << " [1e] ---> " << *transformed << "\n\n"; + return transformed; + } + else if (isOpX(lhs)) + { + // GF: symmetric case to the one above + + Expr iteCond = unfoldITE (lhs->arg(0)); + Expr iteC1 = lhs->arg(1); + Expr iteC2 = lhs->arg(2); + + Expr newCmp1 = unfoldITE (reBuildCmp(term, iteC1, rhs)); + Expr newCmp2 = unfoldITE (reBuildCmp(term, iteC2, rhs)); + + Expr transformed = mk( mk(iteCond, newCmp1), + mk(mkNeg(iteCond), newCmp2)); + + // outs () << " [2b] ---> " << *term << "\n"; + // outs () << " [2e] ---> " << *transformed << "\n\n"; + return transformed; + } + if (isOpX(rhs)) + { + bool found = false; + Expr iteArg; + ExprVector newArgs; + for (auto it = rhs->args_begin(), end = rhs->args_end(); it != end; ++it) + { + // make sure that only one ITE is found + + if (!found && isOpX(*it)) + { + found = true; + iteArg = *it; + } + else + { + newArgs.push_back(*it); + } + } + if (found) + { + Expr iteCond = unfoldITE (iteArg->arg(0)); + Expr iteC1 = iteArg->arg(1); + Expr iteC2 = iteArg->arg(2); + + newArgs.push_back(iteC1); + Expr e1 = unfoldITE (reBuildCmp(term, lhs, mknary(newArgs))); // GF: "unfoldITE" gives error... + + newArgs.pop_back(); + newArgs.push_back(iteC2); + Expr e2 = unfoldITE (reBuildCmp(term, lhs, mknary(newArgs))); + + Expr transformed = mk(mk(iteCond, e1), + mk(mkNeg(iteCond),e2)); + + // outs () << " [3b] ---> " << *term << "\n"; + // outs () << " [3e] ---> " << *transformed << "\n\n"; + + return transformed; + } + } + if (isOpX(lhs)) + { + // symmetric to the above case + bool found = false; + Expr iteArg; + ExprVector newArgs; + for (auto it = lhs->args_begin(), end = lhs->args_end(); it != end; ++it) + { + if (!found && isOpX(*it)) + { + found = true; + iteArg = *it; + } + else + { + newArgs.push_back(*it); + } + } + + if (found) + { + Expr iteCond = unfoldITE (iteArg->arg(0)); + Expr iteC1 = iteArg->arg(1); + Expr iteC2 = iteArg->arg(2); + + newArgs.push_back(iteC1); + Expr e1 = unfoldITE (reBuildCmp(term, mknary(newArgs), rhs)); + + newArgs.pop_back(); + newArgs.push_back(iteC2); + Expr e2 = unfoldITE (reBuildCmp(term, mknary(newArgs), rhs)); + + Expr transformed = mk(mk(iteCond,e1), + mk(mkNeg(iteCond),e2)); + + // outs () << " [4b] ---> " << *term << "\n"; + // outs () << " [4e] ---> " << *transformed << "\n\n"; + + return transformed; + } + } + if (isOpX(lhs)) + { + Expr arrVar = lhs->left(); + if (isOpX(arrVar)) + { + Expr ifExpr = unfoldITE(reBuildCmp(term, arrVar->right(), rhs)); + Expr elseExpr = unfoldITE(reBuildCmp(term, arrVar->last(), rhs)); + + ifExpr = propagateStore(ifExpr, lhs->right(), lhs->last(), rhs); + elseExpr = propagateStore(elseExpr, lhs->right(), lhs->last(), rhs); + + Expr condExpr = unfoldITE (arrVar->left()); + Expr retExpr = mk (mk(condExpr, ifExpr), mk(mkNeg(condExpr), elseExpr)); + + return retExpr; + } + } + if (isOpX(rhs)) + { + Expr arrVar = rhs->left(); + if (isOpX(arrVar)) + { + Expr ifExpr = unfoldITE (reBuildCmp(term, arrVar->right(), lhs)); + Expr elseExpr = unfoldITE (reBuildCmp(term, arrVar->last(), lhs)); + + ifExpr = propagateStore(ifExpr, rhs->right(), rhs->last(), lhs); + elseExpr = propagateStore(elseExpr, rhs->right(), rhs->last(), lhs); + + Expr condExpr = unfoldITE (arrVar->left()); + Expr retExpr = mk (mk(condExpr, ifExpr), mk(mkNeg(condExpr), elseExpr)); + + return retExpr; + } + } + if (isOpX(arrVar->right(), rhs->right()), + mk(exp->right())) + { + Expr cmp = simplifyCmp(mk(ind, exp->right()->right())); + return simplifyIte(mk(cmp, + mk(efac), exp)); + } + return exp; + } + }; + + inline static Expr rewriteSelectStore(Expr exp); + + struct SelectStoreRewriter + { + SelectStoreRewriter () {}; + + Expr operator() (Expr exp) + { + if (isOpX(exp->left()->left(), exp->right())); + } + + // to avoid this, try unfoldITE first + if (containsOp(exp)) return exp; + + Expr sel = NULL, val = NULL; + if (isOpX(exp)) + { + if (isOpX(exp->right())) { sel = exp->right(); val = exp->left(); } + if (isOpX(exp->left())) { sel = exp->left(); val = exp->right(); } + } + + if (sel != NULL) + { + Expr main = mk(sel->last(), mk(exp)) + { + if (sels[exp] != NULL) return sels[exp]; + Expr repl = intConst(mkTerm ("sel_" + lexical_cast(sels.size()), exp->getFactory())); + sels[exp] = repl; + return repl; + } + return exp; + } + }; + + inline static Expr replaceSelects(Expr exp, ExprMap& sels) + { + RW a(new SelectReplacer(sels)); + return dagVisit (a, exp); + } + + struct QuantifiedVarsFilter : public std::unary_function + { + ExprSet& vars; + + QuantifiedVarsFilter (ExprSet& _vars): vars(_vars) {}; + + VisitAction operator() (Expr exp) + { + if (isOp(exp) || isOp(exp)) + { + for (int i = 0; i < exp->arity() - 1; i++) + { + vars.insert(fapp(exp->arg(i))); + } + } + return VisitAction::doKids (); + } + }; + + inline void getQuantifiedVars (Expr exp, ExprSet& vars) + { + QuantifiedVarsFilter qe (vars); + dagVisit (qe, exp); + } + + inline static void getQuantifiedFormulas (Expr a, ExprSet &flas) + { + if (isOpX(a) || isOpX(a)) + flas.insert(a); + else // TODO: remove/generalize later + for (unsigned i = 0; i < a->arity(); i++) + getQuantifiedFormulas(a->arg(i), flas); + } + + template static Expr mkQFla (Expr def, Range& vars, bool forall = false) + { + if (vars.empty()) return def; + ExprVector args; + for (auto & a : vars) args.push_back(a->last()); + args.push_back(def); + if (forall) return mknary(args); + else return mknary(args); + } + + static Expr mkQFla (Expr def, bool forall = false) + { + ExprSet vars; + filter (def, IsConst (), inserter(vars, vars.begin())); + return mkQFla(def, vars, forall); + } + + static Expr mkQFla (Expr def, Expr var, bool forall = false) + { + ExprSet vars = {var}; + return mkQFla(def, vars, forall); + } + + static Expr factor(Expr e, int times = 3) + { + ExprSet dsjs; + getDisj(e, dsjs); + if (dsjs.size() <= 1) return e; + + for (int n = 0; n < times; n++) + { + ExprSet dsjs2; + for (auto & d : dsjs) + { + ExprSet cnjs, mut; + getConj(d, cnjs); + + if (cnjs.size() < 100) // threshold + for (auto & c1 : cnjs) + { + if (isOpX(c1)) + { + if (c1->left() == c1->right()) continue; + for (auto & c2 : cnjs) + { + if (c1 == c2) continue; + + getSingleReplacements (c2, c1->left(), c1->right(), mut); + getSingleReplacements (c2, c1->right(), c1->left(), mut); + } + } + } + cnjs.insert(mut.begin(), mut.end()); + dsjs2.insert(conjoin(cnjs, e->getFactory())); + } + dsjs = dsjs2; + } + return distribDisjoin(dsjs, e->getFactory()); + } + + // rewrite just equalities + template static Expr simpleQE(Expr exp, Range& quantified) + { + ExprFactory& efac = exp->getFactory(); + ExprSet cnjsSet, dsjsSet; + getDisj(exp, dsjsSet); + if (dsjsSet.size() > 1) + { + ExprSet newDsjs; + for (auto & d : dsjsSet) newDsjs.insert(simpleQE(d, quantified)); + return disjoin(newDsjs, efac); + } + + getConj(exp, cnjsSet); + ExprVector cnjs; + ineqMerger(cnjsSet, true); + cnjs.insert(cnjs.end(), cnjsSet.begin(), cnjsSet.end()); + for (auto & var : quantified) + { + ExprSet eqs; + + for (unsigned it = 0; it < cnjs.size(); ) + { + Expr cnj = cnjs[it]; + if (!isOpX(cnj) || !contains(cnj, var)) + { it++; continue;} + + Expr normalized = cnj; + if (isNumeric(var) && isNumeric(cnj->left())) + { + normalized = simplifyArithm( + mk(mk(cnj->arg(0), additiveInverse(cnj->arg(1))), + mkMPZ (0, efac))); + normalized = ineqSimplifier(var, normalized); + } + else if (var == normalized->right()) + { + normalized = mk(var, normalized->left()); + } + + // after the normalization, var can be eliminated + if (!isOpX(normalized) || !contains(normalized, var)) + { it++; continue;} + + if (var == normalized->left()) + { + eqs.insert(normalized->right()); + cnjs.erase (cnjs.begin()+it); + continue; + } + else if (isOpX(normalized->left()) && isOpX(normalized->left()->left())) + { + cnjs.push_back(mk(mk(normalized->right(), normalized->left()->left()), + mkMPZ (0, efac))); + } + else if (isArray(var) && containsNum(exp, var) == 1) + { + Expr store = NULL; + if (isOpX(normalized->right()) && var == normalized->right()->left() && + emptyIntersect(normalized->left(), quantified)) + { + // one level of storing (to be extended) + store = normalized; + } + else if (isOpX(normalized->left()) && var == normalized->left()->left() && + emptyIntersect(normalized->right(), quantified)) + { + normalized = mk(normalized->right(), normalized->left()); + store = normalized; + } + + if (store != NULL) + { + // assume "store" = (A = store(var, x, y)) + cnjs[it] = mk(mk(arrVar, indVar), valVar)); + return arrVar; + } + + struct TransitionOverapprox + { + ExprVector& srcVars; + ExprVector& dstVars; + + TransitionOverapprox (ExprVector& _srcVars, ExprVector& _dstVars): + srcVars(_srcVars), dstVars(_dstVars) {}; + + Expr operator() (Expr exp) + { + if (isOp(exp) && !containsOp(exp)) + { + ExprSet tmp; + if (isOpX(exp->left())) + { + processNestedStores(exp->left(), tmp); + return conjoin(tmp, exp->getFactory()); + } + else if (isOpX(exp->right())) + { + processNestedStores(exp->right(), tmp); + return conjoin(tmp, exp->getFactory()); + } + + ExprVector av; + filter (exp, IsConst (), inserter(av, av.begin())); + if (!emptyIntersect(av, srcVars) && !emptyIntersect(av, dstVars)) + return mk(exp->getFactory()); + } + else if (isOpX(exp)) + { + ExprSet newDsjs; + for (unsigned i = 0; i < exp->arity (); i++) + { + ExprSet cnjs; + getConj(exp->arg(i), cnjs); + map sels; + bool allselects = true; + bool noselects = true; + for (auto & a : cnjs) + { + sels[a] = containsOp(exp) || isOpX(exp)) && !containsOp(exp->right())) + { + accs.insert(exp->right()); + } + return VisitAction::doKids (); + } + }; + + struct DeltaRetriever : public std::unary_function + { + ExprVector& srcVars; + ExprVector& dstVars; + ExprSet& deltas; + + DeltaRetriever (ExprVector& _srcVars, ExprVector& _dstVars, ExprSet& _deltas): + srcVars(_srcVars), dstVars(_dstVars), deltas(_deltas) {}; + + VisitAction operator() (Expr exp) + { + if (isOpX(exp)) + { + ExprVector av; + filter (exp, IsConst (), inserter(av, av.begin())); + if (av.size() != 2) return VisitAction::skipKids ();; + for (int i = 0; i < srcVars.size(); i++) + { + if ((av[0] == srcVars[i] && av[1] == dstVars[i]) || + (av[1] == srcVars[i] && av[0] == dstVars[i])) + { + set coefs; + exp = normalizeAtom(exp, av); + if (!getLinCombCoefs(exp, coefs)) continue; + + bool success = true; + for (auto i : coefs) success = success && (i == -1 || i == 1); + if (success) + { + Expr cExpr = exp->right(); + cpp_int c = abs(lexical_cast(cExpr)); + if (c > 1) + for (int j = 0; j < 2 /*c*/; j++) // GF: for large c it gives many cands + deltas.insert(mk(mk( + srcVars[i], + mkMPZ(c, exp->getFactory())), + mkMPZ ((j), exp->getFactory()))); + } + } + } + return VisitAction::skipKids (); + } + return VisitAction::doKids (); + } + }; + + inline Expr rewriteBoolEq (Expr exp) + { + RW tr(new BoolEqRewriter()); + return dagVisit (tr, exp); + } + + inline void retrieveDeltas (Expr exp, ExprVector& srcVars, ExprVector& dstVars, ExprSet& deltas) + { + DeltaRetriever dr (srcVars, dstVars, deltas); + dagVisit (dr, exp); + } + + inline void retrieveConds (Expr exp, ExprSet& conds) + { + CondRetriever dr (conds); + dagVisit (dr, exp); + } + + inline void retrieveAccFuns (Expr exp, ExprSet& accs) + { + AccessRetriever dr (accs); + dagVisit (dr, exp); + } + + inline void retrieveTransitions (Expr exp, ExprVector& srcVars, ExprVector& dstVars, ExprSet& transitions) + { + TransitionMiner trm (srcVars, dstVars, transitions); + dagVisit (trm, exp); + } + + inline static Expr overapproxTransitions (Expr exp, ExprVector& srcVars, ExprVector& dstVars) + { + RW rw(new TransitionOverapprox(srcVars, dstVars)); + return dagVisit (rw, exp); + } + + inline static Expr mergeIneqs (Expr e1, Expr e2) + { + if (isOpX(e1)) e1 = mkNeg(e1->last()); + if (isOpX(e2)) e2 = mkNeg(e2->last()); + + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->left()) + return mk(e1->left(), e2->right()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->left()) + return mk(e1->left(), e2->right()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->left()) + return mk(e1->left(), e2->right()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->left()) + return mk(e1->left(), e2->right()); + if (isOpX(e1) && isOpX(e2) && (e1->left() == e2->right())) + return mk(e2->left(), e1->right()); + + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->left()) + return mk(e1->left(), e2->right()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->left()) + return mk(e1->left(), e2->right()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->left()) + return mk(e1->left(), e2->right()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->left()) + return mk(e1->left(), e2->right()); + + if (isOpX(e1) && isOpX(e2) && e2->right() == e1->left()) + return mk(e2->left(), e1->right()); + if (isOpX(e2) && isOpX(e1) && e2->right() == e1->left()) + return mk(e2->left(), e1->right()); + if (isOpX(e2) && isOpX(e1) && e2->right() == e1->left()) + return mk(e2->left(), e1->right()); + if (isOpX(e2) && isOpX(e1) && e2->right() == e1->left()) + return mk(e2->left(), e1->right()); + + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->right()) + return mk(e1->left(), e2->left()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->right()) + return mk(e1->left(), e2->left()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->right()) + return mk(e1->left(), e2->left()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->right()) + return mk(e1->left(), e2->left()); + + if (isOpX(e1) && isOpX(e2) && e1->left() == e2->left()) + return mk(e2->right(), e1->right()); + if (isOpX(e1) && isOpX(e2) && e1->left() == e2->left()) + return mk(e2->right(), e1->right()); + if (isOpX(e1) && isOpX(e2) && e1->left() == e2->left()) + return mk(e2->right(), e1->right()); + if (isOpX(e1) && isOpX(e2) && e1->left() == e2->left()) + return mk(e2->right(), e1->right()); + + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->right()) + return mk(e1->left(), e2->left()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->right()) + return mk(e1->left(), e2->left()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->right()) + return mk(e1->left(), e2->left()); + if (isOpX(e1) && isOpX(e2) && e1->right() == e2->right()) + return mk(e1->left(), e2->left()); + + // TODO: support more cases + return NULL; + } + + inline static Expr mergeIneqsWithVar (Expr e, Expr var) + { + ExprSet cnjs; + ExprVector cnjs2; + ExprSet cnjs3; + getConj(e, cnjs); + for (auto & a : cnjs) + if (contains(a, var)) cnjs2.push_back(a); + else cnjs3.insert(a); + + if (cnjs2.size() != 2) return e; + + if(mergeIneqs(cnjs2[0], cnjs2[1]) == NULL) return NULL; + + cnjs3.insert(mergeIneqs(cnjs2[0], cnjs2[1])); + return conjoin(cnjs3, e->getFactory()); + } + + template static void computeTransitiveClosure(ExprSet& r, ExprSet& tr) + { + for (auto &a : r) + { + if (isOpX(a)) + { + for (auto &b : tr) + { + if (isOpX(b)) + { + if (a->left() == b->right()) tr.insert(mk(b->left(), a->right())); + if (b->left() == a->right()) tr.insert(mk(a->left(), b->right())); + + if (isSymmetric(a)) + { + if (a->left() == b->left()) tr.insert(mk(a->right(), b->right())); + if (a->right() == b->right()) tr.insert(mk(a->left(), b->left())); + } + } + } + } + tr.insert(a); + } + } + + struct TransClAdder + { + TransClAdder () {}; + + Expr operator() (Expr exp) + { + if (isOpX(exp)) + { + ExprSet cnjs; + ExprSet trCnjs; + getConj(exp, cnjs); + computeTransitiveClosure(cnjs, trCnjs); + computeTransitiveClosure(cnjs, trCnjs); + computeTransitiveClosure(cnjs, trCnjs); + computeTransitiveClosure(cnjs, trCnjs); + computeTransitiveClosure(cnjs, trCnjs); + return conjoin(trCnjs, exp->getFactory()); + } + + return exp; + } + }; + + inline static Expr enhanceWithMoreClauses (Expr exp) + { + RW tr(new TransClAdder()); + return dagVisit (tr, exp); + } + + inline static Expr propagateEqualities (Expr exp) + { + ExprSet cnjs; + ExprSet eqs; + ExprSet trEqs; + + getConj(exp, cnjs); + + for (auto &a : cnjs) if (isOpX(a)) eqs.insert(a); + if (eqs.size() == 0) return exp; + + computeTransitiveClosure(eqs, trEqs); + + for (auto &a : trEqs) + { + if (isOpX(a)) + { + bool toAdd = true; + for (auto & c : cnjs) + { + if (isOpX(c)) + { + if (c->left() == a->left() && c->right() == a->right()) { toAdd = false; break; } + if (c->left() == a->right() && c->right() == a->left()) { toAdd = false; break; } + } + } + if (toAdd) cnjs.insert(a); + } +// TODO: double-check if it is needed: +/* else + { + Expr neg = mkNeg(a); + for (auto &b : trEqs) + { + Expr repl1 = replaceAll(neg, b->left(), b->right()); + Expr repl2 = replaceAll(neg, b->right(), b->left()); + bool eq1 = (repl1 == neg); + bool eq2 = (repl2 == neg); + bool eq3 = (repl2 == repl1); + + if (eq1 && eq2 && eq3) cnjs.insert(a); + else if (eq1) cnjs.insert (mkNeg (mk(neg, repl2))); + else if (eq2) cnjs.insert (mkNeg (mk(neg, repl1))); + else cnjs.insert(mkNeg (mk(neg, mk(repl1, repl2)))); + } + } */ + } + + return conjoin(cnjs, exp->getFactory()); + } + + bool isNonlinear(Expr e) { + int arity = e->arity(); + if (isOpX(e) || isOpX
(e) || isOpX(e)) { + ExprVector av; + filter (e->right(), IsConst (), inserter(av, av.begin())); + if (av.size() > 0) return true; + else return isNonlinear(e->left()); + } + else if (isOpX(e)) + { + int numVars = 0; + for (int i = 0; i < arity; i++) + { + if (isNonlinear(e->arg(i))) return true; + ExprVector av; + filter (e->arg(i), IsConst (), inserter(av, av.begin())); + if (av.size() > 0) numVars++; + } + return (numVars > 1); + } + bool res = false; + for (int i = 0; i < arity; i++) { + res = res || isNonlinear(e->arg(i)); + } + return res; + } + + struct QVMiner : public std::unary_function + { + map& vars; + QVMiner (map& _vars): vars(_vars) {}; + + VisitAction operator() (Expr exp) + { + if (isOpX(exp) || isOpX(exp)) + { + for (int i = 0; i < exp->arity() - 1; i++) + vars[exp].push_back(fapp(exp->arg(i))); + + reverse(vars[exp].begin(),vars[exp].end()); + + for (auto & a : vars) + if (contains(a.first, exp) && a.first != exp) + vars[exp].insert(vars[exp].end(), a.second.begin(), a.second.end()); + } + return VisitAction::doKids (); + } + }; + + inline void getQVars (Expr exp, map& vars) + { + QVMiner qvm (vars); + dagVisit (qvm, exp); + } + + struct QFregularizer + { + ExprVector& vars; + QFregularizer (ExprVector& _vars): vars(_vars){}; + Expr operator() (Expr exp) + { + if (isBVar(exp)) return vars[bvarId(exp)]; + return exp; + } + }; + + inline static Expr regularizeQF (Expr exp) + { + map vars; + getQVars(exp, vars); + ExprMap replaced; + for (auto & a : vars) + { + Expr sub = replaceAll(a.first, replaced); + RW rw(new QFregularizer(a.second)); + Expr b = dagVisit (rw, sub); + replaced[a.first] = b; + exp = replaceAll(exp, sub, b); + } + + return exp; + } + + inline static bool evalLeq(Expr a, Expr b) + { + if (isOpX(a) && isOpX(b)) + return (lexical_cast(a) <= lexical_cast(b)); + else return (a == b); // GF: to extend + } + + inline static void mutateHeuristic (Expr exp, ExprSet& guesses /*, int bnd = 100*/) + { + exp = unfoldITE(exp); + ExprSet cnjs; + getConj(exp, cnjs); + ExprSet ineqs; + ExprSet eqs; + ExprSet disjs; + for (auto c : cnjs) + { + if (isOpX(c)) c = mkNeg(c->left()); + + if (isOpX(c)) + { + if (isNumeric(c->left())) + { + eqs.insert(c); + ineqs.insert(mk(c->right(), c->left())); + ineqs.insert(mk(c->left(), c->right())); + } + else + { + guesses.insert(simplifyArithm(c)); + } + } + else if (isOp(c)) + { + if (isOpX(c)) ineqs.insert(c); + else if (isOpX(c)) ineqs.insert(mk(c->right(), c->left())); + else if (isOpX(c)) + { + if (isOpX(c->left())) + ineqs.insert(mk(c->right(), mkMPZ (lexical_cast(c->left())-1, exp->getFactory()))); + else if(isOpX(c->right())) + ineqs.insert(mk(mkMPZ (lexical_cast(c->right())+1, exp->getFactory()), c->left())); + else + ineqs.insert(mk(c->right(), mk(c->left(), mkMPZ (1, exp->getFactory())))); + } + else if (isOpX(c)) + { + if (isOpX(c->left())) + ineqs.insert(mk(mkMPZ (lexical_cast(c->left())+1, exp->getFactory()), c->right())); + else if(isOpX(c->right())) + ineqs.insert(mk(c->left(), mkMPZ (lexical_cast(c->right())-1, exp->getFactory()))); + else + ineqs.insert(mk(c->left(), mk(c->right(), mkMPZ (1, exp->getFactory())))); + } + else + { + assert (isOpX(c)); + guesses.insert(c); + } + } +/* else if (isOpX(c)) + { + ExprSet terms; + getDisj(c, terms); + ExprSet newTerms; + for (auto t : terms) + { + if (newTerms.size() > 2) continue; // don't consider large disjunctions + if (isOpX(t)) t = mkNeg(t->left()); + if (!isOp(t)) continue; + if (!isNumeric(t->left())) continue; + newTerms.insert(t); + } + c = disjoin(newTerms, c->getFactory()); + disjs.insert(c); + guesses.insert(c); + }*/ + else guesses.insert(c); + } + + for (auto & z : eqs) + { + for (auto & in : ineqs) + { + //if (bnd > guesses.size()) return; + if (!emptyIntersect(z, in)) continue; + ineqs.insert(mk(mk(in->left(), z->left()), mk(in->right(), z->right()))); + ineqs.insert(mk(mk(in->left(), z->right()), mk(in->right(), z->left()))); + } + + for (auto & d : disjs) + { + //if (bnd > guesses.size()) return; + ExprSet terms; + getDisj(d, terms); + ExprSet newTerms; + for (auto c : terms) + { + if (isOp(c)) + { + if (emptyIntersect(z, c)) + newTerms.insert(reBuildCmp(c, + mk(c->left(), z->left()), mk(c->right(), z->right()))); + else newTerms.insert(c); + } + else newTerms.insert(c); + } + if (newTerms.size() > 0) + guesses.insert(disjoin(newTerms, d->getFactory())); + } + } + + for (auto & a : ineqs) guesses.insert(simplifyArithm(a)); + // guesses.insert(ineqs.begin(), ineqs.end()); + + for (auto & e : eqs) + { + for (auto & in : ineqs) + { + //if (bnd > guesses.size()) return; + assert(isOpX(in)); + Expr g; + if (in->left() == e->left() && !evalLeq(e->right(), in->right())) + g = mk(e->right(), in->right()); + else if (in->left() == e->right() && !evalLeq(e->left(), in->right())) + g = mk(e->left(), in->right()); + else if (in->right() == e->left() && !evalLeq(in->left(), e->right())) + g = mk(in->left(), e->right()); + else if (in->right() == e->right() && !evalLeq(in->left(), e->left())) + g = mk(in->left(), e->left()); + + if (g != NULL) guesses.insert(simplifyArithm(g)); + } + } + + for (auto & in1 : ineqs) + { + for (auto & in2 : ineqs) + { + // if (bnd > guesses.size()) return; + if (in1 == in2) continue; + + assert(isOpX(in1)); + assert(isOpX(in2)); + + if (evalLeq(in1->right(), in2->left()) && + !evalLeq(in1->left(), in2->right())) + { + guesses.insert(simplifyArithm(mk(in1->left(), in2->right()))); + } + } + } + } + + inline static void simplifyPropagate (ExprSet& cnj) + { + bool toRepeat = false; + map vars; + for (auto & a : cnj) + { + filter (a, IsConst (), inserter(vars[a], vars[a].begin())); + } + + vector tmp; + ExprSet newCnjs; + + for (auto it0 = cnj.begin(); it0 != cnj.end(); ++it0) + { + if (find(tmp.begin(), tmp.end(), it0) != tmp.end()) continue; + Expr a = *it0; + for (auto it = cnj.begin(); it != cnj.end(); ++it) + { + Expr b = *it; + if (a == b) continue; + + if (contains(b, a)) + { + if (find(tmp.begin(), tmp.end(), it) != tmp.end()) + { + toRepeat = true; + continue; + } + newCnjs.insert(simplifyBool(replaceAll(b, a, mk(a->getFactory())))); + tmp.push_back(it); + continue; + } + + ExprSet& av = vars[a]; + ExprSet& bv = vars[b]; + if (av.size() != 2 || + !isOpX(a) || + !isSubset(av, bv)) continue; + + for (auto v : av) + { + Expr t = ineqMover(a, v); + if (t->left() == v) + { + if (find(tmp.begin(), tmp.end(), it) != tmp.end()) + { + toRepeat = true; + continue; + } + newCnjs.insert(simplifyBool(simplifyArithm(replaceAll(b, t->left(), t->right())))); + tmp.push_back(it); + break; + } + } + } + } + + for (auto & it : tmp) cnj.erase(it); + cnj.insert(newCnjs.begin(), newCnjs.end()); + if (toRepeat) simplifyPropagate(cnj); + } + + inline static void getKeyVars (Expr fla, Expr key, Expr &var) + { + if (isOpX(fla) && isOpX(fla->right()) && fla->right()->right() == key){ + //assert (var == NULL); + var = fla->left(); + } else if (isOpX(fla) && isOpX(fla->right()) && fla->right()->right() == mk(key)){ + //assert (var == NULL); + var = fla->left(); + } else if (isOpX(fla) && isOpX(fla->right()) && + isOpX(fla->right()->right()) && fla->right()->right()->left() == key){ + //assert (var == NULL); + var = fla->left(); + } else { + for (unsigned i = 0; i < fla->arity(); i++) + getKeyVars(fla->arg(i), key, var); + } + } + + void getLiterals (Expr exp, ExprSet& lits, bool splitEqs = true) + { + ExprFactory& efac = exp->getFactory(); + if (isOp(exp) && !splitEqs) + { + lits.insert(exp); + } + else if (isOpX(exp) && isNumeric(exp->left()) && !containsOp(exp)) + { + getLiterals(mk(exp->left(), exp->right()), lits, splitEqs); + getLiterals(mk(exp->left(), exp->right()), lits, splitEqs); + } + else if (isOpX(exp) && isNumeric(exp->left()) && !containsOp(exp)) + { + getLiterals(mk(exp->left(), exp->right()), lits, splitEqs); + getLiterals(mk(exp->left(), exp->right()), lits, splitEqs); + } + else if ((isOpX(exp) || isOpX(exp) || isOpX(exp)) && isBoolean(exp->left())) + { + getLiterals(exp->left(), lits, splitEqs); + getLiterals(exp->right(), lits, splitEqs); + getLiterals(mkNeg(exp->left()), lits, splitEqs); + getLiterals(mkNeg(exp->right()), lits, splitEqs); + } + else if (isOpX(exp)) + { + if (bind::isBoolConst(exp->left())) + lits.insert(exp); + else + getLiterals(mkNeg(exp->left()), lits, splitEqs); + } + else if (isOpX(exp)) + { + getLiterals(mkNeg(exp->left()), lits, splitEqs); + getLiterals(exp->right(), lits, splitEqs); + } + else if (isOpX(exp)) + { + getLiterals(mkNeg(exp->left()), lits, splitEqs); + getLiterals(exp->right(), lits, splitEqs); + getLiterals(mkNeg(exp->right()), lits, splitEqs); + getLiterals(exp->left(), lits, splitEqs); + } + else if (bind::typeOf(exp) == mk(efac) && + !containsOp(exp) && !containsOp(exp)) + { + if (isOp(exp)) + { + exp = rewriteDivConstraints(exp); + exp = rewriteModConstraints(exp); + if (isOpX(exp) || isOpX(exp)) + getLiterals(exp, lits, splitEqs); + else lits.insert(exp); + } + else lits.insert(exp); + } + else if (isOpX(exp) || isOpX(exp)) + { + for (int i = 0; i < exp->arity(); i++) + getLiterals(exp->arg(i), lits, splitEqs); + } + else if (!isOpX(exp) && !isOpX(exp)) + { + errs () << "unable lit: " << *exp << "\n"; + assert(0); + } + } + + void pprint(Expr exp, int inden, bool upper); + + template static void pprint(Range& exprs, int inden = 0, + string op = ",") + { + for (auto it = exprs.begin(); it != exprs.end(); ++it) + { + pprint(*it, inden, false); + if (inden > 0) outs () << '\n'; + else if (std::next(it) != exprs.end()) + outs () << op << ' '; + + // outs() << (inden > 0 ? "\n" : + // (std::next(it) != exprs.end()) ? ", " : ""); + } + } + + inline void pprint(Expr exp, int inden = 0, bool upper = true) + { + ExprSet flas; + string op; + if (isOpX(exp) || isOpX(exp)) + { + outs() << string(inden, ' ') << (isOpX(exp) ? "[forall " : "[exists "); + int i = 0; + for (; i < exp->arity() - 1; i++) + outs () << fapp(exp->arg(i)) << + (i < exp->arity() - 2 ? ", " : ""); + outs () << ". "; + if (upper) outs () << "\n"; + pprint(exp->arg(i), upper ? inden + 2 : 0, upper); + outs () << "]"; + if (upper) outs() << "\n"; + return; + } + else if (isOpX(exp)) + { + op = " &&"; + getConj(exp, flas); + if (flas.empty()) flas = {mk(exp->getFactory())}; + } + else if (isOpX(exp)) + { + op = " ||"; + getDisj(exp, flas); + } + else if (isOpX(exp)) + { + outs () << string(inden, ' ') << "[! "; + pprint(exp->left(), upper ? inden + 2 : 0, upper); + outs () << "]"; + if (upper) outs() << "\n"; + return; + } + else if (isOpX(exp) && containsOp(exp)) + { + pprint(exp->left(), inden, false); + if (containsOp(exp->left())) + outs () << "\n" << string(inden, ' '); + outs () << " = "; + if (containsOp(exp->right())) + outs () << "\n"; + pprint(exp->right(), inden + 2, false); + return; + } + else if (isOpX(exp)) + { + pprint(exp->left(), inden, false); + outs () << " => "; + if (upper) outs () << '\n'; + pprint(exp->right(), upper ? inden + 2 : 0, false); + return; + } + else if (isOpX(exp)) + { + outs () << string(inden, ' ') << "store(\n"; + pprint(exp->left(), inden + 2); + pprint(exp->right(), inden + 2); + pprint(exp->last(), inden + 2); + outs () << string(inden, ' ') << ")"; + if (upper) outs() << "\n"; + return; + } + else if (isOpX(m, getVar("this", fun)); // hack for now: could be something else instead of `this` + + m = simplifyBool(simplifyArithm(simplifyArr(u.getModel(var)))); + if (c == "state") + testfile << "\"address(this).balance=" << m << "\""; + else if (isArray(m)) + { + testfile << "ARRAY["; + while (isOpX(m)) + { + testfile << "(" << m->right() << "," << m->last() << ")"; + m = m->left(); + } + testfile << "]"; + } + else + testfile << m; + if (i < b.second.size() - 1) + testfile << ", "; + } + } + testfile << ")\n"; + break; + } + } + } + testfile << "END TEST " << ++number_of_tests << "\n"; + testfile.close(); + + // that correspond to inputs of functions + + if (todoCHCs.empty()){ + outs () << "ALL Branches are covered: DONE\n"; + return; + } + } + else { + u.dumpToFile(ssa); + outs () << "unknown\n"; + } + } + for (auto t : trees){ + t->deleteTree(); + } + } + if(!found_potential_tree){ + satTrees[id] = satTrees[prev_id]; + } + chcG->clear(); + + // GIVEN: at this point, there is only one query, and it is re-constructed in each iteration + /* TODO: + 3. for all tree that gave `SAT`, extract tests, and remove CHCs from `todoCHCs` + */ + if (std::get<0>(new_query)) break; + } + } + } + }; + + inline void testgen(char* smt, map>>& signature, unsigned maxAttempts, unsigned to, + bool freqs, bool aggp, bool enableDataLearning, bool doElim, + bool doDisj, int doProp, bool dAllMbp, bool dAddProp, bool dAddDat, + bool dStrenMbp, bool toSkip, int invMode, int lookahead, + bool lb, bool lmax, bool prio, int debug) { + ExprFactory m_efac; + EZ3 z3(m_efac); + ExprMap invs; + CHCs ruleManager(m_efac, z3); + string contract = signature.begin()->first; + + ruleManager.parse(smt, contract, true); + + ruleManager.print(); + + NonlinCHCsolver nonlin(ruleManager, signature); + if (signature.size() != 1) + { + outs () << "multiple contracs case\n"; //"Only a single contract is supported, currently\n"; + //exit(0); + } + + // if (nums.size() > 0) + { + // nonlin.initKeys(nums, lb); + // nonlin.setInvs(invs); + // todo + nonlin.exploreTracesNonLinearTG(7); + } + } +}; + +#endif diff --git a/include/deep/TGTree.hpp b/include/deep/TGTree.hpp new file mode 100644 index 000000000..40b9a0b67 --- /dev/null +++ b/include/deep/TGTree.hpp @@ -0,0 +1,706 @@ +#include +#include +#include + +using namespace std; + +typedef struct { + int chc_index; + vector srs; + int ds; +} chc_structure_input; + +typedef struct { + int chc_index; + vector srs; +} chc_structure; + +vector entry_values; +map> ds_map_glob; + +namespace deep { + class node { + public: + int element; + int chc_index; + vector children; + + node(int theElement) { + element = theElement; + chc_index = -1; + } + + node(int theElement, int theChc_value) { + element = theElement; + chc_index = theChc_value; + } + + node(int theElement, node *p) { + element = theElement; + } + + node(int theElement, int theChc_value, vector c) { + element = theElement; + chc_index = theChc_value; + for (int i = 0; i < c.size(); i++) { + add_child(clone(c[i])); + } + } + + node(int theElement, int theChc_value, vector c) { + element = theElement; + chc_index = theChc_value; + for (int i = 0; i < c.size(); i++) { + node *n = new node{c[i]}; + add_child(n); + } + } + + node (const node &n){ + element = n.element; + chc_index = n.chc_index; + for (int i = 0; i < n.children.size(); i++){ + add_child(n.children[i]); + } + } + + void add_child(node *child) { children.push_back(child); } + + bool equals(node *n){ + if (children.size() != n->children.size()){ + return false; + }else{ + if (element != n->element){return false;} + bool tmp = true; + for (int i = 0; i < children.size(); i++){ + tmp = children[i]->equals(n->children[i]); + if (not tmp) {return false;} + } + } + return true; + } + + static node* clone(node* n){ + int tmp_e = n->element; + int tmp_i = n->chc_index; + vector new_children; + for (auto c : n->children){ + new_children.push_back(clone(c)); + } + node* out = new node{tmp_e, tmp_i, new_children}; + return out; + } + + + + bool empty(){ + return children.empty(); + } + }; + + + class chcTree { + private: + node *root; + vector non_entry_leaves; + int node_index; + public: + chcTree(int r){ + root = new node{r}; + } + + node* getRoot(){return root;} + + chcTree(node* r){ + root = r; + } + + chcTree(int parent, int chc_index_value, chc_structure children){ + vector tmp_nodes; + for (auto c : children.srs){ + auto tmp = new node{c}; + if (!contains_entry(c)){ + non_entry_leaves.push_back(tmp); + } + tmp_nodes.push_back(tmp); + } + root = new node{parent, chc_index_value, children.srs}; + } + + chcTree(int parent, int chc_index_value, chc_structure children, std::vector& preset_children){ + vector tmp_nodes; + for (int i = preset_children.size(); i < children.srs.size(); i++){ + auto tmp = new node{children.srs[i]}; + if (!contains_entry(children.srs[i])){ + non_entry_leaves.push_back(tmp); + } + tmp_nodes.push_back(tmp); + } + for (int i = preset_children.size(); i < children.srs.size(); i++){ + node *n = new node{children.srs[i]}; + preset_children.push_back(n); + } + root = new node{parent, chc_index_value, preset_children}; + } + + + + ~chcTree() { + for (auto c: non_entry_leaves){ + delete c; + } + for(auto v: root->children){ + delete v; + } + delete root; + + } + + void deleteTree() { + if (root == NULL) return; + for(auto v: root->children){ + delete v; + } + free(root); + } + + static chcTree* clone(chcTree *t){ + auto new_root = node::clone(t->root); + auto n_leaves = find_all_non_entry_leaves(new_root); //ToDo: should be improved + auto n_chcTree = new chcTree(new_root); + n_chcTree->set_non_entry_leaves(n_leaves); + return n_chcTree; + } + + set get_set(){ + set out; + out.insert(root->chc_index); + for(auto e: root->children){ + get_set(out, *e); + } + return out; + } + + set get_subset(){ + set out; + out.insert(root->chc_index); + get_set(out, *root->children[root->children.size() - 1]); + return out; + } + + vector get_non_entry_leaves(){ + vector tmp; + for (auto n: non_entry_leaves){ + tmp.push_back(n->element); + } + return tmp; + } + + void get_set(set & out, node & n){ + out.insert(n.chc_index); + for(auto e: n.children){ + get_set(out, *e); + } + } + + void print_set(set & s){ + for (auto e: s){ + outs() << e << " "; + } + outs() << endl; + } + + void set_non_entry_leaves(vector l){ + non_entry_leaves = l; + } + + static vector find_all_non_entry_leaves(node* n){ // ToDo: check again, maybe not the best solution + vector tmp; + if(n->children.empty() && !contains_entry(n->element)) { + tmp.push_back(n); + return tmp; + }else{ + for (auto c: n->children){ + for (auto o: find_all_non_entry_leaves(c)) + tmp.push_back(o); + } + return tmp; + } + } + + const chcTree &operator=(chcTree &&rhs) { + makeEmpty(); + root = std::move(rhs.root); + rhs.root = nullptr; + return *this; + } + + bool empty() { + return root->empty(); + } + + + void printInOrder() { + if (empty()) { + outs() << "tree is empty\n"; + return; + } + printInOrder(root); + outs() << endl; + } + + + void printToDot(const char * filename, ufo::CHCs &ruleManager) { + if (empty()) { + outs() << "tree is empty\n"; + return; + } + node_index = 0; + std::ofstream fout(filename); + fout << "digraph print {\n"; + dump_dot(fout, root, node_index, ruleManager); + fout << "}\n"; + + } + + void dump_dot(std::ofstream &fout, node *t, int parents_index, ufo::CHCs &ruleManager) { + if (t == nullptr) { return; } + //fout << t->element << ":" << t->chc_index << " "; + if (t->element == -1) { + fout << node_index << "[label=\"[-1]\" ordering=\"out\"]\n"; + }else{ + fout << node_index << "[label=\"" << ruleManager.chcs[t->chc_index].dstRelation << "\" ordering=\"out\"]\n"; + } + int p_index = node_index; + for (int i = 0; i < t->children.size(); i++) { + node_index++; + fout << p_index << " -> " << node_index << "\n"; + dump_dot(fout, t->children[i], p_index, ruleManager); + } + } + + int numOfNodes() const { + return numOfNodes(root); + } + + int height() const { + return height(root); + } + + void makeEmpty() { + makeEmpty(root); //? not sure + } + + bool contains(const int &v) { + return contains(v, root); + } + + bool contains(const int &v, node *&t) { + if (v == root->element) { + return true; + } + return contains(v, root->children); + } + + bool contains(const int &v, vector children) { + //ToDo + return false; + } + + void printInOrder(node *t) { + if (t == nullptr) { return; } + outs() << t->element << ":" << t->chc_index << " "; + for (int i = 0; i < t->children.size(); i++) { + printInOrder(t->children[i]); + } + } + + void printLevelOrder(node *t) const { + if (t == nullptr) { return; } + list tmp_list; + tmp_list.push_back(t); + while (!tmp_list.empty()) { + node *current = tmp_list.front(); + outs() << current->element << " "; + for (int i = 0; i < t->children.size(); i++) { + tmp_list.push_back(current->children[i]); + } + tmp_list.pop_front(); + } + } + + void makeEmpty(node *&t) { + if (t != nullptr) { + for (int i = 0; i < t->children.size(); i++) { + makeEmpty(t->children[i]); + } + delete t; + t = nullptr; + } + } + + + int numOfNodes(node *t) const { + if (t == nullptr) { return 0; } + int sum = 1; + for (int i = 0; i < t->children.size(); i++) { + sum += numOfNodes(t->children[i]); + } + return sum; + } + + int height(node *t) const { + if (t == nullptr) { return 0; } + int max_h = 0; + for (int i = 0; i < t->children.size(); i++) { + int tmp = height(t->children[i]); + if (max_h < tmp) { + max_h = tmp; + } + } + return 1 + max_h; + } + + bool is_tree_full(){ + return non_entry_leaves.empty(); + } + + void print_map(){ + map>::iterator it; + for (it = ds_map_glob.begin(); it != ds_map_glob.end(); it++){ + outs() << it->first; + vector::iterator it2; + for (it2 = it->second.begin(); it2 != it->second.end(); it2++) { + vector::iterator it3; + for (it3 = it2->srs.begin(); it3 != it2->srs.end(); it3++) { + outs() << " " << *it3; + } + outs() << " chc_index: " << it2->chc_index; + } + outs() << endl; + } + } + + vector> get_all_permutations(){ + vector> oc; + vector out; + for (int i = 0; i < non_entry_leaves.size(); i++){ + vector tmp; + chc_structure tmp_s = *new chc_structure{-1, tmp}; + out.push_back(tmp_s); + } + get_all_permutations(0, out, oc); + return oc; + } + + void get_all_permutations(int index, vector &out, + vector> &out_collection){ + if (index >= non_entry_leaves.size()){ + vector tmp; + for (int i = 0; i < out.size(); i++) { + tmp.push_back(out[i]); + } + out_collection.push_back(tmp); + return; + } + int el = non_entry_leaves[index]->element; + int sz = ds_map_glob.find(el)->second.size(); + vector::iterator it2; + for (it2 = ds_map_glob.find(el)->second.begin(); it2 != ds_map_glob.find(el)->second.end(); it2++) { + // update current out vector + out[index] = *it2; //??? + get_all_permutations(index + 1, out, out_collection); + } + } + + static bool contains_entry(const int & elem){ + bool result = false; + if(find(entry_values.begin(), entry_values.end(), elem) != entry_values.end() ) + { + result = true; + } + return result; + } + + void extend_non_entry_leaves(vector new_mutations){ + vector new_non_entry_leaves; + for (int i = 0; i < non_entry_leaves.size(); i++){ + auto new_mutation = new_mutations[i]; + vector tmp_node_list; + for (auto nm: new_mutation.srs){ + auto n_tmp_node = new node{nm}; + if (!contains_entry(nm)){ + new_non_entry_leaves.push_back(n_tmp_node); + } + tmp_node_list.push_back(n_tmp_node); + } + non_entry_leaves[i]->children = tmp_node_list; + non_entry_leaves[i]->chc_index = new_mutation.chc_index; + } + non_entry_leaves = new_non_entry_leaves; + } + + + void extend_by_terminals_nodes(map &ds_term_node){ + for (auto n: non_entry_leaves){ + auto tmp = ds_term_node.find(n->element)->second; + n->element = tmp.element; + n->chc_index = tmp.chc_index; + for(auto c: tmp.children){ + n->children.push_back(c); + } + } + } + + }; + + class chcTreeGenerator { + + public: + vector entry; + int exit_v; + int exit_index; + vector chc_int; + vector trees; + map> ds_map; + map ds_term; //store int value, which are terminals only (true) or cycle, branches (false) + map ds_term_node; + + chcTreeGenerator(vector ep, int ex, int exit_index_value){ + entry = ep; + exit_v = ex; + exit_index = exit_index_value; + entry_values = ep; + } + + void clear(){ + entry = vector(); + exit_v = -1; + exit_index = -1; + chc_int = vector(); + trees = vector(); + ds_map.clear(); + ds_term.clear(); + ds_term_node.clear(); + ds_map_glob.clear(); + } + + bool contains_entry(const int & elem){ + bool result = false; + if( find(entry.begin(), entry.end(), elem) != entry.end() ) + { + result = true; + } + return result; + } + + bool isV_Equal(std::vector &first, std::vector &second) + { + if (first.size() != second.size()) { + return false; + } + + std::sort(first.begin(), first.end()); + std::sort(second.begin(), second.end()); + + return first == second; + } + + void add_chc_int(int chc_index, vector srs_input, int dst_input){ + chc_structure_input tmp_s = *new chc_structure_input{chc_index, srs_input, dst_input}; + chc_int.push_back(tmp_s); + } + + bool is_branch_or_cycle(int x, vector &history){ + if (ds_term.find(x) != ds_term.end()){ + return ds_term.find(x)->second; + } + bool result = true; + history.push_back(x); + for (auto e: ds_map.find(x)->second[0].srs){ + if (e == -1){ + continue; + } + for (auto h: history){ + if (e == h){ + history.pop_back(); + return false; + } + } + if (!is_branch_or_cycle(e, history)){ + history.pop_back(); + return false; + } + } + history.pop_back(); + return true; + } + + bool is_branch_or_cycle(int x){ + if (ds_term.find(x) != ds_term.end()){ + return ds_term.find(x)->second; + } + bool result = true; + vector history; + history.push_back(x); + for (auto e: ds_map.find(x)->second[0].srs){ + if (e == -1){ + continue; + } + if (!is_branch_or_cycle(e, history)){ + return false; + } + } + return true; + } + + void init_termination_tree(int x, node &n){ + if(ds_map.find(x) == ds_map.end()){ + assert(false); + } + for (auto e: ds_map.find(x)->second){ + n.chc_index = e.chc_index; + for (int k: e.srs){ + if (k != -1){ + if (ds_term_node.find(k) != ds_term_node.end()){ + n.children.push_back(&ds_term_node.find(k)->second); + } else { + node *tmp = new node(k); + init_termination_tree(k, *tmp); + n.children.push_back(tmp); + } + }else{ + node *tmp = new node(-1,-1); + n.children.push_back(tmp); + } + } + } + } + + + + void create_map(){ + for(auto chc: chc_int){ + if(ds_map.count(chc.ds) > 0){ + //should not happened + chc_structure tmp_s = *new chc_structure{chc.chc_index, chc.srs}; + ds_map.find(chc.ds)->second.push_back(tmp_s); + ds_map_glob.find(chc.ds)->second.push_back(tmp_s); + }else{ + chc_structure tmp_s = *new chc_structure{chc.chc_index, chc.srs}; + vector tmp{tmp_s}; + ds_map.insert({chc.ds, tmp}); + ds_map_glob.insert({chc.ds, tmp}); + + } + } + map>::iterator it; + for (it = ds_map.begin(); it != ds_map.end(); it++) + { + if (it->second.size() > 1){ + ds_term.insert({it->first, false}); + } + } + for (it = ds_map.begin(); it != ds_map.end(); it++) + { + if (ds_term.find(it->first) == ds_term.end()){ + ds_term.insert({it->first, is_branch_or_cycle(it->first)}); + } + } + map::iterator it2; + + for (it2 = ds_term.begin(); it2 != ds_term.end(); it2++) + { + if (it2->second){ + int id = it2->first; + int chc_ind = ds_map.find(it2->first)->second[0].chc_index; + node tmp{id, chc_ind}; + init_termination_tree(id, tmp); + ds_term_node.insert({id, tmp}); + } + } + + } + + void init_tree(){ + auto exit_points = ds_map.find(exit_v); + for (auto items : exit_points->second){ + chcTree *t = new chcTree(exit_v, exit_index, items); + //ToDo: check tree if it if "full" and add to corresponding list + trees.push_back(t); + } + //chcTree t = new chcTree + } +// TODO: Add test case for 2 function F calls + void init_trees(std::vector& init_trees){ + auto exit_points = ds_map.find(exit_v); + for(int i = 0; i funcs = (*(*init_trees[i]).getRoot()).children; + for (auto items: exit_points->second) { + //TODO: need to cut items a bit!! + chcTree *t = new chcTree(exit_v, exit_index, items, funcs); + //ToDo: check tree if it if "full" and add to corresponding list + trees.push_back(t); + } + } + //chcTree t = new chcTree + } + + bool is_only_entries(vector mutation) { + for (int i = 0; i < mutation.size(); i++){ + for (int j = 0; j < mutation[i].srs.size(); j++){ + if (!contains_entry(mutation[i].srs[j])){ + return false; + } + } + } + return true; + } + + bool is_future_terminals(vector leaves) { + for (auto l: leaves){ + if (!ds_term.find(l)->second) + return false; + } + return true; + } + + void print_trees(){ + outs() << "# of existing trees: " << trees.size() << " existing tree : " << endl; + for (auto t: trees){ + t->printInOrder(); + } + } + + void getNext(vector &complete_trees){ + vector new_trees; + for (int i = 0; i < trees.size() ; i++){ + vector> all_permutations = trees[i]->get_all_permutations(); + for (auto ap : all_permutations){ + //clone tree + auto nt = chcTree::clone(trees[i]); + //add values from ap to tree + nt->extend_non_entry_leaves(ap); + if (is_future_terminals(nt->get_non_entry_leaves())){ + nt->extend_by_terminals_nodes(ds_term_node); + complete_trees.push_back(nt); + continue; + } + if (!is_only_entries(ap)) { + // add tree to complete_trees + new_trees.push_back(nt); + continue; + } + //add tree to new_trees + complete_trees.push_back(nt); + + } + } + for (auto t: trees){ + t->deleteTree(); + } + trees = new_trees; + } + }; + +} diff --git a/include/ufo/Expr.hpp b/include/ufo/Expr.hpp index 9e25c2f9a..3812d744f 100644 --- a/include/ufo/Expr.hpp +++ b/include/ufo/Expr.hpp @@ -49,6 +49,11 @@ DM-0002198 #include #include #include +#include + +#undef TRUE +#undef FALSE + #define mk_it_range boost::make_iterator_range @@ -62,3018 +67,3205 @@ DM-0002198 namespace expr { - /* create a namespace op */ - namespace op {} - - using namespace expr::op; - - class ENode; - class ExprFactory; - class ExprFactoryAllocator; - - typedef boost::intrusive_ptr Expr; - typedef std::set ExprSet; - typedef std::vector ExprVector; - typedef std::pair ExprPair; - typedef std::map ExprMap; - - /* Helper functions to convert from different wrappers over - expressions into a pointer to an expression node */ - inline ENode* eptr (ENode *p) { return p; } - inline const ENode* eptr (const ENode *p) { return p; } - inline ENode* eptr (ENode &p) { return &p; } - inline const ENode* eptr (const ENode &p) { return &p; } - inline ENode* eptr (const Expr &e) { return e.get (); } - inline ENode* eptr (Expr &e) { return e.get (); } - //inline ENode* eptr (Expr e) { return e.get (); } - - class Operator; - - /* An operator (a.k.a. a tag) of an expression node */ - class Operator - { - public: - virtual ~Operator () {}; - - /** Print an expression rooted at the operator - OS -- the output strream - args -- the arguments of the operator - depth -- indentation level for any new line - brkt -- whether the context in which the operator is printed - -- might be ambiguous and brakets might be required - **/ - virtual void Print (std::ostream &OS, - const std::vector &args, - int depth = 0, - bool brkt = true) const = 0; - virtual bool operator== (const Operator& rhs) const = 0; - virtual bool operator< (const Operator& rhs) const = 0; - virtual size_t hash () const = 0; - virtual bool isMutable () const { return false; } - /* Returns a heap-allocated clone of this */ - virtual Operator* clone (ExprFactoryAllocator &allocator) const = 0; - }; - - - inline std::ostream &operator<<(std::ostream &OS, const Operator &V) { - std::vector x; - V.Print (OS, x); - return OS; - } - - - /* An expression node (a.k.a. an enode). A pointer into an - expression tree (or DAG) */ - class ENode - { - private: - // // -- no default constructor - ENode () : id(0), count(0), fac(NULL) {} - // // -- no copy constructor - ENode (const ENode &) : count(0), fac(NULL) {} - protected: - /** unique identifier of this expression node */ - unsigned int id; - /** reference counter */ - unsigned int count; - - ExprFactory *fac; - std::vector args; - - std::shared_ptr oper; - - - void Deref () { if (count > 0) count--; } - - - /** assigns a unique id to the node */ - void setId (unsigned int v) { id = v; } - - - public: - ENode (ExprFactory &f, const Operator &o); - ~ENode(); - - ExprFactory& getFactory () const {return *fac; } - ExprFactory& efac () const { return getFactory (); } - - /** returns the unique id of this expression */ - unsigned int getId () const { return id; } - - void Ref () { count++; } - bool isGarbage () const { return count == 0; } - bool isMutable () const { return oper->isMutable (); } - - unsigned int use_count () { return count; } - - ENode* operator[] (size_t p) { return arg (p); } - ENode* arg (size_t p) { return args [p]; } - - ENode* left () - { return (args.size () > 0) ? args [0] : NULL; } - - ENode* right () - { return (args.size () > 1) ? args [1] : NULL; } - - ENode *first () { return left (); } - ENode *last () - { return args.size () > 0 ? args [args.size () - 1] : NULL; } - - - typedef std::vector::const_iterator args_iterator; - - bool args_empty () const { return args.empty () ; } - args_iterator args_begin () const { return args.begin (); } - args_iterator args_end () const { return args.end (); } + /* create a namespace op */ + namespace op {} + + using namespace expr::op; + + class ENode; + class ExprFactory; + class ExprFactoryAllocator; + + typedef boost::intrusive_ptr Expr; + typedef std::set ExprSet; + typedef std::vector ExprVector; + typedef std::pair ExprPair; + typedef std::map ExprMap; + + /* Helper functions to convert from different wrappers over + expressions into a pointer to an expression node */ + inline ENode* eptr (ENode *p) { return p; } + inline const ENode* eptr (const ENode *p) { return p; } + inline ENode* eptr (ENode &p) { return &p; } + inline const ENode* eptr (const ENode &p) { return &p; } + inline ENode* eptr (const Expr &e) { return e.get (); } + inline ENode* eptr (Expr &e) { return e.get (); } + //inline ENode* eptr (Expr e) { return e.get (); } + + class Operator; + + /* An operator (a.k.a. a tag) of an expression node */ + class Operator + { + public: + virtual ~Operator () {}; + + /** Print an expression rooted at the operator + OS -- the output strream + args -- the arguments of the operator + depth -- indentation level for any new line + brkt -- whether the context in which the operator is printed + -- might be ambiguous and brakets might be required + **/ + virtual void Print (std::ostream &OS, + const std::vector &args, + int depth = 0, + bool brkt = true) const = 0; + virtual bool operator== (const Operator& rhs) const = 0; + virtual bool operator< (const Operator& rhs) const = 0; + virtual size_t hash () const = 0; + virtual bool isMutable () const { return false; } + /* Returns a heap-allocated clone of this */ + virtual Operator* clone (ExprFactoryAllocator &allocator) const = 0; + }; - template - void renew_args (iterator b, iterator e); - - void push_back (ENode* a) { args.push_back (a); a->Ref (); } - - size_t arity () const { return args.size (); } - - const Operator& op () const { return *oper; } - void Print (std::ostream &OS, int depth = 0, bool brkt = true) const - { oper->Print (OS, args, depth, brkt); } - - friend struct LessENode; - friend class ExprFactory; - friend struct std::less; - }; - - - - inline std::ostream &operator<<(std::ostream &OS, const ENode &V) { - V.Print(OS); - return OS; - } - inline std::ostream &operator<<(std::ostream &OS, const ENode *v) - { - if (v == NULL) OS << "NULL"; - else OS << *v; - return OS; - } - - struct ENodeUniqueHash - { - std::size_t operator() (const ENode *e) const - { - size_t res = e->op ().hash (); - - size_t a = e->arity (); - if (a == 0) return res; - - ENode::args_iterator it = e->args_begin (); - if (a >= 1) - boost::hash_combine (res, *it); - - if (a >= 2) - boost::hash_combine (res, - boost::hash_range (it, e->args_end ())); - - return res; - - // if (a == 2) - // { - // boost::hash_combine (res, *it); - // ++it; - // boost::hash_combine (res, *it); - // return res; - // } - // if (a == 3) - // { - // boost::hash_combine (res, *it); - // it++; - // boost::hash_combine (res, *it); - // it++; - // boost::hash_combine (res, *it); - // return res; - // } - - // // -- n-arry with more than 3 children - // boost::hash_combine (res, - // boost::hash_range (e->args_begin (), - // e->args_end ())); - // return res; - - } - }; - - struct ENodeUniqueEqual - { - bool operator () (ENode* const &e1, ENode* const &e2) const - { - // -- same type - if (typeid (e1->op ()) == typeid (e2->op ())) - // -- same number of children - if (e1->arity () == e2->arity ()) - // -- operators (if have data) are equal - if (e1->op () == e2->op ()) - // -- children are equal as pointers - return std::equal (e1->args_begin (), - e1->args_end (), - e2->args_begin ()); - return false; - } - }; - - - struct LessENode - { - bool operator() (ENode* e1, ENode* e2) - { - if (typeid (e1->op ()) == typeid (e2->op ())) - { - if (e1->op () == e2->op ()) - return lexicographical_compare (e1->args_begin (), - e1->args_end (), - e2->args_begin (), - e2->args_end ()); - else - return e1->op () < e2->op (); - } - - // -- when all fails, order by type - return typeid (e1->op ()).before (typeid (e2->op ())); - } - }; - - - /** - * A type erasure of a cache - */ - struct CacheStub - { - /** true if the stub own the cahce pointer by p */ - virtual bool owns (const void *p) = 0; - /** erases val from the underlying cache */ - virtual void erase (ENode *val) = 0; - virtual ~CacheStub () { } - }; - - - template - struct CacheStubTmpl : CacheStub - { - C &cache; - - CacheStubTmpl (C &c): cache(c) {}; - - virtual bool owns (const void *p) { return p == static_cast (&cache); } - virtual void erase (ENode *val) { cache.erase (val); } - }; - - - - struct EFADeleter - { - ExprFactoryAllocator &m_efa; - EFADeleter (ExprFactoryAllocator &efa) : m_efa (efa) {} - void operator() (void *p); - }; - - - class ExprFactoryAllocator : boost::noncopyable - { - private: - /** pool for tiny objects */ - boost::pool<> tiny; - /** pool for small objects */ - boost::pool<> small; - - public: - ExprFactoryAllocator () : tiny(8, 65536), small (64, 65536) {}; - - void *allocate (size_t n); - void free (void *block); - - EFADeleter get_deleter (); - }; - - - - class ExprFactory : boost::noncopyable - { - protected: -#define UNORDERED_SET_UNIQUE_TABLE 1 -#ifndef UNORDERED_SET_UNIQUE_TABLE - // -- type of unique table entry - typedef std::set unique_entry_type; -#else - typedef std::unordered_set unique_entry_type; -#endif - typedef const char* unique_key_type; - // -- type of the unique table - typedef std::map unique_type; - - typedef boost::ptr_vector caches_type; - - - /** pool allocator */ - ExprFactoryAllocator allocator; - - /** list of registered caches */ - caches_type caches; - - // -- unique table - unique_type unique; - - /** counter for assigning unique ids*/ - unsigned int idCount; - - /** returns a unique id > 0 */ - unsigned int uniqueId () { return ++idCount; } - - /** - * Remove value from unique table - */ - void Remove (ENode *val) - { - clearCaches (val); - if (!val->isMutable ()) - { - unique_type::iterator it = unique.find (typeid (val->op ()).name ()); - // -- can only remove things that have been inserted before - assert (it != unique.end ()); - it->second.erase (val); - if (it->second.empty ()) unique.erase (it); - } - - freeNode (val); + inline std::ostream &operator<<(std::ostream &OS, const Operator &V) { + std::vector x; + V.Print (OS, x); + return OS; } - /** - * Clear val from all registered caches - */ - void clearCaches (ENode *val) { for (CacheStub &c : caches) c.erase (val); } - - - /** - * Return the canonical (unique) representetive of the input - */ - ENode* canonize (ENode* v) - { - if (v->isMutable ()) - { - v->setId (uniqueId ()); - return v; - } - - - std::pair x = - unique [typeid (v->op ()).name ()].insert (v); - if (x.second) - { - v->setId (uniqueId ()); - return v; - } - else - { - freeNode (v); - return *x.first; - } - } + /* An expression node (a.k.a. an enode). A pointer into an + expression tree (or DAG) */ + class ENode + { + private: + // // -- no default constructor + ENode () : id(0), count(0), fac(NULL) {} + // // -- no copy constructor + ENode (const ENode &) : count(0), fac(NULL) {} + protected: + /** unique identifier of this expression node */ + unsigned int id; + /** reference counter */ + unsigned int count; - ENode* mkExpr (const Operator &op) - { return canonize (allocNode (op)); } + ExprFactory *fac; + std::vector args; - template - ENode* mkExpr (const Operator &op, etype e) - { - ENode* eVal = allocNode (op); - eVal->push_back (eptr (e)); - return canonize (eVal); - } + std::shared_ptr oper; - /** binary */ - template - ENode* mkExpr (const Operator &op, - etype e1, - etype e2) - { - ENode* eVal = allocNode (op); - eVal->push_back (eptr (e1)); - eVal->push_back (eptr (e2)); - return canonize (eVal); - } - /** ternary */ - template - ENode* mkExpr (const Operator &op, - etype e1, - etype e2, - etype e3) - { - ENode* eVal = allocNode (op); - eVal->push_back (eptr (e1)); - eVal->push_back (eptr (e2)); - eVal->push_back (eptr (e3)); - return canonize (eVal); - } + void Deref () { if (count > 0) count--; } - /* n-ary - iterator ranges over cost ENode* - */ - template - ENode* mkNExpr (const Operator &op, - iterator begin, - iterator end) - { - ENode* eVal = allocNode (op); - for (; begin != end; ++begin) - eVal->push_back (eptr (*begin)); - return canonize (eVal); - } - private: + /** assigns a unique id to the node */ + void setId (unsigned int v) { id = v; } -#define FREE_LIST_MAX_SIZE 1024*4 - std::vector freeList; - void freeNode (ENode *n); - ENode *allocNode (const Operator &op); + public: + ENode (ExprFactory &f, const Operator &o); + ~ENode(); - + ExprFactory& getFactory () const {return *fac; } + ExprFactory& efac () const { return getFactory (); } + /** returns the unique id of this expression */ + unsigned int getId () const { return id; } - public: - ExprFactory () : idCount(0) {} + void Ref () { count++; } + bool isGarbage () const { return count == 0; } + bool isMutable () const { return oper->isMutable (); } - /** Derefernce a value */ - void Deref (ENode* val) - { - val->Deref (); - if (val->isGarbage ()) Remove (val); - } + unsigned int use_count () { return count; } - /** User functions */ - Expr mkTerm (const Operator &o) { return Expr (mkExpr (o)); } - Expr mkUnary (const Operator &o, Expr e) - { return Expr (mkExpr (o, e.get ())); } - Expr mkBin (const Operator &o, Expr e1, Expr e2) - { return Expr (mkExpr (o, e1.get (), e2.get ())); } - Expr mkTern (const Operator &o, Expr e1, Expr e2, - Expr e3) - { return Expr (mkExpr (o, e1.get (), e2.get (), e3.get ())); } - template - Expr mkNary (const Operator &o, iterator b, iterator e) - { return Expr (mkNExpr (o, b, e)); } - - template - Expr mkNary (const Operator &o, const Range &r) - { return mkNary (o, begin (r), end (r)); } - - - template - void registerCache (Cache &cache) - { - // -- to avoid double registration - unregisterCache (cache); - caches.push_back (static_cast (new CacheStubTmpl (cache))); - } - - template - bool unregisterCache (const Cache &cache) - { - const void *ptr = static_cast (&cache); - - for (caches_type::iterator it = caches.begin (), end = caches.end (); - it != end; ++it) - if (it->owns (ptr)) - { - caches.erase (it); - return true; - } - return false; - } - - friend class ENode; - }; - - inline ENode::ENode (ExprFactory &f, const Operator &o) : - count(0), fac(&f), - oper(o.clone (f.allocator), - f.allocator.get_deleter (), - boost::pool_allocator ()) {} -} + ENode* operator[] (size_t p) { return arg (p); } + ENode* arg (size_t p) { return args [p]; } -inline void * operator new (size_t n, expr::ExprFactoryAllocator &alloc) -{ return alloc.allocate (n); } - -inline void operator delete (void *p, expr::ExprFactoryAllocator &alloc) -{ alloc.free (p); } + ENode* left () + { return (args.size () > 0) ? args [0] : NULL; } -namespace expr -{ - inline void ExprFactory::freeNode (ENode *n) - { - if (freeList.size () < FREE_LIST_MAX_SIZE) - { - for (ENode *a : n->args) Deref (a); - n->args.clear (); - n->oper.reset (); - - if (freeList.size () < FREE_LIST_MAX_SIZE) - { - assert (n->count == 0); - freeList.push_back (n); - return; - } - } + ENode* right () + { return (args.size () > 1) ? args [1] : NULL; } - operator delete (static_cast(n), allocator); - } - - inline ENode *ExprFactory::allocNode (const Operator &op) - { - if (freeList.empty ()) - return new(allocator) ENode (*this, op); - - ENode *res = freeList.back (); - freeList.pop_back (); - res->oper.reset (op.clone (allocator), - allocator.get_deleter (), - boost::pool_allocator ()); - assert (res->count == 0); - return res; - } - - - inline void *ExprFactoryAllocator::allocate (size_t n) - { - if (n <= tiny.get_requested_size ()) return tiny.malloc (); - else if (n <= small.get_requested_size ()) return small.malloc (); - - return static_cast (new char[n]); - } - - - inline void ExprFactoryAllocator::free (void *block) - { - if (tiny.is_from (block)) tiny.free (block); - else if (small.is_from (block)) small.free (block); - else delete [] static_cast (block); - } - - inline EFADeleter ExprFactoryAllocator::get_deleter () - { return EFADeleter (*this); } - - inline void EFADeleter::operator() (void *p) - { operator delete (p, m_efa); } - - template - struct TerminalTrait {}; - - template > - class Terminal : public Operator - { - protected: - T val; - public: - typedef T base_type; - typedef P terminal_type; - typedef Terminal this_type; - - Terminal (const base_type &v) : val(v) {} - - base_type get () const { return val; } - - this_type* clone (ExprFactoryAllocator &allocator) const - { return new (allocator) this_type (val); } - - - - void Print (std::ostream &OS, - const std::vector &args, - int depth = 0, - bool brkt = true) const - { - terminal_type::print (OS, val, depth, brkt); - } + ENode *first () { return left (); } + ENode *last () + { return args.size () > 0 ? args [args.size () - 1] : NULL; } - bool operator== (const this_type &rhs) const - { return terminal_type::equal_to (val, rhs.val); } - bool operator< (const this_type &rhs) const - { return terminal_type::less (val, rhs.val); } + typedef std::vector::const_iterator args_iterator; - bool operator== (const Operator& rhs) const - { - if (&rhs == this) return true; + bool args_empty () const { return args.empty () ; } + args_iterator args_begin () const { return args.begin (); } + args_iterator args_end () const { return args.end (); } - const this_type *prhs = dynamic_cast (&rhs); - if (prhs == NULL) return false; - return terminal_type::equal_to (val, prhs->val); - } + template + void renew_args (iterator b, iterator e); - bool operator< (const Operator& rhs) const - { - // x < x is false - if (&rhs == this) return false; + void push_back (ENode* a) { args.push_back (a); a->Ref (); } - const this_type *prhs = dynamic_cast (&rhs); - - return (prhs == NULL) ? - typeid(this_type).before (typeid (rhs)) : - terminal_type::less (val, prhs->val); - } + size_t arity () const { return args.size (); } - size_t hash () const { return terminal_type::hash (val); } - - }; - - template<> struct TerminalTrait - { - static inline void print (std::ostream &OS, - const std::string &s, - int depth, bool brkt) - { OS << s; } - static inline bool less (const std::string &s1, const std::string &s2) - { return s1 < s2; } - static inline bool equal_to (const std::string &s1, const std::string &s2) - { return s1 == s2; } - static inline size_t hash (const std::string &s) - { - std::hash hasher; - return hasher (s); - } - - }; - - template<> struct TerminalTrait - { - static inline void print (std::ostream &OS, int s, int depth, bool brkt) - { OS << s; } - static inline bool less (const int &i1, const int &i2) - { return i1 < i2; } - static inline bool equal_to (int i1, int i2) - { return i1 == i2; } - static inline size_t hash (int i) - { - std::hash hasher; - return hasher (i); - } - }; - - template<> struct TerminalTrait - { - static inline void print (std::ostream &OS, unsigned int s, - int depth, bool brkt) - { OS << s; } - static inline bool less (const unsigned int &i1, const unsigned int &i2) - { return i1 < i2; } - static inline bool equal_to (unsigned int i1, unsigned int i2) - { return i1 == i2; } - static inline size_t hash (unsigned int i) - { - std::hash hasher; - return hasher (i); - } - }; - - template<> struct TerminalTrait - { - static inline void print (std::ostream &OS, - unsigned long s, int depth, bool brkt) - { OS << s; } - static inline bool less (const unsigned long &i1, const unsigned long &i2) - { return i1 < i2; } - static inline bool equal_to (unsigned long l1, unsigned long l2) - { - return l1 == l2; - } - static inline size_t hash (unsigned long i) - { - std::hash hasher; - return hasher (i); - } - }; + const Operator& op () const { return *oper; } + void Print (std::ostream &OS, int depth = 0, bool brkt = true) const + { oper->Print (OS, args, depth, brkt); } + friend struct LessENode; + friend class ExprFactory; + friend struct std::less; + }; - template <> struct TerminalTrait - { - static inline void print (std::ostream &OS, const mpz_class &v, - int depth, bool brkt) - { - /* print large numbers in hex */ - if (v >= 65535 || v <= -65535) - OS << std::hex << std::showbase; - - OS << v; - - OS << std::dec << std::noshowbase; - } - - static inline bool less(const mpz_class &v1, const mpz_class &v2) - { return v1 < v2; } - - static inline bool equal_to (const mpz_class &v1, const mpz_class &v2) - { return v1 == v2; } - - static inline size_t hash (const mpz_class &v) - { - std::string str = boost::lexical_cast (v); - std::hash hasher; - return hasher (str); + + + inline std::ostream &operator<<(std::ostream &OS, const ENode &V) { + V.Print(OS); + return OS; } - - - - - }; - - - template <> - struct TerminalTrait - { - static inline void print (std::ostream &OS, const mpq_class &v, - int depth, bool brkt) - { OS << v; } - - static inline bool less(const mpq_class &v1, const mpq_class &v2) - { return v1 < v2; } - - static inline bool equal_to (const mpq_class &v1, const mpq_class &v2) - { return v1 == v2; } - - static inline size_t hash (const mpq_class &v) - { - std::string str = boost::lexical_cast (v); - std::hash hasher; - return hasher (str); - } - }; - - - namespace op - { - typedef Terminal STRING; - typedef Terminal INT; - typedef Terminal UINT; - typedef Terminal ULONG; - - typedef Terminal MPQ; - typedef Terminal MPZ; - } - - namespace ps - { - inline std::ostream &space (std::ostream &OS, size_t c) - { - for (size_t i = 0; i < c; i++) OS << " "; - return OS; + inline std::ostream &operator<<(std::ostream &OS, const ENode *v) + { + if (v == NULL) OS << "NULL"; + else OS << *v; + return OS; } - struct PREFIX + struct ENodeUniqueHash { - static inline void print (std::ostream &OS, - int depth, - bool brkt, - const std::string &name, - const std::vector &args) - { - if (args.size () >= 2) OS << "["; - if (args.size () == 1 && brkt) OS << "("; - - OS << name; - if (args.empty ()) return; - - if (args.size () == 1) - { - //OS << " "; - args [0]->Print (OS, depth + 2, true); - if (brkt) OS << ")"; - return; - } - - - for (std::vector::const_iterator it = args.begin (), - end = args.end (); it != end; ++it) - { - OS << "\n"; - space (OS, depth + 2); - (*it)->Print (OS, depth + 2, false); - } - - OS << "\n"; - space (OS, depth); - OS << "]"; - } + std::size_t operator() (const ENode *e) const + { + size_t res = e->op ().hash (); + + size_t a = e->arity (); + if (a == 0) return res; + + ENode::args_iterator it = e->args_begin (); + if (a >= 1) + boost::hash_combine (res, *it); + + if (a >= 2) + boost::hash_combine (res, + boost::hash_range (it, e->args_end ())); + + return res; + + // if (a == 2) + // { + // boost::hash_combine (res, *it); + // ++it; + // boost::hash_combine (res, *it); + // return res; + // } + // if (a == 3) + // { + // boost::hash_combine (res, *it); + // it++; + // boost::hash_combine (res, *it); + // it++; + // boost::hash_combine (res, *it); + // return res; + // } + + // // -- n-arry with more than 3 children + // boost::hash_combine (res, + // boost::hash_range (e->args_begin (), + // e->args_end ())); + // return res; + + } }; - - struct INFIX + struct ENodeUniqueEqual { - static inline void print (std::ostream &OS, - int depth, - bool brkt, - const std::string &name, - const std::vector &args) - { - - if (args.size () != 2) - { - PREFIX::print (OS, depth, brkt, name, args); - return; - } - - if (brkt) OS << "("; - args[0]->Print (OS, depth, true); - OS << name; - args[1]->Print (OS, depth, true); - if (brkt) OS << ")"; - } - }; - - - - struct FUNCTIONAL - { - static inline void print (std::ostream &OS, - int depth, - bool brkt, - const std::string &name, - const std::vector &args) - { - OS << name << "("; - - - bool first = true; - for (std::vector::const_iterator it = args.begin (), - end = args.end (); it != end; ++it) - { - if (!first) OS << ", "; - (*it)->Print (OS, depth+2, false); - first = false; - } - - OS << ")"; - } + bool operator () (ENode* const &e1, ENode* const &e2) const + { + // -- same type + if (typeid (e1->op ()) == typeid (e2->op ())) + // -- same number of children + if (e1->arity () == e2->arity ()) + // -- operators (if have data) are equal + if (e1->op () == e2->op ()) + // -- children are equal as pointers + return std::equal (e1->args_begin (), + e1->args_end (), + e2->args_begin ()); + return false; + } }; - struct LISP + + struct LessENode { - static inline void print (std::ostream &OS, - int depth, - bool brkt, - const std::string &name, - const std::vector &args) - { - OS << "(" << name << " "; - - bool first = true; - for (std::vector::const_iterator it = args.begin (), - end = args.end (); it != end; ++it) - { - if (!first) OS << " "; - (*it)->Print (OS, depth + 2, true); - first = false; - } - - OS << ")"; - } + bool operator() (ENode* e1, ENode* e2) + { + if (typeid (e1->op ()) == typeid (e2->op ())) + { + if (e1->op () == e2->op ()) + return lexicographical_compare (e1->args_begin (), + e1->args_end (), + e2->args_begin (), + e2->args_end ()); + else + return e1->op () < e2->op (); + } + + // -- when all fails, order by type + return typeid (e1->op ()).before (typeid (e2->op ())); + } }; - } - using namespace ps; - - - - // compare two operators based on their address - template - inline bool addrLT (const T &lhs, const Operator* rhs) - { - if (rhs == NULL || lhs == *rhs) return false; - - const T *prhs = dynamic_cast(rhs); - - if (prhs == NULL) - return typeid(T).before (typeid (*rhs)); - - return &lhs < rhs; - } - - - // compare two operators based on their types - inline bool typeLT (const Operator *lhs, const Operator* rhs) - { - if (lhs == NULL && rhs != NULL) return true; - if (lhs == NULL && rhs == NULL) return false; - - if (rhs == NULL || *lhs == *rhs) return false; - - if (typeid (*lhs) == typeid (*rhs)) return false; - - return typeid(*lhs).before (typeid (*rhs)); - } - - inline size_t typeHash (const Operator *op) - { - if (op == NULL) return 0; - std::hash hasher; - return hasher (static_cast(const_cast (typeid (*op).name ()))); - } - - - template - struct DefOp : public B - { - typedef DefOp this_type; - typedef B base_type; - typedef T op_type; - typedef P ps_type; - - void Print (std::ostream &OS, - const std::vector &args, - int depth = 0, - bool brkt = true) const - { ps_type::print (OS, depth, brkt, op_type::name (), args); } - - bool operator== (const Operator& rhs) const - { return typeid (*this) == typeid (rhs); } - - - bool operator< (const Operator& rhs) const - { return typeLT (this, &rhs); } - - size_t hash () const { return typeHash (this); } - - this_type * clone (ExprFactoryAllocator &allocator) const - { return new (allocator) this_type (*this); } - - }; - - inline ENode::~ENode () - { - for (args_iterator b = args.begin (), e = args.end (); - b != e; ++b) - efac().Deref (*b); - } - - - - template - void ENode::renew_args (iterator b, iterator e) - { - std::vector old = args; - args = std::vector (); - - // -- increment reference count of all new arguments - for (; b != e; ++b) - this->push_back (eptr (*b)); - - // -- decrement reference count of all old arguments - for (args_iterator b = old.begin (), e = old.end (); - b != e; ++b) - efac().Deref (*b); - } - - - /** Required by boost::intrusive_ptr */ - inline void intrusive_ptr_add_ref (ENode *v) - { - v->Ref (); - } - - inline void intrusive_ptr_release (ENode *v) - { - v->efac ().Deref (v); - } - - struct BoolExprFn - { - virtual ~BoolExprFn () {} - virtual bool apply (Expr e) = 0; - }; - - struct TrueBoolExprFn : BoolExprFn - { - bool apply (Expr e) { return true; } - }; - - struct FalseBoolExprFn : BoolExprFn - { - bool apply (Expr e) { return false; } - }; - - - struct IdentityRewriter - { - IdentityRewriter () {}; - Expr operator() (Expr e) { return e; } - }; - - struct ExprFn - { - virtual ~ExprFn () {} - virtual Expr apply (Expr e) = 0; - }; - - namespace - { - template - struct ExprFunctionoid : public ExprFn - { - typedef std::shared_ptr fn_type; - fn_type fn; - ExprFunctionoid (T* f) : fn_type (fn) {} - ExprFunctionoid (fn_type f) : fn(f) {} - Expr apply (Expr e) { return (*fn)(e); } + /** + * A type erasure of a cache + */ + struct CacheStub + { + /** true if the stub own the cahce pointer by p */ + virtual bool owns (const void *p) = 0; + /** erases val from the underlying cache */ + virtual void erase (ENode *val) = 0; + virtual ~CacheStub () { } }; - - } - - - class VisitAction - { - public: - - // skipKids or doKids - VisitAction (bool kids = false) : - _skipKids (kids), fn (new ExprFunctionoid - (std::make_shared ())) {} - - // changeTo or doKidsRewrite - template - VisitAction (Expr e, bool kids = false, - std::shared_ptr r = std::make_shared ()) : - _skipKids(kids), expr(e), fn(new ExprFunctionoid (r)) {} - - bool isSkipKids () { return _skipKids && expr.get () == NULL; } - bool isChangeTo () { return _skipKids && expr.get () != NULL; } - bool isDoKids () { return !_skipKids && expr.get () == NULL; } - bool isChangeDoKidsRewrite () { return !_skipKids && expr.get () != NULL; } - - Expr rewrite (Expr v) { return fn->apply (v); } - - Expr getExpr () { return expr; } - - static inline VisitAction skipKids () { return VisitAction (true); } - static inline VisitAction doKids () { return VisitAction (false); } - static inline VisitAction changeTo (Expr e) - { return VisitAction (e, true, std::make_shared ());} - - static inline VisitAction changeDoKids (Expr e) - { return VisitAction (e, false, std::make_shared ());} - - template - static inline VisitAction changeDoKidsRewrite (Expr e, std::shared_ptr r) - { return VisitAction (e, false, r); } - - protected: - bool _skipKids; - Expr expr; - private: - std::shared_ptr fn; - }; - - - typedef std::unordered_map DagVisitCache; - - template - Expr visit (ExprVisitor &v, Expr expr, DagVisitCache &cache) - { - if (expr->use_count () > 1) - { - DagVisitCache::const_iterator cit - = cache.find (&*expr); - if (cit != cache.end ()) return cit->second; - } - - - VisitAction va = v(expr); - Expr res; - - if (va.isSkipKids ()) - res = expr; - else if (va.isChangeTo ()) - res = va.getExpr (); - else - { - res = va.isChangeDoKidsRewrite () ? va.getExpr () : expr; - if (res->arity () > 0) - { - bool changed = false; - std::vector kids; - - for (ENode::args_iterator b = res->args_begin (), - e = res->args_end (); - b != e; ++b) - { - Expr k = visit (v, *b, cache); - kids.push_back (k); - changed = (changed || k.get () != *b); - } - - if (changed) - { - if (!res->isMutable ()) - res = res->getFactory ().mkNary (res->op (), - kids.begin (), - kids.end ()); - else - res->renew_args (kids.begin (), kids.end ()); - } - } - - res = va.rewrite (res); - } - if (expr->use_count () > 1) - { - expr->Ref (); - cache[&*expr] = res; - } - - return res; - } - - inline void clearDagVisitCache (DagVisitCache &cache) - { - for (DagVisitCache::value_type &kv : cache) - kv.first->efac ().Deref (kv.first); - cache.clear (); - } - - - template - struct DagVisit : public std::unary_function - { - ExprVisitor &m_v; - DagVisitCache m_cache; - - DagVisit (ExprVisitor &v) : m_v (v) {} - DagVisit (const DagVisit &o) : m_v (o.m_v) {} - ~DagVisit () { clearDagVisitCache (m_cache); } - - Expr operator() (Expr e) { return visit (m_v, e, m_cache); } - - }; - - template - Expr dagVisit (ExprVisitor &v, Expr expr) - { - DagVisit dv (v); - return dv (expr); - } - - template - void dagVisit (ExprVector &v, ExprVector &vec) - { - DagVisit dv (v); - for (auto &e : vec) e = dv (e); - } - - template - Expr visit (ExprVisitor &v, Expr expr) - { - VisitAction va = v (expr); - - if (va.isSkipKids ()) return expr; - - if (va.isChangeTo ()) return va.getExpr (); - - Expr res = va.isChangeDoKidsRewrite () ? va.getExpr () : expr; - - if (res->arity () == 0) return va.rewrite (res); - - bool changed = false; - std::vector kids; - - for (ENode::args_iterator b = res->args_begin (), - e = res->args_end (); - b != e; ++b) - { - Expr k = visit (v, *b); - kids.push_back (k); - changed = (changed || k.get () != *b); - } - if (changed) - { - if (!res->isMutable ()) - res = res->getFactory ().mkNary (res->op (), - kids.begin (), - kids.end ()); - else - res->renew_args (kids.begin (), kids.end ()); - } - - res = va.rewrite (res); - - return res; - } - - /**********************************************************************/ - /**********************************************************************/ - /* PUBLIC API */ - /**********************************************************************/ - /**********************************************************************/ - - /**********************************************************************/ - /* Inspection */ - /**********************************************************************/ - - // -- usage isOp(EXPR) . Returns true if top operator of - // -- expression is a subclass of TYPE. - template bool isOp (T e) - { - const Operator *op = & (eptr(e)->op ()); - const O *top = dynamic_cast(op); - return top != NULL; - } - - // -- usage isOpX(EXPR) . Returns true if top operator of - // -- expression is of type TYPE. - template bool isOpX (T e) - { return typeid (eptr (e)->op ()) == typeid (O); } - - /**********************************************************************/ - /* Creation */ - /**********************************************************************/ - - /* Creates a nullary expression with operator T. - * Usage: mk (efac) - */ - template Expr mk (ExprFactory &f) - { return f.mkTerm (T()); } - - - /* Creates a terminal expression with a given terminal value - * Usage: mk (5, efac) - */ - template Expr mkTerm (T v, ExprFactory &f) - { - Terminal op(v); - return f.mkTerm (op); - } - - template T getTerm (Expr e) - { - typedef Terminal term_type; - return dynamic_cast(e->op ()).get (); - } - - - /* Creates a unary expression with a given operator. - * Usage: mk (exp) - */ - template Expr mk (Expr e) - { return e->efac ().mkUnary (T(), e); } - - template Expr mk (Expr e1, Expr e2) - { return e1->efac ().mkBin (T(), e1, e2); } - - template Expr mk (Expr e1, - Expr e2, - Expr e3) - { return e1->efac ().mkTern (T(), e1, e2, e3); } - - /** - * Creates an nary expression with a given operator. - * The arguments are given as first and last iterators. - * Usage: mknary (v.begin (), v.end ()) - */ - template - Expr mknary (iterator bgn, iterator end) - { return eptr (*bgn)->efac ().mkNary (T(), bgn, end); } - - template - Expr mknary (Expr base, iterator bgn, iterator end) - { - if (bgn == end) return base; - if (std::distance (bgn, end) == 1) return eptr (*bgn); - return mknary(bgn, end); - } - - /** boost::range versions of mknary */ - - template - Expr mknary (const Range &r) - { return mknary (boost::begin(r), boost::end(r)); } - - template - Expr mknary (Expr base, const Range &r) - { return mknary (base, boost::begin (r), boost::end (r)); } - - - - - - /**********************************************************************/ - /* Constructors that accept explicit operators. Only use those if - the ones above are not applicable.*/ - /**********************************************************************/ - - /* Creates a nullary expression with a given operator. - * Usage: mk (op, efac) - */ - inline Expr mk (const Operator &op, ExprFactory &f) - { return f.mkTerm (op); } - - inline Expr mk (const Operator &o, Expr e) - { return e->efac ().mkUnary (o, e); } - - inline Expr mk (const Operator &o, Expr e1, Expr e2) - { return e1->efac ().mkBin (o, e1, e2); } - - inline Expr mk (const Operator &o, Expr e1, Expr e2, Expr e3) - { return e1->efac ().mkTern (o, e1, e2, e3); } - - template - Expr mknary (const Operator &o, iterator bgn, iterator end) - { return eptr (*bgn)->efac ().mkNary (o, bgn, end); } - - - /**********************************************************************/ - /* Operators */ - /**********************************************************************/ - - - namespace op - { - // -- Boolean opearators - NOP_BASE(BoolOp) - - /* operator definitions */ - NOP(TRUE,"true",PREFIX,BoolOp) - NOP(FALSE,"false",PREFIX,BoolOp) - NOP(AND,"&&",INFIX,BoolOp) - NOP(OR,"||",INFIX,BoolOp) - NOP(XOR,"^",INFIX,BoolOp) - NOP(NEG,"!",PREFIX,BoolOp) - NOP(IMPL,"->",INFIX,BoolOp) - NOP(ITE,"ite",FUNCTIONAL,BoolOp) - NOP(IFF,"<->",INFIX,BoolOp) - - namespace boolop - { - // -- logical AND. Applies simplifications - inline Expr land (Expr e1, Expr e2) - { - if (e1 == e2) return e1; - - if (isOpX(e1)) return e2; - if (isOpX(e2)) return e1; - if (isOpX(e1) || isOpX(e2)) - return mk(e1->efac ()); - - return mk(e1, e2); - } + template + struct CacheStubTmpl : CacheStub + { + C &cache; - inline Expr lor (Expr e1, Expr e2) - { - if (isOpX(e1)) return e2; - if (isOpX(e2)) return e1; - if (isOpX(e1) || isOpX(e2)) return mk(e1->efac ()); - return mk(e1, e2); - } + CacheStubTmpl (C &c): cache(c) {}; - inline Expr limp (Expr e1, Expr e2) - { - // TRUE -> x IS x - if (isOpX (e1)) return e2; - // x -> TRUE IS TRUE - if (isOpX (e2)) return e2; - // FALSE -> x IS TRUE - if (isOpX (e1)) return mk (e1->efac ()); - // x -> x IS TRUE - if (e1 == e2) return mk (e1->efac ()); - - // x -> FALSE is missing since it adds a negation - - return mk(e1, e2); - } - - inline Expr lite (Expr c, Expr t, Expr e) - { - if (isOpX (c)) return t; - if (isOpX (c)) return e; - if (t == e) return t; - - return mk (c, t, e); - } + virtual bool owns (const void *p) { return p == static_cast (&cache); } + virtual void erase (ENode *val) { cache.erase (val); } + }; - inline Expr lneg(Expr e1) - { - if (isOpX(e1)) return mk (e1->efac ()); - if (isOpX(e1)) return mk (e1->efac ()); - - if (isOpX(e1)) return e1->left(); - return mk(e1); - } - template - Expr land (const R &r) - { - assert (boost::begin (r) != boost::end (r)); - - // -- reduce unary AND to the operand - if (boost::size (r) == 1) return *boost::begin (r); + struct EFADeleter + { + ExprFactoryAllocator &m_efa; + EFADeleter (ExprFactoryAllocator &efa) : m_efa (efa) {} + void operator() (void *p); + }; - // XXX add more logical simplifications - return mknary (r); - } - struct CIRCSIZE : public std::unary_function - { - unsigned ands; - unsigned ors; - unsigned inputs; - - CIRCSIZE () : ands (0), ors (0), inputs (0) {} - - - VisitAction operator() (Expr e) - { - if (isOpX (e)) ands++; - else if (isOpX (e)) ors++; - else if (!isOpX (e)) - { - inputs++; - return VisitAction::skipKids (); - } - return VisitAction::doKids (); - } - - unsigned size () { return ands + ors + inputs; } - - }; - - /// size of an expression in terms of ANDs, ORs, and inputs. - /// NEG is not counted, other BoolOps are treated as inputs. - inline unsigned circSize (Expr e) - { - CIRCSIZE csz; - dagVisit (csz, e); - return csz.size (); - } - + class ExprFactoryAllocator : boost::noncopyable + { + private: + /** pool for tiny objects */ + boost::pool<> tiny; + /** pool for small objects */ + boost::pool<> small; - /** trivial simplifier for Boolean Operators */ - struct TrivialSimplifier : public std::unary_function - { - ExprFactory &efac; - - Expr trueE; - Expr falseE; - - TrivialSimplifier (const TrivialSimplifier &o) : - efac (o.efac), trueE (o.trueE), falseE (o.falseE) {} - - TrivialSimplifier (ExprFactory &fac) : - efac(fac), trueE(mk (efac)), falseE(mk (efac)) {} - - Expr operator() (Expr exp) - { - if (exp == trueE || exp == falseE) return exp; - - if (!isOp (exp)) return exp; - - if (isOpX(exp)) - { - // TRUE -> x == x - if (trueE == exp->left ()) return exp->right(); - - // FALSE -> x == TRUE - if (falseE == exp->left ()) return trueE; - - // x -> TRUE == TRUE - if (trueE == exp->right ()) return trueE; - - // x -> FALSE == !x - if (falseE == exp->right ()) return lneg (exp->left ()); - - return exp; - } - - if (isOpX (exp)) - { - if (exp->left () == exp->right ()) return exp->left (); - if (trueE == exp->left ()) return exp->right (); - if (falseE == exp->left ()) return lneg (exp->right ()); - if (trueE == exp->right ()) return exp->left (); - if (falseE == exp->right ()) return lneg (exp->left ()); - - return exp; - } - - if (isOpX(exp)) - { - // -- !TRUE -> FALSE - if (trueE == exp->left()) return falseE; - // -- !FALSE -> TRUE - if (falseE == exp->left()) return trueE; - // -- ! ! x -> x - if (isOpX (exp->left ())) return exp->left ()->left (); - return exp; - } - - int arity = exp->arity (); - if (isOpX (exp)) - { - if (arity == 0) return falseE; - if (arity == 1) return exp->left (); - if (arity == 2) - { - ENode* lhs = exp->left (); - ENode* rhs = exp->right (); - - if (lhs == rhs) return lhs; - if (trueE == lhs || trueE == rhs) return trueE; - if (falseE == lhs) return rhs; - if (falseE == rhs) return lhs; - // (!a || a) - if (isOpX(lhs) && lhs->left () == rhs) return trueE; - // (a || !a) - if (isOpX(rhs) && rhs->left () == lhs) return trueE; - - return exp; - } - - // -- arity > 2, check if one arguments is true - for (ENode *arg : mk_it_range (exp->args_begin (), exp->args_end ())) - if (trueE == arg) return trueE; - return exp; - } - - if (isOpX(exp)) - { - if (arity == 0) return trueE; - if (arity == 1) return exp->left(); - - if (exp->arity () == 2) - { - ENode *lhs = exp->left (); - ENode *rhs = exp->right (); - - if (lhs == rhs) return lhs; - if (falseE == lhs || falseE == rhs) return falseE; - if (trueE == lhs) return rhs; - if (trueE == rhs) return lhs; - if (isOpX(lhs) && lhs->left () == rhs) return falseE; - if (isOpX(rhs) && rhs->left () == lhs) return falseE; - - return exp; - } - - // -- arity > 2, check if one arguments is false - for (ENode * arg : mk_it_range (exp->args_begin (), - exp->args_end ())) - if (falseE == arg) return falseE; - return exp; - } - - return exp; - } - }; - - /** Rewriter that gathers Boolean operators into n-ary ones */ - struct GatherOps : public std::unary_function - { - Expr trueE; - Expr falseE; - - GatherOps () : trueE (0), falseE (0) {} - - GatherOps (const GatherOps &o) : trueE(o.trueE), falseE (o.falseE) {} - - - Expr operator() (Expr exp) - { - // -- create true/false constants for convinience - if (trueE == NULL) - { - trueE = mk (exp->efac ()); - falseE = mk (exp->efac ()); - } - - // -- skip terminals - if (exp->arity () == 0) return exp; - //if (!isBoolOp (exp)) return exp; - // -- skip anything that is not AND/OR - if (! (isOpX (exp) || isOpX (exp)) ) return exp; - - const Operator &op = exp->op (); - Expr top; - Expr bot; - if (isOpX (exp)) - { - top = trueE; - bot = falseE; - } - else - { - top = falseE; - bot = trueE; - } - - ExprSet newArgs; - for (Expr a : mk_it_range (exp->args_begin (), exp->args_end ())) - if (! (op == a->op ()) ) - { - if (a == bot) return bot; - else if (a != top) newArgs.insert (a); - } - else /* descend into kids that have the same top-level operator */ - for (Expr ka : mk_it_range (a->args_begin (), a->args_end ())) - if (ka == bot) return bot; - else if (ka != top) newArgs.insert (ka); - - if (newArgs.empty ()) return top; - if (newArgs.size () == 1) return *(newArgs.begin ()); - return exp->efac ().mkNary (op, newArgs.begin (), newArgs.end ()); - } - }; - - /** Rewriter that normalizes AND/OR operators */ - struct NormalizeOps : public std::unary_function - { - Expr trueE; - Expr falseE; - - NormalizeOps () : trueE (0), falseE (0) {} - - NormalizeOps (const NormalizeOps &o) : - trueE (o.trueE), falseE (o.falseE) {} - - Expr operator() (Expr exp) - { - // -- create true/false constants for convinience - if (trueE == NULL) - { - trueE = mk (exp->efac ()); - falseE = mk (exp->efac ()); - } - - // -- skip anything that is not AND/OR - if (! (isOpX (exp) || isOpX (exp)) ) return exp; - if (exp->arity () == 1) return exp->left (); - - const Operator &op = exp->op (); - Expr top, bot; - if (isOpX (exp)) - { - top = trueE; - bot = falseE; - } - else - { - top = falseE; - bot = trueE; - } - - if (exp->arity () == 0) return top; - - if (exp->arity () == 2) - { - if (isOpX (exp)) return land (exp->left (), exp->right ()); - else /* isOpX */ return lor (exp->left (), exp->right ()); - } - - ExprSet newArgs; - for (Expr a : mk_it_range (exp->args_begin (), exp->args_end ())) - if (! (op == a->op ()) ) - { - if (a == bot) return bot; - else if (a != top) newArgs.insert (a); - } - else /* descend into kids that have the same top-level operator */ - for (Expr ka : mk_it_range (a->args_begin (), a->args_end ())) - if (ka == bot) return bot; - else if (ka != top) newArgs.insert (ka); - - if (newArgs.empty ()) return top; - if (newArgs.size () == 1) return *(newArgs.begin ()); - - boost::container::flat_set args (newArgs.begin (), - newArgs.end ()); - Expr res = top; - for (Expr arg : boost::adaptors::reverse(args)) - res = isOpX (exp) ? land (arg, res) : lor (arg, res); - - return res; - } - - Expr land (Expr f, Expr g) - { - /** base cases */ - if (f == trueE) return g; - if (g == trueE) return f; - if (f == falseE || g == falseE) return falseE; - if (f == g) return f; - if (f == boolop::lneg (g) || boolop::lneg (f) == g) return falseE; - - // -- both not AND operators. Order in some way - if (!isOpX (f) && !isOpX (g)) - return g < f ? mk (f, g) : mk (g, f); - - Expr topf = isOpX (f) ? f->left () : f; - Expr topg = isOpX (g) ? g->left () : g; - - Expr top, restF, restG; - if (topf < topg || topf == topg) - { - top = topf; - restF = isOpX (f) ? f->right () : trueE; - } - else - restF = f; - - if (topg < topf || topg == topf) - { - top = topg; - restG = isOpX (g) ? g->right () : trueE; - } - else - restG = g; - - return boolop::land (top, land (restF, restG)); - } - - Expr lor (Expr f, Expr g) - { - /** base cases */ - if (f == falseE) return g; - if (g == falseE) return f; - if (f == trueE || g == trueE) return trueE; - if (f == g) return f; - if (f == boolop::lneg (g) || boolop::lneg (f) == g) return trueE; - - // -- both not AND operators. Order in some way - if (!isOpX (f) && !isOpX (g)) - return g < f ? mk (f, g) : mk (g, f); - - Expr topf = isOpX (f) ? f->left () : f; - Expr topg = isOpX (g) ? g->left () : g; - - Expr top, restF, restG; - if (topf < topg || topf == topg) - { - top = topf; - restF = isOpX (f) ? f->right () : falseE; - } - else - restF = f; - - if (topg < topf || topg == topf) - { - top = topg; - restG = isOpX (g) ? g->right () : falseE; - } - else - restG = g; - - return boolop::lor (top, lor (restF, restG)); - } - }; - - - - /** puts an expression into NNF */ - struct NNF : public std::unary_function - { - ExprFactory &efac; - std::shared_ptr r; - - NNF (const NNF &o) : efac (o.efac), r (o.r) {} - - NNF (ExprFactory &fac) : efac (fac), - r (std::make_shared (efac)) {} - - VisitAction operator() (Expr exp) - { - if (exp->arity () == 0) return VisitAction::skipKids (); - - // -- AND / OR -- run the simplifier - if (isOpX (exp) || isOpX (exp)) - return VisitAction::changeDoKidsRewrite (exp, r); - - // -- not a negation, then do not touch, must be non-Boolean - if (!isOpX (exp)) return VisitAction::skipKids (); - - // -- if here, top operator is negation, push it in - Expr lhs = exp->left (); - if (lhs == r->falseE) return VisitAction::changeTo (r->trueE); - if (lhs == r->trueE) return VisitAction::changeTo (r->falseE); - - - // -- !!x -- Trivial simplifer will get rid of unary AND - if (isOpX (lhs)) - return VisitAction::changeDoKidsRewrite (mk (lhs->left ()), r); - - - // -- ! (x & b) ==> !x || !b - if (isOpX (lhs) || isOpX (lhs)) - { - // -- negate arguments - ExprVector args; - for (Expr arg : mk_it_range (lhs->args_begin (), - lhs->args_end ())) - args.push_back (lneg (arg)); - - // -- flip operator - Expr res = isOpX (lhs) ? - mknary (args.begin (), args.end ()) : - mknary (args.begin (), args.end ()); - return VisitAction::changeDoKidsRewrite (res, r); - } - - // -- negation of anything else, don't descend - return VisitAction::skipKids (); - } - }; - - - } - - } + public: + ExprFactoryAllocator () : tiny(8, 65536), small (64, 65536) {}; + void *allocate (size_t n); + void free (void *block); - /// Gates - /// Gates are mutable and are not structurally hashed - namespace op - { - struct GateOp : public expr::Operator - { virtual bool isMutable () const { return true;} }; + EFADeleter get_deleter (); + }; - /// an output gate - NOP(OUT_G,"OUT_G",PREFIX,GateOp) - NOP(AND_G,"/\\",INFIX,GateOp); - NOP(OR_G,"\\/",INFIX,GateOp); - NOP(NEG_G,"~",PREFIX,GateOp); - - namespace gate - { - inline Expr land (Expr e1, Expr e2) - { - if (e1 == e2) return e1; - - if (isOpX(e1)) return e2; - if (isOpX(e2)) return e1; - if (isOpX(e1) || isOpX(e2)) - return mk(e1->efac ()); - - return mk(e1, e2); - } - inline Expr lor (Expr e1, Expr e2) - { - if (isOpX(e1)) return e2; - if (isOpX(e2)) return e1; - if (isOpX(e1) || isOpX(e2)) return mk(e1->efac ()); - return mk(e1, e2); - } + class ExprFactory : boost::noncopyable + { + protected: - inline Expr lneg (Expr e1) - { - if (isOpX(e1)) return mk (e1->efac ()); - if (isOpX(e1)) return mk (e1->efac ()); - - if (isOpX(e1) || isOpX (e1)) return e1->left(); +#define UNORDERED_SET_UNIQUE_TABLE 1 +#ifndef UNORDERED_SET_UNIQUE_TABLE + // -- type of unique table entry + typedef std::set unique_entry_type; +#else + typedef std::unordered_set unique_entry_type; +#endif + typedef const char* unique_key_type; + // -- type of the unique table + typedef std::map unique_type; - return mk(e1); - } - } - } - - - namespace op - { - // -- Numeric operators - NOP_BASE(NumericOp) - - NOP(PLUS,"+",INFIX,NumericOp) - NOP(MINUS,"-",INFIX,NumericOp) - NOP(MULT,"*",INFIX,NumericOp) - NOP(DIV,"/",INFIX,NumericOp) - NOP(IDIV,"/",INFIX,NumericOp); - NOP(MOD,"mod",INFIX,NumericOp) - NOP(REM,"%",INFIX,NumericOp) - NOP(UN_MINUS,"-",PREFIX,NumericOp) - NOP(ABS,"abs",FUNCTIONAL,NumericOp) - - NOP(PINFTY,"oo",PREFIX,NumericOp) - NOP(NINFTY,"-oo",PREFIX,NumericOp) - - namespace numeric - { - struct ITV_PS - { - static inline void print (std::ostream &OS, - int depth, - bool brkt, - const std::string &name, - const std::vector &args) - { - OS << "["; - args [0]->Print (OS, depth, false); - OS << ","; - args [1]->Print (OS, depth, false); - OS << "]"; - } - }; - } - NOP(ITV,"itv",numeric::ITV_PS,NumericOp) - } - - - namespace op - { - // -- Comparisson operators - NOP_BASE(ComparissonOp) - - NOP(EQ,"=",INFIX,ComparissonOp) - NOP(NEQ,"!=",INFIX,ComparissonOp) - NOP(LEQ,"<=",INFIX,ComparissonOp) - NOP(GEQ,">=",INFIX,ComparissonOp) - NOP(LT,"<",INFIX,ComparissonOp) - NOP(GT,">",INFIX,ComparissonOp) - } - - namespace op - { - // -- Not yet sorted operators - NOP_BASE(MiscOp) - - /** A non-deterministic value */ - NOP (NONDET,"nondet",FUNCTIONAL,MiscOp) - /** An assumption */ - NOP(ASM,"ASM", PREFIX, MiscOp) - /** A tupple */ - NOP (TUPLE,"tuple",FUNCTIONAL,MiscOp) - } - - namespace op - { - namespace variant - { - struct PS - { - static inline void print (std::ostream &OS, - int depth, - bool brkt, - const std::string &name, - const std::vector &args) - { - args [1]->Print (OS, depth, true); - OS << "_"; - args [0]->Print (OS, depth, true); - } - }; - - struct PS_TAG - { - static inline void print (std::ostream &OS, - int depth, - bool brkt, - const std::string &name, - const std::vector &args) - { - args [1]->Print (OS, depth, true); - OS << "!"; - args [0]->Print (OS, depth, true); - } - }; - } - NOP_BASE(VariantOp) - NOP(VARIANT,"variant",variant::PS,VariantOp) - NOP(TAG,"tag",variant::PS_TAG,VariantOp) - - namespace variant - { - /** Creates a variant of an expression. For example, - `variant (1, e)` creates an expression `e_1` - */ - inline Expr variant (int v, Expr e) - { return mk(mkTerm(v, e->efac ()), e); } - - inline Expr next (Expr e) { return variant (1, e); } - inline Expr aux (Expr e) { return variant (2, e); } - - - inline Expr mainVariant (Expr e) { return e->right (); } - inline int variantNum (Expr e) - { - const INT &v = dynamic_cast (e->left ()->op ()); - return v.get (); - } + typedef boost::ptr_vector caches_type; - inline Expr prime (Expr e) { return variant (1, e); } - inline bool isPrime (Expr e) { return variantNum (e) == 1; } - /** Creates an expression tagged by another expression (or - string). For example, `variant::tag (e, h)` creates an - expression `e!h`. - */ + /** pool allocator */ + ExprFactoryAllocator allocator; - inline Expr tag (Expr e, Expr tag) - { return mk (tag, e); } + /** list of registered caches */ + caches_type caches; - inline Expr tag (Expr e, const std::string &t) - {return tag (e, mkTerm (t, e->efac ()));} + // -- unique table + unique_type unique; - inline Expr getTag (Expr e) - { return e->left (); } + /** counter for assigning unique ids*/ + unsigned int idCount; - inline std::string getTagStr (Expr e) - {return getTerm (getTag (e));} - } - } - - - namespace op - { - NOP_BASE(SimpleTypeOp) - - NOP(INT_TY,"INT",PREFIX,SimpleTypeOp) - NOP(CHAR_TY,"CHAR",PREFIX,SimpleTypeOp) - NOP(REAL_TY,"REAL",PREFIX,SimpleTypeOp) - NOP(VOID_TY,"VOID",PREFIX,SimpleTypeOp) - NOP(BOOL_TY,"BOOL",PREFIX,SimpleTypeOp) - /** Uninterpreted Type */ - NOP(UNINT_TY,"UNINT",PREFIX,SimpleTypeOp) - /** Array Type */ - NOP(ARRAY_TY,"ARRAY",PREFIX,SimpleTypeOp) - } - - namespace op - { - namespace sort - { - inline Expr intTy (ExprFactory &efac) {return mk (efac);} - inline Expr boolTy (ExprFactory &efac) {return mk (efac);} - inline Expr realTy (ExprFactory &efac) {return mk (efac);} - inline Expr arrayTy (Expr indexTy, Expr valTy) - {return mk (indexTy, valTy);} - - inline Expr arrayIndexTy (Expr a) {return a->left ();} - inline Expr arrayValTy (Expr a) {return a->right ();} - } - } - - - namespace op - { - /// Array operators - NOP_BASE (ArrayOp) - - NOP (SELECT, "select", FUNCTIONAL, ArrayOp) - NOP (STORE, "store", FUNCTIONAL, ArrayOp) - NOP (CONST_ARRAY, "const-array", FUNCTIONAL, ArrayOp) - NOP (ARRAY_MAP, "array-map", FUNCTIONAL, ArrayOp) - NOP (ARRAY_DEFAULT, "array-default", FUNCTIONAL, ArrayOp) - NOP (AS_ARRAY, "as-array", FUNCTIONAL, ArrayOp) - } - - namespace op - { - namespace array - { - inline Expr select (Expr a, Expr idx) {return mk (a, idx);} + inline Expr store (Expr a, Expr idx, Expr v) {return mk (a, idx, v);} + inline Expr constArray (Expr domain, Expr v) {return mk (domain, v);} + inline Expr aDefault (Expr a) {return mk (a);} + } + } + + + namespace op + { + + namespace bind + { + struct SCOPE_PS + { + static inline void print (std::ostream &OS, + int depth, + bool brkt, + const std::string &name, + const std::vector &args) + { + OS << "[" << name << " "; + args[0]->Print (OS, depth+2, false); + OS << " in "; + args[1]->Print (OS, depth+2, false); + OS << "]"; + } + }; + struct FAPP_PS; + } + NOP_BASE(BindOp) + + NOP(BIND,":",INFIX,BindOp) + /** Function declaration */ + NOP(FDECL,"fdecl",PREFIX,BindOp) + /** Function application */ + NOP(FAPP,"fapp",bind::FAPP_PS,BindOp) + + namespace bind + { + inline Expr bind (Expr name, Expr value) + { return mk (name, value); } + inline Expr name (Expr e) { return e->left (); } + inline Expr type (Expr e) { return e->right (); } + inline Expr value (Expr e) { return e->right (); } + + inline Expr var (Expr name, Expr type) { return bind (name, type); } + inline Expr intVar (Expr name) + { return var (name, mk(name->efac ())); } + inline Expr realVar (Expr name) + { return var (name, mk(name->efac ())); } + inline Expr boolVar (Expr name) + { return var (name, mk(name->efac ())); } + inline Expr charVar (Expr name) + { return var (name, mk(name->efac ())); } + inline Expr unintVar (Expr name) + { return var (name, mk (name->efac ())); } + + template bool isVar (Expr v) + { + return isOpX(v) && isOpX(bind::type (v)); + } + inline bool isBoolVar (Expr v) { return isVar (v); } + inline bool isIntVar (Expr v) { return isVar (v); } + inline bool isRealVar (Expr v) { return isVar (v); } + + inline Expr constDecl (Expr name, Expr type) + { return mk (name, type); } + inline Expr boolConstDecl (Expr name) + { return constDecl (name, mk (name->efac ())); } + inline Expr intConstDecl (Expr name) + { return constDecl (name, mk (name->efac ())); } + inline Expr realConstDecl (Expr name) + { return constDecl (name, mk (name->efac ())); } + inline Expr adtConstDecl (Expr name) + { return constDecl (name, mk (name->efac ())); } +// inline Expr bvConstDecl (Expr name, int size) + + template + Expr fdecl (Expr fname, const Range &args) + { + // -- at least one value for range type + assert (boost::size (args) > 0); + ExprVector _args; + _args.push_back (fname); + _args.insert (_args.end (), boost::begin (args), boost::end (args)); + return mknary (_args); + } + + inline bool isFdecl (Expr fdecl) { return isOpX (fdecl); } + inline Expr fname (Expr fdecl) { return fdecl->first (); } + + inline Expr fapp (Expr fdecl) { return mk (fdecl); } + + template + Expr fapp (Expr fdecl, const Range &args) + { + ExprVector _args; + _args.push_back (fdecl); + _args.insert (_args.end (), boost::begin (args), boost::end (args)); + return mknary (_args); + } + + inline Expr fapp (Expr fdecl, Expr a0, Expr a1 = Expr(), + Expr a2 = Expr()) + { + ExprVector args; + args.push_back (fdecl); + + if (a0) + args.push_back (a0); + if (a1) + args.push_back (a1); + if (a2) + args.push_back (a2); + return mknary (args); + } + + + + inline bool isFapp (Expr fapp) { return isOpX (fapp); } + + inline Expr rangeTy (Expr fdecl) { return fdecl->last (); } + + inline size_t domainSz (Expr fdecl) + { + assert (fdecl->arity () >= 2); + return fdecl->arity () - 2; + } + + inline Expr domainTy (Expr fdecl, size_t n) + { + assert (n + 2 < fdecl->arity ()); + return fdecl->arg (n+1); + } + + template bool isFdecl (Expr v) + { + return isOpX (v) && isOpX (rangeTy (v)); + } + + + /** constant is an applied nullary function */ + template bool isConst (Expr v) + { + return isOpX (v) && + v->arity () == 1 && + isFdecl (fname (v)); + } + + inline Expr mkConst (Expr name, Expr sort) {return fapp (constDecl (name, sort));} + inline Expr boolConst (Expr name) { return fapp (boolConstDecl (name)); } + inline Expr intConst (Expr name) { return fapp (intConstDecl (name)); } + inline Expr realConst (Expr name) { return fapp (realConstDecl (name)); } + inline Expr adtConst (Expr name) { return fapp (adtConstDecl (name)); } + + inline bool isBoolConst (Expr v) { return isConst (v); } + inline bool isIntConst (Expr v) { return isConst (v); } + inline bool isRealConst (Expr v) { return isConst (v); } + inline bool isAdtConst (Expr v) { return isConst (v); } + + } + + + namespace bind + { + /// returns true if an expression is int constant + class IsHardIntConst : public std::unary_function + { + public: + bool operator () (Expr e) + { + return isOpX (e); + } + }; + + class IsFApp : public std::unary_function + { + public: + bool operator () (Expr e) + { + return isOpX (e) && isOpX (fname (e)); + } + }; + + /// returns true if an expression is a constant + class IsConst : public std::unary_function + { + public: + bool operator () (Expr e) + { + if (isOpX (e)) return this->operator() (variant::mainVariant (e)); + + return isOpX (e) && e->arity () == 1 && isOpX (fname (e)); + } + }; + + /// returns true if an expression is a variable + class IsVar : public std::unary_function + { + public: + bool operator () (Expr e) + { + return isIntVar(e) || isRealVar(e) || isBoolVar(e) || isVar (e); + } + }; + + class IsSelect : public std::unary_function + { + public: + bool operator () (Expr e) + { + return isOpX(v)) return typeOf(v->right()); + if (isOpX(v)) return sort::arrayTy(v->left(), typeOf(v->right())); + if (isOpX(v)) return sort::adTy(v->left ()); + if (isOpX(v)) return bv::bvsort (bv::width(v), v->efac()); + if (isOpX(v) || isOpX(v) || isOpX(v) || isOpX(v) || isOpX(v) || + isOpX(v) || isOpX(v) || isOpX(v)) return mk (v->efac ()); + if (isOpX(v)) return mk (v->efac ()); + if (isOp(v)) return bv::bvsort (64, v->efac()); + + +// std::cerr << "WARNING: could not infer type of: " << *v << "\n"; +// assert (0 && "Unreachable"); + + return Expr(); + } + inline Expr sortOf (Expr v) {return typeOf (v);} + + Expr mkMPZ(boost::multiprecision::cpp_int a, ExprFactory& efac) + { + return mkTerm (mpz_class (boost::lexical_cast(a)), efac); + } + + Expr mkMPZ(int a, ExprFactory& efac) + { + return mkTerm (mpz_class (a), efac); + } + + struct FAPP_PS + { + static inline void print (std::ostream &OS, + int depth, + int brkt, + const std::string &name, + const std::vector &args) + { + if (args.size () > 1) OS << "("; + + // -- strip fdecl if there is one + ENode *fname = args [0]; + if (isOpX (fname)) fname = fname->arg (0); + fname->Print (OS, depth+2, false); + + for (unsigned i = 1; i < args.size (); ++i) + { + OS << " "; + args [i]->Print (OS, depth+2, false); + } + + if (args.size () > 1) OS << ")"; + } + + }; + + /// Creates a new fdecl with the same signature as the given + /// fdecl and a new name + inline Expr rename (Expr fdecl, Expr name) + { + assert (isOpX (fdecl)); + ExprVector _args; + _args.reserve (fdecl->arity ()); + _args.push_back (name); + _args.insert (_args.end (), ++(fdecl->args_begin ()), fdecl->args_end ()); + return mknary (_args); + } + + /// construct a new expression by applying fdecl to the same + /// arguments as fapp. For example, reapp of g(a,b) and f is f(a, b) + inline Expr reapp (Expr fapp, Expr fdecl) + { + assert (isOpX (fdecl)); + assert (isOpX (fapp)); + + ExprVector _args; + _args.reserve (fapp->arity ()); + _args.push_back (fdecl); + _args.insert (_args.end (), ++(fapp->args_begin ()), fapp->args_end ()); + return mknary (_args); + } + + struct BoundVar + { + unsigned var; + + BoundVar (unsigned v) : var (v) {} + BoundVar (const BoundVar &o) : var (o.var) {} + + + bool operator< (const BoundVar &b) const { return var < b.var; } + bool operator== (const BoundVar &b) const { return var == b.var; } + bool operator!= (const BoundVar &b) const { return var != b.var; } + + size_t hash () const + { + std::hash hasher; + return hasher (var); + } + + void Print (std::ostream &OS) const { OS << "B" << var; } + }; + inline std::ostream &operator<< (std::ostream &OS, const BoundVar &b) + { + b.Print (OS); + return OS; + } + } + } + + template<> struct TerminalTrait + { + static inline void print (std::ostream &OS, + const op::bind::BoundVar &b, + int depth, bool brkt) { OS << b; } + static inline bool less (const op::bind::BoundVar &b1, + const op::bind::BoundVar &b2) + { return b1 < b2; } + + static inline bool equal_to (const op::bind::BoundVar &b1, + const op::bind::BoundVar &b2) + { return b1 == b2; } + + static inline size_t hash (const op::bind::BoundVar &b) + { return b.hash (); } }; - - template - struct RV : public std::unary_function + + namespace op + { + typedef Terminal BVAR; + + + namespace bind + { + inline Expr bvar (unsigned idx, Expr type) + { return var (mkTerm (BoundVar (idx), type->efac ()), type); } + inline Expr intBVar (unsigned idx, ExprFactory &efac) + { return intVar (mkTerm (BoundVar (idx), efac )); } + inline Expr boolBVar (unsigned idx, ExprFactory &efac) + { return boolVar (mkTerm (BoundVar (idx), efac )); } + inline Expr realBVar (unsigned idx, ExprFactory &efac) + { return realVar (mkTerm (BoundVar (idx), efac )); } + inline Expr unintBVar (unsigned idx, ExprFactory &efac) + { return unintVar (mkTerm (BoundVar (idx), efac)); } + + inline bool isBVar (Expr e) + { + return isOpX(e) && isOpX (bind::name (e)); + } + + inline unsigned bvarId (Expr e) + { + Expr t = e; + if (isBVar (e)) t = bind::name (e); + assert (isOpX (t)); + return getTerm(t).var; + } + + } + } + + namespace details + { + template + Expr absConstants (const Range &r, Expr e); + + template + Expr subBndVars (const Range &r, Expr e); + } + + namespace op + { + + /** + Binders with Locally Nameless representation. + + Arthur Charguéraud: The Locally Nameless + Representation. J. Autom. Reasoning 49(3): 363-408 (2012) + */ + + namespace bind + { + struct BINDER + { + static inline void print (std::ostream &OS, + int depth, + bool brkt, + const std::string &name, + const std::vector &args) + { + OS << "(" << name << " "; + + OS << "("; + for (auto it = args.begin (), end = args.end () - 1; it != end; ++it) + { + (*it)->last ()->Print (OS, depth + 2, true); + if (it + 1 != end) OS << " "; + } + OS << ") "; + + args.back ()->Print (OS, depth + 2, true); + + OS << ")"; + } + }; + } + + NOP_BASE(BinderOp) + /** Forall quantifier */ + NOP (FORALL, "forall", bind::BINDER, BinderOp) + /** Exists */ + NOP (EXISTS, "exists", bind::BINDER, BinderOp) + /** Lambda */ + NOP (LAMBDA, "lambda", bind::BINDER, BinderOp) + + namespace bind + { + inline unsigned numBound (Expr e) + { + assert (e->arity () > 0); + return e->arity () - 1; + } + inline Expr decl (Expr e, unsigned i) {return e->arg (i);} + inline Expr boundName (Expr e, unsigned i) + {return fname (decl (e, i));} + inline Expr boundSort (Expr e, unsigned i) + {return rangeTy (decl (e, i));} + + + inline Expr body (Expr e) {return *(--(e->args_end ()));} + + + template + Expr abs (const Range &r, Expr e) + { + Expr abs = expr::details::absConstants (r, e); + if (abs == e) return e; + + ExprVector args; + args.reserve (std::distance (std::begin(r), std::end(r)) + 1); + for (auto &v : r) + { + assert (bind::IsConst() (v)); + args.push_back (bind::fname (v)); + } + + args.push_back (abs); + + return mknary (args); + } + + template + Expr abs (Expr v, Expr e) + { + std::array a = {v} ; + return abs (a, e); + } + + template + Expr abs (Expr v0, Expr v1, Expr e) + { + std::array a = {v0,v1}; + return abs (a, e); + } + + template + Expr abs (Expr v0, Expr v1, Expr v2, Expr e) + { + std::array a = {v0,v1,v2}; + return abs (a, e); + } + + + template + Expr sub (const Range &r, Expr e) {return expr::details::subBndVars (r, e);} + + inline Expr sub (Expr v0, Expr e) + { + std::array a = {v0}; + return sub (a, e); + } + + inline Expr sub (Expr v0, Expr v1, Expr e) + { + std::array a = {v0,v1}; + return sub (a, e); + } + + inline Expr sub (Expr v0, Expr v1, Expr v2, Expr e) + { + std::array a = {v0,v1,v2}; + return sub (a, e); + } + + + template + Expr betaReduce (Expr lambda, const Range &r) + { + // -- nullptr + if (!lambda) return lambda; + // -- not lambda + if (!isOpX (lambda)) return lambda; + + // -- nullary + if (numBound (lambda) == 0) return body (lambda); + + // -- number of arguments must match number of bound variables + assert (std::distance(std::begin(r), std::end(r)) == numBound(lambda)); + + // -- replace bound variables + // XXX Need to decide on the order, this might be opposite from what clients expect + return sub(r, body(lambda)); + } + + inline Expr betaReduce (Expr lambda, Expr v0) + { + std::array a = {v0}; + return betaReduce (lambda, a); + } + inline Expr betaReduce (Expr lambda, Expr v0, Expr v1) + { + std::array a = {v0, v1}; + return betaReduce (lambda, a); + } + inline Expr betaReduce (Expr lambda, Expr v0, Expr v1, Expr v2) + { + std::array a = {v0, v1, v2}; + return betaReduce (lambda, a); + } + inline Expr betaReduce (Expr lambda, Expr v0, Expr v1, Expr v2, Expr v3) + { + std::array a = {v0, v1, v2, v3}; + return betaReduce (lambda, a); + } + } + } + + + + + + + + + + + /**********************************************************************/ + /* Visitors */ + /**********************************************************************/ + namespace + { + /* Visitors are hidden. Only to be used internally. */ + + struct RAVALLMR: public std::unary_function + { + ExprMap* m; + + RAVALLMR (ExprMap* _m) : m(_m) { } + VisitAction operator() (Expr exp) const + { + auto it = m->begin(); + while (it != m->end()) + if (it->second == exp) + return VisitAction::changeTo (it->first); + else ++it; + return VisitAction::doKids (); + } + }; + + struct RAV: public std::unary_function + { + Expr s; + Expr t; + + RAV (const RAV &o) : s(o.s), t(o.t) {} + RAV (Expr _s, Expr _t) : s(_s), t(_t) {} + VisitAction operator() (Expr exp) const + { return exp == s ? VisitAction::changeTo (t) : VisitAction::doKids (); } + }; + + struct RAVALL: public std::unary_function + { + ExprVector* s; + ExprVector* t; + unsigned int sz; + + RAVALL (ExprVector* _s, ExprVector* _t) : s(_s), t(_t), sz(_s->size()) { } + VisitAction operator() (Expr exp) const + { + // TODO: could be optimized further, + // e.g., when all elements of s and t have the same type... + for (unsigned int i = 0; i < sz; i++ ) + if (exp == s->at(i)) return VisitAction::changeTo (t->at(i)); + return VisitAction::doKids (); + } + }; + + struct RAVALLM: public std::unary_function + { + ExprMap& m; + + RAVALLM (ExprMap& _m) : m(_m) { } + VisitAction operator() (Expr exp) const + { + if (m[exp] != NULL) return VisitAction::changeTo (m[exp]); + return VisitAction::doKids (); + } + }; + + struct RAVSIMP: public std::unary_function + { + Expr s; + Expr t; + + std::shared_ptr r; + + RAVSIMP (const RAVSIMP &o) : s(o.s), t(o.t), r(o.r) {} + RAVSIMP (Expr _s, Expr _t) : + s(_s), t(_t), r(std::make_shared (s->efac ())) {} + + VisitAction operator() (Expr exp) const + { + if (exp == s) return VisitAction::changeTo (t); + return VisitAction::changeDoKidsRewrite (exp, r); + } + }; + + + template + struct FV : public std::unary_function + { + F filter; + + OutputIterator out; + ExprSet seen; + + typedef FV this_type; + FV (const this_type &o) : filter (o.filter), out(o.out), seen (o.seen) + { assert (0); } + + FV (F f, OutputIterator o) : filter (f), out (o) {} + VisitAction operator() (Expr exp) + { + if (seen.count (exp) > 0) return VisitAction::skipKids (); + seen.insert (exp); + + if (filter (exp)) + { + *(out++) = exp; + return VisitAction::doKids (); + } + + return VisitAction::doKids (); + } + }; + + template + struct RV : public std::unary_function + { + typedef typename M::const_iterator const_iterator; + + const M ↦ + RV (const M &m) : map (m) {} + VisitAction operator() (Expr exp) const + { + const_iterator it = map.find (exp); + + return it == map.end () ? VisitAction::doKids () : + VisitAction::changeTo (it->second); + } + }; + + + template + struct RVSIMP : public std::unary_function + { + typedef typename M::const_iterator const_iterator; + + const M ↦ + + std::shared_ptr r; + + typedef RVSIMP this_type; + + RVSIMP (const this_type &o) : map (o.map), r (o.r) {} + RVSIMP (ExprFactory &fac, const M &m) : + map (m), + r (std::make_shared (fac)) + {} + + VisitAction operator() (Expr exp) const + { + const_iterator it = map.find (exp); + + if (it == map.end ()) + return VisitAction::changeDoKidsRewrite (exp, r); + + return VisitAction::changeTo (it->second); + } + }; + + + struct CV : public std::unary_function + { + Expr e; + bool found; + ExprSet seen; + + CV (const CV &o) : e(o.e), found (o.found), seen (o.seen) + { assert (0); } + + CV (Expr exp) : e(exp), found(false) {} + + VisitAction operator() (Expr exp) + { + if (found || + seen.count (exp) > 0 || + e == exp) + { + found = true; + return VisitAction::skipKids (); + } + seen.insert (e); + return VisitAction::doKids (); + } + }; + + template + struct ContainsOp : public std::unary_function + { + bool found; + + ContainsOp () : found(false) {} + + VisitAction operator() (Expr exp) + { + if (found || isOpX(exp)) + { + found = true; + return VisitAction::skipKids (); + } + return VisitAction::doKids (); + } + }; + + struct HasUninterp : public std::unary_function + { + bool found; + + HasUninterp () : found(false) {} + + VisitAction operator() (Expr exp) + { + if (found || isOpX(exp)) + { + if (exp->arity() > 0) + { + if (isOpX(exp->arg(0)) && + "BOOL" == boost::lexical_cast (exp->arg(0)->last()) && + exp->arg(0)->arity() > 2) + { + found = true; + return VisitAction::skipKids (); + } + } + } + return VisitAction::doKids (); + } + }; + + struct SIZE : public std::unary_function + { + size_t count; + + SIZE () : count(0) {} + + VisitAction operator() (Expr exp) + { + count++; + return VisitAction::doKids (); + } + }; + + template + struct RW : public std::unary_function + { + std::shared_ptr _r; + + typedef RW this_type; + + RW (const this_type &o) : _r (o._r) {} + RW(std::shared_ptr r) : _r(r) {} + + VisitAction operator() (Expr exp) + { + return VisitAction::changeDoKidsRewrite (exp, _r); + } + }; + + } + + /**********************************************************************/ + /* Utility Functions */ + /**********************************************************************/ + + /** Applies a rewriter */ + template + Expr rewrite (std::shared_ptr r, Expr e) { - typedef typename M::const_iterator const_iterator; - - const M ↦ - RV (const M &m) : map (m) {} - VisitAction operator() (Expr exp) const - { - const_iterator it = map.find (exp); - - return it == map.end () ? VisitAction::doKids () : - VisitAction::changeTo (it->second); - } - }; + RW rw(r); + return dagVisit (rw, e); + } + /** Size of an expression as a DAG */ + inline size_t dagSize (Expr e) + { + SIZE sz; + dagVisit (sz, e); + return sz.count; + } - template - struct RVSIMP : public std::unary_function + /** Size of an expression as a tree */ + inline size_t treeSize (Expr e) { - typedef typename M::const_iterator const_iterator; - - const M ↦ + SIZE sz; + visit (sz, e); + return sz.count; + } - std::shared_ptr r; - typedef RVSIMP this_type; - - RVSIMP (const this_type &o) : map (o.map), r (o.r) {} - RVSIMP (ExprFactory &fac, const M &m) : - map (m), - r (std::make_shared (fac)) - {} + // -- replace all occurrences of s by t + inline Expr replaceAll (Expr exp, Expr s, Expr t) + { + RAV rav(s,t); + return dagVisit (rav, exp); + } - VisitAction operator() (Expr exp) const - { - const_iterator it = map.find (exp); - - if (it == map.end ()) - return VisitAction::changeDoKidsRewrite (exp, r); + // pairwise replacing + inline Expr replaceAll (Expr exp, ExprVector& s, ExprVector& t) + { + assert(s.size() == t.size()); + RAVALL rav(&s, &t); + Expr tmp = dagVisit (rav, exp); + if (tmp == exp) return exp; + else return replaceAll(tmp, s, t); + } - return VisitAction::changeTo (it->second); + inline Expr replaceAll (Expr exp, ExprMap& m, bool rec = true, int iter = 0) + { + if (iter == 1000) + { + std::cout << "WARNING: possible inifinite recursion in replaceAll\n"; + return exp; } - }; + if (m.empty()) return exp; + RAVALLM rav(m); + Expr tmp = dagVisit (rav, exp); + if (tmp == exp || !rec) return tmp; + else return replaceAll(tmp, m, rec, iter+1); + } + // pairwise replacing +// inline Expr replaceAll (Expr exp, ExprMap& m) +// { +// RAVALLM rav(m); +// return dagVisit (rav, exp); +// } - struct CV : public std::unary_function + /** Replace all occurrences of s by t while simplifying the result */ + inline Expr replaceAllSimplify (Expr exp, Expr s, Expr t) { - Expr e; - bool found; - ExprSet seen; + RAVSIMP rav(s,t); + return dagVisit (rav, exp); + } - CV (const CV &o) : e(o.e), found (o.found), seen (o.seen) - { assert (0); } - - CV (Expr exp) : e(exp), found(false) {} - - VisitAction operator() (Expr exp) - { - if (found || - seen.count (exp) > 0 || - e == exp) - { - found = true; - return VisitAction::skipKids (); - } - seen.insert (e); - return VisitAction::doKids (); - } - }; + // pairwise replacing + inline Expr replaceAllRev (Expr exp, ExprMap& m) + { + if (m.empty()) return exp; + RAVALLMR rav(&m); + Expr tmp = dagVisit (rav, exp); + if (tmp == exp) return exp; + else return replaceAllRev(tmp, m); + } - struct SIZE : public std::unary_function + // -- collect all sub-expressions of exp that satisfy the filter + template + void filter (Expr exp, F filter, OutputIterator out) { - size_t count; - - SIZE () : count(0) {} - - VisitAction operator() (Expr exp) - { - count++; - return VisitAction::doKids (); - } - }; + FV fv(filter, out); + dagVisit (fv, exp); + } - template - struct RW : public std::unary_function + // template + // void filter (Expr exp, F f, ExprSet &out) + // { + // filter (exp, f, std::inserter (out, out.begin ())); + // } + + + /** A wrapper to use any functional object as a replace-map */ + template + struct fn_map { - std::shared_ptr _r; - - typedef RW this_type; - - RW (const this_type &o) : _r (o._r) {} - RW(std::shared_ptr r) : _r(r) {} + struct const_iterator + { + ExprPair pair; + const_iterator () : pair (Expr(), Expr()) {} - VisitAction operator() (Expr exp) - { - return VisitAction::changeDoKidsRewrite (exp, _r); - } - }; - - } - - /**********************************************************************/ - /* Utility Functions */ - /**********************************************************************/ - - /** Applies a rewriter */ - template - Expr rewrite (std::shared_ptr r, Expr e) - { - RW rw(r); - return dagVisit (rw, e); - } - - /** Size of an expression as a DAG */ - inline size_t dagSize (Expr e) - { - SIZE sz; - dagVisit (sz, e); - return sz.count; - } - - /** Size of an expression as a tree */ - inline size_t treeSize (Expr e) - { - SIZE sz; - visit (sz, e); - return sz.count; - } - - - // -- replace all occurrences of s by t - inline Expr replaceAll (Expr exp, Expr s, Expr t) - { - RAV rav(s,t); - return dagVisit (rav, exp); - } - - /** Replace all occurrences of s by t while simplifying the result */ - inline Expr replaceAllSimplify (Expr exp, Expr s, Expr t) - { - RAVSIMP rav(s,t); - return dagVisit (rav, exp); - } - - - // -- collect all sub-expressions of exp that satisfy the filter - template - void filter (Expr exp, F filter, OutputIterator out) - { - FV fv(filter, out); - dagVisit (fv, exp); - } - - // template - // void filter (Expr exp, F f, ExprSet &out) - // { - // filter (exp, f, std::inserter (out, out.begin ())); - // } - - - /** A wrapper to use any functional object as a replace-map */ - template - struct fn_map - { - struct const_iterator - { - ExprPair pair; - const_iterator () : pair (Expr(), Expr()) {} - - const_iterator (Expr u, Expr v) : pair (u, v) {} - - bool operator== (const const_iterator &other) const - { return pair == other.pair; } - - const ExprPair &operator*() const { return pair; } - const ExprPair *operator->() const { return &pair; } + const_iterator (Expr u, Expr v) : pair (u, v) {} + + bool operator== (const const_iterator &other) const + { return pair == other.pair; } + + const ExprPair &operator*() const { return pair; } + const ExprPair *operator->() const { return &pair; } + }; + + const_iterator end_iterator; + + /** the function */ + F f; + fn_map (const F &fn) : f(fn) {} + + const_iterator find (Expr exp) const + { + Expr res = f(exp); + if (res) return const_iterator (exp, res); + return end (); + } + + const_iterator end () const + { + return end_iterator; + } }; - const_iterator end_iterator; - /** the function */ - F f; - fn_map (const F &fn) : f(fn) {} - - const_iterator find (Expr exp) const + template + fn_map mk_fn_map (const F &fn) { return fn_map (fn); } + + template + Expr replace (Expr exp, const M &map) { - Expr res = f(exp); - if (res) return const_iterator (exp, res); - return end (); + RV rv(map); + return dagVisit (rv, exp); } - const_iterator end () const + + /** Replace and simplify */ + template + Expr replaceSimplify (Expr exp, const M &map) { - return end_iterator; + RVSIMP rv(exp->efac (), map); + return dagVisit (rv, exp); } - }; - - - template - fn_map mk_fn_map (const F &fn) { return fn_map (fn); } - - template - Expr replace (Expr exp, const M &map) - { - RV rv(map); - return dagVisit (rv, exp); - } - - - /** Replace and simplify */ - template - Expr replaceSimplify (Expr exp, const M &map) - { - RVSIMP rv(exp->efac (), map); - return dagVisit (rv, exp); - } - - - /** Returns true if e1 contains e2 as a sub-expression */ - inline bool contains (Expr e1, Expr e2) - { - CV cv(e2); - dagVisit (cv, e1); - return cv.found; - } - - - namespace op - { - namespace boolop - { - namespace - { - template - struct BS - { - std::shared_ptr _r; - - BS (std::shared_ptr r) : _r(r) {} - BS (T *r) : _r (r) {} - - - VisitAction operator() (Expr exp) - { - // -- apply the rewriter - if (isOp (exp)) - return VisitAction::changeDoKidsRewrite (exp, _r); - - // -- do not descend into non-boolean operators - return VisitAction::skipKids (); - } - }; - } - /** - * Very simple simplifier for Boolean Operators - */ - inline Expr simplify (Expr exp) - { - BS bs(std::make_shared (exp->efac ())); - return dagVisit (bs, exp); - } - - /** - * Very simple normalizer for AND/OR expressions - */ - inline Expr norm (Expr exp) - { - BS bs (new NormalizeOps ()); - return dagVisit (bs, exp); - } - - /** Gather binary Boolean operators into n-ary ones. Helps - readability. Best done after NNF */ - inline Expr gather (Expr exp) - { - BS go (new GatherOps ()); - return dagVisit (go, exp); - } - + /** Returns true if e1 contains e2 as a sub-expression */ + inline bool contains (Expr e1, Expr e2) + { + CV cv(e2); + dagVisit (cv, e1); + return cv.found; + } - /** - * Converts to NNF. Assumes the only Boolean operators of exp - * are AND/OR/NEG. - */ - inline Expr nnf (Expr exp) - { - NNF n(exp->efac ()); - return dagVisit (n, exp); - } + /** Returns true if e1 contains applications of T */ + template inline bool containsOp (Expr e1) + { + ContainsOp co; + dagVisit (co, e1); + return co.found; + } - /** Makes an expression pretty for printing */ - inline Expr pp (Expr exp) { return gather (nnf (exp)); } - + inline bool hasUninterp (Expr e1) + { + HasUninterp co; + dagVisit (co, e1); + return co.found; } - } -} -#include "ExprBv.hh" + namespace op + { + namespace boolop + { + namespace + { + template + struct BS + { + std::shared_ptr _r; + + BS (std::shared_ptr r) : _r(r) {} + BS (T *r) : _r (r) {} + + + VisitAction operator() (Expr exp) + { + // -- apply the rewriter + if (isOp (exp)) + return VisitAction::changeDoKidsRewrite (exp, _r); + + // -- do not descend into non-boolean operators + return VisitAction::skipKids (); + } + }; + } + + /** + * Very simple simplifier for Boolean Operators + */ + inline Expr simplify (Expr exp) + { + BS bs(std::make_shared (exp->efac ())); + return dagVisit (bs, exp); + } + + /** + * Very simple normalizer for AND/OR expressions + */ + inline Expr norm (Expr exp) + { + BS bs (new NormalizeOps ()); + return dagVisit (bs, exp); + } + + + /** Gather binary Boolean operators into n-ary ones. Helps + readability. Best done after NNF */ + inline Expr gather (Expr exp) + { + BS go (new GatherOps ()); + return dagVisit (go, exp); + } + + + /** + * Converts to NNF. Assumes the only Boolean operators of exp + * are AND/OR/NEG. + */ + inline Expr nnf (Expr exp) + { + NNF n(exp->efac ()); + return dagVisit (n, exp); + } + + /** Makes an expression pretty for printing */ + inline Expr pp (Expr exp) { return gather (nnf (exp)); } + + } + } +} namespace std { - /** standard order of expressions by their id */ - template <> struct less - { - bool operator() (const expr::ENode *x, const expr::ENode *y) const - { - if (x == NULL) return y != NULL; - if (y == NULL) return false; - - return x->getId () < y->getId (); - } - - }; + /** standard order of expressions by their id */ + template <> struct less + { + bool operator() (const expr::ENode *x, const expr::ENode *y) const + { + if (x == NULL) return y != NULL; + if (y == NULL) return false; + + return x->getId () < y->getId (); + } + + }; } @@ -3083,293 +3275,293 @@ namespace std #include namespace expr { - using namespace boost; - - /** LRU Cache */ - template - class ExprCache - { - typedef boost::bimaps::bimap, - boost::bimaps::list_of > cache_type; - typedef typename cache_type::left_value_type value_type; - typedef typename cache_type::right_iterator right_iterator; - - - cache_type cache; - size_t capacity; - - public: - typedef typename cache_type::left_const_iterator const_iterator; - typedef typename cache_type::left_iterator iterator; - - - public: - ExprCache (size_t c) : capacity(c) {} - - ~ExprCache () { clear (); } - - void clear () - { - for (iterator it = cache.left.begin (), end = cache.left.end (); - it != end; ++it) - { - ENode *n = it->first; - n->efac ().Deref (n); - } - cache.clear (); - } - - const_iterator find (Expr e) - { - const_iterator it = cache.left.find (&*e); - if (it != cache.left.end ()) - cache.right.relocate (cache.right.end (), - cache.project_right (it)); - return it; - } + using namespace boost; - const_iterator end () const + /** LRU Cache */ + template + class ExprCache { - return cache.left.end (); - } - - std::pair insert (Expr e, T &v) - { - if (cache.size () == capacity) - { - right_iterator it = cache.right.begin (); - // -- dereference a key that is about to be deleted - ENode *old = it->second; - old->efac ().Deref (old); - cache.right.erase (it); - } - ENode *n = &*e; - n->Ref (); - return cache.left.insert (value_type (n, v)); - } + typedef boost::bimaps::bimap, + boost::bimaps::list_of > cache_type; + typedef typename cache_type::left_value_type value_type; + typedef typename cache_type::right_iterator right_iterator; + + + cache_type cache; + size_t capacity; + + public: + typedef typename cache_type::left_const_iterator const_iterator; + typedef typename cache_type::left_iterator iterator; + + + public: + ExprCache (size_t c) : capacity(c) {} + + ~ExprCache () { clear (); } + + void clear () + { + for (iterator it = cache.left.begin (), end = cache.left.end (); + it != end; ++it) + { + ENode *n = it->first; + n->efac ().Deref (n); + } + cache.clear (); + } + + const_iterator find (Expr e) + { + const_iterator it = cache.left.find (&*e); + if (it != cache.left.end ()) + cache.right.relocate (cache.right.end (), + cache.project_right (it)); + return it; + } + + const_iterator end () const + { + return cache.left.end (); + } + + std::pair insert (Expr e, T &v) + { + if (cache.size () == capacity) + { + right_iterator it = cache.right.begin (); + // -- dereference a key that is about to be deleted + ENode *old = it->second; + old->efac ().Deref (old); + cache.right.erase (it); + } + ENode *n = &*e; + n->Ref (); + return cache.left.insert (value_type (n, v)); + } + + size_t size () const { return cache.size (); } + - size_t size () const { return cache.size (); } - - - }; + }; } namespace expr { - inline size_t hash_value (Expr e) - { - std::hash hasher; - return hasher (e.get ()); - } + inline size_t hash_value (Expr e) + { + std::hash hasher; + return hasher (e.get ()); + } } /// implement boost::hash namespace boost { - template<> struct hash : - public std::unary_function - { - std::size_t operator() (const expr::Expr &v) const + template<> struct hash : + public std::unary_function { - return expr::hash_value (v); - } - }; + std::size_t operator() (const expr::Expr &v) const + { + return expr::hash_value (v); + } + }; } /// implement std::hash namespace std { - template<> struct hash : - public std::unary_function - { - std::size_t operator() (const expr::Expr &v) const + template<> struct hash : + public std::unary_function { - return expr::hash_value (v); - } - }; + std::size_t operator() (const expr::Expr &v) const + { + return expr::hash_value (v); + } + }; } namespace expr { - namespace details - { - template struct ABSCST; - - template - struct AbsCst - { - typedef AbsCst this_type; - - const Range &m_r; - std::unordered_map m_evmap; - - std::vector > m_pinned; - - typedef std::map > > cache_type; - cache_type m_cache; - - AbsCst (const Range &r); - DagVisit > &cachedVisitor (unsigned offset); - - }; - - template - struct ABSCST : public std::unary_function - { - Abs &m_a; - unsigned m_offset; - - inline ABSCST (Abs &a, unsigned offset); - inline VisitAction operator() (Expr exp) const; - }; - - template - AbsCst::AbsCst (const Range &r) : m_r(r) + namespace details { - unsigned cnt = std::distance (std::begin(r), std::end(r)); - for (const Expr &v : m_r) m_evmap[v] = --cnt; - } - - template - DagVisit > > &AbsCst::cachedVisitor (unsigned offset) - { - typedef AbsCst this_type; - - auto it = m_cache.find (offset); - if (it != m_cache.end ()) return it->second; - - m_pinned.push_back (ABSCST (*this, offset)); - - auto v = m_cache.insert - (std::make_pair(offset, DagVisit > (m_pinned.back ()))); - return (v.first)->second; - } - - - template - ABSCST::ABSCST (Abs &a, unsigned offset) : - m_a(a), m_offset (offset) {} - - template - VisitAction ABSCST::operator() (Expr exp) const - { - if (op::bind::isFdecl (exp)) return VisitAction::skipKids (); - - if (op::bind::IsConst() (exp)) - { - auto it = m_a.m_evmap.find (exp); - if (it != m_a.m_evmap.end ()) + template struct ABSCST; + + template + struct AbsCst + { + typedef AbsCst this_type; + + const Range &m_r; + std::unordered_map m_evmap; + + std::vector > m_pinned; + + typedef std::map > > cache_type; + cache_type m_cache; + + AbsCst (const Range &r); + DagVisit > &cachedVisitor (unsigned offset); + + }; + + template + struct ABSCST : public std::unary_function { - Expr b = bind::bvar (m_offset + it->second, bind::sortOf (exp)); - return VisitAction::changeTo (b); + Abs &m_a; + unsigned m_offset; + + inline ABSCST (Abs &a, unsigned offset); + inline VisitAction operator() (Expr exp) const; + }; + + template + AbsCst::AbsCst (const Range &r) : m_r(r) + { + unsigned cnt = std::distance (std::begin(r), std::end(r)); + for (const Expr &v : m_r) m_evmap[v] = --cnt; } - return VisitAction::skipKids (); - } - else if (isOp (exp)) - { - auto &dv = m_a.cachedVisitor (m_offset + 1); - ExprVector kids (exp->args_begin (), exp->args_end ()); - Expr &last = kids.back (); - last = dv (last); - return VisitAction::changeTo (exp->efac ().mkNary (exp->op (), kids)); - } - return VisitAction::doKids (); - } - + template + DagVisit > > &AbsCst::cachedVisitor (unsigned offset) + { + typedef AbsCst this_type; - - template - Expr absConstants (const Range &r, Expr e) - { - AbsCst a(r); - auto v = ABSCST > (a, 0); - return dagVisit (v, e); - } + auto it = m_cache.find (offset); + if (it != m_cache.end ()) return it->second; - - template struct SUBBND; - - template - struct SubBnd - { - typedef SubBnd this_type; - - const Range &m_r; - unsigned m_sz; - std::vector > m_pinned; - typedef std::map > > cache_type; - cache_type m_cache; - - SubBnd (const Range &r) : m_r (r) - {m_sz = std::distance (std::begin (m_r), std::end (m_r));} - - DagVisit > &cachedVisitor (unsigned offset); - - Expr sub (unsigned idx) - { - if (idx >= m_sz) return Expr (0); - auto it = std::end (m_r) - 1 - idx; - return *it; - } - - }; - - template - struct SUBBND : public std::unary_function - { - Sub &m_a; - unsigned m_offset; + m_pinned.push_back (ABSCST (*this, offset)); - SUBBND (Sub &a, unsigned offset) : m_a (a), m_offset (offset) {}; + auto v = m_cache.insert + (std::make_pair(offset, DagVisit > (m_pinned.back ()))); + return (v.first)->second; + } - VisitAction operator() (Expr exp) const - { - if (bind::isFdecl (exp)) return VisitAction::skipKids (); - else if (bind::isBVar (exp)) - { - unsigned idx = bind::bvarId (exp); - if (idx < m_offset) return VisitAction::skipKids (); - - Expr u = m_a.sub (idx - m_offset); - return u ? VisitAction::changeTo (u) : VisitAction::skipKids (); + + template + ABSCST::ABSCST (Abs &a, unsigned offset) : + m_a(a), m_offset (offset) {} + + template + VisitAction ABSCST::operator() (Expr exp) const + { + if (op::bind::isFdecl (exp)) return VisitAction::skipKids (); + + if (op::bind::IsConst() (exp)) + { + auto it = m_a.m_evmap.find (exp); + if (it != m_a.m_evmap.end ()) + { + Expr b = bind::bvar (m_offset + it->second, bind::sortOf (exp)); + return VisitAction::changeTo (b); + } + return VisitAction::skipKids (); + } + else if (isOp (exp)) + { + auto &dv = m_a.cachedVisitor (m_offset + 1); + ExprVector kids (exp->args_begin (), exp->args_end ()); + Expr &last = kids.back (); + last = dv (last); + return VisitAction::changeTo (exp->efac ().mkNary (exp->op (), kids)); + } + + return VisitAction::doKids (); } - else if (isOp (exp)) + + + + template + Expr absConstants (const Range &r, Expr e) { - auto &dv = m_a.cachedVisitor (m_offset + 1); - ExprVector kids (exp->args_begin (), exp->args_end ()); - Expr &last = kids.back (); - last = dv (last); - return VisitAction::changeTo (exp->efac ().mkNary (exp->op (), kids)); + AbsCst a(r); + auto v = ABSCST > (a, 0); + return dagVisit (v, e); + } + + + template struct SUBBND; + + template + struct SubBnd + { + typedef SubBnd this_type; + + const Range &m_r; + unsigned m_sz; + std::vector > m_pinned; + typedef std::map > > cache_type; + cache_type m_cache; + + SubBnd (const Range &r) : m_r (r) + {m_sz = std::distance (std::begin (m_r), std::end (m_r));} + + DagVisit > &cachedVisitor (unsigned offset); + + Expr sub (unsigned idx) + { + if (idx >= m_sz) return Expr (0); + auto it = std::end (m_r) - 1 - idx; + return *it; + } + + }; + + template + struct SUBBND : public std::unary_function + { + Sub &m_a; + unsigned m_offset; + + SUBBND (Sub &a, unsigned offset) : m_a (a), m_offset (offset) {}; + + VisitAction operator() (Expr exp) const + { + if (bind::isFdecl (exp)) return VisitAction::skipKids (); + else if (bind::isBVar (exp)) + { + unsigned idx = bind::bvarId (exp); + if (idx < m_offset) return VisitAction::skipKids (); + + Expr u = m_a.sub (idx - m_offset); + return u ? VisitAction::changeTo (u) : VisitAction::skipKids (); + } + else if (isOp (exp)) + { + auto &dv = m_a.cachedVisitor (m_offset + 1); + ExprVector kids (exp->args_begin (), exp->args_end ()); + Expr &last = kids.back (); + last = dv (last); + return VisitAction::changeTo (exp->efac ().mkNary (exp->op (), kids)); + } + return VisitAction::doKids (); + } + }; + + template + DagVisit > > &SubBnd::cachedVisitor (unsigned offset) + { + typedef SubBnd this_type; + auto it = m_cache.find (offset); + if (it != m_cache.end ()) return it->second; + + m_pinned.push_back (SUBBND (*this, offset)); + auto v = m_cache.insert + (std::make_pair (offset, DagVisit > (m_pinned.back ()))); + return (v.first)->second; + } + + + template + Expr subBndVars (const Range &r, Expr e) + { + SubBnd a(r); + auto v = SUBBND > (a, 0); + return dagVisit (v, e); } - return VisitAction::doKids (); - } - }; - - template - DagVisit > > &SubBnd::cachedVisitor (unsigned offset) - { - typedef SubBnd this_type; - auto it = m_cache.find (offset); - if (it != m_cache.end ()) return it->second; - - m_pinned.push_back (SUBBND (*this, offset)); - auto v = m_cache.insert - (std::make_pair (offset, DagVisit > (m_pinned.back ()))); - return (v.first)->second; - } - - - template - Expr subBndVars (const Range &r, Expr e) - { - SubBnd a(r); - auto v = SUBBND > (a, 0); - return dagVisit (v, e); } - } } -#endif +#endif \ No newline at end of file diff --git a/include/ufo/ExprBv.hh b/include/ufo/ExprBv.hh index f17ae3394..0be7d62f4 100644 --- a/include/ufo/ExprBv.hh +++ b/include/ufo/ExprBv.hh @@ -141,7 +141,7 @@ namespace expr NOP(BEXT_ROTATE_RIGHT,"bvextrotright",FUNCTIONAL,BvOp) NOP(INT2BV,"int2bv",FUNCTIONAL,BvOp) NOP(BV2INT,"bv2int",FUNCTIONAL,BvOp) - + namespace bv { /* XXX Add helper methods as needed */ diff --git a/include/ufo/ExprLlvm.hpp b/include/ufo/ExprLlvm.hpp index 63ca200dc..88f595323 100644 --- a/include/ufo/ExprLlvm.hpp +++ b/include/ufo/ExprLlvm.hpp @@ -1,208 +1,7 @@ #ifndef __EXPR__LLVM__HPP_ #define __EXPR__LLVM__HPP_ -#include "ufo/Expr.hpp" - -#include - -#include "llvm/Support/raw_ostream.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Value.h" -#include "llvm/ADT/APInt.h" -#include "llvm/IR/Constants.h" - -#include - - -namespace expr -{ - using namespace llvm; - - inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Expr &p) - { - OS << p.get (); - return OS; - } - - inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const ENode &n) - { - OS << boost::lexical_cast (n); - return OS; - } - - using namespace llvm; - template<> struct TerminalTrait - { - static inline void print (std::ostream &OS, const Function *f, - int depth, bool brkt) - {OS << f->getName ().str ();} - - static inline bool less (const Function *f1, const Function *f2) - {return f1 < f2;} - - static inline bool equal_to (const Function *f1, const Function *f2) - {return f1 == f2;} - - static inline size_t hash (const Function *f) - { - boost::hash hasher; - return hasher (f); - } - }; - - - template<> struct TerminalTrait - { - static inline void print (std::ostream &OS, const BasicBlock* s, - int depth, bool brkt) - { - OS << s->getParent ()->getName ().str () + "@" + s->getName ().str (); - } - static inline bool less (const BasicBlock* s1, const BasicBlock* s2) - { return s1 < s2; } - - static inline bool equal_to (const BasicBlock *b1, const BasicBlock *b2) - { return b1 == b2; } - - static inline size_t hash (const BasicBlock *b) - { - boost::hash hasher; - return hasher (b); - } - - - }; - - template<> struct TerminalTrait - { - static inline void print (std::ostream &OS, const Value* s, - int depth, bool brkt) - { - // -- name instructions uniquely based on the name of their containing function - if (const Instruction *inst = dyn_cast (s)) - { - const BasicBlock *bb = inst->getParent (); - const Function *fn = bb ? bb->getParent () : NULL; - if (fn) OS << fn->getName ().str () << "@"; - } - else if (const Argument *arg = dyn_cast (s)) - { - const Function *fn = arg->getParent (); - if (fn) OS << fn->getName ().str () << "@"; - } - - if (s->hasName ()) - OS << (isa (s) ? '@' : '%') - << s->getName ().str (); - else - { - // names of constant expressions - std::string ssstr; - raw_string_ostream ss(ssstr); - ss << *s; - OS << ss.str (); - - // std::string str = ss.str(); - // int f = str.find_first_not_of(' '); - // std::string s1 = str.substr(f); - // f = s1.find_first_of(' '); - // OS << s1.substr(0,f); - } - } - static inline bool less (const Value* s1, const Value* s2) - { return s1 < s2; } - - static inline bool equal_to (const Value *v1, const Value *v2) - { return v1 == v2; } - - static inline size_t hash (const Value *v) - { - boost::hash hasher; - return hasher (v); - } - }; - - typedef expr::Terminal BB; - typedef expr::Terminal VALUE; - typedef expr::Terminal FUNCTION; - - /** Converts v to mpz_class. Assumes that v is signed */ - inline mpz_class toMpz (const APInt &v) - { - // Based on: - // https://llvm.org/svn/llvm-project/polly/trunk/lib/Support/GICHelper.cpp - // return v.getSExtValue (); - - APInt abs; - abs = v.isNegative () ? v.abs () : v; - - const uint64_t *rawdata = abs.getRawData (); - unsigned numWords = abs.getNumWords (); - - // TODO: Check if this is true for all platforms. - mpz_class res; - mpz_import(res.get_mpz_t (), numWords, 1, sizeof (uint64_t), 0, 0, rawdata); - - return v.isNegative () ? mpz_class(-res) : res; - } - - inline mpz_class toMpz (const Value *v) - { - if (const ConstantInt *k = dyn_cast (v)) - return toMpz (k->getValue ()); - if (isa (v)) return 0; - - assert (0 && "Not a number"); - return 0; - } - - /** Adapted from - https://llvm.org/svn/llvm-project/polly/branches/release_34/lib/Support/GICHelper.cpp - */ - inline APInt toAPInt (const mpz_class &v) - { - uint64_t *p = nullptr; - size_t sz; - - p = (uint64_t*)mpz_export (p, &sz, -1, sizeof(uint64_t), 0, 0, v.get_mpz_t ()); - if (p) - { - APInt A ((unsigned)mpz_sizeinbase (v.get_mpz_t (), 2), (unsigned)sz, p); - A = A.zext (A.getBitWidth () + 1); - free (p); - - if (sgn (v) == -1) - return -A; - else - return A; - } - else - return APInt (1, 0); - } - - inline APInt toAPInt (unsigned numBits, const mpz_class &v) - { - uint64_t *p = nullptr; - size_t sz; - - p = (uint64_t*)mpz_export (p, &sz, -1, sizeof(uint64_t), 0, 0, v.get_mpz_t ()); - if (p) - { - APInt A (numBits, (unsigned)sz, p); - free (p); - - if (sgn (v) == -1) - return -A; - else - return A; - } - else - return APInt (numBits, 0); - } - -} - +#define outs() std::cout +#define errs() std::cerr #endif diff --git a/include/ufo/Smt/Z3n.hpp b/include/ufo/Smt/Z3n.hpp index bce7102d9..f5ecfd222 100644 --- a/include/ufo/Smt/Z3n.hpp +++ b/include/ufo/Smt/Z3n.hpp @@ -19,8 +19,8 @@ DM-0002198 #ifndef __UFO_Z3N_HPP_ #define __UFO_Z3N_HPP_ /** - New Z3 interface based of Z3 v4. - + New Z3 interface based of Z3 v4. + */ #include "z3.h" @@ -46,52 +46,27 @@ DM-0002198 namespace z3 { - struct ast_ptr_hash : public std::unary_function - { - std::size_t operator() (const ast &ast) const + struct ast_ptr_hash : public std::unary_function { - std::hash hasher; - return hasher (static_cast (ast)); - } - }; - - struct ast_ptr_equal_to : public std::binary_function - { - bool operator() (const ast &a1, const ast &a2) const + std::size_t operator() (const ast &ast) const + { + std::hash hasher; + return hasher (static_cast (ast)); + } + }; + + struct ast_ptr_equal_to : public std::binary_function { - return static_cast(a1) == static_cast(a2); - } - }; + bool operator() (const ast &a1, const ast &a2) const + { + return static_cast(a1) == static_cast(a2); + } + }; } namespace z3 { - // -- fixedpoint class is missing from z3++.h - class fixedpoint : public object - { - Z3_fixedpoint m_fixedpoint; - void init (Z3_fixedpoint f) - { - m_fixedpoint = f; - Z3_fixedpoint_inc_ref (ctx(), f); - } - public: - fixedpoint(context & c):object(c) { init(Z3_mk_fixedpoint(c)); } - fixedpoint(context & c, Z3_fixedpoint s):object(c) { init(s); } - fixedpoint(fixedpoint const & s):object(s) { init(s.m_fixedpoint); } - ~fixedpoint() { Z3_fixedpoint_dec_ref(ctx(), m_fixedpoint); } - operator Z3_fixedpoint() const { return m_fixedpoint; } - fixedpoint & operator=(fixedpoint const & s) { - Z3_fixedpoint_inc_ref(s.ctx(), s.m_fixedpoint); - Z3_fixedpoint_dec_ref(ctx(), m_fixedpoint); - m_ctx = s.m_ctx; - m_fixedpoint = s.m_fixedpoint; - return *this; - } - void set(params const & p) - { Z3_fixedpoint_set_params(ctx(), m_fixedpoint, p); check_error(); } - }; - + class ast_map : public object { Z3_ast_map m_map; void init(Z3_ast_map v) { Z3_ast_map_inc_ref(ctx(), v); m_map = v; } @@ -113,7 +88,7 @@ namespace z3 void insert (ast const &k, ast const &v) { Z3_ast_map_insert(ctx(), m_map, k, v); check_error(); }; ast find (ast const &k) { Z3_ast res = Z3_ast_map_find(ctx(), m_map, k); check_error(); return ast (ctx (), res); }; ast_vector get_keys() { Z3_ast_vector res = Z3_ast_map_keys(ctx(), m_map); check_error(); return ast_vector (ctx (), res); }; - + friend std::ostream & operator<<(std::ostream & out, ast_map const & v) { out << Z3_ast_map_to_string(v.ctx(), v); return out; } }; } @@ -121,105 +96,118 @@ namespace z3 namespace ufo { - using namespace expr; - - inline boost::tribool z3l_to_tribool (Z3_lbool l) - { - if (l == Z3_L_TRUE) return true; - else if (l == Z3_L_FALSE) return false; - return boost::indeterminate; - } - - template - Expr z3_lite_simplify (Z &z3, Expr e) - { - z3::context &ctx = z3.get_ctx (); - z3::ast ast (z3.toAst (e)); - - return z3.toExpr (z3::ast (ctx, Z3_simplify (ctx, ast))); - } - - template - Expr z3_simplify (Z &z3, Expr e) - { - return z3_lite_simplify (z3, e); - } - - - template - Expr z3_forall_elim (Z &z3, Expr e, const ExprSet &vars) - { - z3::context &ctx = z3.get_ctx (); - - z3::ast ast (z3.toAst (e)); - std::vector bound (vars.size ()); - z3::ast_vector pinned (ctx); - - size_t cnt = 0; - for (Expr var : vars) - { - z3::ast a (z3.toAst (var)); - pinned.push_back (a); - assert (a.kind () == Z3_APP_AST); - - bound [cnt++] = Z3_to_app (ctx, a); - } - - z3::ast qexpr = z3::ast (ctx, - Z3_mk_forall_const (ctx, 0, - bound.size (), &bound[0], - 0, NULL, ast)); - - z3::goal goal (ctx); - Z3_goal_assert (ctx, goal, qexpr); - - z3::tactic qe (ctx, "qe"); - ctx.check_error (); - - z3::apply_result ares = qe (goal); - ctx.check_error (); - - assert (ares.size () == 1); - goal = ares [0]; - - ExprVector res; - for (unsigned i = 0; i < goal.size (); ++i) - { - z3::ast gast (ctx, Z3_goal_formula (ctx, goal, i)); - res.push_back (z3.toExpr (gast)); - } - - return mknary (mk (e->efac ()), res); - } - - - template - Expr z3_from_smtlib (Z &z3, std::string smt) - { - z3::context &ctx = z3.get_ctx (); - - z3::ast ast (ctx, Z3_parse_smtlib2_string (ctx, smt.c_str (), - 0, NULL, NULL, 0, NULL, NULL)); - ctx.check_error (); - return z3.toExpr (ast); - } - - template - Expr z3_from_smtlib_file (Z &z3, const char *fname) - { - z3::context &ctx = z3.get_ctx (); - z3::ast ast (ctx, Z3_parse_smtlib2_file (ctx, fname, - 0, NULL, NULL, 0, NULL, NULL)); - ctx.check_error (); - return z3.toExpr (ast); - } - - template - std::string z3_to_smtlib (Z &z3, Expr e) - { return z3.toSmtLib (e); } - - template - Expr z3_qe_model_project_skolem (Z &z3, M &model, Expr v, Expr body, ExprMap &map) + using namespace expr; + + inline boost::tribool z3l_to_tribool (Z3_lbool l) + { + if (l == Z3_L_TRUE) return true; + else if (l == Z3_L_FALSE) return false; + return boost::indeterminate; + } + + template + Expr z3_lite_simplify (Z &z3, Expr e) + { + z3::context &ctx = z3.get_ctx (); + z3::ast ast (z3.toAst (e)); + + return z3.toExpr (z3::ast (ctx, Z3_simplify (ctx, ast))); + } + + template + Expr z3_simplify (Z &z3, Expr e) + { + return z3_lite_simplify (z3, e); + } + + + template + Expr z3_forall_elim (Z &z3, Expr e, const ExprSet &vars) + { + z3::context &ctx = z3.get_ctx (); + + z3::ast ast (z3.toAst (e)); + std::vector bound (vars.size ()); + z3::ast_vector pinned (ctx); + + size_t cnt = 0; + for (Expr var : vars) + { + z3::ast a (z3.toAst (var)); + pinned.push_back (a); + assert (a.kind () == Z3_APP_AST); + + bound [cnt++] = Z3_to_app (ctx, a); + } + + z3::ast qexpr = z3::ast (ctx, + Z3_mk_forall_const (ctx, 0, + bound.size (), &bound[0], + 0, NULL, ast)); + + z3::goal goal (ctx); + Z3_goal_assert (ctx, goal, qexpr); + + z3::tactic qe (ctx, "qe"); + ctx.check_error (); + + z3::apply_result ares = qe (goal); + ctx.check_error (); + + assert (ares.size () == 1); + goal = ares [0]; + + ExprVector res; + for (unsigned i = 0; i < goal.size (); ++i) + { + z3::ast gast (ctx, Z3_goal_formula (ctx, goal, i)); + res.push_back (z3.toExpr (gast)); + } + + return mknary (mk (e->efac ()), res); + } + + + template + Expr z3_from_smtlib (Z &z3, std::string smt) + { + z3::context &ctx = z3.get_ctx (); + + Z3_ast_vector b = Z3_parse_smtlib2_string (ctx, smt.c_str (), 0, NULL, NULL, 0, NULL, NULL); + Z3_ast* args = new Z3_ast[Z3_ast_vector_size(ctx, b)]; + + for (unsigned i = 0; i < Z3_ast_vector_size(ctx, b); ++i) { + args[i] = Z3_ast_vector_get(ctx, b, i); + } + + z3::ast ast (ctx, Z3_mk_and(ctx, Z3_ast_vector_size(ctx, b), args)); + ctx.check_error (); + return z3.toExpr (ast); + } + + template + Expr z3_from_smtlib_file (Z &z3, const char *fname) + { + z3::context &ctx = z3.get_ctx (); + + Z3_ast_vector b = Z3_parse_smtlib2_file (ctx, fname, 0, NULL, NULL, 0, NULL, NULL); + Z3_ast* args = new Z3_ast[Z3_ast_vector_size(ctx, b)]; + + for (unsigned i = 0; i < Z3_ast_vector_size(ctx, b); ++i) { + args[i] = Z3_ast_vector_get(ctx, b, i); + } + + z3::ast ast (ctx, Z3_mk_and(ctx, Z3_ast_vector_size(ctx, b), args)); + ctx.check_error (); + return z3.toExpr (ast); + } + + template + std::string z3_to_smtlib (Z &z3, Expr e) + { return z3.toSmtLib (e); } + + template + Expr z3_qe_model_project_skolem (Z &z3, M &model, Expr v, Expr body, ExprMap &map) { z3::context &ctx = z3.get_ctx (); z3::ast b (ctx, z3.toAst (body)); @@ -229,9 +217,9 @@ namespace ufo pinned.push_back (a); bound.push_back (Z3_to_app (ctx, a)); assert (a.kind () == Z3_APP_AST); - + z3::ast_map emap (ctx); - + z3::ast res (ctx, Z3_qe_model_project_skolem (ctx, model.get_model (), bound.size (), &bound [0], b, emap)); @@ -241,7 +229,7 @@ namespace ufo } return z3.toExpr (res); } - + } @@ -250,796 +238,814 @@ namespace ufo namespace ufo { - typedef std::unordered_map ast_expr_map; + typedef std::unordered_map ast_expr_map; - typedef std::unordered_map expr_ast_map; + typedef std::unordered_map expr_ast_map; - template class ZSolver; - template class ZModel; - template class ZFixedPoint; - template class ZParams; + template class ZSolver; + template class ZModel; + template class ZFixedPoint; + template class ZParams; - template - void z3n_set_param (char const *p, V v) { z3::set_param (p, v); } - inline void z3n_reset_params () { z3::reset_params (); } + template + void z3n_set_param (char const *p, V v) { z3::set_param (p, v); } + inline void z3n_reset_params () { z3::reset_params (); } - using namespace boost; + using namespace boost; - /** - * AST manager. Responsible for converting between Z3 ast and Expr. - * - * \tparam M marshaler that converts from Expr to z3::ast - * \tparam U unmarshaler that converts from z3::ast to Expr - */ - template - class ZContext : boost::noncopyable - { - private: - typedef ZContext this_type; - typedef ZModel this_model_type; - typedef bimap< bimaps::unordered_set_of, - bimaps::unordered_set_of > cache_type; + /** + * AST manager. Responsible for converting between Z3 ast and Expr. + * + * \tparam M marshaler that converts from Expr to z3::ast + * \tparam U unmarshaler that converts from z3::ast to Expr + */ + template + class ZContext : boost::noncopyable + { + private: + typedef ZContext this_type; + typedef ZModel this_model_type; + typedef bimap< bimaps::unordered_set_of, + bimaps::unordered_set_of > cache_type; - ExprFactory& efac; - z3::context ctx; + ExprFactory& efac; + z3::context ctx; - cache_type cache; + cache_type cache; - void init () - { - Z3_set_ast_print_mode (ctx, Z3_PRINT_SMTLIB2_COMPLIANT); - } + void init () + { + Z3_set_ast_print_mode (ctx, Z3_PRINT_SMTLIB2_COMPLIANT); + } - protected: - z3::context &get_ctx () { return ctx; } + public: + std::vector adts; + std::vector adts_seen; + + protected: + z3::context &get_ctx () { return ctx; } + std::vector accessors; + ast_expr_map seen_ast; + expr_ast_map seen_expr; + z3::ast toAst (Expr e) + { + return M::marshal (e, get_ctx (), cache.left, seen_expr, adts, adts_seen); + } + Expr toExpr (z3::ast a) + { + if (!a) return Expr(); - z3::ast toAst (Expr e) - { - expr_ast_map seen; - return M::marshal (e, get_ctx (), cache.left, seen); - } - Expr toExpr (z3::ast a) - { - if (!a) return Expr(); +// ast_expr_map seen; + auto res = U::unmarshal (a, get_efac (), cache.right, seen_ast, adts_seen, adts, accessors); + return res; + } - ast_expr_map seen; - return U::unmarshal (a, get_efac (), cache.right, seen); - } + ExprFactory &get_efac () { return efac; } - ExprFactory &get_efac () { return efac; } + typedef std::unordered_set Z3_func_decl_set; - typedef std::unordered_set Z3_func_decl_set; + void allDecls (Z3_ast a, Z3_func_decl_set &seen) + { + if (Z3_get_ast_kind (ctx, a) != Z3_APP_AST) return; - void allDecls (Z3_ast a, Z3_func_decl_set &seen) - { - if (Z3_get_ast_kind (ctx, a) != Z3_APP_AST) return; + Z3_app app = Z3_to_app (ctx, a); + Z3_func_decl fdecl = Z3_get_app_decl (ctx, app); + if (seen.count (fdecl) > 0) return; - Z3_app app = Z3_to_app (ctx, a); - Z3_func_decl fdecl = Z3_get_app_decl (ctx, app); - if (seen.count (fdecl) > 0) return; + if (Z3_get_decl_kind (ctx, fdecl) == Z3_OP_UNINTERPRETED && + Z3_get_domain_size (ctx, fdecl) == 0) + seen.insert (fdecl); - if (Z3_get_decl_kind (ctx, fdecl) == Z3_OP_UNINTERPRETED) - seen.insert (fdecl); + for (unsigned i = 0; i < Z3_get_app_num_args (ctx, app); i++) + { + allDecls (Z3_get_app_arg (ctx, app, i), seen); + } + } - for (unsigned i = 0; i < Z3_get_app_num_args (ctx, app); i++) - allDecls (Z3_get_app_arg (ctx, app, i), seen); - } + public: - public: + ZContext (ExprFactory &ef) : efac(ef) { init (); } + ZContext (ExprFactory &ef, z3::config &c) : efac (ef), ctx(c) { init (); } - ZContext (ExprFactory &ef) : efac(ef) { init (); } - ZContext (ExprFactory &ef, z3::config &c) : efac (ef), ctx(c) { init (); } + ~ZContext () { cache.clear (); } - ~ZContext () { cache.clear (); } + template + void set (char const *p, V v) { ctx.set (p, v); } - template - void set (char const *p, V v) { ctx.set (p, v); } + std::string toSmtLib (Expr e) + { return boost::lexical_cast (this->toAst (e)); } - std::string toSmtLib (Expr e) - { return boost::lexical_cast (this->toAst (e)); } + std::string toSmtLibDecls (Expr e) + { + std::ostringstream out; + Z3_func_decl_set seen; + z3::ast a (toAst (e)); + allDecls (static_cast(a), seen); + for (Z3_func_decl fdecl : seen) + out << Z3_func_decl_to_string (ctx, fdecl) << "\n"; + return out.str (); + } + + ExprVector& getAdtConstructors(){ return adts; } + ExprVector& getAdtAccessors(){ return accessors; } + + template + std::string toSmtLibDecls (const Range &rng) + { return toSmtLibDecls (mknary (mk (efac), rng)); } + + + ExprFactory &getExprFactory () { return get_efac (); } + + friend class ZParams; + friend class ZSolver; + friend class ZModel; + friend class ZFixedPoint; + + friend Expr z3_qe_model_project_skolem + (this_type &z3, this_model_type &model, Expr v, Expr body, ExprMap &map); + friend Expr z3_lite_simplify (this_type &z3, Expr e); + friend Expr z3_simplify (this_type &z3, Expr e); + friend Expr z3_forall_elim (this_type &z3, Expr e, + const ExprSet &vars); + friend Expr z3_from_smtlib (this_type &z3, std::string smt); + friend Expr z3_from_smtlib_file (this_type &z3, + const char *fname); + + friend std::string z3_to_smtlib (this_type &z3, Expr e); + }; - std::string toSmtLibDecls (Expr e) + + template + class ZModel : public std::unary_function { - std::ostringstream out; - Z3_func_decl_set seen; - z3::ast a (toAst (e)); - allDecls (static_cast(a), seen); - for (Z3_func_decl fdecl : seen) - out << Z3_func_decl_to_string (ctx, fdecl) << "\n"; - return out.str (); - } + private: + typedef ZModel this_type; - template - std::string toSmtLibDecls (const Range &rng) - { return toSmtLibDecls (mknary (mk (efac), rng)); } + Z& z3; + z3::context &ctx; + Z3_model model; + ExprFactory &efac; - ExprFactory &getExprFactory () { return get_efac (); } + bool isAsArray (const z3::ast &v) + { + if (v.kind () != Z3_APP_AST) return false; - friend class ZParams; - friend class ZSolver; - friend class ZModel; - friend class ZFixedPoint; - - friend Expr z3_qe_model_project_skolem - (this_type &z3, this_model_type &model, Expr v, Expr body, ExprMap &map); - friend Expr z3_lite_simplify (this_type &z3, Expr e); - friend Expr z3_simplify (this_type &z3, Expr e); - friend Expr z3_forall_elim (this_type &z3, Expr e, - const ExprSet &vars); - friend Expr z3_from_smtlib (this_type &z3, std::string smt); - friend Expr z3_from_smtlib_file (this_type &z3, - const char *fname); + Z3_app app = Z3_to_app (ctx, v); + Z3_func_decl fdecl = Z3_get_app_decl (ctx, app); + return Z3_get_decl_kind (ctx, fdecl) == Z3_OP_AS_ARRAY; + } - friend std::string z3_to_smtlib (this_type &z3, Expr e); - }; + Expr finterpToExpr (const z3::func_interp &zfunc) + { + ExprVector entries; + for (unsigned i = 0, sz = zfunc.num_entries (); i < sz; ++i) + entries.push_back (fentryToExpr (zfunc.entry (i))); + z3::ast elseV (ctx, Z3_func_interp_get_else (ctx, zfunc)); + Expr res = mdl::ftable (entries, z3.toExpr (elseV)); + return res; + } - template - class ZModel : public std::unary_function - { - private: - typedef ZModel this_type; + Expr fentryToExpr (const z3::func_entry &zentry) + { + ExprVector args; + for (unsigned i = 0, sz = zentry.num_args(); i < sz; ++i) + { + z3::ast arg (ctx, Z3_func_entry_get_arg (ctx, zentry, i)); + args.push_back (z3.toExpr (arg)); + } + z3::ast zval (ctx, Z3_func_entry_get_value (ctx, zentry)); + Expr res = mdl::fentry (args, z3.toExpr (zval)); + return res; + } - Z& z3; - z3::context &ctx; - Z3_model model; - ExprFactory &efac; - bool isAsArray (const z3::ast &v) - { - if (v.kind () != Z3_APP_AST) return false; - - Z3_app app = Z3_to_app (ctx, v); - Z3_func_decl fdecl = Z3_get_app_decl (ctx, app); - return Z3_get_decl_kind (ctx, fdecl) == Z3_OP_AS_ARRAY; - } + public: - Expr finterpToExpr (const z3::func_interp &zfunc) - { - ExprVector entries; - for (unsigned i = 0, sz = zfunc.num_entries (); i < sz; ++i) - entries.push_back (fentryToExpr (zfunc.entry (i))); + ZModel (Z &z) : + z3(z), ctx (z.get_ctx ()), model(nullptr), efac(z.get_efac ()) {} - z3::ast elseV (ctx, Z3_func_interp_get_else (ctx, zfunc)); - Expr res = mdl::ftable (entries, z3.toExpr (elseV)); - return res; - } - - Expr fentryToExpr (const z3::func_entry &zentry) - { - ExprVector args; - for (unsigned i = 0, sz = zentry.num_args(); i < sz; ++i) - { - z3::ast arg (ctx, Z3_func_entry_get_arg (ctx, zentry, i)); - args.push_back (z3.toExpr (arg)); - } - z3::ast zval (ctx, Z3_func_entry_get_value (ctx, zentry)); - Expr res = mdl::fentry (args, z3.toExpr (zval)); - return res; - } - - + ZModel (Z &z, const z3::model &m) : + z3(z), ctx(z.get_ctx ()), model (m), efac (z.get_efac ()) + {Z3_model_inc_ref (ctx, model);} - public: + ZModel (const this_type &o) : z3(o.z3), ctx(z3.get_ctx ()), + model (o.model), efac (z3.get_efac ()) + {if (model) Z3_model_inc_ref (ctx, model);} - ZModel (Z &z) : - z3(z), ctx (z.get_ctx ()), model(nullptr), efac(z.get_efac ()) {} - - ZModel (Z &z, const z3::model &m) : - z3(z), ctx(z.get_ctx ()), model (m), efac (z.get_efac ()) - {Z3_model_inc_ref (ctx, model);} + ~ZModel () + { + if (model) Z3_model_dec_ref (ctx, model); + model = nullptr; + } - ZModel (const this_type &o) : z3(o.z3), ctx(z3.get_ctx ()), - model (o.model), efac (z3.get_efac ()) - {if (model) Z3_model_inc_ref (ctx, model);} + this_type &operator= (this_type other) + {swap (*this, other); return *this;} - ~ZModel () - { - if (model) Z3_model_dec_ref (ctx, model); - model = nullptr; - } - - this_type &operator= (this_type other) - {swap (*this, other); return *this;} - - Z3_model &get_model () { return model; } + Z3_model &get_model () { return model; } - friend void swap (this_type &src, this_type &dst) - { - // -- only allow swap between models from the same context - assert (&src.z3 == &dst.z3); - swap (src.model, dst.model); - } - - - Expr eval (Expr e, bool completion = false) - { - assert (model); - z3::ast ast (z3.toAst (e)); + friend void swap (this_type &src, this_type &dst) + { + // -- only allow swap between models from the same context + assert (&src.z3 == &dst.z3); + swap (src.model, dst.model); + } - Z3_ast raw_val = NULL; - if (Z3_model_eval (ctx, model, ast, completion, &raw_val) && raw_val) - { - z3::ast val (ctx, raw_val); - ctx.check_error (); - if (!isAsArray (val)) return z3.toExpr (val); - - - Z3_func_decl fdecl = Z3_get_as_array_func_decl (ctx, val); - z3::func_interp zfunc (ctx, Z3_model_get_func_interp (ctx, model, fdecl)); - ctx.check_error (); - return finterpToExpr (zfunc); - } - ctx.check_error (); - return mk (efac); - } - ExprFactory &getExprFactory () { return z3.getExprFactory (); } - Expr operator() (Expr e) { return eval (e); } + Expr eval (Expr e, bool completion = false) + { + assert (model); + z3::ast ast (z3.toAst (e)); - template - friend OutputStream &operator<< (OutputStream &out, this_type &model) - { - out << Z3_model_to_string (model.ctx, model.model); - return out; - } - - }; - - template - class ZParams - { - private: - typedef ZParams this_type; - - Z& z3; - z3::context &ctx; - z3::params params; - - public: - ZParams (Z &z) : z3(z), ctx(z.get_ctx ()), params (z.get_ctx ()) {} - void set (std::string k, bool b) { params.set (k.c_str (), b); } - void set (std::string k, unsigned n) { params.set (k.c_str (), n); } - void set (std::string k, double n) { params.set (k.c_str (), n); } - void set (std::string k, std::string v) - { params.set (k.c_str (), ctx.str_symbol (v.c_str ())); } - - void set (char const * k, bool b) { params.set (k, b); } - void set (char const * k, unsigned n) { params.set (k, n); } - void set (char const * k, double n) { params.set (k, n); } - void set (char const * k, char const *v ) - { params.set (k, ctx.str_symbol (v)); } - - operator z3::params () const { return params; } - operator Z3_params () const { return static_cast (params); } - - }; - - - template - class ZSolver - { - private: - Z& z3; - z3::context &ctx; - z3::solver solver; - ExprFactory &efac; - - public: - typedef ZSolver this_type; - typedef ZModel Model; - - ZSolver (Z &z) : - z3(z), ctx (z.get_ctx ()), solver (z.get_ctx ()), efac (z.get_efac ()) {} - - ZSolver (Z &z, const char *logic) : - z3(z), ctx (z.get_ctx ()), solver (z.get_ctx (), logic), efac (z.get_efac ()) {} - - Z& getContext () {return z3;} - void set (const ZParams &p) { solver.set (p); } - - template - OutputStream &toSmtLib (OutputStream &out) - { - ExprVector v; - return toSmtLibAssuming (out, v); - } + Z3_ast raw_val = NULL; + if (Z3_model_eval (ctx, model, ast, completion, &raw_val) && raw_val) + { + z3::ast val (ctx, raw_val); + ctx.check_error (); + if (!isAsArray (val)) return z3.toExpr (val); - template - OutputStream &toSmtLibAssuming (OutputStream &out, - const Range &rng) - { - ExprVector asserts; - assertions (std::back_inserter (asserts)); - out << z3.toSmtLibDecls (asserts); - out << "\n"; - for (const Expr &a : asserts) - out << "(assert " << z3.toSmtLib (a) << ")\n"; - - out << "(check-sat"; - for (const Expr &a : rng) out << " " << *a; - out << ")\n"; - return out; - } + Z3_func_decl fdecl = Z3_get_as_array_func_decl (ctx, val); + z3::func_interp zfunc (ctx, Z3_model_get_func_interp (ctx, model, fdecl)); + ctx.check_error (); + return finterpToExpr (zfunc); + } + ctx.check_error (); + return mk (efac); + } + ExprFactory &getExprFactory () { return z3.getExprFactory (); } + Expr operator() (Expr e) { return eval (e); } - /// Asserts (forall vars body). Work-around until quantifiers are - /// properly supported by Expr - template - void assertForallExpr (const Range &vars, Expr body) - { - z3::ast ast (z3.toAst (body)); - std::vector bound; - bound.reserve (boost::size (vars)); - for (const Expr &v : vars) - bound.push_back (Z3_to_app (ctx, z3.toAst (v))); - - Z3_ast forall = Z3_mk_forall_const (ctx, 0, - bound.size (), &bound[0], - 0, NULL, ast); - Z3_solver_assert (ctx, solver, forall); - ctx.check_error (); - } + template + friend OutputStream &operator<< (OutputStream &out, this_type &model) + { + out << Z3_model_to_string (model.ctx, model.model); + return out; + } - void assertExpr (Expr e) - { - z3::ast ast (z3.toAst (e)); - Z3_solver_assert (ctx, solver, ast); - ctx.check_error (); - } + }; - /// return assertions currently in the solver - template - void assertions (OutputIterator out) const + template + class ZParams { - z3::ast_vector r (ctx, Z3_solver_get_assertions (ctx, solver)); - ctx.check_error (); - for (unsigned i = 0; i < r.size (); ++i) - *(out++) = z3.toExpr (r [i]); - } + private: + typedef ZParams this_type; + Z& z3; + z3::context &ctx; + z3::params params; - boost::tribool solve () - { - boost::tribool res = z3l_to_tribool (Z3_solver_check (ctx, solver)); - ctx.check_error (); - return res; - } + public: + ZParams (Z &z) : z3(z), ctx(z.get_ctx ()), params (z.get_ctx ()) {} + void set (std::string k, bool b) { params.set (k.c_str (), b); } + void set (std::string k, unsigned n) { params.set (k.c_str (), n); } + void set (std::string k, double n) { params.set (k.c_str (), n); } + void set (std::string k, std::string v) + { params.set (k.c_str (), ctx.str_symbol (v.c_str ())); } + + void set (char const * k, bool b) { params.set (k, b); } + void set (char const * k, unsigned n) { params.set (k, n); } + void set (char const * k, double n) { params.set (k, n); } + void set (char const * k, char const *v ) + { params.set (k, ctx.str_symbol (v)); } + + operator z3::params () const { return params; } + operator Z3_params () const { return static_cast (params); } + + }; - template - boost::tribool solveAssuming (const Range &lits) - { - z3::ast_vector av (ctx); - for (Expr a : lits) av.push_back (z3.toAst (a)); - - std::vector raw_av (av.size ()); - for (unsigned i = 0; i < av.size (); ++i) - raw_av [i] = Z3_ast_vector_get (ctx, av, i); - - boost::tribool res = - z3l_to_tribool (Z3_solver_check_assumptions (ctx, solver, - raw_av.size (), - &raw_av[0])); - ctx.check_error (); - return res; - } - template - void unsatCore (OutputIterator out) const + template + class ZSolver { - z3::ast_vector core (ctx, Z3_solver_get_unsat_core (ctx, solver)); - ctx.check_error (); + private: + Z& z3; + z3::context &ctx; + z3::solver solver; + ExprFactory &efac; - for (unsigned i = 0; i < core.size (); ++i) - *(out++) = z3.toExpr (core [i]); - } + public: + typedef ZSolver this_type; + typedef ZModel Model; - /** - * Combines solveAssuming(lits) and unsatCore (out) - */ - template - boost::tribool solveAssuming (const Range &lits, OutputIterator out) - { - boost::tribool res = solveAssuming (lits); - if (!res) unsatCore (out); - return res; - } + ZSolver (Z &z) : + z3(z), ctx (z.get_ctx ()), solver (z.get_ctx ()), efac (z.get_efac ()) {} + ZSolver (Z &z, const char *logic) : + z3(z), ctx (z.get_ctx ()), solver (z.get_ctx (), logic), efac (z.get_efac ()) {} - Model getModel () const - { - z3::model m (ctx, Z3_solver_get_model (ctx, solver)); - return ZModel (z3, m); - } + ZSolver (Z &z, unsigned to) : + z3(z), ctx (z.get_ctx ()), solver (z.get_ctx ()), efac (z.get_efac ()) { + ZParams p(z); + p.set("timeout", to); + solver.set(p); + } - void push () { solver.push (); } - void pop (unsigned n = 1) { solver.pop (n); } - void reset () { solver.reset (); } - }; + Z& getContext () {return z3;} + void set (const ZParams &p) { solver.set (p); } + template + OutputStream &toSmtLib (OutputStream &out) + { + ExprVector v; + return toSmtLibAssuming (out, v); + } - template - class ZFixedPoint - { - private: - typedef ZFixedPoint this_type; + template + OutputStream &toSmtLibAssuming (OutputStream &out, + const Range &rng) + { + ExprVector asserts; + assertions (std::back_inserter (asserts)); + out << z3.toSmtLibDecls (asserts); + out << "\n"; + for (const Expr &a : asserts) + out << "(assert " << z3.toSmtLib (a) << ")\n"; + + out << "(check-sat"; + for (const Expr &a : rng) out << " " << *a; + out << ")\n"; + return out; + } - Z& z3; - z3::context &ctx; - z3::fixedpoint fp; - ExprFactory &efac; - public: - - ExprVector m_rels; - ExprVector m_vars; - ExprVector m_rules; - ExprVector m_queries; - - ZFixedPoint (Z &z) : - z3(z), ctx(z.get_ctx ()), fp (z.get_ctx ()), efac(z.get_efac ()) {} + /// Asserts (forall vars body). Work-around until quantifiers are + /// properly supported by Expr + template + void assertForallExpr (const Range &vars, Expr body) + { + z3::ast ast (z3.toAst (body)); + std::vector bound; + bound.reserve (boost::size (vars)); + for (const Expr &v : vars) + bound.push_back (Z3_to_app (ctx, z3.toAst (v))); + + Z3_ast forall = Z3_mk_forall_const (ctx, 0, + bound.size (), &bound[0], + 0, NULL, ast); + Z3_solver_assert (ctx, solver, forall); + ctx.check_error (); + } - Z& getContext () {return z3;} + void assertExpr (Expr e) + { + z3::ast ast (z3.toAst (e)); + Z3_solver_assert (ctx, solver, ast); + ctx.check_error (); + } - void set (const ZParams &p) { fp.set (p); } + /// return assertions currently in the solver + template + void assertions (OutputIterator out) const + { + z3::ast_vector r (ctx, Z3_solver_get_assertions (ctx, solver)); + ctx.check_error (); + for (unsigned i = 0; i < r.size (); ++i) + *(out++) = z3.toExpr (r [i]); + } - void registerRelation (Expr fdecl) - { - m_rels.push_back (fdecl); - Z3_fixedpoint_register_relation (ctx, fp, - Z3_to_func_decl (ctx, z3.toAst (fdecl))); - } - template - void addRule (const Range &vars, Expr rule) - { - if (isOpX (rule)) return; - - boost::copy (vars, std::back_inserter (m_vars)); - m_rules.push_back (rule); + boost::tribool solve () + { + boost::tribool res = z3l_to_tribool (Z3_solver_check (ctx, solver)); + ctx.check_error (); + return res; + } - z3::ast ast (z3.toAst (rule)); + template + boost::tribool solveAssuming (const Range &lits) + { + z3::ast_vector av (ctx); + for (Expr a : lits) av.push_back (z3.toAst (a)); + + std::vector raw_av (av.size ()); + for (unsigned i = 0; i < av.size (); ++i) + raw_av [i] = Z3_ast_vector_get (ctx, av, i); + + boost::tribool res = + z3l_to_tribool (Z3_solver_check_assumptions (ctx, solver, + raw_av.size (), + &raw_av[0])); + ctx.check_error (); + return res; + } - z3::ast qexpr (ast); + template + void unsatCore (OutputIterator out) const + { + z3::ast_vector core (ctx, Z3_solver_get_unsat_core (ctx, solver)); + ctx.check_error (); - // -- universally quantify all free variables - if (boost::distance (vars) > 0) - { - z3::ast_vector pinned(ctx); - pinned.resize (boost::distance(vars)); - std::vector bound (boost::distance(vars)); + for (unsigned i = 0; i < core.size (); ++i) + *(out++) = z3.toExpr (core [i]); + } - size_t cnt = 0; - for (Expr v : vars) + /** + * Combines solveAssuming(lits) and unsatCore (out) + */ + template + boost::tribool solveAssuming (const Range &lits, OutputIterator out) { - z3::ast zv (z3.toAst (v)); - pinned.push_back (zv); - bound [cnt++] = Z3_to_app (ctx, zv); + boost::tribool res = solveAssuming (lits); + if (!res) unsatCore (out); + return res; } + Model getModel () const + { + z3::model m (ctx, Z3_solver_get_model (ctx, solver)); + return ZModel (z3, m); + } - qexpr = z3::ast (ctx, Z3_mk_forall_const (ctx, 0, bound.size (), - &bound[0], - 0, NULL, ast)); - } + ZSolver::Model* getModelPtr () const + { + z3::model m (ctx, Z3_solver_get_model (ctx, solver)); + return new ZModel (z3, m); + } - Z3_fixedpoint_add_rule (ctx, fp, qexpr, static_cast(0)); - } + void push () { solver.push (); } + void pop (unsigned n = 1) { solver.pop (n); } + void reset () { solver.reset (); } + }; - void addQuery (Expr q) {m_queries.push_back (q);} - void addQueries (ExprVector qs) + template + class ZFixedPoint { - std::copy (qs.begin (), qs.end (), - std::back_inserter (m_queries)); - } + private: - boost::tribool query (Expr q = Expr()) - { - if (q) m_queries.push_back (q); + typedef ZFixedPoint this_type; - std::vector qs; - for (Expr e : m_queries) - qs.push_back (z3.toAst (e)); + Z& z3; + z3::context &ctx; + z3::fixedpoint fp; + ExprFactory &efac; - z3::ast ast = z3::ast (ctx, Z3_mk_and (ctx, qs.size (), &qs [0])); + public: - // -- existentially quantify all variables - if (!m_vars.empty ()) - { - // getVars() removes duplicates - const ExprVector &vars = getVars (); - z3::ast_vector pinned(ctx); - pinned.resize (vars.size ()); - std::vector bound (vars.size ()); + ExprVector m_rels; + ExprVector m_vars; + ExprVector m_rules; + ExprVector m_queries; - unsigned cnt = 0; - for (Expr v : vars) + ZFixedPoint (Z &z) : + z3(z), ctx(z.get_ctx ()), fp (z.get_ctx ()), efac(z.get_efac ()) {} + + Z& getContext () {return z3;} + + void set (const ZParams &p) { fp.set (p); } + + void registerRelation (Expr fdecl) { - z3::ast zv (z3.toAst (v)); - pinned.push_back (zv); - bound [cnt++] = Z3_to_app (ctx, zv); + m_rels.push_back (fdecl); + Z3_fixedpoint_register_relation (ctx, fp, + Z3_to_func_decl (ctx, z3.toAst (fdecl))); } - ast = z3::ast (ctx, Z3_mk_exists_const (ctx, 0, bound.size (), - &bound [0], 0, NULL, ast)); - } - - tribool res = z3l_to_tribool (Z3_fixedpoint_query (ctx, fp, ast)); - ctx.check_error (); - return res; - } - std::string toString (Expr query = Expr()) - { - if (query) m_queries.push_back (query); + template + void addRule (const Range &vars, Expr rule) + { + if (isOpX (rule)) return; - std::vector qs; - for (Expr e : m_queries) - qs.push_back (z3.toAst (e)); + boost::copy (vars, std::back_inserter (m_vars)); + m_rules.push_back (rule); - z3::ast ast = z3::ast (ctx, Z3_mk_and (ctx, qs.size (), &qs [0])); + z3::ast ast (z3.toAst (rule)); - // -- existentially quantify all variables - if (!m_vars.empty ()) - { - // getVars() removes duplicates - const ExprVector &vars = getVars (); - z3::ast_vector pinned(ctx); - pinned.resize (vars.size ()); - std::vector bound (vars.size ()); + z3::ast qexpr (ast); + + // -- universally quantify all free variables + if (boost::distance (vars) > 0) + { + z3::ast_vector pinned(ctx); + pinned.resize (boost::distance(vars)); + std::vector bound (boost::distance(vars)); + + size_t cnt = 0; + for (Expr v : vars) + { + z3::ast zv (z3.toAst (v)); + pinned.push_back (zv); + bound [cnt++] = Z3_to_app (ctx, zv); + } + + + qexpr = z3::ast (ctx, Z3_mk_forall_const (ctx, 0, bound.size (), + &bound[0], + 0, NULL, ast)); + } + + Z3_fixedpoint_add_rule (ctx, fp, qexpr, static_cast(0)); + } + + void addQuery (Expr q) {m_queries.push_back (q);} - unsigned cnt = 0; - for (Expr v : vars) + void addQueries (ExprVector qs) { - z3::ast zv (z3.toAst (v)); - pinned.push_back (zv); - bound [cnt++] = Z3_to_app (ctx, zv); + std::copy (qs.begin (), qs.end (), + std::back_inserter (m_queries)); } - ast = z3::ast (ctx, Z3_mk_exists_const (ctx, 0, bound.size (), - &bound [0], 0, NULL, ast)); - } - - - Z3_ast qptr = static_cast (ast); - Z3_string str = Z3_fixedpoint_to_string (ctx, fp, 1, &qptr); - return std::string (str); - } - const ExprVector &getVars () - { - boost::sort (m_vars); - m_vars.resize (std::distance (m_vars.begin (), - std::unique (m_vars.begin (), - m_vars.end ()))); - return m_vars; - } + boost::tribool query (Expr q = Expr()) + { + if (q) m_queries.push_back (q); + + std::vector qs; + for (Expr e : m_queries) + qs.push_back (z3.toAst (e)); + + z3::ast ast = z3::ast (ctx, Z3_mk_and (ctx, qs.size (), &qs [0])); + + // -- existentially quantify all variables + if (!m_vars.empty ()) + { + // getVars() removes duplicates + const ExprVector &vars = getVars (); + z3::ast_vector pinned(ctx); + pinned.resize (vars.size ()); + std::vector bound (vars.size ()); + + unsigned cnt = 0; + for (Expr v : vars) + { + z3::ast zv (z3.toAst (v)); + pinned.push_back (zv); + bound [cnt++] = Z3_to_app (ctx, zv); + } + ast = z3::ast (ctx, Z3_mk_exists_const (ctx, 0, bound.size (), + &bound [0], 0, NULL, ast)); + } + tribool res = z3l_to_tribool (Z3_fixedpoint_query (ctx, fp, ast)); + ctx.check_error (); + return res; + } - template - friend OutputStream &operator<< (OutputStream &out, this_type &fp) - { - for (Expr decl : fp.m_rels) - { - out << "(declare-rel " << *bind::fname (decl) << " ("; - for (unsigned i = 0; i < bind::domainSz (decl); i++) + std::string toString (Expr query = Expr()) { - Expr ty = bind::domainTy (decl, i); - if (isOpX (ty)) out << "Bool "; - else if (isOpX (ty)) out << "Real "; - else if (isOpX (ty)) out << "Int "; - else if (isOpX (ty)) - { - out << "(Array "; - if (isOpX (sort::arrayIndexTy (ty))) - out << "Int "; - else out << "UfoUnknownSort "; - if (isOpX (sort::arrayValTy (ty))) - out << "Int"; - else out << "UfoUnknownSort"; - out << ") "; - } - - else out << "UfoUnknownSort "; + if (query) m_queries.push_back (query); + + std::vector qs; + for (Expr e : m_queries) + qs.push_back (z3.toAst (e)); + + z3::ast ast = z3::ast (ctx, Z3_mk_and (ctx, qs.size (), &qs [0])); + + // -- existentially quantify all variables + if (!m_vars.empty ()) + { + // getVars() removes duplicates + const ExprVector &vars = getVars (); + z3::ast_vector pinned(ctx); + pinned.resize (vars.size ()); + std::vector bound (vars.size ()); + + unsigned cnt = 0; + for (Expr v : vars) + { + z3::ast zv (z3.toAst (v)); + pinned.push_back (zv); + bound [cnt++] = Z3_to_app (ctx, zv); + } + ast = z3::ast (ctx, Z3_mk_exists_const (ctx, 0, bound.size (), + &bound [0], 0, NULL, ast)); + } + + + Z3_ast qptr = static_cast (ast); + Z3_string str = Z3_fixedpoint_to_string (ctx, fp, 1, &qptr); + return std::string (str); } - out << "))\n"; - } - - - for (const Expr &v : fp.getVars ()) - { - assert (bind::IsConst() (v)); - out << "(declare-var " << fp.z3.toSmtLib (v) << " "; - Expr ty = bind::typeOf (v); - if (isOpX (ty)) out << "Bool "; - else if (isOpX (ty)) out << "Real "; - else if (isOpX (ty)) out << "Int "; - else if (isOpX (ty)) + + const ExprVector &getVars () { - out << "(Array "; - if (isOpX (sort::arrayIndexTy (ty))) - out << "Int "; - else out << "UfoUnknownSort "; - if (isOpX (sort::arrayValTy (ty))) - out << "Int"; - else out << "UfoUnknownSort"; - out << ") "; + boost::sort (m_vars); + m_vars.resize (std::distance (m_vars.begin (), + std::unique (m_vars.begin (), + m_vars.end ()))); + return m_vars; } - else out << "UfoUnknownSort "; - out << ")\n"; - } - for (Expr &rule : fp.m_rules) - out << "(rule " << fp.z3.toSmtLib (rule) << ")\n"; - for (auto q: fp.m_queries) - out << "(query " << fp.z3.toSmtLib (q) << ")\n"; - return out; - } + template + friend OutputStream &operator<< (OutputStream &out, this_type &fp) + { + for (Expr decl : fp.m_rels) + { + out << "(declare-rel " << *bind::fname (decl) << " ("; + for (unsigned i = 0; i < bind::domainSz (decl); i++) + { + Expr ty = bind::domainTy (decl, i); + if (isOpX (ty)) out << "Bool "; + else if (isOpX (ty)) out << "Real "; + else if (isOpX (ty)) out << "Int "; + else if (isOpX (ty)) + { + out << "(Array "; + if (isOpX (sort::arrayIndexTy (ty))) + out << "Int "; + else out << "UfoUnknownSort "; + if (isOpX (sort::arrayValTy (ty))) + out << "Int"; + else out << "UfoUnknownSort"; + out << ") "; + } - /** - * Given a function application P(x, y, z) returns the set of - * current lemmas of P in terms of variables x, y, z - */ - Expr getCoverDelta (Expr pred, int lvl = -1) - { - assert (bind::isFapp (pred)); - z3::ast zpred (ctx, z3.toAst (pred)); - Z3_app app = Z3_to_app (ctx, zpred); + else out << "UfoUnknownSort "; + } + out << "))\n"; + } - unsigned arity = Z3_get_app_num_args (ctx, app); - std::vector to (arity); - for (unsigned i = 0; i < arity; ++i) - to [i] = Z3_get_app_arg (ctx, app, i); + for (const Expr &v : fp.getVars ()) + { + assert (bind::IsConst() (v)); + out << "(declare-var " << fp.z3.toSmtLib (v) << " "; + Expr ty = bind::typeOf (v); + if (isOpX (ty)) out << "Bool "; + else if (isOpX (ty)) out << "Real "; + else if (isOpX (ty)) out << "Int "; + else if (isOpX (ty)) + { + out << "(Array "; + if (isOpX (sort::arrayIndexTy (ty))) + out << "Int "; + else out << "UfoUnknownSort "; + if (isOpX (sort::arrayValTy (ty))) + out << "Int"; + else out << "UfoUnknownSort"; + out << ") "; + } + else out << "UfoUnknownSort "; + out << ")\n"; + } - Z3_func_decl zdecl = Z3_get_app_decl (ctx, app); + for (Expr &rule : fp.m_rules) + out << "(rule " << fp.z3.toSmtLib (rule) << ")\n"; - z3::ast lemma (ctx, Z3_fixedpoint_get_cover_delta (ctx, fp, lvl, zdecl)); + for (auto q: fp.m_queries) + out << "(query " << fp.z3.toSmtLib (q) << ")\n"; + return out; + } - z3::ast res (ctx, lemma); - if (Z3_get_bool_value (ctx, res) == Z3_L_UNDEF) - { - assert (arity > 0); - res = z3::ast (ctx, Z3_substitute_vars (ctx, lemma, arity, &to[0])); - } + /** + * Given a function application P(x, y, z) returns the set of + * current lemmas of P in terms of variables x, y, z + */ + Expr getCoverDelta (Expr pred, int lvl = -1) + { + assert (bind::isFapp (pred)); + z3::ast zpred (ctx, z3.toAst (pred)); + Z3_app app = Z3_to_app (ctx, zpred); - return z3.toExpr (res); - } - /** - * Given a function application P(x, y, z), adds a given lemma to - * the given level of P. The lemma must be in terms of x, y, z - */ - void addCover (Expr pred, Expr lemma, int lvl = -1) - { - if (isOpX (lemma)) return; - - assert (bind::isFapp (pred)); - z3::ast zpred (ctx, z3.toAst (pred)); - Z3_app app = Z3_to_app (ctx, zpred); - - if (isOpX (lemma)) - { - Z3_fixedpoint_add_cover (ctx, fp, lvl, Z3_get_app_decl (ctx, app), - Z3_mk_false (ctx)); - ctx.check_error (); - return; - } + unsigned arity = Z3_get_app_num_args (ctx, app); + std::vector to (arity); + for (unsigned i = 0; i < arity; ++i) + to [i] = Z3_get_app_arg (ctx, app, i); - unsigned arg_size = Z3_get_app_num_args (ctx, app); - std::vector from (arg_size); - std::vector to (arg_size); + Z3_func_decl zdecl = Z3_get_app_decl (ctx, app); - // -- saves content of 'to' from garbage collection - z3::ast_vector pinned (ctx); + z3::ast lemma (ctx, Z3_fixedpoint_get_cover_delta (ctx, fp, lvl, zdecl)); - for (unsigned i = 0; i < Z3_get_app_num_args (ctx, app); ++i) - { - Z3_ast arg = Z3_get_app_arg (ctx, app, i); - assert (Z3_is_app (ctx, arg)); + z3::ast res (ctx, lemma); + if (Z3_get_bool_value (ctx, res) == Z3_L_UNDEF) + { + assert (arity > 0); + res = z3::ast (ctx, Z3_substitute_vars (ctx, lemma, arity, &to[0])); + } - Z3_app arg_app = Z3_to_app (ctx, arg); - Z3_func_decl arg_decl = Z3_get_app_decl (ctx, arg_app); - assert (Z3_get_domain_size (ctx, arg_decl) == 0); + return z3.toExpr (res); + } - from [i] = arg; - to [i] = Z3_mk_bound (ctx, i, Z3_get_range (ctx, arg_decl)); - pinned.push_back (z3::ast (ctx, to [i])); - } + /** + * Given a function application P(x, y, z), adds a given lemma to + * the given level of P. The lemma must be in terms of x, y, z + */ + void addCover (Expr pred, Expr lemma, int lvl = -1) + { + if (isOpX (lemma)) return; + + assert (bind::isFapp (pred)); + z3::ast zpred (ctx, z3.toAst (pred)); + Z3_app app = Z3_to_app (ctx, zpred); + + if (isOpX (lemma)) + { + Z3_fixedpoint_add_cover (ctx, fp, lvl, Z3_get_app_decl (ctx, app), + Z3_mk_false (ctx)); + ctx.check_error (); + return; + } - assert (from.size () > 0); - z3::ast zlemma (ctx, Z3_substitute (ctx, z3.toAst (lemma), - from.size (), &from [0], &to [0])); + unsigned arg_size = Z3_get_app_num_args (ctx, app); + std::vector from (arg_size); + std::vector to (arg_size); - Z3_fixedpoint_add_cover (ctx, fp, lvl, - Z3_get_app_decl (ctx, app), zlemma); - ctx.check_error (); - } + // -- saves content of 'to' from garbage collection + z3::ast_vector pinned (ctx); + for (unsigned i = 0; i < Z3_get_app_num_args (ctx, app); ++i) + { + Z3_ast arg = Z3_get_app_arg (ctx, app, i); + assert (Z3_is_app (ctx, arg)); - unsigned getNumLevels (Expr pred) - { - z3::func_decl pdecl (ctx, Z3_to_func_decl (ctx, z3.toAst (pred))); - return Z3_fixedpoint_get_num_levels (ctx, fp, pdecl); - } + Z3_app arg_app = Z3_to_app (ctx, arg); + Z3_func_decl arg_decl = Z3_get_app_decl (ctx, arg_app); + assert (Z3_get_domain_size (ctx, arg_decl) == 0); - std::string getAnswer () - { - z3::ast res (ctx, Z3_fixedpoint_get_answer (ctx, fp)); - //return z3.toExpr (res); - return std::string (Z3_ast_to_string (ctx, res)); - } + from [i] = arg; + to [i] = Z3_mk_bound (ctx, i, Z3_get_range (ctx, arg_decl)); + pinned.push_back (z3::ast (ctx, to [i])); + } - /** - ** Return a bottom-up (from query) formula of ground predicates - ** that together from a ground derivation to query - **/ - Expr getGroundSatAnswer () - { - z3::ast res (ctx, Z3_fixedpoint_get_ground_sat_answer (ctx, fp)); - return z3.toExpr (res); - } + assert (from.size () > 0); + z3::ast zlemma (ctx, Z3_substitute (ctx, z3.toAst (lemma), + from.size (), &from [0], &to [0])); - Expr getCex () - { - z3::ast res (ctx, Z3_fixedpoint_get_answer (ctx, fp)); - return z3.toExpr (res); - } + Z3_fixedpoint_add_cover (ctx, fp, lvl, + Z3_get_app_decl (ctx, app), zlemma); + ctx.check_error (); + } - void getCexRules (ExprVector &res) - { - z3::ast_vector rules (ctx, - Z3_fixedpoint_get_rules_along_trace (ctx, fp)); - for (unsigned i = 0; i < rules.size (); ++i) - { - z3::ast rule (rules [i]); - // XXX strip quantifiers because we do not support them in Expr - if (rule.kind () == Z3_QUANTIFIER_AST) - rule = z3::ast (ctx, Z3_get_quantifier_body (ctx, rule)); - res.push_back (z3.toExpr (rule)); - } - } - - void loadFPfromFile(std::string smt){ - z3::ast_vector queries (ctx, Z3_fixedpoint_from_file(ctx, fp, smt.c_str ())); - ctx.check_error (); - - z3::ast_vector rules (ctx, Z3_fixedpoint_get_rules(ctx, fp)); - - ExprSet relations; - for (unsigned i = 0; i < rules.size (); ++i){ - Expr rule = z3.toExpr (rules [i]); - m_rules.push_back(rule); - - Expr head = rule->arg(rule->arity() - 1)->arg(1); - if (isOpX(head)){ - if (head->arity () > 0){ - if (isOpX(head->arg(0))){ - relations.insert(head->arg(0)); - } - } + + unsigned getNumLevels (Expr pred) + { + z3::func_decl pdecl (ctx, Z3_to_func_decl (ctx, z3.toAst (pred))); + return Z3_fixedpoint_get_num_levels (ctx, fp, pdecl); + } + + std::string getAnswer () + { + z3::ast res (ctx, Z3_fixedpoint_get_answer (ctx, fp)); + //return z3.toExpr (res); + return std::string (Z3_ast_to_string (ctx, res)); + } + + /** + ** Return a bottom-up (from query) formula of ground predicates + ** that together from a ground derivation to query + **/ + Expr getGroundSatAnswer () + { + z3::ast res (ctx, Z3_fixedpoint_get_ground_sat_answer (ctx, fp)); + return z3.toExpr (res); + } + + Expr getCex () + { + z3::ast res (ctx, Z3_fixedpoint_get_answer (ctx, fp)); + return z3.toExpr (res); + } + + void getCexRules (ExprVector &res) + { + z3::ast_vector rules (ctx, + Z3_fixedpoint_get_rules_along_trace (ctx, fp)); + for (unsigned i = 0; i < rules.size (); ++i) + { + z3::ast rule (rules [i]); + // XXX strip quantifiers because we do not support them in Expr + if (rule.kind () == Z3_QUANTIFIER_AST) + rule = z3::ast (ctx, Z3_get_quantifier_body (ctx, rule)); + res.push_back (z3.toExpr (rule)); } } - - for (unsigned i = 0; i < queries.size (); ++i){ - m_queries.push_back(z3.toExpr (queries [i])); + + void loadFPfromFile(std::string smt){ + z3::ast_vector queries (ctx, Z3_fixedpoint_from_file(ctx, fp, smt.c_str ())); + ctx.check_error (); + + z3::ast_vector rules (ctx, Z3_fixedpoint_get_rules(ctx, fp)); + z3::ast_vector asss (ctx, Z3_fixedpoint_get_assertions(ctx, fp)); + + for (unsigned i = 0; i < rules.size (); ++i){ + Expr rule = z3.toExpr (rules [i]); + m_rules.push_back(rule); + } + + for (unsigned i = 0; i < asss.size (); ++i){ + Expr rule = z3.toExpr (asss [i]); + m_rules.push_back(rule); + } + + for (unsigned i = 0; i < queries.size (); ++i){ + m_queries.push_back(z3.toExpr (queries [i])); + } + + //TODO: vars } - - for (auto &r: relations) m_rels.push_back (r); - - //TODO: vars - } - }; + }; @@ -1047,63 +1053,63 @@ namespace ufo namespace ufo { - template - boost::tribool z3_is_sat (Z &z3, Expr e) - { - ZSolver s(z3); - s.assertExpr (e); - return s.solve (); - } - - template - boost::tribool z3_is_sat_assuming (Z &z3, Expr e, - const Range &assumptions, - OutputIterator out) - { - ZSolver s(z3); - - s.assertExpr (e); - boost::tribool res = s.solveAssuming (assumptions); - if (!res) s.unsatCore (out); - return res; - } - - template - boost::tribool z3_is_sat_assuming (Z &z3, Expr e, const Range &assumptions, - ExprSet &result) - { - return - z3_is_sat_assuming (z3, e, assumptions, - std::inserter (result, result.begin ())); - } - - template - Expr z3_all_sat (Z &z3, Expr e, const Range &terms) - { - // -- z3 must be configured to produce models for this to work - - ZSolver s (z3); - s.assertExpr (e); - - Expr res (mk (e->efac ())); - - while (s.solve ()) - { - ZModel m = s.getModel (); - - Expr cube = mk (e->efac ()); - for (Expr t : terms) - { - Expr v = m.eval (t); - if (isOpX (v)) cube = boolop::land (cube, t); - else if (isOpX (v)) - cube = boolop::land (cube, boolop::lneg (t)); - } - res = boolop::lor (res, cube); - s.assertExpr (boolop::lneg (cube)); - } - return res; - } + template + boost::tribool z3_is_sat (Z &z3, Expr e) + { + ZSolver s(z3); + s.assertExpr (e); + return s.solve (); + } + + template + boost::tribool z3_is_sat_assuming (Z &z3, Expr e, + const Range &assumptions, + OutputIterator out) + { + ZSolver s(z3); + + s.assertExpr (e); + boost::tribool res = s.solveAssuming (assumptions); + if (!res) s.unsatCore (out); + return res; + } + + template + boost::tribool z3_is_sat_assuming (Z &z3, Expr e, const Range &assumptions, + ExprSet &result) + { + return + z3_is_sat_assuming (z3, e, assumptions, + std::inserter (result, result.begin ())); + } + + template + Expr z3_all_sat (Z &z3, Expr e, const Range &terms) + { + // -- z3 must be configured to produce models for this to work + + ZSolver s (z3); + s.assertExpr (e); + + Expr res (mk (e->efac ())); + + while (s.solve ()) + { + ZModel m = s.getModel (); + + Expr cube = mk (e->efac ()); + for (Expr t : terms) + { + Expr v = m.eval (t); + if (isOpX (v)) cube = boolop::land (cube, t); + else if (isOpX (v)) + cube = boolop::land (cube, boolop::lneg (t)); + } + res = boolop::lor (res, cube); + s.assertExpr (boolop::lneg (cube)); + } + return res; + } } diff --git a/include/ufo/Smt/ZExprConverter.hpp b/include/ufo/Smt/ZExprConverter.hpp index 5c3b04877..eee0daf73 100644 --- a/include/ufo/Smt/ZExprConverter.hpp +++ b/include/ufo/Smt/ZExprConverter.hpp @@ -6,7 +6,6 @@ // -- used for CL options #include "Z3n.hpp" -#include "llvm/Support/raw_ostream.h" #include "ufo/ExprLlvm.hpp" namespace ufo @@ -18,7 +17,7 @@ namespace ufo static z3::ast marshal (Expr e, z3::context &ctx, C &cache, expr_ast_map &seen) { - llvm::errs () << "Cannot marshal: " << *e << "\n"; + errs () << "Cannot marshal: " << *e << "\n"; assert (0); exit (1); } }; @@ -29,19 +28,21 @@ namespace ufo static Expr unmarshal (const z3::ast &a, ExprFactory &efac, C &cache, ast_expr_map &seen) { - llvm::errs () << "Cannot unmarshal: " << lexical_cast (a) << "\n"; + errs () << "Cannot unmarshal: " << lexical_cast (a) << "\n"; assert (0); exit (1); } }; + ExprVector subexpr; + template struct BasicExprMarshal { template static z3::ast marshal (Expr e, z3::context &ctx, - C &cache, expr_ast_map &seen) + C &cache, expr_ast_map &seen, std::vector adts, std::vector &adts_seen) { assert (e); if (isOpX(e)) return z3::ast (ctx, Z3_mk_true (ctx)); @@ -64,7 +65,7 @@ namespace ufo if (bind::isBVar (e)) { - z3::ast sort (marshal (bind::type (e), ctx, cache, seen)); + z3::ast sort (marshal (bind::type (e), ctx, cache, seen, adts, adts_seen)); res = Z3_mk_bound (ctx, bind::bvarId (e), reinterpret_cast (static_cast (sort))); @@ -72,13 +73,17 @@ namespace ufo else if (isOpX (e)) res = reinterpret_cast (Z3_mk_int_sort (ctx)); else if (isOpX (e)) - res = reinterpret_cast (Z3_mk_real_sort (ctx)); + res = reinterpret_cast (Z3_mk_real_sort(ctx)); else if (isOpX (e)) res = reinterpret_cast (Z3_mk_bool_sort (ctx)); + else if (isOpX (e)) { + res = reinterpret_cast (Z3_mk_int_sort (ctx)); +// res = reinterpret_cast (Z3_mk_datatype_sort(ctx, Z3_mk_string_symbol(ctx, lexical_cast(e->left ()).c_str()))); + }// GF: hack for now else if (isOpX (e)) { - z3::ast _idx_sort (marshal (e->left (), ctx, cache, seen)); - z3::ast _val_sort (marshal (e->right (), ctx, cache, seen)); + z3::ast _idx_sort (marshal (e->left (), ctx, cache, seen, adts, adts_seen)); + z3::ast _val_sort (marshal (e->right (), ctx, cache, seen, adts, adts_seen)); Z3_sort idx_sort = reinterpret_cast (static_cast (_idx_sort)); Z3_sort val_sort = reinterpret_cast @@ -173,7 +178,7 @@ namespace ufo for (size_t i = 0; i < bind::domainSz (e); ++i) { - z3::ast a (marshal (bind::domainTy (e, i), ctx, cache, seen)); + z3::ast a (marshal (bind::domainTy (e, i), ctx, cache, seen, adts, adts_seen)); pinned.push_back (a); domain [i] = reinterpret_cast (static_cast(a)); } @@ -182,7 +187,7 @@ namespace ufo z3::sort range (ctx, reinterpret_cast (static_cast - (marshal (bind::rangeTy (e), ctx, cache, seen)))); + (marshal (bind::rangeTy (e), ctx, cache, seen, adts, adts_seen)))); Expr fname = bind::fname (e); @@ -208,9 +213,7 @@ namespace ufo z3::func_decl zfdecl (ctx, reinterpret_cast (static_cast - (marshal (bind::fname (e), ctx, cache, seen)))); - - + (marshal (bind::fname (e), ctx, cache, seen, adts, adts_seen)))); // -- marshall all arguments except for the first one // -- (which is the fdecl) std::vector args (e->arity ()); @@ -221,7 +224,7 @@ namespace ufo for (ENode::args_iterator it = ++ (e->args_begin ()), end = e->args_end (); it != end; ++it) { - z3::ast a (marshal (*it, ctx, cache, seen)); + z3::ast a (marshal (*it, ctx, cache, seen, adts, adts_seen)); pinned_args.push_back (a); args [pos++] = a; } @@ -232,28 +235,24 @@ namespace ufo /** quantifier */ else if (isOpX (e) || isOpX (e)) { - unsigned num_bound = bind::numBound (e); - z3::ast_vector pinned (ctx); - pinned.resize (num_bound); - std::vector bound_sorts; - bound_sorts.reserve (num_bound); - std::vector bound_names; - bound_names.reserve (num_bound); - - for (unsigned i = 0; i < num_bound; ++i) - { - z3::ast z (marshal (bind::decl (e, i), ctx, cache, seen)); - pinned.push_back (z); - - Z3_func_decl decl = Z3_to_func_decl (ctx, z); - bound_sorts.push_back (Z3_get_range (ctx, decl)); - bound_names.push_back (Z3_get_decl_name (ctx, decl)); - } - - - z3::ast body (marshal (bind::body (e), ctx, cache, seen)); - res = Z3_mk_quantifier (ctx, isOpX (e), 0, 0, NULL, - num_bound, &bound_sorts[0], &bound_names[0], body); + ExprVector vars; + for (int i = 0; i < e->arity() - 1; i++) + vars.push_back(bind::fapp(e->arg(i))); + + z3::ast ast (marshal (e->last(), ctx, cache, seen, adts, adts_seen)); //z3.toAst (e->last())); + std::vector bound; + bound.reserve (boost::size (vars)); + for (const Expr &v : vars) + bound.push_back (Z3_to_app (ctx, marshal (v, ctx, cache, seen, adts, adts_seen))); + + if (isOpX (e)) + res = Z3_mk_forall_const (ctx, 0, + bound.size (), &bound[0], + 0, NULL, ast); + else + res = Z3_mk_exists_const (ctx, 0, + bound.size (), &bound[0], + 0, NULL, ast); } // -- cache the result for unmarshaling @@ -273,47 +272,59 @@ namespace ufo // -- then it's a NEG or UN_MINUS if (isOpX(e)) { - z3::ast arg = marshal (e->left(), ctx, cache, seen); + z3::ast arg = marshal (e->left(), ctx, cache, seen, adts, adts_seen); return z3::ast (ctx, Z3_mk_unary_minus(ctx, arg)); } if (isOpX(e)) { - z3::ast arg = marshal (e->left(), ctx, cache, seen); + z3::ast arg = marshal (e->left(), ctx, cache, seen, adts, adts_seen); return z3::ast (ctx, Z3_mk_not(ctx, arg)); } if (isOpX (e)) { - z3::ast arg = marshal (e->left(), ctx, cache, seen); + z3::ast arg = marshal (e->left(), ctx, cache, seen, adts, adts_seen); return z3::ast (ctx, Z3_mk_array_default (ctx, arg)); } if (isOpX(e)) { - z3::ast arg = marshal (e->left(), ctx, cache, seen); + z3::ast arg = marshal (e->left(), ctx, cache, seen, adts, adts_seen); return z3::ast (ctx, Z3_mk_bvnot(ctx, arg)); } if (isOpX(e)) { - z3::ast arg = marshal (e->left(), ctx, cache, seen); + z3::ast arg = marshal (e->left(), ctx, cache, seen, adts, adts_seen); return z3::ast (ctx, Z3_mk_bvneg(ctx, arg)); } if (isOpX(e)) { - z3::ast arg = marshal (e->left(), ctx, cache, seen); + z3::ast arg = marshal (e->left(), ctx, cache, seen, adts, adts_seen); return z3::ast (ctx, Z3_mk_bvredand(ctx, arg)); } if (isOpX(e)) { - z3::ast arg = marshal (e->left(), ctx, cache, seen); + z3::ast arg = marshal (e->left(), ctx, cache, seen, adts, adts_seen); return z3::ast (ctx, Z3_mk_bvredor(ctx, arg)); } + if (isOpX(e)) + { + z3::ast arg = marshal (e->left(), ctx, cache, seen, adts, adts_seen); + //TODO: SECOND NUMBER IS THE AMOUNT OF BITS IN THE BV ENCODING + return z3::ast (ctx, Z3_mk_int2bv(ctx, 64, arg)); + } + if (isOpX(e)) + { + z3::ast arg = marshal (e->left(), ctx, cache, seen, adts, adts_seen); + //TODO: BOOL DESCRIBES IF NUMBER IS UNSIGNED OR NOT + return z3::ast (ctx, Z3_mk_bv2int(ctx, arg, true)); + } return M::marshal (e, ctx, cache, seen); } else if (arity == 2) { - z3::ast t1 = marshal(e->left(), ctx, cache, seen); - z3::ast t2 = marshal(e->right(), ctx, cache, seen); + z3::ast t1 = marshal(e->left(), ctx, cache, seen, adts, adts_seen); + z3::ast t2 = marshal(e->right(), ctx, cache, seen, adts, adts_seen); Z3_ast args [2] = {t1, t2}; @@ -447,7 +458,7 @@ namespace ufo else if (isOpX (e)) { assert (bv::high (e) > bv::low (e)); - z3::ast a (ctx, marshal (bv::earg (e), ctx, cache, seen)); + z3::ast a (ctx, marshal (bv::earg (e), ctx, cache, seen, adts, adts_seen)); res = Z3_mk_extract (ctx, bv::high (e), bv::low (e), a); } else if (isOpX (e) || isOpX (e) || @@ -462,7 +473,7 @@ namespace ufo for (ENode::args_iterator it = e->args_begin(), end = e->args_end(); it != end; ++it) { - z3::ast a = z3::ast (ctx, marshal (*it, ctx, cache, seen)); + z3::ast a = z3::ast (ctx, marshal (*it, ctx, cache, seen, adts, adts_seen)); args.push_back (a); pinned.push_back (a); } @@ -514,8 +525,8 @@ namespace ufo { template static Expr unmarshal (const z3::ast &z, - ExprFactory &efac, C &cache, - ast_expr_map &seen) + ExprFactory &efac, C &cache, ast_expr_map &seen, + std::vector &adts_seen, std::vector &adts, std::vector &accessors) { z3::context &ctx = z.ctx (); @@ -565,22 +576,56 @@ namespace ufo unmarshal (z3::ast (ctx, Z3_sort_to_ast (ctx, Z3_get_array_sort_domain (ctx, sort))), - efac, cache, seen); + efac, cache, seen, adts_seen, adts, accessors); range = unmarshal (z3::ast (ctx, Z3_sort_to_ast (ctx, Z3_get_array_sort_range (ctx, sort))), - efac, cache, seen); + efac, cache, seen, adts_seen, adts, accessors); return sort::arrayTy (domain, range); + case Z3_DATATYPE_SORT: + { + unsigned num = Z3_get_datatype_sort_num_constructors(ctx, sort); + while (num > 0) { + num--; + auto c = Z3_get_datatype_sort_constructor(ctx, sort, num); + unsigned num_accessors = Z3_get_domain_size(ctx, c); + + while(num_accessors > 0){ + num_accessors--; + auto as = Z3_get_datatype_sort_constructor_accessor(ctx, sort, num, num_accessors); + } + } + std::string name = Z3_get_symbol_string(ctx, Z3_get_sort_name(ctx, sort)); + Expr adt_name = mkTerm (name, efac); + if (find(adts_seen.begin(), adts_seen.end(), name) == adts_seen.end()) + { + adts_seen.push_back(name); + for (int i = 0; i < Z3_get_datatype_sort_num_constructors(ctx, sort); i++) + { + Z3_func_decl decl = Z3_get_datatype_sort_constructor(ctx, sort, i); + Z3_ast zdecl = Z3_func_decl_to_ast(ctx, decl); + adts.push_back(unmarshal(z3::ast(ctx, zdecl), efac, cache, seen, adts_seen, adts, accessors)); + } + + } + return sort::adTy (adt_name); + } default: - assert (0 && "Unsupported sort"); + std::string name = Z3_get_symbol_string(ctx, Z3_get_sort_name(ctx, sort)); + Expr adt_name = mkTerm (name, efac); + if (find(adts_seen.begin(), adts_seen.end(), name) == adts_seen.end()) + { + adts_seen.push_back(name); + } + return sort::adTy (adt_name); } } else if (kind == Z3_VAR_AST) { unsigned idx = Z3_get_index_value (ctx, z); z3::ast zsort (ctx, Z3_sort_to_ast (ctx, Z3_get_sort (ctx, z))); - Expr sort = unmarshal (zsort, efac, cache, seen); + Expr sort = unmarshal (zsort, efac, cache, seen, adts_seen, adts, accessors); return bind::bvar (idx, sort); } @@ -610,16 +655,14 @@ namespace ufo for (unsigned p = 0; p < Z3_get_domain_size (ctx, fdecl); ++p) { Z3_sort sort = Z3_get_domain (ctx, fdecl, p); - type.push_back - (unmarshal (z3::ast (ctx, Z3_sort_to_ast (ctx, sort)), - efac, cache, seen)); + type.push_back(unmarshal (z3::ast (ctx, Z3_sort_to_ast (ctx, sort)), + efac, cache, seen, adts_seen, adts, accessors)); } - type.push_back - (unmarshal (z3::ast (ctx, - Z3_sort_to_ast (ctx, - Z3_get_range (ctx, fdecl))), - efac, cache, seen)); + type.push_back(unmarshal (z3::ast (ctx, + Z3_sort_to_ast (ctx, + Z3_get_range (ctx, fdecl))), + efac, cache, seen, adts_seen, adts, accessors)); return bind::fdecl (name, type); } @@ -635,11 +678,11 @@ namespace ufo 0, nullptr, Z3_get_quantifier_bound_sort (ctx, z, i)); z3::ast zdecl (ctx, Z3_func_decl_to_ast (ctx, decl)); - args.push_back (unmarshal (zdecl, efac, cache, seen)); + args.push_back (unmarshal (zdecl, efac, cache, seen, adts_seen, adts, accessors)); assert (args.back ().get ()); } args.push_back (unmarshal (z3::ast (ctx, Z3_get_quantifier_body (ctx, z)), - efac, cache, seen)); + efac, cache, seen, adts_seen, adts, accessors)); return Z3_is_quantifier_forall (ctx, z) ? mknary (args) : mknary (args); } @@ -656,38 +699,36 @@ namespace ufo if (dkind == Z3_OP_NOT) { assert (Z3_get_app_num_args (ctx, app) == 1); - return mk (unmarshal - (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), - efac, cache, seen)); + return mk (unmarshal (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), + efac, cache, seen, adts_seen, adts, accessors)); } if (dkind == Z3_OP_UMINUS) - return mk (unmarshal - (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), - efac, cache, seen)); + return mk (unmarshal (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), + efac, cache, seen, adts_seen, adts, accessors)); // XXX ignore to_real and to_int operators if (dkind == Z3_OP_TO_REAL || dkind == Z3_OP_TO_INT) - return unmarshal (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), - efac, cache, seen); + return unmarshal(z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), + efac, cache, seen, adts_seen, adts, accessors); if (dkind == Z3_OP_BNOT) return mk (unmarshal (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), - efac, cache, seen)); + efac, cache, seen, adts_seen, adts, accessors)); if (dkind == Z3_OP_BNEG) return mk (unmarshal (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), - efac, cache, seen)); + efac, cache, seen, adts_seen, adts, accessors)); if (dkind == Z3_OP_BREDAND) return mk (unmarshal (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), - efac, cache, seen)); + efac, cache, seen, adts_seen, adts, accessors)); if (dkind == Z3_OP_BREDOR) return mk (unmarshal (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), - efac, cache, seen)); + efac, cache, seen, adts_seen, adts, accessors)); if (dkind == Z3_OP_SIGN_EXT || dkind == Z3_OP_ZERO_EXT) { Expr sort = bv::bvsort (Z3_get_bv_sort_size (ctx, Z3_get_sort (ctx, z)), efac); Expr arg = unmarshal (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), - efac, cache, seen); + efac, cache, seen, adts_seen, adts, accessors); switch (dkind) { case Z3_OP_SIGN_EXT: @@ -701,7 +742,7 @@ namespace ufo if (dkind == Z3_OP_EXTRACT) { Expr arg = unmarshal (z3::ast (ctx, Z3_get_app_arg (ctx, app, 0)), - efac, cache, seen); + efac, cache, seen, adts_seen, adts, accessors); Z3_func_decl d = Z3_get_app_decl (ctx, app); unsigned high = Z3_get_decl_int_parameter (ctx, d, 0); @@ -715,7 +756,7 @@ namespace ufo z3::ast zdecl (ctx, Z3_func_decl_to_ast (ctx, Z3_get_as_array_func_decl (ctx, z))); - return mk (unmarshal (zdecl, efac, cache, seen)); + return mk (unmarshal (zdecl, efac, cache, seen, adts_seen, adts, accessors)); } { @@ -730,22 +771,83 @@ namespace ufo Expr e; ExprVector args; - for (size_t i = 0; i < (size_t)Z3_get_app_num_args (ctx, app); i++) - args.push_back (unmarshal - (z3::ast(ctx, Z3_get_app_arg(ctx, app, i)), efac, cache, seen)); + Expr left; + for (size_t i = 0; i < (size_t)Z3_get_app_num_args (ctx, app); i++){ + // TODO: Disequality, constructor inside constructor(maybe), IF then else + if(dkind == Z3_OP_EQ && i == 1 && Z3_get_decl_kind (ctx, Z3_get_app_decl (ctx, Z3_to_app(ctx, z3::ast(ctx, Z3_get_app_arg(ctx, app, i))))) == Z3_OP_DT_CONSTRUCTOR){ + left = args[0]; + } + args.push_back (unmarshal + (z3::ast(ctx, Z3_get_app_arg(ctx, app, i)), efac, cache, seen, adts_seen, adts, accessors)); + } /** newly introduced Z3 symbol */ - if (dkind == Z3_OP_UNINTERPRETED) - { - Expr res = bind::fapp (unmarshal (z3::func_decl (ctx, fdecl), - efac, cache, seen), args); - // -- XXX maybe use seen instead. not sure what is best. - cache.insert (typename C::value_type (z, res)); - return res; - } + if (dkind == Z3_OP_DT_CONSTRUCTOR ) + { + if (left != NULL) { + Z3_sort sort = Z3_get_sort (ctx, z); + unsigned num = Z3_get_datatype_sort_num_constructors(ctx, sort); + while (num > 0) { + num--; + auto c = Z3_get_datatype_sort_constructor(ctx, sort, num); + unsigned num_accessors = Z3_get_domain_size(ctx, c); + if(c != fdecl){ + continue; + } + + while(num_accessors > 0){ + num_accessors--; + auto as = Z3_get_datatype_sort_constructor_accessor(ctx, sort, num, num_accessors); + ExprVector eq; + // Put value from the constructor into equality + eq.push_back (bind::fapp (unmarshal (z3::func_decl (ctx, as), + efac, cache, seen, adts_seen, adts, accessors), { left })); + eq.push_back (args[num_accessors]); + + accessors.push_back(bind::fname((unmarshal (z3::func_decl (ctx, as), + efac, cache, seen, adts_seen, adts, accessors)))); + // accessor(data) = value + subexpr.push_back(mknary (eq.begin(), eq.end())); + } + } + } + + Expr res = bind::fapp (unmarshal (z3::func_decl (ctx, fdecl), + efac, cache, seen, adts_seen, adts, accessors), args); + cache.insert (typename C::value_type (z, res)); + left = NULL; + return res; + } + + if (dkind == Z3_OP_UNINTERPRETED) { + Expr res = bind::fapp (unmarshal (z3::func_decl (ctx, fdecl), + efac, cache, seen, adts_seen, adts, accessors), args); + // -- XXX maybe use seen instead. not sure what is best. + cache.insert (typename C::value_type (z, res)); + return res; + } + + if (dkind == Z3_OP_DT_ACCESSOR) { + Z3_sort srt = Z3_get_sort(ctx, z); + // TODO: CHANGE THIS HARDCODE OF ACCESSORS + Z3_func_decl acc = Z3_get_datatype_sort_constructor_accessor(ctx, srt, 1, 1); + Expr res = bind::fapp (unmarshal (z3::func_decl (ctx, fdecl), + efac, cache, seen, adts_seen, adts, accessors), args); + accessors.push_back(bind::fname(res)); +// -- XXX maybe use seen instead. not sure what is best. + cache.insert (typename C::value_type (z, res)); + return res; + } switch (dkind) { + + case Z3_OP_INT2BV: + e = mknary (args.begin (), args.end ()); + break; + case Z3_OP_BV2INT: + e = mknary (args.begin (), args.end ()); + break; case Z3_OP_ITE: e = mknary (args.begin (), args.end ()); break; @@ -764,9 +866,15 @@ namespace ufo case Z3_OP_IMPLIES: e = mknary (args.begin(), args.end()); break; - case Z3_OP_EQ: - e = mknary (args.begin(), args.end()); - break; + case Z3_OP_EQ: + e = mknary (args.begin(), args.end()); + if(subexpr.size() > 0){ + subexpr.push_back(e); + + e = mknary(subexpr.begin(), subexpr.end()); + subexpr.clear(); + } + break; case Z3_OP_LT: e = mknary (args.begin(), args.end()); break; @@ -800,6 +908,9 @@ namespace ufo case Z3_OP_REM: e = mknary (args.begin (), args.end ()); break; + case Z3_OP_DISTINCT: + e = mknary (args.begin(), args.end()); + break; case Z3_OP_CONST_ARRAY: { assert (args.size () == 1); @@ -807,7 +918,7 @@ namespace ufo Expr domain = unmarshal (z3::ast (ctx, Z3_sort_to_ast (ctx, Z3_get_array_sort_domain (ctx, sort))), - efac, cache, seen); + efac, cache, seen, adts_seen, adts, accessors); e = op::array::constArray (domain, args[0]); } diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 8c170a888..4d3e7f26b 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1 +1 @@ -add_subdirectory(aeval) +add_subdirectory(nonlin) \ No newline at end of file diff --git a/tools/aeval/Ae.cpp b/tools/aeval/Ae.cpp deleted file mode 100644 index a86c0f065..000000000 --- a/tools/aeval/Ae.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "ae/AeValSolver.hpp" -#include "ufo/Smt/EZ3.hh" - -using namespace ufo; - -/** An AE-VAL wrapper for cmd - * - * Usage: specify 2 smt2-files that describe the formula \foral x. S(x) => \exists y . T (x, y) - * = S-part (over x) - * = T-part (over x, y) - * - * Notably, the tool automatically recognizes x and y based on their appearances in S or T. - * - * Example: - * - * ./tools/aeval/aeval - * ../test/ae/example1_s_part.smt2 - * ../test/ae/example1_t_part.smt2 - * - */ - -int main (int argc, char ** argv) -{ - - ExprFactory efac; - EZ3 z3(efac); - - ExprVector params; - - if (argc != 3) - { - outs() << "Unable to parse arguments\n"; - return 0; - } - - aeSolveAndSkolemize(z3_from_smtlib_file (z3, argv [1]), - z3_from_smtlib_file (z3, argv [2])); - - return 0; -} diff --git a/tools/aeval/CMakeLists.txt b/tools/aeval/CMakeLists.txt deleted file mode 100644 index ad7f16edc..000000000 --- a/tools/aeval/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_executable (aeval Ae.cpp) -target_link_libraries (aeval ${Z3_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${GMPXX_LIB} ${GMP_LIB}) -llvm_config (aeval bitwriter) -install(TARGETS aeval RUNTIME DESTINATION bin) diff --git a/tools/nonlin/CMakeLists.txt b/tools/nonlin/CMakeLists.txt new file mode 100644 index 000000000..29d0a222a --- /dev/null +++ b/tools/nonlin/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable (tgnonlin NonlinSolver.cpp) +target_link_libraries (tgnonlin ${Z3_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${GMPXX_LIB} ${GMP_LIB}) +install(TARGETS tgnonlin RUNTIME DESTINATION bin) diff --git a/tools/nonlin/NonlinSolver.cpp b/tools/nonlin/NonlinSolver.cpp new file mode 100644 index 000000000..3ebe23859 --- /dev/null +++ b/tools/nonlin/NonlinSolver.cpp @@ -0,0 +1,186 @@ +#include "deep/NonlinCHCsolver.hpp" + +using namespace ufo; +using namespace std; + +bool getBoolValue(const char * opt, bool defValue, int argc, char ** argv) +{ + for (int i = 1; i < argc; i++) + { + if (strcmp(argv[i], opt) == 0) return true; + } + return defValue; +} + +char * getStrValue(const char * opt, char * defValue, int argc, char ** argv) +{ + for (int i = 1; i < argc-1; i++) + { + if (strcmp(argv[i], opt) == 0) + { + return argv[i+1]; + } + } + return defValue; +} + +int getIntValue(const char * opt, int defValue, int argc, char ** argv) +{ + for (int i = 1; i < argc-1; i++) + { + if (strcmp(argv[i], opt) == 0) + { + char* p; + int num = strtol(argv[i+1], &p, 10); + if (*p) return 1; // if used w/o arg, return boolean + else return num; + } + } + return defValue; +} +void getStrValues(const char * opt, vector & values, int argc, char ** argv) +{ + for (int i = 1; i < argc-1; i++) + { + if (strcmp(argv[i], opt) == 0) + { + values.push_back(string(argv[i+1])); + } + } +} + +static inline void splitLine(vector& input, string input_str, char d){ + if (input_str.empty()) return; + string s = input_str; + size_t pos = 0; + std::string token; + while ((pos = s.find(d)) != std::string::npos) { + token = s.substr(0, pos); + input.push_back(token); + s.erase(0, pos + 1); + } + input.push_back(s); +} + +bool isContract(string w1){ + string contract_string = "contract"; + return w1.find(contract_string) != std::string::npos; +} + +void print_signature(map>>& signature){ + for (auto const &pair: signature) { + outs() << "contract:" << pair.first << "\n"; + for(auto f: pair.second){ + outs() << "{" << f.first << ": "; + for(auto p: f.second){ + outs() << p << " "; + } + outs() << "}\n"; + } + } +} + + +static inline void getSignature(map>>& signature, string str) +{ + vector tmp_split; + splitLine(tmp_split, str, '^'); + map> current; + string current_contract_name; + + for (auto i: tmp_split){ + if (isContract(i)){ + if (!current.empty()){ + signature.insert({current_contract_name, current}); + } + map> new_current; + current = new_current; + vector tmp; + splitLine(tmp, i, ':'); + vector tmp_f; + splitLine(tmp_f, tmp[1], ','); + vector tmp_name; + splitLine(tmp_name, tmp[0], '_'); + current.insert({tmp[0], tmp_f}); + current_contract_name = tmp_name[1]; + }else{ + vector tmp; + splitLine(tmp, i, ':'); + vector tmp_f; + splitLine(tmp_f, tmp[1], ','); + current.insert({tmp[0], tmp_f}); + } + } + + if (!current.empty()){ + signature.insert({current_contract_name, current}); + } + + //print_signature(signature); +} + + +const char *OPT_HELP = "--help"; +const char *OPT_MAX_ATTEMPTS = "--attempts"; +const char *OPT_TO = "--to"; +const char *OPT_LB = "--lb"; +const char *OPT_LMAX = "--max"; +const char *OPT_ELIM = "--skip-elim"; +const char *OPT_ARITHM = "--skip-arithm"; +const char *OPT_SEED = "--inv-mode"; +const char *OPT_GET_FREQS = "--freqs"; +const char *OPT_ADD_EPSILON = "--eps"; +const char *OPT_AGG_PRUNING = "--aggp"; +const char *OPT_DATA_LEARNING = "--data"; +const char *OPT_PROP = "--prop"; +const char *OPT_DISJ = "--disj"; +const char *OPT_D1 = "--all-mbp"; +const char *OPT_D2 = "--phase-prop"; +const char *OPT_D3 = "--phase-data"; +const char *OPT_D4 = "--stren-mbp"; +const char *OPT_MBP = "--eqs-mbp"; +const char *OPT_DEBUG = "--debug"; + +int main (int argc, char ** argv) +{ + map>> signature; + getSignature(signature, getStrValue("--keys", NULL, argc, argv)); + bool to_skip = getBoolValue("--no-term", false, argc, argv); + int lookahead = getIntValue("--lookahead", 3, argc, argv); + bool prio = getBoolValue("--prio", false, argc, argv); + bool lb = getBoolValue(OPT_LB, false, argc, argv); + bool lmax = getBoolValue(OPT_LMAX, false, argc, argv); + + // All other attrs are inherited from FreqHorn: + int max_attempts = getIntValue(OPT_MAX_ATTEMPTS, 10, argc, argv); + int to = getIntValue(OPT_TO, 1000, argc, argv); + bool densecode = getBoolValue(OPT_GET_FREQS, false, argc, argv); + bool aggressivepruning = getBoolValue(OPT_AGG_PRUNING, false, argc, argv); + bool do_elim = !getBoolValue(OPT_ELIM, false, argc, argv); + bool do_arithm = !getBoolValue(OPT_ARITHM, false, argc, argv); + int invMode = getIntValue(OPT_SEED, 0, argc, argv); + int do_prop = getIntValue(OPT_PROP, 0, argc, argv); + int do_disj = getBoolValue(OPT_DISJ, false, argc, argv); + bool do_dl = getBoolValue(OPT_DATA_LEARNING, false, argc, argv); + int mbp_eqs = getIntValue(OPT_MBP, 0, argc, argv); + bool d_m = getBoolValue(OPT_D1, false, argc, argv); + bool d_p = getBoolValue(OPT_D2, false, argc, argv); + bool d_d = getBoolValue(OPT_D3, false, argc, argv); + bool d_s = getBoolValue(OPT_D4, false, argc, argv); + int debug = getIntValue(OPT_DEBUG, 0, argc, argv); + + if (do_disj && (!d_p && !d_d)) + { + errs() << "WARNING: either \"" << OPT_D2 << "\" or \"" << OPT_D3 << "\" should be enabled\n" + << "enabling \"" << OPT_D3 << "\"\n"; + d_d = true; + } + + if (d_m || d_p || d_d || d_s) do_disj = true; + if (do_disj) do_dl = true; + + testgen(argv[argc-1], signature, max_attempts, to, densecode, aggressivepruning, + do_dl, do_elim, do_disj, do_prop, d_m, d_p, d_d, d_s, + to_skip, invMode, lookahead, lb, lmax, prio, debug); + return 0; +}