mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-03 21:09:11 +00:00 
			
		
		
		
	merge
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
						commit
						733f44d141
					
				
					 151 changed files with 3249 additions and 1504 deletions
				
			
		
							
								
								
									
										35
									
								
								.github/workflows/android-build.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								.github/workflows/android-build.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
name: Android Build
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  BUILD_TYPE: Release
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        android-abi: [arm64-v8a, armeabi-v7a, x86, x86_64]
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout code
 | 
			
		||||
      uses: actions/checkout@v2
 | 
			
		||||
 | 
			
		||||
    - name: Configure CMake and build
 | 
			
		||||
      run:  |
 | 
			
		||||
        mkdir build
 | 
			
		||||
        cd build
 | 
			
		||||
        cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=21 -DCMAKE_ANDROID_ARCH_ABI=${{ matrix.android-abi }} -DCMAKE_ANDROID_NDK=$ANDROID_NDK_HOME -DZ3_BUILD_JAVA_BINDINGS=TRUE -G "Unix Makefiles" -DJAVA_AWT_LIBRARY=NotNeeded -DJAVA_JVM_LIBRARY=NotNeeded -DJAVA_INCLUDE_PATH2=NotNeeded -DJAVA_AWT_INCLUDE_PATH=NotNeeded ../
 | 
			
		||||
        make -j $(nproc)
 | 
			
		||||
        tar -cvf z3-build-${{ matrix.android-abi }}.tar *.jar *.so
 | 
			
		||||
        
 | 
			
		||||
    - name: Archive production artifacts
 | 
			
		||||
      uses: actions/upload-artifact@v2
 | 
			
		||||
      with:
 | 
			
		||||
        name: android-build-${{ matrix.android-abi }}
 | 
			
		||||
        path: build/z3-build-${{ matrix.android-abi }}.tar
 | 
			
		||||
							
								
								
									
										42
									
								
								.github/workflows/wasm.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								.github/workflows/wasm.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
name: WASM Build
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  BUILD_TYPE: Release
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout code
 | 
			
		||||
      uses: actions/checkout@v2
 | 
			
		||||
 | 
			
		||||
    - name: Import emscripten
 | 
			
		||||
      uses: mymindstorm/setup-emsdk@v9
 | 
			
		||||
      
 | 
			
		||||
    - name: Configure CMake and build
 | 
			
		||||
      run:  |
 | 
			
		||||
        mkdir build
 | 
			
		||||
        cd build
 | 
			
		||||
 | 
			
		||||
        emcmake cmake \
 | 
			
		||||
          -DCMAKE_BUILD_TYPE=MinSizeRel \
 | 
			
		||||
          -DZ3_BUILD_LIBZ3_SHARED=OFF \
 | 
			
		||||
          -DZ3_ENABLE_EXAMPLE_TARGETS=OFF \
 | 
			
		||||
          -DZ3_BUILD_TEST_EXECUTABLES=OFF \
 | 
			
		||||
          -DZ3_BUILD_EXECUTABLE=OFF \
 | 
			
		||||
          -DZ3_SINGLE_THREADED=ON \
 | 
			
		||||
          -DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" \
 | 
			
		||||
          ..; 
 | 
			
		||||
        make
 | 
			
		||||
        tar -cvf z3-build-wasm.tar *.a
 | 
			
		||||
        
 | 
			
		||||
    - name: Archive production artifacts
 | 
			
		||||
      uses: actions/upload-artifact@v2
 | 
			
		||||
      with:
 | 
			
		||||
        name: z3-build-wasm
 | 
			
		||||
        path: build/z3-build-wasm.tar
 | 
			
		||||
| 
						 | 
				
			
			@ -14,9 +14,9 @@ See the [release notes](RELEASE_NOTES) for notes on various stable releases of Z
 | 
			
		|||
 | 
			
		||||
## Build status
 | 
			
		||||
 | 
			
		||||
| Azure Pipelines | Code Coverage | Open Bugs |
 | 
			
		||||
| --------------- | --------------|-----------|
 | 
			
		||||
| [](https://dev.azure.com/Z3Public/Z3/_build/latest?definitionId=1&branchName=master) | [](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml) | [](https://github.com/Z3Prover/z3/actions/workflows/wip.yml) |
 | 
			
		||||
| Azure Pipelines | Code Coverage | Open Bugs | Android Build | WASM Build |
 | 
			
		||||
| --------------- | --------------|-----------|---------------|------------|
 | 
			
		||||
| [](https://dev.azure.com/Z3Public/Z3/_build/latest?definitionId=1&branchName=master) | [](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml) | [](https://github.com/Z3Prover/z3/actions/workflows/wip.yml) |[](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml) | [](https://github.com/Z3Prover/z3/actions/workflows/wasm.yml) |
 | 
			
		||||
 | 
			
		||||
[1]: #building-z3-on-windows-using-visual-studio-command-prompt
 | 
			
		||||
[2]: #building-z3-using-make-and-gccclang
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,29 +43,61 @@ jobs:
 | 
			
		|||
    - ${{if eq(variables['runRegressions'], 'True')}}:
 | 
			
		||||
      - template: scripts/test-regressions.yml
 | 
			
		||||
 | 
			
		||||
- job: "Ubuntu18Python"
 | 
			
		||||
  displayName: "Ubuntu 18 with ocaml"
 | 
			
		||||
- job: "Ubuntu20OCaml"
 | 
			
		||||
  displayName: "Ubuntu 20 with OCaml"
 | 
			
		||||
  pool:
 | 
			
		||||
    vmImage: "Ubuntu-18.04"
 | 
			
		||||
    vmImage: "Ubuntu-20.04"
 | 
			
		||||
  steps:
 | 
			
		||||
    - script: sudo apt-get install ocaml opam libgmp-dev
 | 
			
		||||
    - script: opam init -y
 | 
			
		||||
    - script: eval `opam config env`; opam install zarith ocamlfind -y
 | 
			
		||||
    - script: python scripts/mk_make.py --ml --staticlib
 | 
			
		||||
    - script: eval `opam config env`; python scripts/mk_make.py --ml
 | 
			
		||||
    - script: |
 | 
			
		||||
        set -e
 | 
			
		||||
        cd build
 | 
			
		||||
        eval `opam config env` 
 | 
			
		||||
        eval `opam config env`
 | 
			
		||||
        make -j3
 | 
			
		||||
        make -j3 examples
 | 
			
		||||
        make -j3 test-z3
 | 
			
		||||
        ./ml_example
 | 
			
		||||
        cd ..
 | 
			
		||||
    - script: eval `opam config env`; ocamlfind install z3 build/api/ml/* -dll build/libz3.*
 | 
			
		||||
    - template: scripts/test-z3.yml
 | 
			
		||||
    - template: scripts/test-regressions.yml
 | 
			
		||||
    - template: scripts/generate-doc.yml
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- job: "Ubuntu20OCamlStatic"
 | 
			
		||||
  displayName: "Ubuntu 20 with OCaml on z3-static"
 | 
			
		||||
  pool:
 | 
			
		||||
    vmImage: "Ubuntu-20.04"
 | 
			
		||||
  steps:
 | 
			
		||||
    - script: sudo apt-get install ocaml opam libgmp-dev
 | 
			
		||||
    - script: opam init -y
 | 
			
		||||
    - script: eval `opam config env`; opam install zarith ocamlfind -y
 | 
			
		||||
    - script: eval `opam config env`; python scripts/mk_make.py --ml --staticlib
 | 
			
		||||
    - script: |
 | 
			
		||||
        set -e
 | 
			
		||||
        cd build
 | 
			
		||||
        eval `opam config env`
 | 
			
		||||
        make -j3
 | 
			
		||||
        make -j3 examples
 | 
			
		||||
        make -j3 test-z3
 | 
			
		||||
        cd ..
 | 
			
		||||
    - script: eval `opam config env`; ocamlfind install z3-static build/api/ml/* build/libz3-static.a
 | 
			
		||||
    - script: |
 | 
			
		||||
        set -e
 | 
			
		||||
        cd build
 | 
			
		||||
        eval `opam config env`
 | 
			
		||||
        make -j3
 | 
			
		||||
        make -j3 _ex_ml_example_post_install
 | 
			
		||||
        ./ml_example_static.byte
 | 
			
		||||
        ./ml_example_static_custom.byte
 | 
			
		||||
        ./ml_example_static
 | 
			
		||||
        cd ..
 | 
			
		||||
    - template: scripts/test-z3.yml
 | 
			
		||||
    - template: scripts/test-regressions.yml
 | 
			
		||||
    - template: scripts/generate-doc.yml
 | 
			
		||||
 | 
			
		||||
- job: "LinuxMSan"
 | 
			
		||||
  displayName: "Ubuntu build - cmake"
 | 
			
		||||
  condition: eq(0,1) 
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +132,7 @@ jobs:
 | 
			
		|||
#   - template: scripts/test-java-cmake.yml
 | 
			
		||||
#   - template: scripts/test-regressions.yml
 | 
			
		||||
  
 | 
			
		||||
- job: "Ubuntu16CMake"
 | 
			
		||||
- job: "UbuntuCMake"
 | 
			
		||||
  displayName: "Ubuntu build - cmake"
 | 
			
		||||
  pool:
 | 
			
		||||
    vmImage: "Ubuntu-latest"
 | 
			
		||||
| 
						 | 
				
			
			@ -252,3 +284,36 @@ jobs:
 | 
			
		|||
#   - template: scripts/test-examples-cmake.yml
 | 
			
		||||
    - template: scripts/test-regressions.yml
 | 
			
		||||
#   - template: scripts/test-java-cmake.yml
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- job: "MacOSOCaml"
 | 
			
		||||
  displayName: "MacOS build with OCaml"
 | 
			
		||||
  pool:
 | 
			
		||||
    vmImage: "macOS-latest"
 | 
			
		||||
  steps:
 | 
			
		||||
    - script: brew install opam
 | 
			
		||||
    - script: opam init -y
 | 
			
		||||
    - script: eval `opam config env`; opam install zarith ocamlfind -y
 | 
			
		||||
    - script: eval `opam config env`; python scripts/mk_make.py --ml
 | 
			
		||||
    - script: |
 | 
			
		||||
        set -e
 | 
			
		||||
        cd build
 | 
			
		||||
        eval `opam config env`
 | 
			
		||||
        make -j3
 | 
			
		||||
        make -j3 examples
 | 
			
		||||
        make -j3 test-z3
 | 
			
		||||
        cd ..
 | 
			
		||||
    - script: eval `opam config env`; ocamlfind install z3 build/api/ml/* -dll build/libz3.*
 | 
			
		||||
    - script: |
 | 
			
		||||
        set -e
 | 
			
		||||
        cd build
 | 
			
		||||
        eval `opam config env`
 | 
			
		||||
        make -j3
 | 
			
		||||
        make -j3 _ex_ml_example_post_install
 | 
			
		||||
        ./ml_example_shared.byte
 | 
			
		||||
        ./ml_example_shared_custom.byte
 | 
			
		||||
        ./ml_example_shared
 | 
			
		||||
        cd ..
 | 
			
		||||
# Skip as dead-slow in debug mode:
 | 
			
		||||
#  - template: scripts/test-z3.yml
 | 
			
		||||
    - template: scripts/test-regressions.yml
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,6 +39,27 @@ set(CLANG_WARNINGS_AS_ERRORS
 | 
			
		|||
  "-Werror=delete-non-virtual-dtor"
 | 
			
		||||
  # https://clang.llvm.org/docs/DiagnosticsReference.html#woverloaded-virtual
 | 
			
		||||
  "-Werror=overloaded-virtual"
 | 
			
		||||
  # warn the user if a class with virtual functions has a
 | 
			
		||||
  # non-virtual destructor. This helps catch hard to
 | 
			
		||||
  # track down memory errors
 | 
			
		||||
  "-Werror=non-virtual-dtor"
 | 
			
		||||
  # warn if a null dereference is detected
 | 
			
		||||
  "-Werror=null-dereference"
 | 
			
		||||
  # warn for potential performance problem casts
 | 
			
		||||
  # "-Werror=cast-align"
 | 
			
		||||
  # warn if float is implicit promoted to double
 | 
			
		||||
  # "-Werror=double-promotion"
 | 
			
		||||
  "-Werror=no-unreachable-code-return"
 | 
			
		||||
  # warn the user if a variable declaration shadows one from a parent context
 | 
			
		||||
  # "-Werror=shadow"
 | 
			
		||||
  # warn for c-style casts
 | 
			
		||||
  # "-Werror=old-style-cast"
 | 
			
		||||
  # warn on sign conversions
 | 
			
		||||
  # "-Werror=sign-conversion"
 | 
			
		||||
  # warn on type conversions that may lose data
 | 
			
		||||
  # "-Werror=conversion"
 | 
			
		||||
  # warn on anything being unused
 | 
			
		||||
  # "-Werror=unused"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -106,6 +106,24 @@ ExternalProject_Add(z3_tptp5
 | 
			
		|||
)
 | 
			
		||||
set_target_properties(z3_tptp5 PROPERTIES EXCLUDE_FROM_ALL TRUE)
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# Build example user-propagator project using libz3's C++ API as an external project
 | 
			
		||||
################################################################################
 | 
			
		||||
ExternalProject_Add(userPropagator
 | 
			
		||||
        DEPENDS libz3
 | 
			
		||||
        # Configure step
 | 
			
		||||
        SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/userPropagator"
 | 
			
		||||
        CMAKE_ARGS
 | 
			
		||||
        "-DZ3_DIR=${PROJECT_BINARY_DIR}"
 | 
			
		||||
        "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}"
 | 
			
		||||
        # Build step
 | 
			
		||||
        ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG}
 | 
			
		||||
        BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/userPropagator_build_dir"
 | 
			
		||||
        # Install Step
 | 
			
		||||
        INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "" # Dummy command
 | 
			
		||||
        )
 | 
			
		||||
set_target_properties(userPropagator PROPERTIES EXCLUDE_FROM_ALL TRUE)
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# Build Python examples
 | 
			
		||||
################################################################################
 | 
			
		||||
| 
						 | 
				
			
			@ -118,4 +136,4 @@ endif()
 | 
			
		|||
################################################################################
 | 
			
		||||
if (Z3_BUILD_DOTNET_BINDINGS)
 | 
			
		||||
    add_subdirectory(dotnet)
 | 
			
		||||
endif()
 | 
			
		||||
endif()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,8 @@ add_executable(cpp_example example.cpp)
 | 
			
		|||
target_include_directories(cpp_example PRIVATE ${Z3_CXX_INCLUDE_DIRS})
 | 
			
		||||
target_link_libraries(cpp_example PRIVATE ${Z3_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
target_compile_options(cpp_example PRIVATE ${Z3_COMPONENT_CXX_FLAGS})
 | 
			
		||||
 | 
			
		||||
if (CMAKE_SYSTEM_NAME MATCHES "[Ww]indows")
 | 
			
		||||
  # On Windows we need to copy the Z3 libraries
 | 
			
		||||
  # into the same directory as the executable
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -304,7 +304,7 @@ void error_example() {
 | 
			
		|||
        // The next call fails because x is a Boolean.
 | 
			
		||||
        expr n = x + 1;
 | 
			
		||||
    }
 | 
			
		||||
    catch (exception ex) {
 | 
			
		||||
    catch (exception & ex) {
 | 
			
		||||
        std::cout << "failed: " << ex << "\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -312,7 +312,7 @@ void error_example() {
 | 
			
		|||
    try {
 | 
			
		||||
        expr arg = to_expr(c, Z3_get_app_arg(c, x, 0));
 | 
			
		||||
    }
 | 
			
		||||
    catch (exception ex) {
 | 
			
		||||
    catch (exception & ex) {
 | 
			
		||||
        std::cout << "failed: " << ex << "\n";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1249,10 +1249,14 @@ void recfun_example() {
 | 
			
		|||
 | 
			
		||||
static void string_values() {
 | 
			
		||||
    context c;
 | 
			
		||||
    std::cout << "string_values\n";
 | 
			
		||||
    expr s = c.string_val("abc\n\n\0\0", 7);
 | 
			
		||||
    std::cout << s << "\n";
 | 
			
		||||
    std::string s1 = s.get_string();
 | 
			
		||||
    std::cout << s1 << "\n";
 | 
			
		||||
    std::u32string buffer = s.get_u32string();
 | 
			
		||||
    for (unsigned ch : buffer)
 | 
			
		||||
        std::cout << "char: " << ch << "\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr MakeStringConstant(context* context, std::string value) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,11 +22,11 @@ Copyright (c) 2015 Microsoft Corporation
 | 
			
		|||
/**
 | 
			
		||||
   \defgroup capi_ex C API examples
 | 
			
		||||
*/
 | 
			
		||||
/*@{*/
 | 
			
		||||
/**@{*/
 | 
			
		||||
/**
 | 
			
		||||
   @name Auxiliary Functions
 | 
			
		||||
*/
 | 
			
		||||
/*@{*/
 | 
			
		||||
/**@{*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   \brief exit gracefully in case of error.
 | 
			
		||||
| 
						 | 
				
			
			@ -694,12 +694,12 @@ void display_version()
 | 
			
		|||
    Z3_get_version(&major, &minor, &build, &revision);
 | 
			
		||||
    printf("Z3 %d.%d.%d.%d\n", major, minor, build, revision);
 | 
			
		||||
}
 | 
			
		||||
/*@}*/
 | 
			
		||||
/**@}*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   @name Examples
 | 
			
		||||
*/
 | 
			
		||||
/*@{*/
 | 
			
		||||
/**@{*/
 | 
			
		||||
/**
 | 
			
		||||
   \brief "Hello world" example: create a Z3 logical context, and delete it.
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -2947,8 +2947,8 @@ void mk_model_example() {
 | 
			
		|||
    Z3_del_context(ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*@}*/
 | 
			
		||||
/*@}*/
 | 
			
		||||
/**@}*/
 | 
			
		||||
/**@}*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ Copyright (c) 2015 Microsoft Corporation
 | 
			
		|||
/** 
 | 
			
		||||
   \defgroup maxsat_ex MaxSAT/MaxSMT examples
 | 
			
		||||
*/
 | 
			
		||||
/*@{*/
 | 
			
		||||
/**@{*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   \brief Exit gracefully in case of error.
 | 
			
		||||
| 
						 | 
				
			
			@ -638,5 +638,5 @@ int main(int argc, char * argv[]) {
 | 
			
		|||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*@}*/
 | 
			
		||||
/**@}*/
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										47
									
								
								examples/python/efsmt.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								examples/python/efsmt.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
from z3 import *
 | 
			
		||||
from z3.z3util import get_vars
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
Modified from the example in  pysmt
 | 
			
		||||
https://github.com/pysmt/pysmt/blob/97088bf3b0d64137c3099ef79a4e153b10ccfda7/examples/efsmt.py
 | 
			
		||||
'''
 | 
			
		||||
def efsmt(y, phi, maxloops=None):
 | 
			
		||||
    """Solving exists x. forall y. phi(x, y)"""
 | 
			
		||||
    vars = get_vars(phi)
 | 
			
		||||
    x = [item for item in vars if item not in y]
 | 
			
		||||
    esolver = Solver()
 | 
			
		||||
    fsolver = Solver()
 | 
			
		||||
    esolver.add(BoolVal(True))
 | 
			
		||||
    loops = 0
 | 
			
		||||
    while maxloops is None or loops <= maxloops:
 | 
			
		||||
        loops += 1
 | 
			
		||||
        eres = esolver.check()
 | 
			
		||||
        if eres == unsat:
 | 
			
		||||
            return unsat
 | 
			
		||||
        else:
 | 
			
		||||
            emodel = esolver.model()
 | 
			
		||||
            tau = [emodel.eval(var, True) for var in x]
 | 
			
		||||
            sub_phi = phi
 | 
			
		||||
            for i in range(len(x)):
 | 
			
		||||
                sub_phi = simplify(substitute(sub_phi, (x[i], tau[i])))
 | 
			
		||||
            fsolver.add(Not(sub_phi))
 | 
			
		||||
            if fsolver.check() == sat:
 | 
			
		||||
                fmodel = fsolver.model()
 | 
			
		||||
                sigma = [fmodel.eval(v, True) for v in y]
 | 
			
		||||
                sub_phi = phi
 | 
			
		||||
                for j in range(len(y)):
 | 
			
		||||
                    sub_phi = simplify(substitute(sub_phi, (y[j], sigma[j])))
 | 
			
		||||
                esolver.add(sub_phi)
 | 
			
		||||
            else:
 | 
			
		||||
                return sat
 | 
			
		||||
    return unknown
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test():
 | 
			
		||||
    x, y, z = Reals("x y z")
 | 
			
		||||
    fmla = Implies(And(y > 0, y < 10), y - 2 * x < 7)
 | 
			
		||||
    fmlb = And(y > 3, x == 1)
 | 
			
		||||
    print(efsmt([y], fmla))
 | 
			
		||||
    print(efsmt([y], fmlb))
 | 
			
		||||
    
 | 
			
		||||
test()
 | 
			
		||||
							
								
								
									
										45
									
								
								examples/userPropagator/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								examples/userPropagator/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
################################################################################
 | 
			
		||||
# Example C++ project
 | 
			
		||||
################################################################################
 | 
			
		||||
project(Z3_USER_PROPAGATOR_EXAMPLE CXX)
 | 
			
		||||
cmake_minimum_required(VERSION 3.4)
 | 
			
		||||
find_package(Z3
 | 
			
		||||
  REQUIRED
 | 
			
		||||
  CONFIG
 | 
			
		||||
  # `NO_DEFAULT_PATH` is set so that -DZ3_DIR has to be passed to find Z3.
 | 
			
		||||
  # This should prevent us from accidentally picking up an installed
 | 
			
		||||
  # copy of Z3. This is here to benefit Z3's build system when building
 | 
			
		||||
  # this project. When making your own project you probably shouldn't
 | 
			
		||||
  # use this option.
 | 
			
		||||
  NO_DEFAULT_PATH
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# Z3 C++ API bindings require C++11
 | 
			
		||||
################################################################################
 | 
			
		||||
set(CMAKE_CXX_STANDARD 11)
 | 
			
		||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
 | 
			
		||||
 | 
			
		||||
message(STATUS "Z3_FOUND: ${Z3_FOUND}")
 | 
			
		||||
message(STATUS "Found Z3 ${Z3_VERSION_STRING}")
 | 
			
		||||
message(STATUS "Z3_DIR: ${Z3_DIR}")
 | 
			
		||||
 | 
			
		||||
add_executable(user_propagator_example example.cpp)
 | 
			
		||||
target_include_directories(user_propagator_example PRIVATE ${Z3_CXX_INCLUDE_DIRS})
 | 
			
		||||
target_link_libraries(user_propagator_example PRIVATE ${Z3_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
if (CMAKE_SYSTEM_NAME MATCHES "[Ww]indows")
 | 
			
		||||
  # On Windows we need to copy the Z3 libraries
 | 
			
		||||
  # into the same directory as the executable
 | 
			
		||||
  # so that they can be found.
 | 
			
		||||
  foreach (z3_lib ${Z3_LIBRARIES})
 | 
			
		||||
    message(STATUS "Adding copy rule for ${z3_lib}")
 | 
			
		||||
    add_custom_command(TARGET user_propagator_example
 | 
			
		||||
      POST_BUILD
 | 
			
		||||
      COMMAND
 | 
			
		||||
        ${CMAKE_COMMAND} -E copy_if_different
 | 
			
		||||
        $<TARGET_FILE:${z3_lib}>
 | 
			
		||||
        $<TARGET_FILE_DIR:user_propagator_example>
 | 
			
		||||
    )
 | 
			
		||||
  endforeach()
 | 
			
		||||
endif()
 | 
			
		||||
							
								
								
									
										10
									
								
								examples/userPropagator/README
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								examples/userPropagator/README
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
Small example using the user-propagator.
 | 
			
		||||
To build the example execute
 | 
			
		||||
   make examples
 | 
			
		||||
in the build directory.
 | 
			
		||||
 | 
			
		||||
This command will create the executable user_propagator_example.
 | 
			
		||||
On Windows, you can just execute it.
 | 
			
		||||
On macOS and Linux, you must install z3 first using
 | 
			
		||||
   sudo make install
 | 
			
		||||
OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library.
 | 
			
		||||
							
								
								
									
										370
									
								
								examples/userPropagator/example.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										370
									
								
								examples/userPropagator/example.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,370 @@
 | 
			
		|||
#include <algorithm>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <random>
 | 
			
		||||
#include <stack>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include "z3++.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The program solves the n-queens problem (number of solutions) with 4 different approaches
 | 
			
		||||
 * 1) Bit-Vector constraints + Default solver + Blocking Clauses
 | 
			
		||||
 * 2) Bit-Vector constraints + Simple solver + Blocking Clauses
 | 
			
		||||
 * 3) Bit-Vector constraints + Simple solver + Adding contradictions in the propagator
 | 
			
		||||
 * 4) Constraints only implicit via the propagator + Simple solver + Adding contradictions in the propagator
 | 
			
		||||
 *
 | 
			
		||||
 * Runs 1 + 2 are done for comparison with 3 + 4
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
using namespace std::chrono;
 | 
			
		||||
using std::to_string;
 | 
			
		||||
 | 
			
		||||
#define QUEEN
 | 
			
		||||
#define REPETITIONS 5
 | 
			
		||||
 | 
			
		||||
#define SIZE(x) std::extent<decltype(x)>::value
 | 
			
		||||
 | 
			
		||||
#ifdef LOG
 | 
			
		||||
#define WriteEmptyLine std::cout << std::endl
 | 
			
		||||
#define WriteLine(x) std::cout << (x) << std::endl
 | 
			
		||||
#define Write(x) std::cout << x
 | 
			
		||||
#else
 | 
			
		||||
#define WriteEmptyLine
 | 
			
		||||
#define WriteLine(x)
 | 
			
		||||
#define Write(x)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef std::vector<unsigned> model;
 | 
			
		||||
 | 
			
		||||
struct model_hash_function {
 | 
			
		||||
    std::size_t operator()(const model &m) const {
 | 
			
		||||
        size_t hash = 0;
 | 
			
		||||
        for (unsigned i = 0; i < m.size(); i++) {
 | 
			
		||||
            hash *= m.size();
 | 
			
		||||
            hash += m[i];
 | 
			
		||||
        }
 | 
			
		||||
        return hash;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class user_propagator : public z3::user_propagator_base {
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
    unsigned board;
 | 
			
		||||
    std::unordered_map<unsigned, unsigned>& id_mapping;
 | 
			
		||||
    model currentModel;
 | 
			
		||||
    std::unordered_set<model, model_hash_function> modelSet;
 | 
			
		||||
    std::vector<unsigned> fixedValues;
 | 
			
		||||
    std::stack<unsigned> fixedCnt;
 | 
			
		||||
 | 
			
		||||
    int solutionId = 1;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    int getModelCount() const {
 | 
			
		||||
        return solutionId - 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void final() final {
 | 
			
		||||
        this->conflict((unsigned) fixedValues.size(), fixedValues.data());
 | 
			
		||||
        if (modelSet.find(currentModel) != modelSet.end()) {
 | 
			
		||||
            WriteLine("Got already computed model");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        Write("Model #" << solutionId << ":\n");
 | 
			
		||||
        solutionId++;
 | 
			
		||||
#ifdef LOG
 | 
			
		||||
        for (unsigned i = 0; i < fixedValues.size(); i++) {
 | 
			
		||||
            unsigned id = fixedValues[i];
 | 
			
		||||
            WriteLine("q" + to_string(id_mapping[id]) + " = " + to_string(currentModel[id]));
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        modelSet.insert(currentModel);
 | 
			
		||||
        WriteEmptyLine;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static unsigned bvToInt(z3::expr e) {
 | 
			
		||||
        return (unsigned)e.get_numeral_int();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fixed(unsigned id, z3::expr const &e) override {
 | 
			
		||||
        fixedValues.push_back(id);
 | 
			
		||||
        unsigned value = bvToInt(e);
 | 
			
		||||
        currentModel[id_mapping[id]] = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    user_propagator(z3::solver *s, std::unordered_map<unsigned, unsigned>& idMapping, unsigned board)
 | 
			
		||||
            : user_propagator_base(s), board(board), id_mapping(idMapping), currentModel(board, (unsigned)-1) {
 | 
			
		||||
 | 
			
		||||
        this->register_fixed();
 | 
			
		||||
        this->register_final();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual ~user_propagator() = default;
 | 
			
		||||
 | 
			
		||||
    void push() override {
 | 
			
		||||
        fixedCnt.push((unsigned) fixedValues.size());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void pop(unsigned num_scopes) override {
 | 
			
		||||
        for (unsigned i = 0; i < num_scopes; i++) {
 | 
			
		||||
            unsigned lastCnt = fixedCnt.top();
 | 
			
		||||
            fixedCnt.pop();
 | 
			
		||||
            for (auto j = fixedValues.size(); j > lastCnt; j--) {
 | 
			
		||||
                currentModel[fixedValues[j - 1]] = (unsigned)-1;
 | 
			
		||||
            }
 | 
			
		||||
            fixedValues.resize(lastCnt);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    user_propagator_base *fresh(Z3_context) override { return this; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class user_propagator_with_theory : public user_propagator {
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    void fixed(unsigned id, z3::expr const &e) override {
 | 
			
		||||
        unsigned queenId = id_mapping[id];
 | 
			
		||||
        unsigned queenPos = bvToInt(e);
 | 
			
		||||
 | 
			
		||||
        if (queenPos >= board) {
 | 
			
		||||
            this->conflict(1, &id);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (unsigned fixed : fixedValues) {
 | 
			
		||||
            unsigned otherId = id_mapping[fixed];
 | 
			
		||||
            unsigned otherPos = currentModel[fixed];
 | 
			
		||||
 | 
			
		||||
            if (queenPos == otherPos) {
 | 
			
		||||
                const unsigned conflicting[] = {id, fixed};
 | 
			
		||||
                this->conflict(2, conflicting);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
#ifdef QUEEN
 | 
			
		||||
            int diffY = abs((int)queenId - (int)otherId);
 | 
			
		||||
            int diffX = abs((int)queenPos - (int)otherPos);
 | 
			
		||||
            if (diffX == diffY) {
 | 
			
		||||
                const unsigned conflicting[] = {id, fixed};
 | 
			
		||||
                this->conflict(2, conflicting);
 | 
			
		||||
            }
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fixedValues.push_back(id);
 | 
			
		||||
        currentModel[id_mapping[id]] = queenPos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    user_propagator_with_theory(z3::solver *s, std::unordered_map<unsigned, unsigned>& idMapping, unsigned board)
 | 
			
		||||
            : user_propagator(s, idMapping, board) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int log2i(unsigned n) {
 | 
			
		||||
    if (n <= 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (n <= 2) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    unsigned l = 1;
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    while (l < n) {
 | 
			
		||||
        l <<= 1;
 | 
			
		||||
        i++;
 | 
			
		||||
    }
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<z3::expr> createQueens(z3::context &context, unsigned num) {
 | 
			
		||||
    std::vector<z3::expr> queens;
 | 
			
		||||
    int bits = log2i(num) + 1 /*to detect potential overflow in the diagonal*/;
 | 
			
		||||
    for (unsigned i = 0; i < num; i++) {
 | 
			
		||||
        queens.push_back(context.bv_const((std::string("q") + to_string(i)).c_str(), bits));
 | 
			
		||||
    }
 | 
			
		||||
    return queens;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void createConstraints(z3::context &context, z3::solver &solver, const std::vector<z3::expr> &queens) {
 | 
			
		||||
    for (unsigned i = 0; i < queens.size(); i++) {
 | 
			
		||||
        // assert column range
 | 
			
		||||
        solver.add(z3::uge(queens[i], 0));
 | 
			
		||||
        solver.add(z3::ule(queens[i], (int) (queens.size() - 1)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    z3::expr_vector distinct(context);
 | 
			
		||||
    for (const z3::expr &queen : queens) {
 | 
			
		||||
        distinct.push_back(queen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    solver.add(z3::distinct(distinct));
 | 
			
		||||
 | 
			
		||||
#ifdef QUEEN
 | 
			
		||||
    for (unsigned i = 0; i < queens.size(); i++) {
 | 
			
		||||
        for (unsigned j = i + 1; j < queens.size(); j++) {
 | 
			
		||||
            solver.add((int)(j - i) != (queens[j] - queens[i]));
 | 
			
		||||
            solver.add((int)(j - i) != (queens[i] - queens[j]));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int test01(unsigned num, bool simple) {
 | 
			
		||||
    z3::context context;
 | 
			
		||||
    z3::solver solver(context, !simple ? Z3_mk_solver(context) : Z3_mk_simple_solver(context));
 | 
			
		||||
 | 
			
		||||
    std::vector<z3::expr> queens = createQueens(context, num);
 | 
			
		||||
 | 
			
		||||
    createConstraints(context, solver, queens);
 | 
			
		||||
 | 
			
		||||
    int solutionId = 1;
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
        z3::check_result res = solver.check();
 | 
			
		||||
 | 
			
		||||
        if (res != z3::check_result::sat) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        z3::model model = solver.get_model();
 | 
			
		||||
 | 
			
		||||
        WriteLine("Model #" + to_string(solutionId) + ":");
 | 
			
		||||
        solutionId++;
 | 
			
		||||
 | 
			
		||||
        z3::expr_vector blocking(context);
 | 
			
		||||
 | 
			
		||||
        for (unsigned i = 0; i < num; i++) {
 | 
			
		||||
            z3::expr eval = model.eval(queens[i]);
 | 
			
		||||
            WriteLine(("q" + to_string(i) + " = " + to_string(eval.get_numeral_int())));
 | 
			
		||||
            blocking.push_back(queens[i] != eval);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        solver.add(z3::mk_or(blocking));
 | 
			
		||||
 | 
			
		||||
        WriteEmptyLine;
 | 
			
		||||
    }
 | 
			
		||||
    return solutionId - 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int test0(unsigned num) {
 | 
			
		||||
    return test01(num, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int test1(unsigned num) {
 | 
			
		||||
    return test01(num, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int test23(unsigned num, bool withTheory) {
 | 
			
		||||
    z3::context context;
 | 
			
		||||
    z3::solver solver(context, Z3_mk_simple_solver(context));
 | 
			
		||||
    std::unordered_map<unsigned, unsigned> idMapping;
 | 
			
		||||
 | 
			
		||||
    user_propagator *propagator;
 | 
			
		||||
    if (!withTheory) {
 | 
			
		||||
        propagator = new user_propagator(&solver, idMapping, num);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        propagator = new user_propagator_with_theory(&solver, idMapping, num);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<z3::expr> queens = createQueens(context, num);
 | 
			
		||||
 | 
			
		||||
    for (unsigned i = 0; i < queens.size(); i++) {
 | 
			
		||||
        unsigned id = propagator->add(queens[i]);
 | 
			
		||||
        idMapping[id] = i;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!withTheory) {
 | 
			
		||||
        createConstraints(context, solver, queens);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    solver.check();
 | 
			
		||||
    int res = propagator->getModelCount();
 | 
			
		||||
    delete propagator;
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int test2(unsigned num) {
 | 
			
		||||
    return test23(num, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int test3(unsigned num) {
 | 
			
		||||
    return test23(num, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main() {
 | 
			
		||||
 | 
			
		||||
    for (int num = 4; num <= 11; num++) {
 | 
			
		||||
 | 
			
		||||
        std::cout << "num = " << num << ":\n" << std::endl;
 | 
			
		||||
 | 
			
		||||
        unsigned seed = (unsigned) high_resolution_clock::now().time_since_epoch().count();
 | 
			
		||||
        const char *testName[] =
 | 
			
		||||
                {
 | 
			
		||||
                        "BV + Blocking clauses (Default solver)",
 | 
			
		||||
                        "BV + Blocking clauses (Simple solver)",
 | 
			
		||||
                        "BV + Adding conflicts",
 | 
			
		||||
                        "Custom theory + conflicts",
 | 
			
		||||
                };
 | 
			
		||||
        int permutation[4] =
 | 
			
		||||
                {
 | 
			
		||||
                        0,
 | 
			
		||||
                        1,
 | 
			
		||||
                        2,
 | 
			
		||||
                        3,
 | 
			
		||||
                };
 | 
			
		||||
        double timeResults[REPETITIONS * SIZE(permutation)];
 | 
			
		||||
 | 
			
		||||
        for (int rep = 0; rep < REPETITIONS; rep++) {
 | 
			
		||||
            // Execute strategies in a randomised order
 | 
			
		||||
            std::shuffle(&permutation[0], &permutation[SIZE(permutation) - 1], std::default_random_engine(seed));
 | 
			
		||||
 | 
			
		||||
            for (int i : permutation) {
 | 
			
		||||
                int modelCount = -1;
 | 
			
		||||
 | 
			
		||||
                auto now1 = high_resolution_clock::now();
 | 
			
		||||
 | 
			
		||||
                switch (i) {
 | 
			
		||||
                    case 0:
 | 
			
		||||
                        modelCount = test0(num);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 1:
 | 
			
		||||
                        modelCount = test1(num);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 2:
 | 
			
		||||
                        modelCount = test2(num);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 3:
 | 
			
		||||
                        modelCount = test3(num);
 | 
			
		||||
                        break;
 | 
			
		||||
                    default:
 | 
			
		||||
                        WriteLine("Unknown case");
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
                auto now2 = high_resolution_clock::now();
 | 
			
		||||
                duration<double, std::milli> ms = now2 - now1;
 | 
			
		||||
                std::cout << testName[i] << " took " << ms.count() << "ms (" << modelCount << " models)" << std::endl;
 | 
			
		||||
                timeResults[rep * SIZE(permutation) + i] = ms.count();
 | 
			
		||||
                WriteLine("-------------");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::cout << "\n" << std::endl;
 | 
			
		||||
 | 
			
		||||
        for (unsigned i = 0; i < SIZE(permutation); i++) {
 | 
			
		||||
            std::cout << testName[i];
 | 
			
		||||
            double sum = 0;
 | 
			
		||||
            for (int j = 0; j < REPETITIONS; j++) {
 | 
			
		||||
                std::cout << " " << timeResults[j * SIZE(permutation) + i] << "ms";
 | 
			
		||||
                sum += timeResults[j * SIZE(permutation) + i];
 | 
			
		||||
            }
 | 
			
		||||
            std::cout << " | avg: " << sum / REPETITIONS << "ms" << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::cout << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								examples/userPropagator/example.pdf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/userPropagator/example.pdf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -2014,21 +2014,34 @@ class MLComponent(Component):
 | 
			
		|||
 | 
			
		||||
            LIBZ3 = LIBZ3 + ' ' + ' '.join(map(lambda x: '-cclib ' + x, LDFLAGS.split()))
 | 
			
		||||
 | 
			
		||||
            stubs_install_path = '$$(%s printconf path)/stublibs' % OCAMLFIND
 | 
			
		||||
            if not STATIC_LIB:
 | 
			
		||||
                loadpath = '-ccopt -L' + stubs_install_path
 | 
			
		||||
                dllpath = '-dllpath ' + stubs_install_path
 | 
			
		||||
                LIBZ3 = LIBZ3 + ' ' + loadpath + ' ' + dllpath
 | 
			
		||||
 | 
			
		||||
            if DEBUG_MODE and not(is_cygwin()):
 | 
			
		||||
                # Some ocamlmklib's don't like -g; observed on cygwin, but may be others as well.
 | 
			
		||||
                OCAMLMKLIB += ' -g'
 | 
			
		||||
 | 
			
		||||
            z3mls = os.path.join(self.sub_dir, 'z3ml')
 | 
			
		||||
 | 
			
		||||
            LIBZ3ML = ''
 | 
			
		||||
            if STATIC_LIB:
 | 
			
		||||
                LIBZ3ML = '-oc ' + os.path.join(self.sub_dir, 'z3ml-static')
 | 
			
		||||
 | 
			
		||||
            out.write('%s.cma: %s %s %s\n' % (z3mls, cmos, stubso, z3linkdep))
 | 
			
		||||
            out.write('\t%s -o %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, self.sub_dir, stubso, cmos, LIBZ3))
 | 
			
		||||
            out.write('\t%s -o %s %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, LIBZ3ML, self.sub_dir, stubso, cmos, LIBZ3))
 | 
			
		||||
            out.write('%s.cmxa: %s %s %s %s.cma\n' % (z3mls, cmxs, stubso, z3linkdep, z3mls))
 | 
			
		||||
            out.write('\t%s -o %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, self.sub_dir, stubso, cmxs, LIBZ3))
 | 
			
		||||
            out.write('\t%s -o %s %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, LIBZ3ML, self.sub_dir, stubso, cmxs, LIBZ3))
 | 
			
		||||
            out.write('%s.cmxs: %s.cmxa\n' % (z3mls, z3mls))
 | 
			
		||||
            out.write('\t%s -linkall -shared -o %s.cmxs -I . -I %s %s.cmxa\n' % (OCAMLOPTF, z3mls, self.sub_dir, z3mls))
 | 
			
		||||
 | 
			
		||||
            out.write('\n')
 | 
			
		||||
            out.write('ml: %s.cma %s.cmxa %s.cmxs\n' % (z3mls, z3mls, z3mls))
 | 
			
		||||
            if IS_OSX:
 | 
			
		||||
                out.write('\tinstall_name_tool -id %s/libz3.dylib libz3.dylib\n' % (stubs_install_path))
 | 
			
		||||
                out.write('\tinstall_name_tool -change libz3.dylib %s/libz3.dylib api/ml/dllz3ml.so\n' % (stubs_install_path))                
 | 
			
		||||
            out.write('\n')
 | 
			
		||||
 | 
			
		||||
            if IS_WINDOWS:
 | 
			
		||||
| 
						 | 
				
			
			@ -2041,6 +2054,7 @@ class MLComponent(Component):
 | 
			
		|||
                self.mk_uninstall(out)
 | 
			
		||||
                out.write('\n')
 | 
			
		||||
 | 
			
		||||
    # The following three functions may be out of date.
 | 
			
		||||
    def mk_install_deps(self, out):
 | 
			
		||||
        if is_ml_enabled() and self._install_bindings():
 | 
			
		||||
            out.write(get_component(Z3_DLL_COMPONENT).dll_name + '$(SO_EXT) ')
 | 
			
		||||
| 
						 | 
				
			
			@ -2286,6 +2300,41 @@ class MLExampleComponent(ExampleComponent):
 | 
			
		|||
            out.write('\n')
 | 
			
		||||
            out.write('_ex_%s: ml_example.byte ml_example$(EXE_EXT)\n\n' % self.name)
 | 
			
		||||
 | 
			
		||||
        debug_opt = '-g ' if DEBUG_MODE else ''
 | 
			
		||||
 | 
			
		||||
        if STATIC_LIB:
 | 
			
		||||
            opam_z3_opts = '-thread -package z3-static -linkpkg'
 | 
			
		||||
            ml_post_install_tests = [
 | 
			
		||||
                (OCAMLC,              'ml_example_static.byte'),
 | 
			
		||||
                (OCAMLC + ' -custom', 'ml_example_static_custom.byte'),
 | 
			
		||||
                (OCAMLOPT,            'ml_example_static$(EXE_EXT)')
 | 
			
		||||
            ]
 | 
			
		||||
        else:
 | 
			
		||||
            opam_z3_opts = '-thread -package z3 -linkpkg'
 | 
			
		||||
            ml_post_install_tests = [
 | 
			
		||||
                (OCAMLC,              'ml_example_shared.byte'),
 | 
			
		||||
                (OCAMLC + ' -custom', 'ml_example_shared_custom.byte'),
 | 
			
		||||
                (OCAMLOPT,            'ml_example_shared$(EXE_EXT)')
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        for ocaml_compiler, testname in ml_post_install_tests:
 | 
			
		||||
            out.write(testname + ':')
 | 
			
		||||
            for mlfile in get_ml_files(self.ex_dir):
 | 
			
		||||
                out.write(' %s' % os.path.join(self.to_ex_dir, mlfile))
 | 
			
		||||
            out.write('\n')
 | 
			
		||||
            out.write('\tocamlfind %s -o %s %s %s ' % (ocaml_compiler, debug_opt, testname, opam_z3_opts))
 | 
			
		||||
            for mlfile in get_ml_files(self.ex_dir):
 | 
			
		||||
                out.write(' %s/%s' % (self.to_ex_dir, mlfile))
 | 
			
		||||
            out.write('\n')
 | 
			
		||||
 | 
			
		||||
        if STATIC_LIB:
 | 
			
		||||
            out.write('_ex_ml_example_post_install: ml_example_static.byte ml_example_static_custom.byte ml_example_static$(EXE_EXT)\n')
 | 
			
		||||
        else:
 | 
			
		||||
            out.write('_ex_ml_example_post_install: ml_example_shared.byte ml_example_shared_custom.byte ml_example_shared$(EXE_EXT)\n')
 | 
			
		||||
 | 
			
		||||
        out.write('\n')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PythonExampleComponent(ExampleComponent):
 | 
			
		||||
    def __init__(self, name, path):
 | 
			
		||||
        ExampleComponent.__init__(self, name, path)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,7 +58,7 @@ extern "C" {
 | 
			
		|||
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned sz, Z3_string str) {
 | 
			
		||||
        Z3_TRY;
 | 
			
		||||
        LOG_Z3_mk_string(c, str);
 | 
			
		||||
        LOG_Z3_mk_lstring(c, sz, str);
 | 
			
		||||
        RESET_ERROR_CODE();
 | 
			
		||||
        unsigned_vector chs;
 | 
			
		||||
        for (unsigned i = 0; i < sz; ++i) chs.push_back((unsigned char)str[i]);
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +69,17 @@ extern "C" {
 | 
			
		|||
        Z3_CATCH_RETURN(nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_u32string(Z3_context c, unsigned sz, unsigned const chars[]) {
 | 
			
		||||
        Z3_TRY;
 | 
			
		||||
        LOG_Z3_mk_u32string(c, sz, chars);
 | 
			
		||||
        RESET_ERROR_CODE();
 | 
			
		||||
        zstring s(sz, chars);
 | 
			
		||||
        app* a = mk_c(c)->sutil().str.mk_string(s);
 | 
			
		||||
        mk_c(c)->save_ast_trail(a);
 | 
			
		||||
        RETURN_Z3(of_ast(a));
 | 
			
		||||
        Z3_CATCH_RETURN(nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Z3_sort Z3_API Z3_mk_string_sort(Z3_context c) {
 | 
			
		||||
        Z3_TRY;
 | 
			
		||||
        LOG_Z3_mk_string_sort(c);
 | 
			
		||||
| 
						 | 
				
			
			@ -187,10 +198,9 @@ extern "C" {
 | 
			
		|||
        svector<char> buff;
 | 
			
		||||
        for (unsigned i = 0; i < str.length(); ++i) {
 | 
			
		||||
            unsigned ch = str[i];
 | 
			
		||||
            if (ch <= 32 || ch >= 127) {
 | 
			
		||||
            if (ch == 0 || ch >= 256 || (ch == '\\' && i + 1 < str.length() && str[i+1] == 'u')) {
 | 
			
		||||
                buff.reset();
 | 
			
		||||
                buffer.push_back('\\');
 | 
			
		||||
//                buffer.push_back('\\');  // possibly replace by native non-escaped version?
 | 
			
		||||
                buffer.push_back('u');
 | 
			
		||||
                buffer.push_back('{');
 | 
			
		||||
                while (ch > 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -215,6 +225,38 @@ extern "C" {
 | 
			
		|||
        Z3_CATCH_RETURN("");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unsigned Z3_API Z3_get_string_length(Z3_context c, Z3_ast s) {
 | 
			
		||||
        Z3_TRY;
 | 
			
		||||
        LOG_Z3_get_string_length(c, s);
 | 
			
		||||
        RESET_ERROR_CODE();
 | 
			
		||||
        zstring str;
 | 
			
		||||
        if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) {
 | 
			
		||||
            SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal");
 | 
			
		||||
        }
 | 
			
		||||
        return str.length();
 | 
			
		||||
        Z3_CATCH_RETURN(0);
 | 
			
		||||
    }    
 | 
			
		||||
 | 
			
		||||
    void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned contents[]) {
 | 
			
		||||
        Z3_TRY;
 | 
			
		||||
        LOG_Z3_get_string_contents(c, s, length, contents);
 | 
			
		||||
        RESET_ERROR_CODE();
 | 
			
		||||
        zstring str;
 | 
			
		||||
        if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) {
 | 
			
		||||
            SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (str.length() != length) {
 | 
			
		||||
            SET_ERROR_CODE(Z3_INVALID_ARG, "string size disagrees with supplied buffer length");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        for (unsigned i = 0; i < length; ++i)
 | 
			
		||||
            contents[i] = str[i];
 | 
			
		||||
        
 | 
			
		||||
        Z3_CATCH;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MK_SORTED(NAME, FN )                                    \
 | 
			
		||||
    Z3_ast Z3_API NAME(Z3_context c, Z3_sort s) {               \
 | 
			
		||||
    Z3_TRY;                                                     \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -875,7 +875,7 @@ extern "C" {
 | 
			
		|||
        init_solver(c, s);
 | 
			
		||||
        solver::push_eh_t _push = push_eh;
 | 
			
		||||
        solver::pop_eh_t _pop = pop_eh;
 | 
			
		||||
        solver::fresh_eh_t _fresh = [&](void * user_ctx, ast_manager& m, solver::context_obj*& _ctx) {
 | 
			
		||||
        solver::fresh_eh_t _fresh = [=](void * user_ctx, ast_manager& m, solver::context_obj*& _ctx) {
 | 
			
		||||
            ast_context_params params;
 | 
			
		||||
            params.set_foreign_manager(&m);
 | 
			
		||||
            auto* ctx = alloc(api::context, ¶ms, false);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,12 +36,12 @@ Notes:
 | 
			
		|||
   \defgroup cppapi C++ API
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
/*@{*/
 | 
			
		||||
/**@{*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   @name C++ API classes and functions
 | 
			
		||||
*/
 | 
			
		||||
/*@{*/
 | 
			
		||||
/**@{*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   \brief Z3 C++ namespace
 | 
			
		||||
| 
						 | 
				
			
			@ -379,6 +379,7 @@ namespace z3 {
 | 
			
		|||
        expr string_val(char const* s);
 | 
			
		||||
        expr string_val(char const* s, unsigned n);
 | 
			
		||||
        expr string_val(std::string const& s);
 | 
			
		||||
        expr string_val(std::u32string const& s);
 | 
			
		||||
 | 
			
		||||
        expr num_val(int n, sort const & s);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1100,23 +1101,29 @@ namespace z3 {
 | 
			
		|||
        bool is_string_value() const { return Z3_is_string(ctx(), m_ast); }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
           \brief for a string value expression return an escaped or unescaped string value.
 | 
			
		||||
           \brief for a string value expression return an escaped string value.
 | 
			
		||||
           \pre expression is for a string value.
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        std::string get_escaped_string() const {            
 | 
			
		||||
        std::string get_string() const {            
 | 
			
		||||
            assert(is_string_value());
 | 
			
		||||
            char const* s = Z3_get_string(ctx(), m_ast);
 | 
			
		||||
            check_error();
 | 
			
		||||
            return std::string(s);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string get_string() const {
 | 
			
		||||
        /**
 | 
			
		||||
           \brief for a string value expression return an unespaced string value.
 | 
			
		||||
           \pre expression is for a string value.
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        std::u32string get_u32string() const {
 | 
			
		||||
            assert(is_string_value());
 | 
			
		||||
            unsigned n;
 | 
			
		||||
            char const* s = Z3_get_lstring(ctx(), m_ast, &n);
 | 
			
		||||
            check_error();
 | 
			
		||||
            return std::string(s, n);
 | 
			
		||||
            unsigned n = Z3_get_string_length(ctx(), m_ast);
 | 
			
		||||
            std::u32string s;
 | 
			
		||||
            s.resize(n);
 | 
			
		||||
            Z3_get_string_contents(ctx(), m_ast, n, (unsigned*)s.data());
 | 
			
		||||
            return s;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        operator Z3_app() const { assert(is_app()); return reinterpret_cast<Z3_app>(m_ast); }
 | 
			
		||||
| 
						 | 
				
			
			@ -1516,7 +1523,7 @@ namespace z3 {
 | 
			
		|||
        expr substitute(expr_vector const& dst);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	class iterator {
 | 
			
		||||
    class iterator {
 | 
			
		||||
            expr& e;
 | 
			
		||||
            unsigned i;
 | 
			
		||||
        public:
 | 
			
		||||
| 
						 | 
				
			
			@ -1912,14 +1919,14 @@ namespace z3 {
 | 
			
		|||
        Z3_ast r;
 | 
			
		||||
        if (a.is_int()) {
 | 
			
		||||
            expr zero = a.ctx().int_val(0);
 | 
			
		||||
	    expr ge = a >= zero;
 | 
			
		||||
	    expr na = -a;
 | 
			
		||||
        expr ge = a >= zero;
 | 
			
		||||
        expr na = -a;
 | 
			
		||||
            r = Z3_mk_ite(a.ctx(), ge, a, na);	    
 | 
			
		||||
        }
 | 
			
		||||
        else if (a.is_real()) {
 | 
			
		||||
            expr zero = a.ctx().real_val(0);
 | 
			
		||||
	    expr ge = a >= zero;
 | 
			
		||||
	    expr na = -a;
 | 
			
		||||
        expr ge = a >= zero;
 | 
			
		||||
        expr na = -a;
 | 
			
		||||
            r = Z3_mk_ite(a.ctx(), ge, a, na);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
| 
						 | 
				
			
			@ -3480,6 +3487,7 @@ namespace z3 {
 | 
			
		|||
    inline expr context::string_val(char const* s, unsigned n) { Z3_ast r = Z3_mk_lstring(m_ctx, n, s); check_error(); return expr(*this, r); }
 | 
			
		||||
    inline expr context::string_val(char const* s) { Z3_ast r = Z3_mk_string(m_ctx, s); check_error(); return expr(*this, r); }
 | 
			
		||||
    inline expr context::string_val(std::string const& s) { Z3_ast r = Z3_mk_string(m_ctx, s.c_str()); check_error(); return expr(*this, r); }
 | 
			
		||||
    inline expr context::string_val(std::u32string const& s) { Z3_ast r = Z3_mk_u32string(m_ctx, (unsigned)s.size(), (unsigned const*)s.c_str()); check_error(); return expr(*this, r); }
 | 
			
		||||
 | 
			
		||||
    inline expr context::num_val(int n, sort const & s) { Z3_ast r = Z3_mk_int(m_ctx, n, s); check_error(); return expr(*this, r); }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3932,6 +3940,8 @@ namespace z3 {
 | 
			
		|||
        virtual void push() = 0;
 | 
			
		||||
        virtual void pop(unsigned num_scopes) = 0;
 | 
			
		||||
 | 
			
		||||
        virtual ~user_propagator_base() = default;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
           \brief user_propagators created using \c fresh() are created during 
 | 
			
		||||
           search and their lifetimes are restricted to search time. They should
 | 
			
		||||
| 
						 | 
				
			
			@ -3954,12 +3964,28 @@ namespace z3 {
 | 
			
		|||
            Z3_solver_propagate_fixed(ctx(), *s, fixed_eh); 
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void register_fixed() {
 | 
			
		||||
            assert(s);
 | 
			
		||||
            m_fixed_eh = [this](unsigned id, expr const& e) {
 | 
			
		||||
                fixed(id, e);
 | 
			
		||||
            };
 | 
			
		||||
            Z3_solver_propagate_fixed(ctx(), *s, fixed_eh);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void register_eq(eq_eh_t& f) { 
 | 
			
		||||
            assert(s);
 | 
			
		||||
            m_eq_eh = f; 
 | 
			
		||||
            Z3_solver_propagate_eq(ctx(), *s, eq_eh); 
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void register_eq() {
 | 
			
		||||
            assert(s);
 | 
			
		||||
            m_eq_eh = [this](unsigned x, unsigned y) {
 | 
			
		||||
                eq(x, y);
 | 
			
		||||
            };
 | 
			
		||||
            Z3_solver_propagate_eq(ctx(), *s, eq_eh);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
           \brief register a callback on final-check.
 | 
			
		||||
           During the final check stage, all propagations have been processed.
 | 
			
		||||
| 
						 | 
				
			
			@ -3973,6 +3999,21 @@ namespace z3 {
 | 
			
		|||
            m_final_eh = f; 
 | 
			
		||||
            Z3_solver_propagate_final(ctx(), *s, final_eh); 
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        void register_final() { 
 | 
			
		||||
            assert(s);
 | 
			
		||||
            m_final_eh = [this]() {
 | 
			
		||||
                final();
 | 
			
		||||
            };
 | 
			
		||||
            Z3_solver_propagate_final(ctx(), *s, final_eh); 
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        virtual void fixed(unsigned /*id*/, expr const& /*e*/) { }
 | 
			
		||||
 | 
			
		||||
        virtual void eq(unsigned /*x*/, unsigned /*y*/) { }
 | 
			
		||||
 | 
			
		||||
        virtual void final() { }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
           \brief tracks \c e by a unique identifier that is returned by the call.
 | 
			
		||||
| 
						 | 
				
			
			@ -4020,7 +4061,7 @@ namespace z3 {
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*@}*/
 | 
			
		||||
/*@}*/
 | 
			
		||||
/**@}*/
 | 
			
		||||
/**@}*/
 | 
			
		||||
#undef Z3_THROW
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -214,7 +214,6 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
 | 
			
		|||
        .MM(expr, numerator)
 | 
			
		||||
        .MM(expr, denominator)
 | 
			
		||||
        .MM(expr, is_string_value)
 | 
			
		||||
        .MM(expr, get_escaped_string)
 | 
			
		||||
        .MM(expr, get_string)
 | 
			
		||||
        .MM(expr, decl)
 | 
			
		||||
        .MM(expr, num_args)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -76,14 +76,14 @@ int compare_pointers(void* pt1, void* pt2) {
 | 
			
		|||
    return +1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MK_CTX_OF(X)                                                    \
 | 
			
		||||
#define MK_CTX_OF(X, USED)                                         \
 | 
			
		||||
  CAMLprim DLL_PUBLIC value n_context_of_ ## X(value v) {               \
 | 
			
		||||
    CAMLparam1(v);                                                      \
 | 
			
		||||
    CAMLlocal1(result);                                                 \
 | 
			
		||||
    Z3_context_plus cp;                                                 \
 | 
			
		||||
    Z3_ ## X ## _plus * p = (Z3_ ## X ## _plus *) Data_custom_val(v);   \
 | 
			
		||||
    cp = p->cp;                                                         \
 | 
			
		||||
    result = caml_alloc_custom(&Z3_context_plus_custom_ops, sizeof(Z3_context_plus), 0, 1); \
 | 
			
		||||
    result = caml_alloc_custom_mem(&Z3_context_plus_custom_ops, sizeof(Z3_context_plus), USED); \
 | 
			
		||||
    *(Z3_context_plus *)Data_custom_val(result) = cp;                   \
 | 
			
		||||
    /* We increment the usage counter of the context, as we just        \
 | 
			
		||||
       created a second custom block holding that context        */     \
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ int compare_pointers(void* pt1, void* pt2) {
 | 
			
		|||
    CAMLlocal1(result);                                                 \
 | 
			
		||||
    Z3_context_plus cp = *(Z3_context_plus*)(Data_custom_val(v));       \
 | 
			
		||||
    Z3_ ## X ## _plus a = Z3_ ## X ## _plus_mk(cp, NULL);               \
 | 
			
		||||
    result = caml_alloc_custom(&Z3_ ## X ## _plus_custom_ops, sizeof(Z3_ ## X ## _plus), 0, 1); \
 | 
			
		||||
    result = caml_alloc_custom_mem(&Z3_ ## X ## _plus_custom_ops, sizeof(Z3_ ## X ## _plus), USED); \
 | 
			
		||||
    *(Z3_ ## X ## _plus*)(Data_custom_val(result)) = a;                 \
 | 
			
		||||
    CAMLreturn(result);                                                 \
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -294,9 +294,9 @@ static struct custom_operations Z3_ast_plus_custom_ops = {
 | 
			
		|||
  Z3_ast_compare_ext
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MK_CTX_OF(ast)
 | 
			
		||||
MK_CTX_OF(ast, 16) // let's say 16 bytes per ast
 | 
			
		||||
 | 
			
		||||
#define MK_PLUS_OBJ_NO_REF(X)                                           \
 | 
			
		||||
#define MK_PLUS_OBJ_NO_REF(X, USED)                                \
 | 
			
		||||
  typedef struct {                                                      \
 | 
			
		||||
    Z3_context_plus cp;                                                 \
 | 
			
		||||
    Z3_ ## X p;                                                         \
 | 
			
		||||
| 
						 | 
				
			
			@ -349,9 +349,9 @@ MK_CTX_OF(ast)
 | 
			
		|||
    Z3_ ## X ## _compare_ext                                            \
 | 
			
		||||
  };                                                                    \
 | 
			
		||||
                                                                        \
 | 
			
		||||
  MK_CTX_OF(X)
 | 
			
		||||
  MK_CTX_OF(X, USED)
 | 
			
		||||
 | 
			
		||||
#define MK_PLUS_OBJ(X)                                                  \
 | 
			
		||||
#define MK_PLUS_OBJ(X, USED)                                            \
 | 
			
		||||
  typedef struct {                                                      \
 | 
			
		||||
    Z3_context_plus cp;                                                 \
 | 
			
		||||
    Z3_ ## X p;                                                         \
 | 
			
		||||
| 
						 | 
				
			
			@ -408,27 +408,27 @@ MK_CTX_OF(ast)
 | 
			
		|||
    Z3_ ## X ## _compare_ext                                            \
 | 
			
		||||
  };                                                                    \
 | 
			
		||||
                                                                        \
 | 
			
		||||
  MK_CTX_OF(X)
 | 
			
		||||
  MK_CTX_OF(X, USED)
 | 
			
		||||
 | 
			
		||||
MK_PLUS_OBJ_NO_REF(symbol)
 | 
			
		||||
MK_PLUS_OBJ_NO_REF(constructor)
 | 
			
		||||
MK_PLUS_OBJ_NO_REF(constructor_list)
 | 
			
		||||
MK_PLUS_OBJ_NO_REF(rcf_num)
 | 
			
		||||
MK_PLUS_OBJ(params)
 | 
			
		||||
MK_PLUS_OBJ(param_descrs)
 | 
			
		||||
MK_PLUS_OBJ(model)
 | 
			
		||||
MK_PLUS_OBJ(func_interp)
 | 
			
		||||
MK_PLUS_OBJ(func_entry)
 | 
			
		||||
MK_PLUS_OBJ(goal)
 | 
			
		||||
MK_PLUS_OBJ(tactic)
 | 
			
		||||
MK_PLUS_OBJ(probe)
 | 
			
		||||
MK_PLUS_OBJ(apply_result)
 | 
			
		||||
MK_PLUS_OBJ(solver)
 | 
			
		||||
MK_PLUS_OBJ(stats)
 | 
			
		||||
MK_PLUS_OBJ(ast_map)
 | 
			
		||||
MK_PLUS_OBJ(ast_vector)
 | 
			
		||||
MK_PLUS_OBJ(fixedpoint)
 | 
			
		||||
MK_PLUS_OBJ(optimize)
 | 
			
		||||
MK_PLUS_OBJ_NO_REF(symbol, 32)
 | 
			
		||||
MK_PLUS_OBJ_NO_REF(constructor, 32)
 | 
			
		||||
MK_PLUS_OBJ_NO_REF(constructor_list, 32)
 | 
			
		||||
MK_PLUS_OBJ_NO_REF(rcf_num, 32)
 | 
			
		||||
MK_PLUS_OBJ(params, 128)
 | 
			
		||||
MK_PLUS_OBJ(param_descrs, 128)
 | 
			
		||||
MK_PLUS_OBJ(model, 512)
 | 
			
		||||
MK_PLUS_OBJ(func_interp, 128)
 | 
			
		||||
MK_PLUS_OBJ(func_entry, 128)
 | 
			
		||||
MK_PLUS_OBJ(goal, 128)
 | 
			
		||||
MK_PLUS_OBJ(tactic, 128)
 | 
			
		||||
MK_PLUS_OBJ(probe, 128)
 | 
			
		||||
MK_PLUS_OBJ(apply_result, 128)
 | 
			
		||||
MK_PLUS_OBJ(solver, 20 * 1000 * 1000) // pretend a solver is 20MB
 | 
			
		||||
MK_PLUS_OBJ(stats, 128)
 | 
			
		||||
MK_PLUS_OBJ(ast_map, 1024 * 2)
 | 
			
		||||
MK_PLUS_OBJ(ast_vector, 128)
 | 
			
		||||
MK_PLUS_OBJ(fixedpoint, 20 * 1000 * 1000)
 | 
			
		||||
MK_PLUS_OBJ(optimize, 20 * 1000 * 1000)
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11102,33 +11102,34 @@ class PropClosures:
 | 
			
		|||
        self.bases = {}
 | 
			
		||||
        self.lock = None
 | 
			
		||||
 | 
			
		||||
    def set_threaded():
 | 
			
		||||
    def set_threaded(self):
 | 
			
		||||
        if self.lock is None:
 | 
			
		||||
            import threading
 | 
			
		||||
            self.lock = threading.thread.Lock()
 | 
			
		||||
            self.lock = threading.Lock()
 | 
			
		||||
 | 
			
		||||
    def get(self, ctx):
 | 
			
		||||
        if self.lock:
 | 
			
		||||
            self.lock.acquire()
 | 
			
		||||
        r = self.bases[ctx]
 | 
			
		||||
        if self.lock:
 | 
			
		||||
            self.lock.release()
 | 
			
		||||
            with self.lock:
 | 
			
		||||
                r = self.bases[ctx]
 | 
			
		||||
        else:
 | 
			
		||||
            r = self.bases[ctx]            
 | 
			
		||||
        return r
 | 
			
		||||
 | 
			
		||||
    def set(self, ctx, r):
 | 
			
		||||
        if self.lock:
 | 
			
		||||
            self.lock.acquire()
 | 
			
		||||
        self.bases[ctx] = r
 | 
			
		||||
        if self.lock:
 | 
			
		||||
            self.lock.release()
 | 
			
		||||
            with self.lock:
 | 
			
		||||
                self.bases[ctx] = r
 | 
			
		||||
        else:
 | 
			
		||||
            self.bases[ctx] = r
 | 
			
		||||
 | 
			
		||||
    def insert(self, r):
 | 
			
		||||
        if self.lock:
 | 
			
		||||
            self.lock.acquire()
 | 
			
		||||
        id = len(self.bases) + 3
 | 
			
		||||
        self.bases[id] = r
 | 
			
		||||
        if self.lock:
 | 
			
		||||
            self.lock.release()
 | 
			
		||||
            with self.lock:
 | 
			
		||||
                id = len(self.bases) + 3
 | 
			
		||||
                self.bases[id] = r
 | 
			
		||||
        else:
 | 
			
		||||
            id = len(self.bases) + 3
 | 
			
		||||
            self.bases[id] = r
 | 
			
		||||
        return id
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11151,8 +11152,9 @@ def user_prop_pop(ctx, num_scopes):
 | 
			
		|||
 | 
			
		||||
def user_prop_fresh(id, ctx):
 | 
			
		||||
    _prop_closures.set_threaded()
 | 
			
		||||
    new_prop = UsePropagateBase(None, ctx)
 | 
			
		||||
    _prop_closures.set(new_prop.id, new_prop.fresh())
 | 
			
		||||
    prop = _prop_closures.get(id)
 | 
			
		||||
    new_prop = prop.fresh()
 | 
			
		||||
    _prop_closures.set(new_prop.id, new_prop)
 | 
			
		||||
    return ctypes.c_void_p(new_prop.id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11214,11 +11216,12 @@ class UserPropagateBase:
 | 
			
		|||
        self.eq = None
 | 
			
		||||
        self.diseq = None
 | 
			
		||||
        if ctx:
 | 
			
		||||
            # TBD fresh is broken: ctx is not of the right type when we reach here.
 | 
			
		||||
            self._ctx = Context()
 | 
			
		||||
            Z3_del_context(self._ctx.ctx)
 | 
			
		||||
            self._ctx.ctx = ctx
 | 
			
		||||
            self._ctx.eh = Z3_set_error_handler(ctx, z3_error_handler)
 | 
			
		||||
            Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT)
 | 
			
		||||
            #Z3_del_context(self._ctx.ctx)
 | 
			
		||||
            #self._ctx.ctx = ctx
 | 
			
		||||
            #self._ctx.eh = Z3_set_error_handler(ctx, z3_error_handler)
 | 
			
		||||
            #Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT)
 | 
			
		||||
        if s:
 | 
			
		||||
            Z3_solver_propagate_init(self.ctx_ref(),
 | 
			
		||||
                                     s.solver,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,10 +25,10 @@ extern "C" {
 | 
			
		|||
#endif // __cplusplus
 | 
			
		||||
 | 
			
		||||
    /** \defgroup capi C API */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /** @name Algebraic Numbers */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Return \c true if \c a can be used as value in the Z3 real algebraic
 | 
			
		||||
       number package.
 | 
			
		||||
| 
						 | 
				
			
			@ -240,8 +240,8 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    unsigned Z3_API Z3_algebraic_get_i(Z3_context c, Z3_ast a);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										162
									
								
								src/api/z3_api.h
									
										
									
									
									
								
							
							
						
						
									
										162
									
								
								src/api/z3_api.h
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -37,11 +37,11 @@ DEFINE_TYPE(Z3_optimize);
 | 
			
		|||
DEFINE_TYPE(Z3_rcf_num);
 | 
			
		||||
 | 
			
		||||
/** \defgroup capi C API */
 | 
			
		||||
/*@{*/
 | 
			
		||||
/**@{*/
 | 
			
		||||
 | 
			
		||||
/** @name Types */
 | 
			
		||||
 | 
			
		||||
///@{
 | 
			
		||||
/**@{*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   Most of the types in the C API are opaque pointers.
 | 
			
		||||
| 
						 | 
				
			
			@ -1449,7 +1449,7 @@ typedef enum
 | 
			
		|||
    Z3_GOAL_UNDER_OVER
 | 
			
		||||
} Z3_goal_prec;
 | 
			
		||||
 | 
			
		||||
///@}
 | 
			
		||||
/**@}*/
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
| 
						 | 
				
			
			@ -1514,7 +1514,7 @@ extern "C" {
 | 
			
		|||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Create configuration */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
        \brief Create a configuration object for the Z3 context object.
 | 
			
		||||
| 
						 | 
				
			
			@ -1569,10 +1569,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    void Z3_API Z3_set_param_value(Z3_config c, Z3_string param_id, Z3_string param_value);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Context and AST Reference Counting */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a context using the given configuration.
 | 
			
		||||
| 
						 | 
				
			
			@ -1678,10 +1678,10 @@ extern "C" {
 | 
			
		|||
    void Z3_API Z3_interrupt(Z3_context c);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Parameters */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a Z3 (empty) parameter set.
 | 
			
		||||
| 
						 | 
				
			
			@ -1754,10 +1754,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    void Z3_API Z3_params_validate(Z3_context c, Z3_params p, Z3_param_descrs d);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Parameter Descriptions */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Increment the reference counter of the given parameter description set.
 | 
			
		||||
| 
						 | 
				
			
			@ -1811,10 +1811,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Symbols */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a Z3 symbol using an integer.
 | 
			
		||||
| 
						 | 
				
			
			@ -1843,10 +1843,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, Z3_string s);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Sorts */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a free (uninterpreted) type using the given name (symbol).
 | 
			
		||||
| 
						 | 
				
			
			@ -2150,10 +2150,10 @@ extern "C" {
 | 
			
		|||
                                     Z3_func_decl* tester,
 | 
			
		||||
                                     Z3_func_decl accessors[]);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Constants and Applications */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Declare a constant or function.
 | 
			
		||||
| 
						 | 
				
			
			@ -2287,10 +2287,10 @@ extern "C" {
 | 
			
		|||
     */
 | 
			
		||||
    void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Propositional Logic and Equality */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
        \brief Create an AST node representing \c true.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2397,10 +2397,10 @@ extern "C" {
 | 
			
		|||
       def_API('Z3_mk_or', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST)))
 | 
			
		||||
    */
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_or(Z3_context c, unsigned num_args, Z3_ast const args[]);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Integers and Reals */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create an AST node representing \ccode{args[0] + ... + args[num_args-1]}.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2573,10 +2573,10 @@ extern "C" {
 | 
			
		|||
        def_API('Z3_mk_is_int', AST, (_in(CONTEXT), _in(AST)))
 | 
			
		||||
    */
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_is_int(Z3_context c, Z3_ast t1);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Bit-vectors */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Bitwise negation.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3097,10 +3097,10 @@ extern "C" {
 | 
			
		|||
       def_API('Z3_mk_bvmul_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST)))
 | 
			
		||||
    */
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Arrays */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Array read.
 | 
			
		||||
       The argument \c a is the array and \c i is the index of the array that gets read.
 | 
			
		||||
| 
						 | 
				
			
			@ -3212,10 +3212,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_set_has_size(Z3_context c, Z3_ast set, Z3_ast k);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Sets */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create Set type.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3308,10 +3308,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_array_ext(Z3_context c, Z3_ast arg1, Z3_ast arg2);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Numerals */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a numeral of a given sort.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3400,10 +3400,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_bv_numeral(Z3_context c, unsigned sz, bool const* bits);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Sequences and regular expressions */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a sequence sort out of the sort for the elements.
 | 
			
		||||
| 
						 | 
				
			
			@ -3480,6 +3480,11 @@ extern "C" {
 | 
			
		|||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a string constant out of the string that is passed in
 | 
			
		||||
       The string may contain escape encoding for non-printable characters
 | 
			
		||||
       or characters outside of the basic printable ASCII range. For example, 
 | 
			
		||||
       the escape encoding \u{0} represents the character 0 and the encoding
 | 
			
		||||
       \u{100} represents the character 256.
 | 
			
		||||
 | 
			
		||||
       def_API('Z3_mk_string', AST, (_in(CONTEXT), _in(STRING)))
 | 
			
		||||
     */
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string s);
 | 
			
		||||
| 
						 | 
				
			
			@ -3487,12 +3492,22 @@ extern "C" {
 | 
			
		|||
    /**
 | 
			
		||||
       \brief Create a string constant out of the string that is passed in
 | 
			
		||||
       It takes the length of the string as well to take into account
 | 
			
		||||
       0 characters. The string is unescaped.
 | 
			
		||||
       0 characters. The string is treated as if it is unescaped so a sequence
 | 
			
		||||
       of characters \u{0} is treated as 5 characters and not the character 0.
 | 
			
		||||
 | 
			
		||||
       def_API('Z3_mk_lstring', AST, (_in(CONTEXT), _in(UINT), _in(STRING)))
 | 
			
		||||
     */
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned len, Z3_string s);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a string constant out of the string that is passed in
 | 
			
		||||
       It takes the length of the string as well to take into account
 | 
			
		||||
       0 characters. The string is unescaped.
 | 
			
		||||
 | 
			
		||||
       def_API('Z3_mk_u32string', AST, (_in(CONTEXT), _in(UINT), _in_array(1, UINT)))
 | 
			
		||||
     */
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_u32string(Z3_context c, unsigned len, unsigned const chars[]);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Determine if \c s is a string constant.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3502,6 +3517,7 @@ extern "C" {
 | 
			
		|||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Retrieve the string constant stored in \c s.
 | 
			
		||||
       Characters outside the basic printiable ASCII range are escaped.
 | 
			
		||||
 | 
			
		||||
       \pre  Z3_is_string(c, s)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3510,14 +3526,36 @@ extern "C" {
 | 
			
		|||
    Z3_string Z3_API Z3_get_string(Z3_context c, Z3_ast s);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Retrieve the unescaped string constant stored in \c s.
 | 
			
		||||
       \brief Retrieve the string constant stored in \c s. The string can contain escape sequences.
 | 
			
		||||
       Characters in the range 1 to 255 are literal. 
 | 
			
		||||
       Characters in the range 0, and 256 above are escaped.
 | 
			
		||||
 | 
			
		||||
       \pre  Z3_is_string(c, s)
 | 
			
		||||
 | 
			
		||||
       def_API('Z3_get_lstring', CHAR_PTR, (_in(CONTEXT), _in(AST), _out(UINT)))
 | 
			
		||||
     */
 | 
			
		||||
    Z3_char_ptr Z3_API Z3_get_lstring(Z3_context c, Z3_ast s, unsigned* length);
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Retrieve the length of the unescaped string constant stored in \c s. 
 | 
			
		||||
 | 
			
		||||
       \pre  Z3_is_string(c, s)
 | 
			
		||||
 | 
			
		||||
       def_API('Z3_get_string_length', UINT, (_in(CONTEXT), _in(AST)))
 | 
			
		||||
    */
 | 
			
		||||
    unsigned Z3_API Z3_get_string_length(Z3_context c, Z3_ast s);
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Retrieve the unescaped string constant stored in \c s. 
 | 
			
		||||
 | 
			
		||||
       \pre  Z3_is_string(c, s)
 | 
			
		||||
 | 
			
		||||
       \pre length contains the number of characters in s
 | 
			
		||||
 | 
			
		||||
       def_API('Z3_get_string_contents', VOID, (_in(CONTEXT), _in(AST), _in(UINT), _out_array(2, UINT)))
 | 
			
		||||
     */
 | 
			
		||||
    void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned contents[]);
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create an empty sequence of the sequence sort \c seq.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3821,11 +3859,11 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_char_is_digit(Z3_context c, Z3_ast ch);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /** @name Special relations */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief create a linear ordering relation over signature \c a.
 | 
			
		||||
       The relation is identified by the index \c id.
 | 
			
		||||
| 
						 | 
				
			
			@ -3866,10 +3904,10 @@ extern "C" {
 | 
			
		|||
     */
 | 
			
		||||
    Z3_func_decl Z3_API Z3_mk_transitive_closure(Z3_context c, Z3_func_decl f);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Quantifiers */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a pattern for quantifier instantiation.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4174,10 +4212,10 @@ extern "C" {
 | 
			
		|||
                                     Z3_ast body);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Accessors */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Return \c Z3_INT_SYMBOL if the symbol was constructed
 | 
			
		||||
       using #Z3_mk_int_symbol, and \c Z3_STRING_SYMBOL if the symbol
 | 
			
		||||
| 
						 | 
				
			
			@ -5112,10 +5150,10 @@ extern "C" {
 | 
			
		|||
       def_API('Z3_simplify_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT),))
 | 
			
		||||
    */
 | 
			
		||||
    Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(Z3_context c);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Modifiers */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Update the arguments of term \c a using the arguments \c args.
 | 
			
		||||
       The number of arguments \c num_args should coincide
 | 
			
		||||
| 
						 | 
				
			
			@ -5158,10 +5196,10 @@ extern "C" {
 | 
			
		|||
       def_API('Z3_translate', AST, (_in(CONTEXT), _in(AST), _in(CONTEXT)))
 | 
			
		||||
    */
 | 
			
		||||
    Z3_ast Z3_API Z3_translate(Z3_context source, Z3_ast a, Z3_context target);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Models */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a fresh model object. It has reference count 0.
 | 
			
		||||
| 
						 | 
				
			
			@ -5496,10 +5534,10 @@ extern "C" {
 | 
			
		|||
       def_API('Z3_func_entry_get_arg', AST, (_in(CONTEXT), _in(FUNC_ENTRY), _in(UINT)))
 | 
			
		||||
    */
 | 
			
		||||
    Z3_ast Z3_API Z3_func_entry_get_arg(Z3_context c, Z3_func_entry e, unsigned i);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Interaction logging */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Log interaction to a file.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5534,10 +5572,10 @@ extern "C" {
 | 
			
		|||
       def_API('Z3_toggle_warning_messages', VOID, (_in(BOOL),))
 | 
			
		||||
    */
 | 
			
		||||
    void Z3_API Z3_toggle_warning_messages(bool enabled);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name String conversion */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Select mode for the format used for pretty-printing AST nodes.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5624,10 +5662,10 @@ extern "C" {
 | 
			
		|||
                                                   Z3_ast const assumptions[],
 | 
			
		||||
                                                   Z3_ast formula);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Parser interface */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Parse the given string using the SMT-LIB2 parser.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5671,10 +5709,10 @@ extern "C" {
 | 
			
		|||
 | 
			
		||||
    Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Error Handling */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
#ifndef SAFE_ERRORS
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Return the error code for the last API call.
 | 
			
		||||
| 
						 | 
				
			
			@ -5717,10 +5755,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    Z3_string Z3_API Z3_get_error_msg(Z3_context c, Z3_error_code err);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Miscellaneous */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Return Z3 version number information.
 | 
			
		||||
| 
						 | 
				
			
			@ -5781,10 +5819,10 @@ extern "C" {
 | 
			
		|||
       def_API('Z3_finalize_memory', VOID, ())
 | 
			
		||||
    */
 | 
			
		||||
    void Z3_API Z3_finalize_memory(void);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Goals */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a goal (aka problem). A goal is essentially a set
 | 
			
		||||
       of formulas, that can be solved and/or transformed using
 | 
			
		||||
| 
						 | 
				
			
			@ -5934,10 +5972,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g, bool include_names);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Tactics and Probes */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Return a tactic associated with the given name.
 | 
			
		||||
       The complete list of tactics may be obtained using the procedures #Z3_get_num_tactics and #Z3_get_tactic_name.
 | 
			
		||||
| 
						 | 
				
			
			@ -6286,10 +6324,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Solvers*/
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a new solver. This solver is a "combined solver" (see
 | 
			
		||||
       combined_solver module) that internally uses a non-incremental (solver1) and an
 | 
			
		||||
| 
						 | 
				
			
			@ -6812,10 +6850,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    Z3_string Z3_API Z3_solver_to_dimacs_string(Z3_context c, Z3_solver s, bool include_names);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name Statistics */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Convert a statistics into a string.
 | 
			
		||||
| 
						 | 
				
			
			@ -6897,11 +6935,11 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    uint64_t Z3_API Z3_get_estimated_alloc_size(void);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif // __cplusplus
 | 
			
		||||
 | 
			
		||||
/*@}*/
 | 
			
		||||
/**@}*/
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,10 +23,10 @@ extern "C" {
 | 
			
		|||
#endif // __cplusplus
 | 
			
		||||
 | 
			
		||||
    /** \defgroup capi C API */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /** @name AST vectors */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Return an empty AST vector.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -104,10 +104,10 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
    /** @name AST maps */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
    \brief Return an empty mapping from AST to AST
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -189,8 +189,8 @@ extern "C" {
 | 
			
		|||
    def_API('Z3_ast_map_to_string', STRING, (_in(CONTEXT), _in(AST_MAP)))
 | 
			
		||||
    */
 | 
			
		||||
    Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,10 +23,10 @@ extern "C" {
 | 
			
		|||
#endif // __cplusplus
 | 
			
		||||
 | 
			
		||||
    /** \defgroup capi C API */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /** @name Fixedpoint facilities */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a new fixedpoint context.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -373,8 +373,8 @@ extern "C" {
 | 
			
		|||
 | 
			
		||||
    void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,10 +23,10 @@ extern "C" {
 | 
			
		|||
#endif // __cplusplus
 | 
			
		||||
 | 
			
		||||
    /** \defgroup capi C API */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /** @name Floating-Point Arithmetic */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
        \brief Create the RoundingMode sort.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -841,7 +841,7 @@ extern "C" {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
    /** @name Z3-specific floating-point extensions */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
        \brief Retrieves the number of bits reserved for the exponent in a FloatingPoint sort.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1080,9 +1080,9 @@ extern "C" {
 | 
			
		|||
        def_API('Z3_mk_fpa_to_fp_int_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(SORT)))
 | 
			
		||||
    */
 | 
			
		||||
    Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(Z3_context c, Z3_ast rm, Z3_ast exp, Z3_ast sig, Z3_sort s);
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,10 +28,10 @@ extern "C" {
 | 
			
		|||
#endif // __cplusplus
 | 
			
		||||
 | 
			
		||||
    /** \defgroup capi C API */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /** @name Optimization facilities */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Create a new optimize context.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -368,8 +368,8 @@ extern "C" {
 | 
			
		|||
        Z3_model_eh  model_eh);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,11 +24,11 @@ extern "C" {
 | 
			
		|||
#endif // __cplusplus
 | 
			
		||||
 | 
			
		||||
    /** \defgroup capi C API */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /** @name Polynomials */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x.
 | 
			
		||||
| 
						 | 
				
			
			@ -43,8 +43,8 @@ extern "C" {
 | 
			
		|||
    Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,10 +26,10 @@ extern "C" {
 | 
			
		|||
#endif // __cplusplus
 | 
			
		||||
 | 
			
		||||
    /** \defgroup capi C API */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /** @name Real Closed Fields */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
       \brief Delete a RCF numeral created using the RCF API.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -196,8 +196,8 @@ extern "C" {
 | 
			
		|||
    */
 | 
			
		||||
    void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,10 +23,10 @@ extern "C" {
 | 
			
		|||
#endif // __cplusplus
 | 
			
		||||
 | 
			
		||||
    /** \defgroup capi C API */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
 | 
			
		||||
    /** @name Spacer facilities */
 | 
			
		||||
    /*@{*/
 | 
			
		||||
    /**@{*/
 | 
			
		||||
    /**
 | 
			
		||||
        \brief Pose a query against the asserted rules at the given level.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -132,8 +132,8 @@ extern "C" {
 | 
			
		|||
       Z3_ast_vector vars,
 | 
			
		||||
       Z3_ast body);
 | 
			
		||||
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /*@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
    /**@}*/
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1399,6 +1399,7 @@ inline bool has_labels(expr const * n) {
 | 
			
		|||
class some_value_proc {
 | 
			
		||||
public:
 | 
			
		||||
    virtual expr * operator()(sort * s) = 0;
 | 
			
		||||
    virtual ~some_value_proc() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// -----------------------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,7 @@ public:
 | 
			
		|||
    public:
 | 
			
		||||
        virtual bool operator()(func_decl* d) const { return false; }
 | 
			
		||||
        virtual bool operator()(sort* s) const { return false; }
 | 
			
		||||
        virtual ~is_declared() = default;
 | 
			
		||||
    };
 | 
			
		||||
private:
 | 
			
		||||
    ast_manager& m_manager;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -406,7 +406,7 @@ namespace datatype {
 | 
			
		|||
            VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
 | 
			
		||||
            VALIDATE_PARAM(u().is_datatype(domain[0]));
 | 
			
		||||
            VALIDATE_PARAM_PP(domain[0] == to_func_decl(parameters[0].get_ast())->get_range(), "invalid sort argument passed to recognizer");
 | 
			
		||||
            // blindly trust that parameter is a constructor
 | 
			
		||||
            VALIDATE_PARAM_PP(u().is_constructor(to_func_decl(parameters[0].get_ast())), "expecting constructor argument to recognizer");
 | 
			
		||||
            sort* range = m_manager->mk_bool_sort();
 | 
			
		||||
            func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters);
 | 
			
		||||
            info.m_private_parameters = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -681,6 +681,7 @@ namespace euf {
 | 
			
		|||
    void egraph::begin_explain() {
 | 
			
		||||
        SASSERT(m_todo.empty());
 | 
			
		||||
        m_uses_congruence = false;
 | 
			
		||||
        DEBUG_CODE(for (enode* n : m_nodes) SASSERT(!n->is_marked1()););
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void egraph::end_explain() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ expr_ref bv2fpa_converter::convert_bv2fp(sort * s, expr * sgn, expr * exp, expr
 | 
			
		|||
    rational exp_unbiased_q;
 | 
			
		||||
    exp_unbiased_q = exp_q - m_fpa_util.fm().m_powers2.m1(ebits - 1);
 | 
			
		||||
 | 
			
		||||
    scoped_mpz sig_z(mpzm); 
 | 
			
		||||
    scoped_mpz sig_z(mpzm);
 | 
			
		||||
    mpf_exp_t exp_z;
 | 
			
		||||
    mpzm.set(sig_z, sig_q.to_mpq().numerator());
 | 
			
		||||
    exp_z = mpzm.get_int64(exp_unbiased_q.to_mpq().numerator());
 | 
			
		||||
| 
						 | 
				
			
			@ -346,7 +346,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
 | 
			
		|||
        app * a0 = to_app(val->get_arg(0));
 | 
			
		||||
 | 
			
		||||
        expr_ref v0(m), v1(m), v2(m);
 | 
			
		||||
#ifdef Z3DEBUG
 | 
			
		||||
#ifdef Z3DEBUG_FPA2BV_NAMES
 | 
			
		||||
        app * a1 = to_app(val->get_arg(1));
 | 
			
		||||
        app * a2 = to_app(val->get_arg(2));
 | 
			
		||||
        v0 = mc->get_const_interp(a0->get_decl());
 | 
			
		||||
| 
						 | 
				
			
			@ -378,7 +378,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
 | 
			
		|||
 | 
			
		||||
        SASSERT(val->is_app_of(m_fpa_util.get_family_id(), OP_FPA_FP));
 | 
			
		||||
 | 
			
		||||
#ifdef Z3DEBUG
 | 
			
		||||
#ifdef Z3DEBUG_FPA2BV_NAMES
 | 
			
		||||
        SASSERT(to_app(val->get_arg(0))->get_decl()->get_arity() == 0);
 | 
			
		||||
        SASSERT(to_app(val->get_arg(1))->get_decl()->get_arity() == 0);
 | 
			
		||||
        SASSERT(to_app(val->get_arg(2))->get_decl()->get_arity() == 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -386,9 +386,10 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
 | 
			
		|||
        seen.insert(to_app(val->get_arg(1))->get_decl());
 | 
			
		||||
        seen.insert(to_app(val->get_arg(2))->get_decl());
 | 
			
		||||
#else
 | 
			
		||||
        SASSERT(a->get_arg(0)->get_kind() == OP_EXTRACT);
 | 
			
		||||
        SASSERT(to_app(a->get_arg(0))->get_arg(0)->get_kind() == OP_EXTRACT);
 | 
			
		||||
        SASSERT(is_app(val->get_arg(0)));
 | 
			
		||||
        SASSERT(m_bv_util.is_extract(val->get_arg(0)));
 | 
			
		||||
        seen.insert(to_app(to_app(val->get_arg(0))->get_arg(0))->get_decl());
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        if (!sgn && !sig && !exp)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -192,7 +192,7 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) {
 | 
			
		|||
 | 
			
		||||
        app_ref sgn(m), s(m), e(m);
 | 
			
		||||
 | 
			
		||||
#ifdef Z3DEBUG
 | 
			
		||||
#ifdef Z3DEBUG_FPA2BV_NAMES
 | 
			
		||||
        std::string p("fpa2bv");
 | 
			
		||||
        std::string name = f->get_name().str();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -326,7 +326,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) {
 | 
			
		|||
 | 
			
		||||
        expr_ref bv3(m);
 | 
			
		||||
        bv3 = m.mk_fresh_const(
 | 
			
		||||
#ifdef Z3DEBUG
 | 
			
		||||
#ifdef Z3DEBUG_FPA2BV_NAMES
 | 
			
		||||
            "fpa2bv_rm"
 | 
			
		||||
#else
 | 
			
		||||
            nullptr
 | 
			
		||||
| 
						 | 
				
			
			@ -465,7 +465,7 @@ void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits,
 | 
			
		|||
 | 
			
		||||
    SASSERT(is_well_sorted(m, big_d_sig));
 | 
			
		||||
    if (ebits > sbits)
 | 
			
		||||
        throw default_exception("there is no floating point support for division for representations with non-standard bit representations");
 | 
			
		||||
        throw default_exception("addition/subtract with ebits > sbits not supported");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    expr_ref shifted_big(m), shifted_d_sig(m), sticky_raw(m), sticky(m);
 | 
			
		||||
| 
						 | 
				
			
			@ -950,7 +950,7 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref &
 | 
			
		|||
    unsigned ebits = m_util.get_ebits(s);
 | 
			
		||||
    unsigned sbits = m_util.get_sbits(s);
 | 
			
		||||
    if (ebits > sbits)
 | 
			
		||||
        throw default_exception("there is no floating point support for division for representations with non-standard bit representations");
 | 
			
		||||
        throw default_exception("division with ebits > sbits not supported");
 | 
			
		||||
    SASSERT(ebits <= sbits);
 | 
			
		||||
 | 
			
		||||
    expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m);
 | 
			
		||||
| 
						 | 
				
			
			@ -2561,9 +2561,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
 | 
			
		|||
            res_sig = sig;
 | 
			
		||||
 | 
			
		||||
        res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder.
 | 
			
		||||
        unsigned sig_sz = m_bv_util.get_bv_size(res_sig);
 | 
			
		||||
        (void) sig_sz;
 | 
			
		||||
        SASSERT(sig_sz == to_sbits + 4);
 | 
			
		||||
        SASSERT(m_bv_util.get_bv_size(res_sig) == to_sbits + 4);
 | 
			
		||||
 | 
			
		||||
        expr_ref exponent_overflow(m), exponent_underflow(m);
 | 
			
		||||
        exponent_overflow = m.mk_false();
 | 
			
		||||
| 
						 | 
				
			
			@ -2577,7 +2575,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
 | 
			
		|||
            lz_ext = m_bv_util.mk_zero_extend(to_ebits - from_ebits + 2, lz);
 | 
			
		||||
            res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext);
 | 
			
		||||
        }
 | 
			
		||||
        else if (from_ebits > (to_ebits + 2)) {
 | 
			
		||||
        else if (from_ebits >= (to_ebits + 2)) {
 | 
			
		||||
            unsigned ebits_diff = from_ebits - (to_ebits + 2);
 | 
			
		||||
 | 
			
		||||
            // subtract lz for subnormal numbers.
 | 
			
		||||
| 
						 | 
				
			
			@ -2617,9 +2615,6 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
 | 
			
		|||
            res_exp = m.mk_ite(ovf_cond, max_exp, res_exp);
 | 
			
		||||
            res_exp = m.mk_ite(udf_cond, min_exp, res_exp);
 | 
			
		||||
        }
 | 
			
		||||
        else { // from_ebits == (to_ebits + 2)
 | 
			
		||||
            res_exp = m_bv_util.mk_bv_sub(exp, lz);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits + 2);
 | 
			
		||||
        SASSERT(is_well_sorted(m, res_exp));
 | 
			
		||||
| 
						 | 
				
			
			@ -3839,7 +3834,7 @@ void fpa2bv_converter::mk_rounding_mode(decl_kind k, expr_ref & result)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) {
 | 
			
		||||
#ifdef Z3DEBUG
 | 
			
		||||
#ifdef Z3DEBUG_FPA2BV_NAMES
 | 
			
		||||
    return;
 | 
			
		||||
    // CMW: This works only for quantifier-free formulas.
 | 
			
		||||
    if (m_util.is_fp(e)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,7 +61,7 @@ protected:
 | 
			
		|||
public:
 | 
			
		||||
 | 
			
		||||
    fpa2bv_converter(ast_manager & m);
 | 
			
		||||
    ~fpa2bv_converter();
 | 
			
		||||
    virtual ~fpa2bv_converter();
 | 
			
		||||
 | 
			
		||||
    fpa_util & fu() { return m_util; }
 | 
			
		||||
    bv_util & bu() { return m_bv_util; }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -207,7 +207,7 @@ sort * fpa_decl_plugin::mk_float_sort(unsigned ebits, unsigned sbits) {
 | 
			
		|||
        m_manager->raise_exception("minimum number of exponent bits is 2");
 | 
			
		||||
    if (ebits > 63)
 | 
			
		||||
        m_manager->raise_exception("maximum number of exponent bits is 63");
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    parameter p1(ebits), p2(sbits);
 | 
			
		||||
    parameter ps[2] = { p1, p2 };
 | 
			
		||||
    sort_size sz;
 | 
			
		||||
| 
						 | 
				
			
			@ -929,16 +929,22 @@ bool fpa_decl_plugin::is_unique_value(app* e) const {
 | 
			
		|||
    case OP_FPA_RM_TOWARD_NEGATIVE:
 | 
			
		||||
    case OP_FPA_RM_TOWARD_ZERO:
 | 
			
		||||
        return true;
 | 
			
		||||
    case OP_FPA_PLUS_INF:  /* No; +oo == fp(#b0 #b11 #b00) */
 | 
			
		||||
    case OP_FPA_MINUS_INF: /* No; -oo == fp #b1 #b11 #b00) */
 | 
			
		||||
    case OP_FPA_PLUS_ZERO: /* No; +zero == fp #b0 #b00 #b000) */
 | 
			
		||||
    case OP_FPA_MINUS_ZERO: /* No; -zero == fp #b1 #b00 #b000) */
 | 
			
		||||
    case OP_FPA_PLUS_INF:  /* No; +oo == (fp #b0 #b11 #b00) */
 | 
			
		||||
    case OP_FPA_MINUS_INF: /* No; -oo == (fp #b1 #b11 #b00) */
 | 
			
		||||
    case OP_FPA_PLUS_ZERO: /* No; +zero == (fp #b0 #b00 #b000) */
 | 
			
		||||
    case OP_FPA_MINUS_ZERO: /* No; -zero == (fp #b1 #b00 #b000) */
 | 
			
		||||
    case OP_FPA_NAN: /* No; NaN == (fp #b0 #b111111 #b0000001) */
 | 
			
		||||
    case OP_FPA_NUM: /* see NaN */
 | 
			
		||||
        return false;
 | 
			
		||||
    case OP_FPA_FP:
 | 
			
		||||
        return false; /*No; generally not because of clashes with +oo, -oo, +zero, -zero, NaN */
 | 
			
		||||
        // a refinement would require to return true only if there is no clash with these cases.
 | 
			
		||||
    case OP_FPA_FP: {
 | 
			
		||||
        if (m_manager->is_value(e->get_arg(0)) &&
 | 
			
		||||
            m_manager->is_value(e->get_arg(1)) &&
 | 
			
		||||
            m_manager->is_value(e->get_arg(2))) {
 | 
			
		||||
          bv_util bu(*m_manager);
 | 
			
		||||
          return !bu.is_allone(e->get_arg(1)) && !bu.is_zero(e->get_arg(1));
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ Revision History:
 | 
			
		|||
 | 
			
		||||
class is_variable_proc {
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~is_variable_proc() = default;
 | 
			
		||||
    virtual bool operator()(const expr* e) const = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ Notes:
 | 
			
		|||
 | 
			
		||||
class expr_predicate {
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~expr_predicate() = default;
 | 
			
		||||
    virtual bool operator()(expr * t) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,8 @@ public:
 | 
			
		|||
        m_ignore_quantifiers(ignore_quantifiers) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual ~num_occurs() = default;
 | 
			
		||||
 | 
			
		||||
    void validate();
 | 
			
		||||
    virtual void reset() { m_num_occurs.reset(); }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -93,6 +93,7 @@ namespace recfun {
 | 
			
		|||
    // closure for computing whether a `rhs` expression is immediate
 | 
			
		||||
    struct is_immediate_pred {
 | 
			
		||||
        virtual bool operator()(expr * rhs) = 0;
 | 
			
		||||
        virtual ~is_immediate_pred() = default;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class def {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -703,8 +703,60 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
 | 
			
		|||
        result = m().update_quantifier(lam, quantifier_kind::forall_k, e);
 | 
			
		||||
        return BR_REWRITE2; 
 | 
			
		||||
    }
 | 
			
		||||
    expr_ref lh1(m()), rh1(m());
 | 
			
		||||
 | 
			
		||||
    expr_ref_vector fmls(m());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    auto has_large_domain = [&](sort* s, unsigned num_stores) {
 | 
			
		||||
        unsigned sz = get_array_arity(s);
 | 
			
		||||
        uint64_t dsz = 1;
 | 
			
		||||
        for (unsigned i = 0; i < sz; ++i) {
 | 
			
		||||
            sort* d = get_array_domain(s, i);
 | 
			
		||||
            if (d->is_infinite() || d->is_very_big())
 | 
			
		||||
                return true;
 | 
			
		||||
            auto const& n = d->get_num_elements();
 | 
			
		||||
            if (n.size() > num_stores)
 | 
			
		||||
                return true;
 | 
			
		||||
            dsz *= n.size();
 | 
			
		||||
            if (dsz > num_stores)
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;        
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (m_expand_store_eq) {
 | 
			
		||||
        expr* lhs1 = lhs;
 | 
			
		||||
        expr* rhs1 = rhs;
 | 
			
		||||
        unsigned num_lhs = 0, num_rhs = 0;
 | 
			
		||||
        while (m_util.is_store(lhs1)) {
 | 
			
		||||
            lhs1 = to_app(lhs1)->get_arg(0);
 | 
			
		||||
            ++num_lhs;
 | 
			
		||||
        }
 | 
			
		||||
        while (m_util.is_store(rhs1)) {
 | 
			
		||||
            rhs1 = to_app(rhs1)->get_arg(0);
 | 
			
		||||
            ++num_rhs;
 | 
			
		||||
        }
 | 
			
		||||
        if (lhs1 == rhs1) {
 | 
			
		||||
            mk_eq(lhs, lhs, rhs, fmls);
 | 
			
		||||
            mk_eq(rhs, lhs, rhs, fmls);
 | 
			
		||||
            result = m().mk_and(fmls);
 | 
			
		||||
            return BR_REWRITE_FULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) &&
 | 
			
		||||
            has_large_domain(lhs->get_sort(), std::max(num_lhs, num_rhs))) {
 | 
			
		||||
            mk_eq(lhs, lhs, rhs, fmls);
 | 
			
		||||
            mk_eq(rhs, lhs, rhs, fmls);
 | 
			
		||||
            fmls.push_back(m().mk_eq(v, w));
 | 
			
		||||
            result = m().mk_and(fmls);
 | 
			
		||||
            return BR_REWRITE_FULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (m_expand_nested_stores) {
 | 
			
		||||
        expr_ref lh1(m()), rh1(m());
 | 
			
		||||
        if (is_expandable_store(lhs)) {
 | 
			
		||||
            lh1 = expand_store(lhs);        
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -719,10 +771,6 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!m_expand_store_eq) {
 | 
			
		||||
        return BR_FAILED;
 | 
			
		||||
    }
 | 
			
		||||
    expr_ref_vector fmls(m());
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
    // lambda friendly version of array equality rewriting.
 | 
			
		||||
| 
						 | 
				
			
			@ -744,46 +792,5 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
 | 
			
		|||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    expr* lhs1 = lhs;
 | 
			
		||||
    unsigned num_lhs = 0, num_rhs = 0;
 | 
			
		||||
    while (m_util.is_store(lhs1)) {
 | 
			
		||||
        lhs1 = to_app(lhs1)->get_arg(0);
 | 
			
		||||
        ++num_lhs;
 | 
			
		||||
    }
 | 
			
		||||
    expr* rhs1 = rhs;
 | 
			
		||||
    while (m_util.is_store(rhs1)) {
 | 
			
		||||
        rhs1 = to_app(rhs1)->get_arg(0);
 | 
			
		||||
        ++num_rhs;
 | 
			
		||||
    }
 | 
			
		||||
    if (lhs1 == rhs1) {
 | 
			
		||||
        mk_eq(lhs, lhs, rhs, fmls);
 | 
			
		||||
        mk_eq(rhs, lhs, rhs, fmls);
 | 
			
		||||
        result = m().mk_and(fmls);
 | 
			
		||||
        return BR_REWRITE_FULL;
 | 
			
		||||
    }
 | 
			
		||||
    auto has_large_domain = [&](sort* s, unsigned num_stores) {
 | 
			
		||||
        unsigned sz = get_array_arity(s);
 | 
			
		||||
        uint64_t dsz = 1;
 | 
			
		||||
        for (unsigned i = 0; i < sz; ++i) {
 | 
			
		||||
            sort* d = get_array_domain(s, i);
 | 
			
		||||
            if (d->is_infinite() || d->is_very_big())
 | 
			
		||||
                return true;
 | 
			
		||||
            auto const& n = d->get_num_elements();
 | 
			
		||||
            if (n.size() > num_stores)
 | 
			
		||||
                return true;
 | 
			
		||||
            dsz *= n.size();
 | 
			
		||||
            if (dsz > num_stores)
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;        
 | 
			
		||||
    };
 | 
			
		||||
    if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) &&
 | 
			
		||||
        has_large_domain(lhs->get_sort(), std::max(num_lhs, num_rhs))) {
 | 
			
		||||
        mk_eq(lhs, lhs, rhs, fmls);
 | 
			
		||||
        mk_eq(rhs, lhs, rhs, fmls);
 | 
			
		||||
        fmls.push_back(m().mk_eq(v, w));
 | 
			
		||||
        result = m().mk_and(fmls);
 | 
			
		||||
        return BR_REWRITE_FULL;
 | 
			
		||||
    }
 | 
			
		||||
    return BR_FAILED;    
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ struct push_app_ite_cfg : public default_rewriter_cfg {
 | 
			
		|||
    virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
 | 
			
		||||
    br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr);
 | 
			
		||||
    push_app_ite_cfg(ast_manager& m): m(m), m_conservative(true) {}
 | 
			
		||||
    virtual ~push_app_ite_cfg() = default;
 | 
			
		||||
    void set_conservative(bool c) { m_conservative = c; }
 | 
			
		||||
    bool rewrite_patterns() const { return false; }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,10 +22,9 @@ Author:
 | 
			
		|||
    
 | 
			
		||||
br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
 | 
			
		||||
    if (m_rec.is_defined(f) && num_args > 0) {
 | 
			
		||||
        for (unsigned i = 0; i < num_args; ++i) {
 | 
			
		||||
        for (unsigned i = 0; i < num_args; ++i) 
 | 
			
		||||
            if (!m.is_value(args[i]))
 | 
			
		||||
                return BR_FAILED;
 | 
			
		||||
        }
 | 
			
		||||
        if (!m_rec.has_def(f))
 | 
			
		||||
            return BR_FAILED;
 | 
			
		||||
        recfun::def const& d = m_rec.get_def(f);
 | 
			
		||||
| 
						 | 
				
			
			@ -35,9 +34,8 @@ br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr *
 | 
			
		|||
        result = sub(d.get_rhs(), num_args, args);
 | 
			
		||||
        return BR_REWRITE_FULL;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
    else 
 | 
			
		||||
        return BR_FAILED;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -850,7 +850,7 @@ namespace seq {
 | 
			
		|||
            add_clause(~eq, ge10k);
 | 
			
		||||
 | 
			
		||||
        for (unsigned i = 0; i < k; ++i) {
 | 
			
		||||
            expr* ch = seq.str.mk_nth_i(ubvs, i);
 | 
			
		||||
            expr* ch = seq.str.mk_nth_c(ubvs, i);
 | 
			
		||||
            is_digit = seq.mk_char_is_digit(ch);
 | 
			
		||||
            add_clause(~ge_len, is_digit);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1142,8 +1142,8 @@ namespace seq {
 | 
			
		|||
 | 
			
		||||
    /**
 | 
			
		||||
       ~contains(a, b) => ~prefix(b, a)
 | 
			
		||||
       ~contains(a, b) => ~contains(tail(a), b) or a = empty
 | 
			
		||||
       ~contains(a, b) & a = empty => b != empty
 | 
			
		||||
       ~contains(a, b) => ~contains(tail(a), b) 
 | 
			
		||||
       a = empty => tail(a) = empty
 | 
			
		||||
       ~(a = empty) => a = head + tail 
 | 
			
		||||
    */
 | 
			
		||||
    void axioms::unroll_not_contains(expr* e) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1165,7 +1165,7 @@ namespace seq {
 | 
			
		|||
        expr_ref bound_tracker = m_sk.mk_length_limit(s, k);
 | 
			
		||||
        expr* s0 = nullptr;
 | 
			
		||||
        if (seq.str.is_stoi(s, s0)) 
 | 
			
		||||
            s = s0; 
 | 
			
		||||
            s = s0;
 | 
			
		||||
        add_clause(~bound_tracker, mk_le(mk_len(s), k));
 | 
			
		||||
        return bound_tracker;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ Author:
 | 
			
		|||
 | 
			
		||||
--*/
 | 
			
		||||
 | 
			
		||||
#include "ast/ast_pp.h"
 | 
			
		||||
#include "ast/rewriter/seq_eq_solver.h"
 | 
			
		||||
#include "ast/bv_decl_plugin.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -675,7 +676,7 @@ namespace seq {
 | 
			
		|||
            if (rs.size() > i) {
 | 
			
		||||
                unsigned diff = rs.size() - (i + 1);
 | 
			
		||||
                for (unsigned j = 0; same && j < i; ++j) 
 | 
			
		||||
                    same = !m.are_distinct(ls[j], rs[diff + j]);                               
 | 
			
		||||
                    same = !m.are_distinct(ls[j], rs[diff + j]);
 | 
			
		||||
            }
 | 
			
		||||
            // ls = x ++ rs ++ y, diff = |x|
 | 
			
		||||
            else {
 | 
			
		||||
| 
						 | 
				
			
			@ -704,8 +705,9 @@ namespace seq {
 | 
			
		|||
            bool same = true;
 | 
			
		||||
            // ls = x ++ rs' && rs = rs' ++ y, diff = |x|
 | 
			
		||||
            if (rs.size() > i) {
 | 
			
		||||
                for (unsigned j = 1; same && j <= i; ++j) 
 | 
			
		||||
                    same = !m.are_distinct(ls[diff + j], rs[j]);                
 | 
			
		||||
                for (unsigned j = 1; same && j <= i; ++j) {
 | 
			
		||||
                    same = !m.are_distinct(ls[diff + j], rs[j]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // ls = x ++ rs ++ y, diff = |x|
 | 
			
		||||
            else {
 | 
			
		||||
| 
						 | 
				
			
			@ -715,6 +717,7 @@ namespace seq {
 | 
			
		|||
            if (same)
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,6 +39,7 @@ namespace seq {
 | 
			
		|||
 | 
			
		||||
    class eq_solver_context {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~eq_solver_context() = default;
 | 
			
		||||
        virtual void  add_consequence(bool uses_dep, expr_ref_vector const& clause) = 0;
 | 
			
		||||
        virtual void  add_solution(expr* var, expr* term) = 0;
 | 
			
		||||
        virtual expr* expr2rep(expr* e) = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -859,13 +859,12 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
 | 
			
		|||
        // elif offset >= len(s) then 0
 | 
			
		||||
        // elif offset + length > len(s) then len(s) - offset
 | 
			
		||||
        // else length
 | 
			
		||||
        expr_ref zero(m_autil.mk_int(0), m());
 | 
			
		||||
        result = length;
 | 
			
		||||
        result = m().mk_ite(m_autil.mk_gt(m_autil.mk_add(offset, length), len_s),
 | 
			
		||||
                            m_autil.mk_sub(len_s, offset),
 | 
			
		||||
                            result);
 | 
			
		||||
        result = m().mk_ite(m().mk_or(m_autil.mk_le(len_s, offset), m_autil.mk_le(length, zero), m_autil.mk_lt(offset, zero)),
 | 
			
		||||
                            zero,
 | 
			
		||||
        result = m().mk_ite(m().mk_or(m_autil.mk_le(len_s, offset), m_autil.mk_le(length, zero()), m_autil.mk_lt(offset, zero())),
 | 
			
		||||
                            zero(),
 | 
			
		||||
                            result);
 | 
			
		||||
        return BR_REWRITE_FULL;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -883,52 +882,75 @@ expr_ref seq_rewriter::mk_seq_first(expr* t) {
 | 
			
		|||
    if (str().is_extract(t, s, j, k))
 | 
			
		||||
        result = str().mk_nth_i(s, j);
 | 
			
		||||
    else
 | 
			
		||||
        result = str().mk_nth_i(t, m_autil.mk_int(0));
 | 
			
		||||
        result = str().mk_nth_c(t, 0);
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_sub(expr* a, rational const& n) {
 | 
			
		||||
    expr* a1, *a2;
 | 
			
		||||
    SASSERT(n.is_int());
 | 
			
		||||
    rational k;
 | 
			
		||||
    if (m_autil.is_sub(a, a1, a2) && m_autil.is_numeral(a2, k))
 | 
			
		||||
        return expr_ref(m_autil.mk_sub(a1, m_autil.mk_int(k + n)), m());
 | 
			
		||||
    if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a2, k))
 | 
			
		||||
        return expr_ref(m_autil.mk_add(a1, m_autil.mk_int(k - n)), m());        
 | 
			
		||||
    if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a1, k))
 | 
			
		||||
        return expr_ref(m_autil.mk_add(a2, m_autil.mk_int(k - n)), m());
 | 
			
		||||
    return expr_ref(m_autil.mk_sub(a, m_autil.mk_int(n)), m());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
*  In general constructs substring(t,1,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j+1,k-1)
 | 
			
		||||
*  This method assumes that |t| > 0.
 | 
			
		||||
*/
 | 
			
		||||
expr_ref seq_rewriter::mk_seq_rest(expr* t) {
 | 
			
		||||
    expr_ref result(m());
 | 
			
		||||
    expr* s, * j, * k; 
 | 
			
		||||
    expr_ref one(m_autil.mk_int(1), m());
 | 
			
		||||
    if (str().is_extract(t, s, j, k))
 | 
			
		||||
        result = str().mk_substr(s, m_autil.mk_add(j, one), m_autil.mk_sub(k, one));
 | 
			
		||||
    else
 | 
			
		||||
        result = str().mk_substr(t, one, m_autil.mk_sub(str().mk_length(t), one));
 | 
			
		||||
    expr* s, * j, * k;
 | 
			
		||||
    rational jv;
 | 
			
		||||
    if (str().is_extract(t, s, j, k) &&  m_autil.is_numeral(j, jv) && jv >= 0) 
 | 
			
		||||
        result = str().mk_substr(s, m_autil.mk_int(jv + 1), mk_sub(k, 1));
 | 
			
		||||
    else 
 | 
			
		||||
        result = str().mk_substr(t, one(), mk_sub(str().mk_length(t), 1));
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
*  In general constructs nth(t,|t|-1) but if t = substring(s,j,k) then simplifies to nth(s,j+k-1) 
 | 
			
		||||
*  In general constructs nth(t,|t|-1) but if t = substring(s,j,|s|-j) j >= 0, then simplifies to nth(s,|s|-1) 
 | 
			
		||||
*  This method assumes that |t| > 0.
 | 
			
		||||
*/
 | 
			
		||||
expr_ref seq_rewriter::mk_seq_last(expr* t) {
 | 
			
		||||
    expr_ref result(m());
 | 
			
		||||
    expr* s, * j, * k;
 | 
			
		||||
    expr_ref one(m_autil.mk_int(1), m());
 | 
			
		||||
    if (str().is_extract(t, s, j, k))
 | 
			
		||||
        result = str().mk_nth_i(s, m_autil.mk_sub(m_autil.mk_add(j, k), one));
 | 
			
		||||
    expr* s, * j, * k, * s_, * len_s;
 | 
			
		||||
    rational jv, i;
 | 
			
		||||
    if (str().is_extract(t, s, j, k) &&
 | 
			
		||||
        m_autil.is_numeral(j, jv) && jv >= 0 &&
 | 
			
		||||
        str().is_len_sub(k, len_s, s_, i) &&
 | 
			
		||||
        s == s_ && jv == i) {
 | 
			
		||||
        expr_ref lastpos = mk_sub(len_s, 1);
 | 
			
		||||
        result = str().mk_nth_i(s, lastpos);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one));
 | 
			
		||||
        result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one()));
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
*  In general constructs substring(t,0,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j,k-1) 
 | 
			
		||||
*  This method assumes that |t| > 0 holds.
 | 
			
		||||
*  In general constructs substring(t,0,|t|-1) but if t = substring(s,0,k) then simplifies to substring(s,0,k-1) 
 | 
			
		||||
*  This method assumes that |t| > 0, thus, if t = substring(s,0,k) then k > 0 so substring(s,0,k-1) is correct.
 | 
			
		||||
*/
 | 
			
		||||
expr_ref seq_rewriter::mk_seq_butlast(expr* t) {
 | 
			
		||||
    expr_ref result(m());
 | 
			
		||||
    expr* s, * j, * k;
 | 
			
		||||
    expr_ref one(m_autil.mk_int(1), m());
 | 
			
		||||
    if (str().is_extract(t, s, j, k))
 | 
			
		||||
        result = str().mk_substr(s, j, m_autil.mk_sub(k, one));
 | 
			
		||||
    rational v;
 | 
			
		||||
    if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, v) && v.is_zero()) {
 | 
			
		||||
        expr_ref_vector k_min_1(m());
 | 
			
		||||
        k_min_1.push_back(k);
 | 
			
		||||
        k_min_1.push_back(minus_one());
 | 
			
		||||
        result = str().mk_substr(s, j, m_autil.mk_add_simplify(k_min_1));
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        result = str().mk_substr(t, m_autil.mk_int(0), m_autil.mk_sub(str().mk_length(t), one));
 | 
			
		||||
        result = str().mk_substr(t, zero(), m_autil.mk_sub(str().mk_length(t), one()));
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1582,23 +1604,33 @@ br_status seq_rewriter::mk_seq_nth(expr* a, expr* b, expr_ref& result) {
 | 
			
		|||
        result = s;
 | 
			
		||||
        return BR_DONE;
 | 
			
		||||
    }
 | 
			
		||||
    if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1)) {
 | 
			
		||||
    if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1) && pos1 > 0) {
 | 
			
		||||
        expr_ref_vector lens(m());
 | 
			
		||||
        rational pos2;
 | 
			
		||||
        /*
 | 
			
		||||
         * nth(s[k, |s| - k], b) =
 | 
			
		||||
         *   b < 0             -> nth_u(a, b)
 | 
			
		||||
         *   b + k < |s|       -> nth(s, b + k)
 | 
			
		||||
         *   k >= |s|          -> nth_u(empty, b)
 | 
			
		||||
         *   k < |s| <= b + k  -> nth_u(a, b)
 | 
			
		||||
         */
 | 
			
		||||
        if (get_lengths(len, lens, pos2) && (pos1 == -pos2) && (lens.size() == 1) && (lens.get(0) == s)) {
 | 
			
		||||
            expr_ref idx(m_autil.mk_int(pos1), m());
 | 
			
		||||
            idx = m_autil.mk_add(b, idx);
 | 
			
		||||
            expr* es[2] = { s, idx };
 | 
			
		||||
            result = m().mk_app(m_util.get_family_id(), OP_SEQ_NTH, 2, es);
 | 
			
		||||
            expr_ref k(m_autil.mk_int(pos1), m());
 | 
			
		||||
            expr_ref case1(str().mk_nth_i(s, m_autil.mk_add(b, k)), m());
 | 
			
		||||
            expr_ref case2(str().mk_nth_u(str().mk_empty(s->get_sort()), b), m());
 | 
			
		||||
            expr_ref case3(str().mk_nth_u(a, b), m());
 | 
			
		||||
            result = case3;
 | 
			
		||||
            result = m().mk_ite(m_autil.mk_lt(m_autil.mk_add(k, b), str().mk_length(s)), case1, result);
 | 
			
		||||
            result = m().mk_ite(m_autil.mk_ge(k, str().mk_length(s)), case2, result);
 | 
			
		||||
            result = m().mk_ite(m_autil.mk_lt(b, zero()), case3, result);   
 | 
			
		||||
            return BR_REWRITE_FULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    expr* es[2] = { a, b};
 | 
			
		||||
    expr* la = str().mk_length(a);
 | 
			
		||||
    result = m().mk_ite(m().mk_and(m_autil.mk_ge(b, zero()), m().mk_not(m_autil.mk_le(la, b))), 
 | 
			
		||||
                        m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_I, 2, es), 
 | 
			
		||||
                        m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_U, 2, es));
 | 
			
		||||
                        str().mk_nth_i(a, b),
 | 
			
		||||
                        str().mk_nth_u(a, b));
 | 
			
		||||
    return BR_REWRITE_FULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1678,7 +1710,7 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result
 | 
			
		|||
        return BR_DONE;
 | 
			
		||||
    }
 | 
			
		||||
    if (m_autil.is_numeral(c, r) && r.is_neg()) {
 | 
			
		||||
        result = m_autil.mk_int(-1);
 | 
			
		||||
        result = minus_one();
 | 
			
		||||
        return BR_DONE;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -1688,10 +1720,10 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    if (str().is_empty(b)) {
 | 
			
		||||
        result = m().mk_ite(m().mk_and(m_autil.mk_le(m_autil.mk_int(0), c),
 | 
			
		||||
        result = m().mk_ite(m().mk_and(m_autil.mk_le(zero(), c),
 | 
			
		||||
                                       m_autil.mk_le(c, str().mk_length(a))),
 | 
			
		||||
                            c,
 | 
			
		||||
                            m_autil.mk_int(-1));
 | 
			
		||||
                            minus_one());
 | 
			
		||||
        return BR_REWRITE2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2307,7 +2339,7 @@ br_status seq_rewriter::mk_str_to_code(expr* a, expr_ref& result) {
 | 
			
		|||
        if (s.length() == 1) 
 | 
			
		||||
            result = m_autil.mk_int(s[0]);
 | 
			
		||||
        else
 | 
			
		||||
            result = m_autil.mk_int(-1);
 | 
			
		||||
            result = minus_one();
 | 
			
		||||
        return BR_DONE;
 | 
			
		||||
    }    
 | 
			
		||||
    return BR_FAILED;
 | 
			
		||||
| 
						 | 
				
			
			@ -2448,7 +2480,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
 | 
			
		|||
            result = m_autil.mk_int(ch - '0');
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            result = m_autil.mk_int(-1);
 | 
			
		||||
            result = minus_one();
 | 
			
		||||
        }
 | 
			
		||||
        return BR_DONE;
 | 
			
		||||
    }        
 | 
			
		||||
| 
						 | 
				
			
			@ -2456,7 +2488,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
 | 
			
		|||
    expr_ref_vector as(m());
 | 
			
		||||
    str().get_concat_units(a, as);
 | 
			
		||||
    if (as.empty()) {
 | 
			
		||||
        result = m_autil.mk_int(-1);
 | 
			
		||||
        result = minus_one();
 | 
			
		||||
        return BR_DONE;
 | 
			
		||||
    }
 | 
			
		||||
    if (str().is_unit(as.back())) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2466,11 +2498,11 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
 | 
			
		|||
        expr_ref tail(str().mk_stoi(as.back()), m());
 | 
			
		||||
        expr_ref head(str().mk_concat(as.size() - 1, as.data(), a->get_sort()), m());
 | 
			
		||||
        expr_ref stoi_head(str().mk_stoi(head), m());
 | 
			
		||||
        result = m().mk_ite(m_autil.mk_ge(stoi_head, m_autil.mk_int(0)), 
 | 
			
		||||
        result = m().mk_ite(m_autil.mk_ge(stoi_head, zero()), 
 | 
			
		||||
                            m_autil.mk_add(m_autil.mk_mul(m_autil.mk_int(10), stoi_head), tail),
 | 
			
		||||
                            m_autil.mk_int(-1));
 | 
			
		||||
                            minus_one());
 | 
			
		||||
        
 | 
			
		||||
        result = m().mk_ite(m_autil.mk_ge(tail, m_autil.mk_int(0)), 
 | 
			
		||||
        result = m().mk_ite(m_autil.mk_ge(tail, zero()), 
 | 
			
		||||
                            result,
 | 
			
		||||
                            tail);
 | 
			
		||||
        result = m().mk_ite(str().mk_is_empty(head), 
 | 
			
		||||
| 
						 | 
				
			
			@ -2481,7 +2513,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
 | 
			
		|||
    if (str().is_unit(as.get(0), u) && m_util.is_const_char(u, ch) && '0' == ch) {
 | 
			
		||||
        result = str().mk_concat(as.size() - 1, as.data() + 1, as[0]->get_sort());
 | 
			
		||||
        result = m().mk_ite(str().mk_is_empty(result),
 | 
			
		||||
                            m_autil.mk_int(0),
 | 
			
		||||
                            zero(),
 | 
			
		||||
                            str().mk_stoi(result));
 | 
			
		||||
        return BR_REWRITE_FULL;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -2573,7 +2605,7 @@ bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    s = head + tail where |head| = 1
 | 
			
		||||
    s = [head] + tail where head is the first element of s
 | 
			
		||||
*/
 | 
			
		||||
bool seq_rewriter::get_head_tail(expr* s, expr_ref& head, expr_ref& tail) {
 | 
			
		||||
    expr* h = nullptr, *t = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -2670,10 +2702,10 @@ expr_ref seq_rewriter::re_predicate(expr* cond, sort* seq_sort) {
 | 
			
		|||
expr_ref seq_rewriter::is_nullable(expr* r) {
 | 
			
		||||
    STRACE("seq_verbose", tout << "is_nullable: "
 | 
			
		||||
                               << mk_pp(r, m()) << std::endl;);
 | 
			
		||||
    expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr), m());
 | 
			
		||||
    expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr, nullptr), m());
 | 
			
		||||
    if (!result) {
 | 
			
		||||
        result = is_nullable_rec(r);
 | 
			
		||||
        m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, result);        
 | 
			
		||||
        m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, nullptr, result);        
 | 
			
		||||
    }
 | 
			
		||||
    STRACE("seq_verbose", tout << "is_nullable result: "
 | 
			
		||||
                               << result << std::endl;);
 | 
			
		||||
| 
						 | 
				
			
			@ -2691,7 +2723,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
 | 
			
		|||
        re().is_intersection(r, r1, r2)) { 
 | 
			
		||||
        m_br.mk_and(is_nullable(r1), is_nullable(r2), result);
 | 
			
		||||
    }
 | 
			
		||||
    else if (re().is_union(r, r1, r2)) {
 | 
			
		||||
    else if (re().is_union(r, r1, r2) || re().is_antimorov_union(r, r1, r2)) {
 | 
			
		||||
        m_br.mk_or(is_nullable(r1), is_nullable(r2), result);
 | 
			
		||||
    }
 | 
			
		||||
    else if (re().is_diff(r, r1, r2)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2701,6 +2733,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
 | 
			
		|||
    else if (re().is_star(r) || 
 | 
			
		||||
        re().is_opt(r) ||
 | 
			
		||||
        re().is_full_seq(r) ||
 | 
			
		||||
        re().is_epsilon(r) ||
 | 
			
		||||
        (re().is_loop(r, r1, lo) && lo == 0) || 
 | 
			
		||||
        (re().is_loop(r, r1, lo, hi) && lo == 0)) {
 | 
			
		||||
        result = m().mk_true();
 | 
			
		||||
| 
						 | 
				
			
			@ -2724,7 +2757,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
 | 
			
		|||
        result = is_nullable(r1);
 | 
			
		||||
    }
 | 
			
		||||
    else if (m().is_ite(r, cond, r1, r2)) {
 | 
			
		||||
        result = m().mk_ite(cond, is_nullable(r1), is_nullable(r2));
 | 
			
		||||
        m_br.mk_ite(cond, is_nullable(r1), is_nullable(r2), result);
 | 
			
		||||
    }
 | 
			
		||||
    else if (m_util.is_re(r, seq_sort)) {
 | 
			
		||||
        result = is_nullable_symbolic_regex(r, seq_sort);
 | 
			
		||||
| 
						 | 
				
			
			@ -2881,7 +2914,8 @@ br_status seq_rewriter::mk_re_reverse(expr* r, expr_ref& result) {
 | 
			
		|||
br_status seq_rewriter::mk_re_derivative(expr* ele, expr* r, expr_ref& result) {
 | 
			
		||||
    result = mk_derivative(ele, r);
 | 
			
		||||
    // TBD: we may even declare BR_DONE here and potentially miss some simplifications
 | 
			
		||||
    return re().is_derivative(result) ? BR_DONE : BR_REWRITE_FULL;
 | 
			
		||||
    // return re().is_derivative(result) ? BR_DONE : BR_REWRITE_FULL;
 | 
			
		||||
    return BR_DONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -2976,29 +3010,406 @@ bool seq_rewriter::check_deriv_normal_form(expr* r, int level) {
 | 
			
		|||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    Memoized, recursive implementation of the symbolic derivative such that
 | 
			
		||||
    the result is in normal form.
 | 
			
		||||
expr_ref seq_rewriter::mk_derivative(expr* r) {
 | 
			
		||||
    sort* seq_sort = nullptr, * ele_sort = nullptr;
 | 
			
		||||
    VERIFY(m_util.is_re(r, seq_sort));
 | 
			
		||||
    VERIFY(m_util.is_seq(seq_sort, ele_sort));
 | 
			
		||||
    expr_ref v(m().mk_var(0, ele_sort), m());
 | 
			
		||||
    return mk_antimirov_deriv(v, r, m().mk_true());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    Functions without _rec are memoized wrappers, which call the _rec
 | 
			
		||||
    version if lookup fails.
 | 
			
		||||
 | 
			
		||||
    The main logic is in mk_der_op_rec for combining normal forms.
 | 
			
		||||
*/
 | 
			
		||||
expr_ref seq_rewriter::mk_derivative(expr* ele, expr* r) {
 | 
			
		||||
    STRACE("seq_verbose", tout << "derivative: " << mk_pp(ele, m())
 | 
			
		||||
                               << "," << mk_pp(r, m()) << std::endl;);
 | 
			
		||||
    expr_ref result(m_op_cache.find(OP_RE_DERIVATIVE, ele, r), m());
 | 
			
		||||
    return mk_antimirov_deriv(ele, r, m().mk_true());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_antimirov_deriv(expr* e, expr* r, expr* path) {
 | 
			
		||||
    // Ensure references are owned
 | 
			
		||||
    expr_ref _e(e, m()), _path(path, m()), _r(r, m());
 | 
			
		||||
    expr_ref result(m_op_cache.find(OP_RE_DERIVATIVE, e, r, path), m());
 | 
			
		||||
    if (!result) {
 | 
			
		||||
        result = mk_derivative_rec(ele, r);
 | 
			
		||||
        m_op_cache.insert(OP_RE_DERIVATIVE, ele, r, result);
 | 
			
		||||
        mk_antimirov_deriv_rec(e, r, path, result);
 | 
			
		||||
        m_op_cache.insert(OP_RE_DERIVATIVE, e, r, path, result);
 | 
			
		||||
        STRACE("seq_regex", tout << "D(" << mk_pp(e, m()) << "," << mk_pp(r, m()) << "," << mk_pp(path, m()) << ")" << std::endl;);
 | 
			
		||||
        STRACE("seq_regex", tout << "= " << mk_pp(result, m()) << std::endl;);
 | 
			
		||||
    }
 | 
			
		||||
    STRACE("seq_verbose", tout << "derivative result: "
 | 
			
		||||
                               << mk_pp(result, m()) << std::endl;);
 | 
			
		||||
    CASSERT("seq_regex", check_deriv_normal_form(r));
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result) {
 | 
			
		||||
    sort* seq_sort = nullptr, * ele_sort = nullptr;
 | 
			
		||||
    VERIFY(m_util.is_re(r, seq_sort));
 | 
			
		||||
    VERIFY(m_util.is_seq(seq_sort, ele_sort));
 | 
			
		||||
    SASSERT(ele_sort == e->get_sort());
 | 
			
		||||
    expr* r1 = nullptr, * r2 = nullptr, * c = nullptr;
 | 
			
		||||
    expr_ref c1(m());
 | 
			
		||||
    expr_ref c2(m());
 | 
			
		||||
    auto nothing = [&]() { return expr_ref(re().mk_empty(r->get_sort()), m()); };
 | 
			
		||||
    auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); };
 | 
			
		||||
    auto dotstar = [&]() { return expr_ref(re().mk_full_seq(r->get_sort()), m()); };
 | 
			
		||||
    unsigned lo = 0, hi = 0;
 | 
			
		||||
    if (re().is_empty(r) || re().is_epsilon(r))
 | 
			
		||||
        // D(e,[]) = D(e,()) = []
 | 
			
		||||
        result = nothing();
 | 
			
		||||
    else if (re().is_full_seq(r) || re().is_dot_plus(r))
 | 
			
		||||
        // D(e,.*) = D(e,.+) = .*
 | 
			
		||||
        result = dotstar();
 | 
			
		||||
    else if (re().is_full_char(r))
 | 
			
		||||
        // D(e,.) = ()
 | 
			
		||||
        result = epsilon();
 | 
			
		||||
    else if (re().is_to_re(r, r1)) {
 | 
			
		||||
        expr_ref h(m());
 | 
			
		||||
        expr_ref t(m());
 | 
			
		||||
        // here r1 is a sequence
 | 
			
		||||
        if (get_head_tail(r1, h, t)) {
 | 
			
		||||
            if (eq_char(e, h))
 | 
			
		||||
                result = re().mk_to_re(t);
 | 
			
		||||
            else if (neq_char(e, h))
 | 
			
		||||
                result = nothing();
 | 
			
		||||
            else
 | 
			
		||||
                result = re().mk_ite_simplify(m().mk_eq(e, h), re().mk_to_re(t), nothing());
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            // observe that the precondition |r1|>0 is is implied by c1 for use of mk_seq_first
 | 
			
		||||
            m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_first(r1), e), c1);
 | 
			
		||||
            m_br.mk_and(path, c1, c2);
 | 
			
		||||
            if (m().is_false(c2))
 | 
			
		||||
                result = nothing();
 | 
			
		||||
            else
 | 
			
		||||
                // observe that the precondition |r1|>0 is implied by c1 for use of mk_seq_rest
 | 
			
		||||
                result = m().mk_ite(c1, re().mk_to_re(mk_seq_rest(r1)), nothing());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (re().is_reverse(r, r2))
 | 
			
		||||
        if (re().is_to_re(r2, r1)) {
 | 
			
		||||
            // here r1 is a sequence
 | 
			
		||||
            // observe that the precondition |r1|>0 of mk_seq_last is implied by c1
 | 
			
		||||
            m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_last(r1), e), c1);
 | 
			
		||||
            m_br.mk_and(path, c1, c2);
 | 
			
		||||
            if (m().is_false(c2))
 | 
			
		||||
                result = nothing();
 | 
			
		||||
            else
 | 
			
		||||
                // observe that the precondition |r1|>0 of mk_seq_rest is implied by c1
 | 
			
		||||
                result = re().mk_ite_simplify(c1, re().mk_reverse(re().mk_to_re(mk_seq_butlast(r1))), nothing());
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            result = mk_regex_reverse(r2);
 | 
			
		||||
            if (result.get() == r)
 | 
			
		||||
                //r2 is an uninterpreted regex that is stuck
 | 
			
		||||
                //for example if r = (re.reverse R) where R is a regex variable then
 | 
			
		||||
                //here result.get() == r
 | 
			
		||||
                result = re().mk_derivative(e, result);
 | 
			
		||||
            else
 | 
			
		||||
                result = mk_antimirov_deriv(e, result, path);
 | 
			
		||||
        }
 | 
			
		||||
    else if (re().is_concat(r, r1, r2)) {
 | 
			
		||||
        expr_ref r1nullable(is_nullable(r1), m());
 | 
			
		||||
        c1 = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), r2);
 | 
			
		||||
        expr_ref r1nullable_and_path(m());
 | 
			
		||||
        m_br.mk_and(r1nullable, path, r1nullable_and_path);
 | 
			
		||||
        if (m().is_false(r1nullable_and_path))
 | 
			
		||||
            // D(e,r1)r2
 | 
			
		||||
            result = c1;
 | 
			
		||||
        else
 | 
			
		||||
            // D(e,r1)r2|(ite (r1nullable) (D(e,r2)) [])
 | 
			
		||||
            // observe that (mk_ite_simplify(true, D(e,r2), []) = D(e,r2)
 | 
			
		||||
            result = mk_antimirov_deriv_union(c1, re().mk_ite_simplify(r1nullable, mk_antimirov_deriv(e, r2, path), nothing()));
 | 
			
		||||
    }
 | 
			
		||||
    else if (m().is_ite(r, c, r1, r2)) {
 | 
			
		||||
        c1 = simplify_path(m().mk_and(c, path));
 | 
			
		||||
        c2 = simplify_path(m().mk_and(m().mk_not(c), path));
 | 
			
		||||
        if (m().is_false(c1))
 | 
			
		||||
            result = mk_antimirov_deriv(e, r2, c2);
 | 
			
		||||
        else if (m().is_false(c2))
 | 
			
		||||
            result = mk_antimirov_deriv(e, r1, c1);
 | 
			
		||||
        else
 | 
			
		||||
            result = re().mk_ite_simplify(c, mk_antimirov_deriv(e, r1, c1), mk_antimirov_deriv(e, r2, c2));
 | 
			
		||||
    }
 | 
			
		||||
    else if (re().is_range(r, r1, r2)) {
 | 
			
		||||
        expr_ref range(m());
 | 
			
		||||
        expr_ref psi(m().mk_false(), m());
 | 
			
		||||
        if (str().is_unit_string(r1, c1) && str().is_unit_string(r2, c2)) {
 | 
			
		||||
            SASSERT(u().is_char(c1));
 | 
			
		||||
            SASSERT(u().is_char(c2));
 | 
			
		||||
            // case: c1 <= e <= c2
 | 
			
		||||
            range = simplify_path(m().mk_and(u().mk_le(c1, e), u().mk_le(e, c2)));
 | 
			
		||||
            psi = simplify_path(m().mk_and(path, range));
 | 
			
		||||
        }
 | 
			
		||||
        else if (!str().is_string(r1) && str().is_unit_string(r2, c2)) {
 | 
			
		||||
            SASSERT(u().is_char(c2));
 | 
			
		||||
            // r1 nonground: |r1|=1 & r1[0] <= e <= c2
 | 
			
		||||
            expr_ref one(m_autil.mk_int(1), m());
 | 
			
		||||
            expr_ref zero(m_autil.mk_int(0), m());
 | 
			
		||||
            expr_ref r1_length_eq_one(m().mk_eq(str().mk_length(r1), one), m());
 | 
			
		||||
            expr_ref r1_0(str().mk_nth_i(r1, zero), m());
 | 
			
		||||
            range = simplify_path(m().mk_and(r1_length_eq_one, m().mk_and(u().mk_le(r1_0, e), u().mk_le(e, c2))));
 | 
			
		||||
            psi = simplify_path(m().mk_and(path, range));
 | 
			
		||||
        }
 | 
			
		||||
        else if (!str().is_string(r2) && str().is_unit_string(r1, c1)) {
 | 
			
		||||
            SASSERT(u().is_char(c1));
 | 
			
		||||
            // r2 nonground: |r2|=1 & c1 <= e <= r2_0
 | 
			
		||||
            expr_ref one(m_autil.mk_int(1), m());
 | 
			
		||||
            expr_ref zero(m_autil.mk_int(0), m());
 | 
			
		||||
            expr_ref r2_length_eq_one(m().mk_eq(str().mk_length(r2), one), m());
 | 
			
		||||
            expr_ref r2_0(str().mk_nth_i(r2, zero), m());
 | 
			
		||||
            range = simplify_path(m().mk_and(r2_length_eq_one, m().mk_and(u().mk_le(c1, e), u().mk_le(e, r2_0))));
 | 
			
		||||
            psi = simplify_path(m().mk_and(path, range));
 | 
			
		||||
        }
 | 
			
		||||
        else if (!str().is_string(r1) && !str().is_string(r2)) {
 | 
			
		||||
            // both r1 and r2 nonground: |r1|=1 & |r2|=1 & r1[0] <= e <= r2[0]
 | 
			
		||||
            expr_ref one(m_autil.mk_int(1), m());
 | 
			
		||||
            expr_ref zero(m_autil.mk_int(0), m());
 | 
			
		||||
            expr_ref r1_length_eq_one(m().mk_eq(str().mk_length(r1), one), m());
 | 
			
		||||
            expr_ref r1_0(str().mk_nth_i(r1, zero), m());
 | 
			
		||||
            expr_ref r2_length_eq_one(m().mk_eq(str().mk_length(r2), one), m());
 | 
			
		||||
            expr_ref r2_0(str().mk_nth_i(r2, zero), m());
 | 
			
		||||
            range = simplify_path(m().mk_and(r1_length_eq_one, m().mk_and(r2_length_eq_one, m().mk_and(u().mk_le(r1_0, e), u().mk_le(e, r2_0)))));
 | 
			
		||||
            psi = simplify_path(m().mk_and(path, range));
 | 
			
		||||
        }
 | 
			
		||||
        if (m().is_false(psi))
 | 
			
		||||
            result = nothing();
 | 
			
		||||
        else
 | 
			
		||||
            result = re().mk_ite_simplify(range, epsilon(), nothing());
 | 
			
		||||
    }
 | 
			
		||||
    else if (re().is_union(r, r1, r2))
 | 
			
		||||
        result = mk_antimirov_deriv_union(mk_antimirov_deriv(e, r1, path), mk_antimirov_deriv(e, r2, path));
 | 
			
		||||
    else if (re().is_intersection(r, r1, r2))
 | 
			
		||||
        result = mk_antimirov_deriv_intersection(
 | 
			
		||||
            mk_antimirov_deriv(e, r1, path),
 | 
			
		||||
            mk_antimirov_deriv(e, r2, path), m().mk_true());
 | 
			
		||||
    else if (re().is_star(r, r1) || re().is_plus(r, r1) || (re().is_loop(r, r1, lo) && 0 <= lo && lo <= 1))
 | 
			
		||||
        result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_star(r1));
 | 
			
		||||
    else if (re().is_loop(r, r1, lo))
 | 
			
		||||
        result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, lo - 1));
 | 
			
		||||
    else if (re().is_loop(r, r1, lo, hi)) {
 | 
			
		||||
        if ((lo == 0 && hi == 0) || hi < lo)
 | 
			
		||||
            result = nothing();
 | 
			
		||||
        else
 | 
			
		||||
            result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, (lo == 0 ? 0 : lo - 1), hi - 1));
 | 
			
		||||
    }
 | 
			
		||||
    else if (re().is_opt(r, r1))
 | 
			
		||||
        result = mk_antimirov_deriv(e, r1, path);
 | 
			
		||||
    else if (re().is_complement(r, r1))
 | 
			
		||||
        // D(e,~r1) = ~D(e,r1)
 | 
			
		||||
        result = mk_antimirov_deriv_negate(mk_antimirov_deriv(e, r1, path));
 | 
			
		||||
    else if (re().is_diff(r, r1, r2))
 | 
			
		||||
        result = mk_antimirov_deriv_intersection(
 | 
			
		||||
            mk_antimirov_deriv(e, r1, path),
 | 
			
		||||
            mk_antimirov_deriv_negate(mk_antimirov_deriv(e, r2, path)), m().mk_true());
 | 
			
		||||
    else if (re().is_of_pred(r, r1)) {
 | 
			
		||||
        array_util array(m());
 | 
			
		||||
        expr* args[2] = { r1, e };
 | 
			
		||||
        result = array.mk_select(2, args);
 | 
			
		||||
        // Use mk_der_cond to normalize
 | 
			
		||||
        result = mk_der_cond(result, e, seq_sort);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        // stuck cases
 | 
			
		||||
        result = re().mk_derivative(e, r);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path) {
 | 
			
		||||
    sort* seq_sort = nullptr, * ele_sort = nullptr;
 | 
			
		||||
    VERIFY(m_util.is_re(d1, seq_sort));
 | 
			
		||||
    VERIFY(m_util.is_seq(seq_sort, ele_sort));
 | 
			
		||||
    expr_ref result(m());
 | 
			
		||||
    expr* c, * a, * b;
 | 
			
		||||
    if (d1 == d2 || re().is_full_seq(d2) || re().is_empty(d1))
 | 
			
		||||
        result = d1;
 | 
			
		||||
    else if (re().is_full_seq(d1) || re().is_empty(d2))
 | 
			
		||||
        result = d2;
 | 
			
		||||
    else if (m().is_ite(d1, c, a, b)) {
 | 
			
		||||
        expr_ref path_and_c(simplify_path(m().mk_and(path, c)), m());
 | 
			
		||||
        expr_ref path_and_notc(simplify_path(m().mk_and(path, m().mk_not(c))), m());
 | 
			
		||||
        if (m().is_false(path_and_c))
 | 
			
		||||
            result = mk_antimirov_deriv_intersection(b, d2, path);
 | 
			
		||||
        else if (m().is_false(path_and_notc))
 | 
			
		||||
            result = mk_antimirov_deriv_intersection(a, d2, path);
 | 
			
		||||
        else
 | 
			
		||||
            result = m().mk_ite(c, mk_antimirov_deriv_intersection(a, d2, path_and_c),
 | 
			
		||||
                mk_antimirov_deriv_intersection(b, d2, path_and_notc));
 | 
			
		||||
    }
 | 
			
		||||
    else if (m().is_ite(d2))
 | 
			
		||||
        // swap d1 and d2
 | 
			
		||||
        result = mk_antimirov_deriv_intersection(d2, d1, path);
 | 
			
		||||
    else if (re().is_union(d1, a, b))
 | 
			
		||||
        // distribute intersection over the union in d1
 | 
			
		||||
        result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(a, d2, path), mk_antimirov_deriv_intersection(b, d2, path));
 | 
			
		||||
    else if (re().is_union(d2, a, b))
 | 
			
		||||
        // distribute intersection over the union in d2
 | 
			
		||||
        result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(d1, a, path), mk_antimirov_deriv_intersection(d1, b, path));
 | 
			
		||||
    else
 | 
			
		||||
        // in all other cases create the intersection regex
 | 
			
		||||
        // TODO: flatten, order and merge d1 and d2 to maintain equality under similarity
 | 
			
		||||
        result = (d1->get_id() <= d2->get_id() ? re().mk_inter(d1, d2) : re().mk_inter(d2, d1));
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_antimirov_deriv_concat(expr* d, expr* r) {
 | 
			
		||||
    expr_ref result(m());
 | 
			
		||||
    // Take reference count of r and d
 | 
			
		||||
    expr_ref _r(r, m()), _d(d, m());
 | 
			
		||||
    expr* c, * t, * e;
 | 
			
		||||
    if (m().is_ite(d, c, t, e))
 | 
			
		||||
        result = m().mk_ite(c, mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
 | 
			
		||||
    else if (re().is_union(d, t, e))
 | 
			
		||||
        result = re().mk_union(mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
 | 
			
		||||
    else
 | 
			
		||||
        result = mk_re_append(d, r);
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_antimirov_deriv_negate(expr* d) {
 | 
			
		||||
    sort* seq_sort = nullptr;
 | 
			
		||||
    VERIFY(m_util.is_re(d, seq_sort));
 | 
			
		||||
    auto nothing = [&]() { return expr_ref(re().mk_empty(d->get_sort()), m()); };
 | 
			
		||||
    auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); };
 | 
			
		||||
    auto dotstar = [&]() { return expr_ref(re().mk_full_seq(d->get_sort()), m()); };
 | 
			
		||||
    auto dotplus = [&]() { return expr_ref(re().mk_plus(re().mk_full_char(d->get_sort())), m()); };
 | 
			
		||||
    expr_ref result(m());
 | 
			
		||||
    expr* c, * t, * e;
 | 
			
		||||
    if (re().is_empty(d))
 | 
			
		||||
        result = dotstar();
 | 
			
		||||
    else if (re().is_epsilon(d))
 | 
			
		||||
        result = dotplus();
 | 
			
		||||
    else if (re().is_full_seq(d))
 | 
			
		||||
        result = nothing();
 | 
			
		||||
    else if (re().is_dot_plus(d))
 | 
			
		||||
        result = epsilon();
 | 
			
		||||
    else if (m().is_ite(d, c, t, e))
 | 
			
		||||
        result = m().mk_ite(c, mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
 | 
			
		||||
    else if (re().is_union(d, t, e))
 | 
			
		||||
        result = re().mk_inter(mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
 | 
			
		||||
    else if (re().is_intersection(d, t, e))
 | 
			
		||||
        result = re().mk_union(mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
 | 
			
		||||
    else if (re().is_complement(d, t))
 | 
			
		||||
        result = t;
 | 
			
		||||
    else
 | 
			
		||||
        result = re().mk_complement(d);
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_antimirov_deriv_union(expr* d1, expr* d2) {
 | 
			
		||||
    expr_ref result(m());
 | 
			
		||||
    if (re().is_empty(d1) || re().is_full_seq(d2))
 | 
			
		||||
        result = d2;
 | 
			
		||||
    else if (re().is_empty(d2) || re().is_full_seq(d1))
 | 
			
		||||
        result = d1;
 | 
			
		||||
    else if (re().is_dot_plus(d1) && re().get_info(d2).min_length > 0)
 | 
			
		||||
        result = d1;
 | 
			
		||||
    else if (re().is_dot_plus(d2) && re().get_info(d1).min_length > 0)
 | 
			
		||||
        result = d2;
 | 
			
		||||
    else
 | 
			
		||||
        // TODO: flatten, order and merge d1 and d2 to maintain equality under similarity
 | 
			
		||||
        result = (d1->get_id() <= d2->get_id() ? re().mk_union(d1, d2) : re().mk_union(d2, d1));
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_regex_reverse(expr* r) {
 | 
			
		||||
    expr* r1 = nullptr, * r2 = nullptr, * c = nullptr;
 | 
			
		||||
    unsigned lo = 0, hi = 0;
 | 
			
		||||
    expr_ref result(m());
 | 
			
		||||
    if (re().is_empty(r) || re().is_range(r) || re().is_epsilon(r) || re().is_full_seq(r) ||
 | 
			
		||||
        re().is_full_char(r) || re().is_dot_plus(r) || re().is_of_pred(r))
 | 
			
		||||
        result = r;
 | 
			
		||||
    else if (re().is_to_re(r))
 | 
			
		||||
        result = re().mk_reverse(r);
 | 
			
		||||
    else if (re().is_reverse(r, r1))
 | 
			
		||||
        result = r1;
 | 
			
		||||
    else if (re().is_concat(r, r1, r2))
 | 
			
		||||
        result = mk_regex_concat(mk_regex_reverse(r2), mk_regex_reverse(r1));
 | 
			
		||||
    else if (m().is_ite(r, c, r1, r2))
 | 
			
		||||
        result = m().mk_ite(c, mk_regex_reverse(r1), mk_regex_reverse(r2));
 | 
			
		||||
    else if (re().is_union(r, r1, r2))
 | 
			
		||||
        result = re().mk_union(mk_regex_reverse(r1), mk_regex_reverse(r2));
 | 
			
		||||
    else if (re().is_intersection(r, r1, r2))
 | 
			
		||||
        result = re().mk_inter(mk_regex_reverse(r1), mk_regex_reverse(r2));
 | 
			
		||||
    else if (re().is_diff(r, r1, r2))
 | 
			
		||||
        result = re().mk_diff(mk_regex_reverse(r1), mk_regex_reverse(r2));
 | 
			
		||||
    else if (re().is_star(r, r1))
 | 
			
		||||
        result = re().mk_star(mk_regex_reverse(r1));
 | 
			
		||||
    else if (re().is_plus(r, r1))
 | 
			
		||||
        result = re().mk_plus(mk_regex_reverse(r1));
 | 
			
		||||
    else if (re().is_loop(r, r1, lo))
 | 
			
		||||
        result = re().mk_loop(mk_regex_reverse(r1), lo);
 | 
			
		||||
    else if (re().is_loop(r, r1, lo, hi))
 | 
			
		||||
        result = re().mk_loop(mk_regex_reverse(r1), lo, hi);
 | 
			
		||||
    else if (re().is_opt(r, r1))
 | 
			
		||||
        result = re().mk_opt(mk_regex_reverse(r1));
 | 
			
		||||
    else if (re().is_complement(r, r1))
 | 
			
		||||
        result = re().mk_complement(mk_regex_reverse(r1));
 | 
			
		||||
    else
 | 
			
		||||
        //stuck cases: such as r being a regex variable
 | 
			
		||||
        //observe that re().mk_reverse(to_re(s)) is not a stuck case
 | 
			
		||||
        result = re().mk_reverse(r);
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_regex_concat(expr* r, expr* s) {
 | 
			
		||||
    sort* seq_sort = nullptr;
 | 
			
		||||
    VERIFY(m_util.is_re(r, seq_sort));
 | 
			
		||||
    SASSERT(r->get_sort() == s->get_sort());
 | 
			
		||||
    expr_ref result(m());
 | 
			
		||||
    expr* r1, * r2;
 | 
			
		||||
    if (re().is_epsilon(r) || re().is_empty(s))
 | 
			
		||||
        result = s;
 | 
			
		||||
    else if (re().is_epsilon(s) || re().is_empty(r))
 | 
			
		||||
        result = r;
 | 
			
		||||
    else if (re().is_full_seq(r) && re().is_full_seq(s))
 | 
			
		||||
        result = r;
 | 
			
		||||
    else if (re().is_concat(r, r1, r2))
 | 
			
		||||
        //create the resulting concatenation in right-associative form
 | 
			
		||||
        result = mk_regex_concat(r1, mk_regex_concat(r2, s));
 | 
			
		||||
    else {
 | 
			
		||||
        //TODO: perhaps simplifiy some further cases such as .*. = ..* = .*.+ = .+.* = .+
 | 
			
		||||
        result = re().mk_concat(r, s);
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_in_antimirov(expr* s, expr* d){
 | 
			
		||||
    expr_ref result(mk_in_antimirov_rec(s, d), m());
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_in_antimirov_rec(expr* s, expr* d) {
 | 
			
		||||
    expr* c, * d1, * d2;
 | 
			
		||||
    expr_ref result(m());
 | 
			
		||||
    if (re().is_full_seq(d) || (str().min_length(s) > 0 && re().is_dot_plus(d)))
 | 
			
		||||
        // s in .* <==> true, also: s in .+ <==> true when |s|>0
 | 
			
		||||
        result = m().mk_true();
 | 
			
		||||
    else if (re().is_empty(d) || (str().min_length(s) > 0 && re().is_epsilon(d)))
 | 
			
		||||
        // s in [] <==> false, also: s in () <==> false when |s|>0
 | 
			
		||||
        result = m().mk_false();
 | 
			
		||||
    else if (m().is_ite(d, c, d1, d2))
 | 
			
		||||
        result = re().mk_ite_simplify(c, mk_in_antimirov_rec(s, d1), mk_in_antimirov_rec(s, d2));
 | 
			
		||||
    else if (re().is_union(d, d1, d2))
 | 
			
		||||
        m_br.mk_or(mk_in_antimirov_rec(s, d1), mk_in_antimirov_rec(s, d2), result);
 | 
			
		||||
    else
 | 
			
		||||
        result = re().mk_in_re(s, d);
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
path is typically a conjunction of (negated) character equations or constraints that can potentially be simplified
 | 
			
		||||
the first element of each equation is assumed to be the element parameter, for example x = (:var 0),
 | 
			
		||||
for example a constraint x='a' & x='b' is simplified to false
 | 
			
		||||
*/
 | 
			
		||||
expr_ref  seq_rewriter::simplify_path(expr* path) {
 | 
			
		||||
    //TODO: more systematic simplifications
 | 
			
		||||
    expr_ref result(path, m());
 | 
			
		||||
    expr* h = nullptr, * t = nullptr, * lhs = nullptr, * rhs = nullptr, * h1 = nullptr;
 | 
			
		||||
    if (m().is_and(path, h, t)) {
 | 
			
		||||
        if (m().is_true(h))
 | 
			
		||||
            result = simplify_path(t);
 | 
			
		||||
        else if (m().is_true(t))
 | 
			
		||||
            result = simplify_path(h);
 | 
			
		||||
        else if (m().is_eq(h, lhs, rhs) || (m().is_not(h, h1) && m().is_eq(h1, lhs, rhs)))
 | 
			
		||||
            elim_condition(lhs, result);
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
expr_ref seq_rewriter::mk_der_antimorov_union(expr* r1, expr* r2) {
 | 
			
		||||
    return mk_der_op(_OP_RE_ANTIMOROV_UNION, r1, r2);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3016,7 +3427,7 @@ expr_ref seq_rewriter::mk_der_concat(expr* r1, expr* r2) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    Utility functions to decide char <, ==, and <=.
 | 
			
		||||
    Utility functions to decide char <, ==, !=, and <=.
 | 
			
		||||
    Return true if deduced, false if unknown.
 | 
			
		||||
*/
 | 
			
		||||
bool seq_rewriter::lt_char(expr* ch1, expr* ch2) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3027,6 +3438,11 @@ bool seq_rewriter::lt_char(expr* ch1, expr* ch2) {
 | 
			
		|||
bool seq_rewriter::eq_char(expr* ch1, expr* ch2) {
 | 
			
		||||
    return ch1 == ch2;
 | 
			
		||||
}
 | 
			
		||||
bool seq_rewriter::neq_char(expr* ch1, expr* ch2) {
 | 
			
		||||
    unsigned u1, u2;
 | 
			
		||||
    return u().is_const_char(ch1, u1) &&
 | 
			
		||||
        u().is_const_char(ch2, u2) && (u1 != u2);
 | 
			
		||||
}
 | 
			
		||||
bool seq_rewriter::le_char(expr* ch1, expr* ch2) {
 | 
			
		||||
    return eq_char(ch1, ch2) || lt_char(ch1, ch2);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3257,10 +3673,10 @@ expr_ref seq_rewriter::mk_der_op(decl_kind k, expr* a, expr* b) {
 | 
			
		|||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    result = m_op_cache.find(k, a, b);
 | 
			
		||||
    result = m_op_cache.find(k, a, b, nullptr);
 | 
			
		||||
    if (!result) {
 | 
			
		||||
        result = mk_der_op_rec(k, a, b);
 | 
			
		||||
        m_op_cache.insert(k, a, b, result);
 | 
			
		||||
        m_op_cache.insert(k, a, b, nullptr, result);
 | 
			
		||||
    }
 | 
			
		||||
    CASSERT("seq_regex", check_deriv_normal_form(result));
 | 
			
		||||
    return result;
 | 
			
		||||
| 
						 | 
				
			
			@ -3269,7 +3685,7 @@ expr_ref seq_rewriter::mk_der_op(decl_kind k, expr* a, expr* b) {
 | 
			
		|||
expr_ref seq_rewriter::mk_der_compl(expr* r) {
 | 
			
		||||
    STRACE("seq_verbose", tout << "mk_der_compl: " << mk_pp(r, m())
 | 
			
		||||
                               << std::endl;);
 | 
			
		||||
    expr_ref result(m_op_cache.find(OP_RE_COMPLEMENT, r, nullptr), m());
 | 
			
		||||
    expr_ref result(m_op_cache.find(OP_RE_COMPLEMENT, r, nullptr, nullptr), m());
 | 
			
		||||
    if (!result) {
 | 
			
		||||
        expr* c = nullptr, * r1 = nullptr, * r2 = nullptr;
 | 
			
		||||
        if (re().is_antimorov_union(r, r1, r2)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3285,7 +3701,7 @@ expr_ref seq_rewriter::mk_der_compl(expr* r) {
 | 
			
		|||
        }
 | 
			
		||||
        else if (BR_FAILED == mk_re_complement(r, result))
 | 
			
		||||
            result = re().mk_complement(r);        
 | 
			
		||||
        m_op_cache.insert(OP_RE_COMPLEMENT, r, nullptr, result);
 | 
			
		||||
        m_op_cache.insert(OP_RE_COMPLEMENT, r, nullptr, nullptr, result);
 | 
			
		||||
    }
 | 
			
		||||
    CASSERT("seq_regex", check_deriv_normal_form(result));
 | 
			
		||||
    return result;
 | 
			
		||||
| 
						 | 
				
			
			@ -3509,7 +3925,7 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
 | 
			
		|||
                // construct the term (if (r2 != () and (ele = (last r2)) then reverse(to_re (butlast r2)) else []))
 | 
			
		||||
                // hd = first of reverse(r2) i.e. last of r2
 | 
			
		||||
                // tl = rest of reverse(r2) i.e. butlast of r2
 | 
			
		||||
                //hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), m_autil.mk_int(1)));
 | 
			
		||||
                //hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), one()));
 | 
			
		||||
                hd = mk_seq_last(r2);
 | 
			
		||||
                m_br.mk_and(m().mk_not(m().mk_eq(r2, str().mk_empty(seq_sort))), m().mk_eq(hd, ele), result);
 | 
			
		||||
                tl = re().mk_to_re(mk_seq_butlast(r2));
 | 
			
		||||
| 
						 | 
				
			
			@ -3537,9 +3953,9 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
 | 
			
		|||
                return mk_empty();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        expr* e1 = nullptr, *e2 = nullptr;
 | 
			
		||||
        expr* e1 = nullptr, * e2 = nullptr;
 | 
			
		||||
        if (str().is_unit(r1, e1) && str().is_unit(r2, e2)) {
 | 
			
		||||
  	    SASSERT(u().is_char(e1));
 | 
			
		||||
            SASSERT(u().is_char(e1));
 | 
			
		||||
            // Use mk_der_cond to normalize
 | 
			
		||||
            STRACE("seq_verbose", tout << "deriv range str" << std::endl;);
 | 
			
		||||
            expr_ref p1(u().mk_le(e1, ele), m());
 | 
			
		||||
| 
						 | 
				
			
			@ -3760,7 +4176,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
 | 
			
		|||
        (re().is_union(b, b1, eps) && re().is_epsilon(eps)) ||
 | 
			
		||||
        (re().is_union(b, eps, b1) && re().is_epsilon(eps)))
 | 
			
		||||
    {
 | 
			
		||||
        result = m().mk_ite(m().mk_eq(str().mk_length(a), m_autil.mk_int(0)),
 | 
			
		||||
        result = m().mk_ite(m().mk_eq(str().mk_length(a), zero()),
 | 
			
		||||
            m().mk_true(),
 | 
			
		||||
            re().mk_in_re(a, b1));
 | 
			
		||||
        return BR_REWRITE_FULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -3775,8 +4191,10 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
 | 
			
		|||
 | 
			
		||||
    expr_ref hd(m()), tl(m());
 | 
			
		||||
    if (get_head_tail(a, hd, tl)) {
 | 
			
		||||
        result = re().mk_in_re(tl, re().mk_derivative(hd, b));
 | 
			
		||||
        return BR_REWRITE2;
 | 
			
		||||
        //result = re().mk_in_re(tl, re().mk_derivative(hd, b));
 | 
			
		||||
        //result = re().mk_in_re(tl, mk_derivative(hd, b));
 | 
			
		||||
        result = mk_in_antimirov(tl, mk_antimirov_deriv(hd, b, m().mk_true()));
 | 
			
		||||
        return BR_REWRITE_FULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (get_head_tail_reversed(a, hd, tl)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3791,7 +4209,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
 | 
			
		|||
        expr_ref len_a(str().mk_length(a), m());
 | 
			
		||||
        expr_ref len_tl(m_autil.mk_sub(len_a, len_hd), m());
 | 
			
		||||
        result = m().mk_and(m_autil.mk_ge(len_a, len_hd),
 | 
			
		||||
                            re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd),
 | 
			
		||||
                            re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd),
 | 
			
		||||
                            re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl));
 | 
			
		||||
        return BR_REWRITE_FULL;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -3802,7 +4220,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
 | 
			
		|||
        expr_ref len_hd(m_autil.mk_sub(len_a, len_tl), m());
 | 
			
		||||
        expr* s = nullptr;
 | 
			
		||||
        result = m().mk_and(m_autil.mk_ge(len_a, len_tl),
 | 
			
		||||
            re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd),
 | 
			
		||||
            re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd),
 | 
			
		||||
            (re().is_to_re(tl, s) ? m().mk_eq(s, str().mk_substr(a, len_hd, len_tl)) :
 | 
			
		||||
                re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl)));
 | 
			
		||||
        return BR_REWRITE_FULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -3912,6 +4330,10 @@ br_status seq_rewriter::mk_re_concat(expr* a, expr* b, expr_ref& result) {
 | 
			
		|||
        return BR_REWRITE2;
 | 
			
		||||
    }
 | 
			
		||||
    expr* a1 = nullptr, *b1 = nullptr;
 | 
			
		||||
    if (re().is_to_re(a, a1) && re().is_to_re(b, b1)) {
 | 
			
		||||
        result = re().mk_to_re(str().mk_concat(a1, b1));
 | 
			
		||||
        return BR_DONE;
 | 
			
		||||
    }
 | 
			
		||||
    if (re().is_star(a, a1) && re().is_star(b, b1) && a1 == b1) {
 | 
			
		||||
        result = a;
 | 
			
		||||
        return BR_DONE;
 | 
			
		||||
| 
						 | 
				
			
			@ -5151,15 +5573,15 @@ bool seq_rewriter::reduce_eq_empty(expr* l, expr* r, expr_ref& result) {
 | 
			
		|||
    if (str().is_extract(r, s, offset, len)) {
 | 
			
		||||
        expr_ref len_s(str().mk_length(s), m()); 
 | 
			
		||||
        expr_ref_vector fmls(m());
 | 
			
		||||
        fmls.push_back(m_autil.mk_lt(offset, m_autil.mk_int(0)));
 | 
			
		||||
        fmls.push_back(m_autil.mk_lt(offset, zero()));
 | 
			
		||||
        fmls.push_back(m().mk_eq(s, l));
 | 
			
		||||
        fmls.push_back(m_autil.mk_le(len, m_autil.mk_int(0)));
 | 
			
		||||
        fmls.push_back(m_autil.mk_le(len, zero()));
 | 
			
		||||
        fmls.push_back(m_autil.mk_le(len_s, offset));
 | 
			
		||||
        result = m().mk_or(fmls);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (str().is_itos(r, s)) {
 | 
			
		||||
        result = m_autil.mk_lt(s, m_autil.mk_int(0));
 | 
			
		||||
        result = m_autil.mk_lt(s, zero());
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -5275,19 +5697,20 @@ seq_rewriter::op_cache::op_cache(ast_manager& m):
 | 
			
		|||
    m_trail(m)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b) {
 | 
			
		||||
    op_entry e(op, a, b, nullptr);
 | 
			
		||||
expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b, expr* c) {
 | 
			
		||||
    op_entry e(op, a, b, c, nullptr);
 | 
			
		||||
    m_table.find(e, e);
 | 
			
		||||
 | 
			
		||||
    return e.r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* r) {
 | 
			
		||||
void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* c, expr* r) {
 | 
			
		||||
    cleanup();
 | 
			
		||||
    if (a) m_trail.push_back(a);
 | 
			
		||||
    if (b) m_trail.push_back(b);
 | 
			
		||||
    if (c) m_trail.push_back(c);
 | 
			
		||||
    if (r) m_trail.push_back(r);
 | 
			
		||||
    m_table.insert(op_entry(op, a, b, r));
 | 
			
		||||
    m_table.insert(op_entry(op, a, b, c, r));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void seq_rewriter::op_cache::cleanup() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -117,20 +117,20 @@ class seq_rewriter {
 | 
			
		|||
    class op_cache {
 | 
			
		||||
        struct op_entry {
 | 
			
		||||
            decl_kind k;
 | 
			
		||||
            expr* a, *b, *r;
 | 
			
		||||
            op_entry(decl_kind k, expr* a, expr* b, expr* r): k(k), a(a), b(b), r(r) {}
 | 
			
		||||
            op_entry():k(0), a(nullptr), b(nullptr), r(nullptr) {}
 | 
			
		||||
            expr* a, *b, *c, *r;
 | 
			
		||||
            op_entry(decl_kind k, expr* a, expr* b, expr* c, expr* r): k(k), a(a), b(b), c(c), r(r) {}
 | 
			
		||||
            op_entry():k(0), a(nullptr), b(nullptr), c(nullptr), r(nullptr) {}
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        struct hash_entry {
 | 
			
		||||
            unsigned operator()(op_entry const& e) const { 
 | 
			
		||||
                return mk_mix(e.k, e.a ? e.a->get_id() : 0, e.b ? e.b->get_id() : 0);
 | 
			
		||||
                return combine_hash(mk_mix(e.k, e.a ? e.a->get_id() : 0, e.b ? e.b->get_id() : 0), e.c ? e.c->get_id() : 0);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        struct eq_entry {
 | 
			
		||||
            bool operator()(op_entry const& a, op_entry const& b) const { 
 | 
			
		||||
                return a.k == b.k && a.a == b.a && a.b == b.b;
 | 
			
		||||
            bool operator()(op_entry const& a, op_entry const& b) const {
 | 
			
		||||
                return a.k == b.k && a.a == b.a && a.b == b.b && a.c == b.c;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -143,8 +143,8 @@ class seq_rewriter {
 | 
			
		|||
 | 
			
		||||
    public:
 | 
			
		||||
        op_cache(ast_manager& m);
 | 
			
		||||
        expr* find(decl_kind op, expr* a, expr* b);
 | 
			
		||||
        void insert(decl_kind op, expr* a, expr* b, expr* r);
 | 
			
		||||
        expr* find(decl_kind op, expr* a, expr* b, expr* c);
 | 
			
		||||
        void insert(decl_kind op, expr* a, expr* b, expr* c, expr* r);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    seq_util       m_util;
 | 
			
		||||
| 
						 | 
				
			
			@ -208,8 +208,24 @@ class seq_rewriter {
 | 
			
		|||
    bool check_deriv_normal_form(expr* r, int level = 3);
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    void mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result);
 | 
			
		||||
 | 
			
		||||
    expr_ref mk_antimirov_deriv(expr* e, expr* r, expr* path);
 | 
			
		||||
    expr_ref mk_in_antimirov_rec(expr* s, expr* d);
 | 
			
		||||
    expr_ref mk_in_antimirov(expr* s, expr* d);
 | 
			
		||||
 | 
			
		||||
    expr_ref mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path);
 | 
			
		||||
    expr_ref mk_antimirov_deriv_concat(expr* d, expr* r);
 | 
			
		||||
    expr_ref mk_antimirov_deriv_negate(expr* d);
 | 
			
		||||
    expr_ref mk_antimirov_deriv_union(expr* d1, expr* d2);
 | 
			
		||||
    expr_ref mk_regex_reverse(expr* r);
 | 
			
		||||
    expr_ref mk_regex_concat(expr* r1, expr* r2);
 | 
			
		||||
 | 
			
		||||
    expr_ref simplify_path(expr* path);
 | 
			
		||||
 | 
			
		||||
    bool lt_char(expr* ch1, expr* ch2);
 | 
			
		||||
    bool eq_char(expr* ch1, expr* ch2);
 | 
			
		||||
    bool neq_char(expr* ch1, expr* ch2);
 | 
			
		||||
    bool le_char(expr* ch1, expr* ch2);
 | 
			
		||||
    bool pred_implies(expr* a, expr* b);
 | 
			
		||||
    bool are_complements(expr* r1, expr* r2) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -286,6 +302,8 @@ class seq_rewriter {
 | 
			
		|||
    expr_ref zero() { return expr_ref(m_autil.mk_int(0), m()); }
 | 
			
		||||
    expr_ref one() { return expr_ref(m_autil.mk_int(1), m()); }
 | 
			
		||||
    expr_ref minus_one() { return expr_ref(m_autil.mk_int(-1), m()); }
 | 
			
		||||
    expr_ref mk_sub(expr* a, rational const& n);
 | 
			
		||||
    expr_ref mk_sub(expr* a, unsigned n) { return mk_sub(a, rational(n)); }
 | 
			
		||||
 | 
			
		||||
    bool is_suffix(expr* s, expr* offset, expr* len);
 | 
			
		||||
    bool is_prefix(expr* s, expr* offset, expr* len);
 | 
			
		||||
| 
						 | 
				
			
			@ -379,9 +397,19 @@ public:
 | 
			
		|||
 | 
			
		||||
    void add_seqs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_pair_vector& new_eqs);
 | 
			
		||||
 | 
			
		||||
    // Expose derivative and nullability check
 | 
			
		||||
    /*
 | 
			
		||||
    create the nullability check for r
 | 
			
		||||
    */
 | 
			
		||||
    expr_ref is_nullable(expr* r);
 | 
			
		||||
    /*
 | 
			
		||||
    make the derivative of r wrt the given element ele
 | 
			
		||||
    */
 | 
			
		||||
    expr_ref mk_derivative(expr* ele, expr* r);
 | 
			
		||||
    /*
 | 
			
		||||
    make the derivative of r wrt the canonical variable v0 = (:var 0), 
 | 
			
		||||
    for example mk_derivative(a+) = (if (v0 = 'a') then a* else [])
 | 
			
		||||
    */
 | 
			
		||||
    expr_ref mk_derivative(expr* r);
 | 
			
		||||
 | 
			
		||||
    // heuristic elimination of element from condition that comes form a derivative.
 | 
			
		||||
    // special case optimization for conjunctions of equalities, disequalities and ranges.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,8 +28,8 @@ skolem::skolem(ast_manager& m, th_rewriter& rw):
 | 
			
		|||
    m_tail           = "seq.tail";
 | 
			
		||||
    m_seq_first      = "seq.first";
 | 
			
		||||
    m_seq_last       = "seq.last";
 | 
			
		||||
    m_indexof_left   = "seq.idx.left";
 | 
			
		||||
    m_indexof_right  = "seq.idx.right";
 | 
			
		||||
    m_indexof_left   = "seq.idx.l";
 | 
			
		||||
    m_indexof_right  = "seq.idx.r";
 | 
			
		||||
    m_aut_step       = "aut.step";
 | 
			
		||||
    m_pre            = "seq.pre";  // (seq.pre s l):  prefix of string s of length l
 | 
			
		||||
    m_post           = "seq.post"; // (seq.post s l): suffix of string s of length k, based on extract starting at index i of length l
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,8 +77,10 @@ namespace seq {
 | 
			
		|||
 | 
			
		||||
        expr_ref mk_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_left, t, s, offset); }
 | 
			
		||||
        expr_ref mk_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_right, t, s, offset); }
 | 
			
		||||
        expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_left", t, s, offset); }
 | 
			
		||||
        expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_right", t, s, offset); }
 | 
			
		||||
        expr_ref mk_contains_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.cnt.l", t, s, offset); }
 | 
			
		||||
        expr_ref mk_contains_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.cnt.r", t, s, offset); }
 | 
			
		||||
        expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.lidx.l", t, s, offset); }
 | 
			
		||||
        expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.lidx.r", t, s, offset); }
 | 
			
		||||
 | 
			
		||||
        expr_ref mk_tail(expr* s, expr* i) { return mk(m_tail, s, i); }
 | 
			
		||||
        expr_ref mk_post(expr* s, expr* i) { return mk(m_post, s, i); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -839,7 +839,7 @@ bool seq_util::str::is_nth_i(expr const* n, expr*& s, unsigned& idx) const {
 | 
			
		|||
    return arith_util(m).is_unsigned(i, idx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
app* seq_util::str::mk_nth_i(expr* s, unsigned i) const {
 | 
			
		||||
app* seq_util::str::mk_nth_c(expr* s, unsigned i) const {
 | 
			
		||||
    return mk_nth_i(s, arith_util(m).mk_int(i));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -854,6 +854,48 @@ void seq_util::str::get_concat(expr* e, expr_ref_vector& es) const {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Returns true if s is an expression of the form (l = |u|) |u|-k or (-k)+|u| or |u|+(-k).
 | 
			
		||||
Also returns true and assigns k=0 and l=s if s is |u|.
 | 
			
		||||
*/
 | 
			
		||||
bool seq_util::str::is_len_sub(expr const* s, expr*& l, expr*& u, rational& k) const {
 | 
			
		||||
    expr* x;
 | 
			
		||||
    rational v;
 | 
			
		||||
    arith_util a(m);
 | 
			
		||||
    if (is_length(s, l)) {
 | 
			
		||||
        k = 0;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else if (a.is_sub(s, l, x) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonneg()) {
 | 
			
		||||
        k = v;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else if (a.is_add(s, l, x) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonpos()) {
 | 
			
		||||
        k = - v;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else if (a.is_add(s, x, l) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonpos()) {
 | 
			
		||||
        k = - v;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool seq_util::str::is_unit_string(expr const* s, expr_ref& c) const {
 | 
			
		||||
    zstring z;
 | 
			
		||||
    expr* ch = nullptr;
 | 
			
		||||
    if (is_string(s, z) && z.length() == 1) {
 | 
			
		||||
        c = mk_char(z[0]);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else if (is_unit(s, ch)) {
 | 
			
		||||
        c = ch;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void seq_util::str::get_concat_units(expr* e, expr_ref_vector& es) const {
 | 
			
		||||
    expr* e1, *e2;
 | 
			
		||||
    while (is_concat(e, e1, e2)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -876,8 +918,6 @@ app* seq_util::str::mk_is_empty(expr* s) const {
 | 
			
		|||
    return m.mk_eq(s, mk_empty(s->get_sort()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
unsigned seq_util::str::min_length(expr* s) const {
 | 
			
		||||
    SASSERT(u.is_seq(s));
 | 
			
		||||
    unsigned result = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -892,7 +932,10 @@ unsigned seq_util::str::min_length(expr* s) const {
 | 
			
		|||
            return 0u;
 | 
			
		||||
    };
 | 
			
		||||
    while (is_concat(s, s1, s2)) {
 | 
			
		||||
        result += get_length(s1);
 | 
			
		||||
        if (is_concat(s1))
 | 
			
		||||
            result += min_length(s1);
 | 
			
		||||
        else
 | 
			
		||||
            result += get_length(s1);
 | 
			
		||||
        s = s2;
 | 
			
		||||
    }
 | 
			
		||||
    result += get_length(s);
 | 
			
		||||
| 
						 | 
				
			
			@ -920,7 +963,10 @@ unsigned seq_util::str::max_length(expr* s) const {
 | 
			
		|||
            return UINT_MAX;
 | 
			
		||||
    };
 | 
			
		||||
    while (is_concat(s, s1, s2)) {
 | 
			
		||||
        result = u.max_plus(get_length(s1), result);
 | 
			
		||||
        if (is_concat(s1))
 | 
			
		||||
            result = u.max_plus(max_length(s1), result);
 | 
			
		||||
        else
 | 
			
		||||
            result = u.max_plus(get_length(s1), result);
 | 
			
		||||
        s = s2;
 | 
			
		||||
    }
 | 
			
		||||
    result = u.max_plus(get_length(s), result);
 | 
			
		||||
| 
						 | 
				
			
			@ -1065,38 +1111,70 @@ app* seq_util::rex::mk_epsilon(sort* seq_sort) {
 | 
			
		|||
/*
 | 
			
		||||
  Produces compact view of concrete concatenations such as (abcd).
 | 
			
		||||
*/
 | 
			
		||||
std::ostream& seq_util::rex::pp::compact_helper_seq(std::ostream& out, expr* s) const {
 | 
			
		||||
    SASSERT(re.u.is_seq(s));
 | 
			
		||||
bool seq_util::rex::pp::print_seq(std::ostream& out, expr* s) const {
 | 
			
		||||
    zstring z;
 | 
			
		||||
    expr* x, * j, * k, * l, * i, * x_;
 | 
			
		||||
    if (re.u.str.is_empty(s))
 | 
			
		||||
        out << "()";
 | 
			
		||||
    else if (re.u.str.is_unit(s))
 | 
			
		||||
        seq_unit(out, s);
 | 
			
		||||
    else if (re.u.str.is_concat(s)) {
 | 
			
		||||
        expr_ref_vector es(re.m);
 | 
			
		||||
        re.u.str.get_concat(s, es);
 | 
			
		||||
        for (expr* e : es)
 | 
			
		||||
            compact_helper_seq(out, e);
 | 
			
		||||
            print(out, e);
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.u.str.is_string(s, z)) {
 | 
			
		||||
        for (unsigned i = 0; i < z.length(); i++)
 | 
			
		||||
            out << (char)z[i];
 | 
			
		||||
    }
 | 
			
		||||
    //using braces to indicate 'full' output
 | 
			
		||||
    //for example an uninterpreted constant X will be printed as {X}
 | 
			
		||||
    //while a unit sequence "X" will be printed as X
 | 
			
		||||
    //thus for example (concat "X" "Y" Z "W") where Z is uninterpreted is printed as XY{Z}W
 | 
			
		||||
    else out << "{" << mk_pp(s, re.m) << "}";
 | 
			
		||||
    return out;
 | 
			
		||||
    else if (re.u.str.is_at(s, x, i))
 | 
			
		||||
        print(out, x) << "@", print(out, i);
 | 
			
		||||
    else if (re.u.str.is_extract(s, x, j, k)) {
 | 
			
		||||
        rational jv, iv;
 | 
			
		||||
        print(out, x);
 | 
			
		||||
        if (arith_util(re.m).is_numeral(j, jv)) {
 | 
			
		||||
            if (arith_util(re.m).is_numeral(k, iv)) {
 | 
			
		||||
                // output X[j,k]
 | 
			
		||||
                out << "[" << jv.get_int32() << "," << jv.get_int32() << "]";
 | 
			
		||||
            }
 | 
			
		||||
            else if (arith_util(re.m).is_sub(k, l, i) && re.u.str.is_length(l, x_) && x == x_ &&
 | 
			
		||||
                arith_util(re.m).is_numeral(i, iv) && iv == jv) {
 | 
			
		||||
                // case X[j,|X|-j] is denoted by X[j..]
 | 
			
		||||
                out << "[" << jv.get_int32() << "..]";
 | 
			
		||||
            }
 | 
			
		||||
            else if (((arith_util(re.m).is_add(k, l, i) && re.u.str.is_length(l, x_)) ||
 | 
			
		||||
                (arith_util(re.m).is_add(k, i, l) && re.u.str.is_length(l, x_))) && x == x_ &&
 | 
			
		||||
                arith_util(re.m).is_numeral(i, iv) && iv.get_int32() + jv.get_int32() == 0) {
 | 
			
		||||
                // case X[j,|X|-j] is denoted by X[j..]
 | 
			
		||||
                out << "[" << jv.get_int32() << "..]";
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                out << "[" << jv.get_int32() << ",";
 | 
			
		||||
                print(out, k);
 | 
			
		||||
                out << "]";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            out << "[";
 | 
			
		||||
            print(out, j);
 | 
			
		||||
            out << ",";
 | 
			
		||||
            print(out, k);
 | 
			
		||||
            out << "]";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        return false;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  Produces output such as [a-z] for a range.
 | 
			
		||||
*/
 | 
			
		||||
std::ostream& seq_util::rex::pp::compact_helper_range(std::ostream& out, expr* s1, expr* s2) const {
 | 
			
		||||
std::ostream& seq_util::rex::pp::print_range(std::ostream& out, expr* s1, expr* s2) const {
 | 
			
		||||
    out << "[";
 | 
			
		||||
    seq_unit(out, s1) << "-";
 | 
			
		||||
    seq_unit(out, s2) << "]";
 | 
			
		||||
    print(out, s1);
 | 
			
		||||
    out << "-";
 | 
			
		||||
    print(out, s2);
 | 
			
		||||
    out << "]";
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1111,9 +1189,10 @@ bool seq_util::rex::pp::can_skip_parenth(expr* r) const {
 | 
			
		|||
/*
 | 
			
		||||
  Specialize output for a unit sequence converting to visible ASCII characters if possible.
 | 
			
		||||
*/
 | 
			
		||||
std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
 | 
			
		||||
    expr* e;
 | 
			
		||||
bool seq_util::rex::pp::print_unit(std::ostream& out, expr* s) const {
 | 
			
		||||
    expr* e, * i;
 | 
			
		||||
    unsigned n = 0;
 | 
			
		||||
 | 
			
		||||
    if ((re.u.str.is_unit(s, e) && re.u.is_const_char(e, n)) || re.u.is_const_char(s, n)) {
 | 
			
		||||
        char c = (char)n;
 | 
			
		||||
        if (c == '\n')
 | 
			
		||||
| 
						 | 
				
			
			@ -1122,22 +1201,21 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
 | 
			
		|||
            out << "\\r";
 | 
			
		||||
        else if (c == '\f')
 | 
			
		||||
            out << "\\f";
 | 
			
		||||
        else if (c == ' ')
 | 
			
		||||
            out << "\\s";
 | 
			
		||||
        else if (c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || c == '.' || c == '\\')
 | 
			
		||||
            out << "\\" << c;
 | 
			
		||||
        else if (32 < n && n < 127) {
 | 
			
		||||
        else if (32 <= n && n < 127 && n != '\"' && n != ' '
 | 
			
		||||
            && n != '\\' && n != '\'' && n != '?' && n != '.' && n != '(' && n != ')' && n != '[' && n != ']'
 | 
			
		||||
            && n != '{' && n != '}' && n != '&') {
 | 
			
		||||
            if (html_encode) {
 | 
			
		||||
                if (c == '<')
 | 
			
		||||
                    out << "<";
 | 
			
		||||
                else if (c == '>')
 | 
			
		||||
                    out << ">";
 | 
			
		||||
                else if (c == '&')
 | 
			
		||||
                    out << "&";
 | 
			
		||||
                else if (c == '\"')
 | 
			
		||||
                    out << """;
 | 
			
		||||
                //else if (c == '&')
 | 
			
		||||
                //    out << "&";
 | 
			
		||||
                //else if (c == '\"')
 | 
			
		||||
                //    out << """;
 | 
			
		||||
                else
 | 
			
		||||
                    out << "\\x" << std::hex << n;
 | 
			
		||||
                    //out << "\\x" << std::hex << n;
 | 
			
		||||
                    out << c;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                out << c;
 | 
			
		||||
| 
						 | 
				
			
			@ -1148,95 +1226,193 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
 | 
			
		|||
            out << "\\x" << std::hex << n;
 | 
			
		||||
        else if (n <= 0xFFF)
 | 
			
		||||
            out << "\\u0" << std::hex << n;
 | 
			
		||||
        else 
 | 
			
		||||
        else
 | 
			
		||||
            out << "\\u" << std::hex << n;
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.u.str.is_nth_i(s, e, i)) {
 | 
			
		||||
        print(out, e) << "[";
 | 
			
		||||
        print(out, i) << "]";
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.u.str.is_length(s, e))
 | 
			
		||||
        print(out << "|", e) << "|";    
 | 
			
		||||
    else
 | 
			
		||||
        out << "{" << mk_pp(s, re.m) << "}";
 | 
			
		||||
    return out;
 | 
			
		||||
        return false;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  Pretty prints the regex r into the out stream
 | 
			
		||||
  Pretty prints the regex r into the ostream out
 | 
			
		||||
*/
 | 
			
		||||
std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
 | 
			
		||||
std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const {
 | 
			
		||||
    expr* r1 = nullptr, * r2 = nullptr, * s = nullptr, * s2 = nullptr;
 | 
			
		||||
    unsigned lo = 0, hi = 0;
 | 
			
		||||
    if (re.u.is_char(e))
 | 
			
		||||
        return seq_unit(out, e);
 | 
			
		||||
    else if (re.u.is_seq(e))
 | 
			
		||||
        return compact_helper_seq(out, e);
 | 
			
		||||
    arith_util a(re.m);
 | 
			
		||||
    rational v;
 | 
			
		||||
    if (!e)
 | 
			
		||||
        out << "null";
 | 
			
		||||
    else if (print_unit(out, e))
 | 
			
		||||
        ;
 | 
			
		||||
    else if (print_seq(out, e))
 | 
			
		||||
        ;
 | 
			
		||||
    else if (re.is_full_char(e))
 | 
			
		||||
        return out << ".";
 | 
			
		||||
        out << ".";
 | 
			
		||||
    else if (re.is_full_seq(e))
 | 
			
		||||
        return out << ".*";
 | 
			
		||||
        out << ".*";
 | 
			
		||||
    else if (re.is_to_re(e, s))
 | 
			
		||||
        return compact_helper_seq(out, s);
 | 
			
		||||
    else if (re.is_range(e, s, s2)) 
 | 
			
		||||
        return compact_helper_range(out, s, s2);
 | 
			
		||||
        print(out, s);
 | 
			
		||||
    else if (re.is_range(e, s, s2))
 | 
			
		||||
        print_range(out, s, s2);
 | 
			
		||||
    else if (re.is_epsilon(e))
 | 
			
		||||
        return out << "()";
 | 
			
		||||
        // ε = epsilon
 | 
			
		||||
        out << (html_encode ? "ε" : "()");
 | 
			
		||||
    else if (re.is_empty(e))
 | 
			
		||||
        return out << "[]";
 | 
			
		||||
    else if (re.is_concat(e, r1, r2)) 
 | 
			
		||||
        return out << pp(re, r1) << pp(re, r2);
 | 
			
		||||
    else if (re.is_union(e, r1, r2)) 
 | 
			
		||||
        return out << "(" << pp(re, r1) << "|" << pp(re, r2) << ")";
 | 
			
		||||
    else if (re.is_intersection(e, r1, r2)) 
 | 
			
		||||
        return out << "(" << pp(re, r1) << "&" /*(html_encode ? ")&(" : ")&(")*/ << pp(re, r2) << ")";
 | 
			
		||||
        // ∅ = emptyset
 | 
			
		||||
        out << (html_encode ? "∅" : "[]");
 | 
			
		||||
    else if (re.is_concat(e, r1, r2)) {
 | 
			
		||||
        print(out, r1);
 | 
			
		||||
        print(out, r2);
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_antimorov_union(e, r1, r2) || re.is_union(e, r1, r2)) {
 | 
			
		||||
        out << "(";
 | 
			
		||||
        print(out, r1);
 | 
			
		||||
        out << (html_encode ? "⋃" : "|");
 | 
			
		||||
        print(out, r2);
 | 
			
		||||
        out << ")";
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_intersection(e, r1, r2)) {
 | 
			
		||||
        out << "(";
 | 
			
		||||
        print(out, r1);
 | 
			
		||||
        out << (html_encode ? "⋂" : "&");
 | 
			
		||||
        print(out, r2);
 | 
			
		||||
        out << ")";
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_complement(e, r1)) {
 | 
			
		||||
        out << "~";
 | 
			
		||||
        if (can_skip_parenth(r1))
 | 
			
		||||
            return out << "~" << pp(re, r1);
 | 
			
		||||
        else 
 | 
			
		||||
            return out << "~(" << pp(re, r1) << ")";
 | 
			
		||||
            print(out, r1);
 | 
			
		||||
        else {
 | 
			
		||||
            out << "(";
 | 
			
		||||
            print(out, r1);
 | 
			
		||||
            out << ")";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_plus(e, r1)) {
 | 
			
		||||
        if (can_skip_parenth(r1)) 
 | 
			
		||||
            return out << pp(re, r1) << "+";
 | 
			
		||||
        else 
 | 
			
		||||
            return out << "(" << pp(re, r1) << ")+";
 | 
			
		||||
        if (can_skip_parenth(r1)) {
 | 
			
		||||
            print(out, r1);
 | 
			
		||||
            out << "+";
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            out << "(";
 | 
			
		||||
            print(out, r1);
 | 
			
		||||
            out << ")+";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_star(e, r1)) {
 | 
			
		||||
        if (can_skip_parenth(r1))
 | 
			
		||||
            return out << pp(re, r1) << "*";
 | 
			
		||||
        else
 | 
			
		||||
            return out << "(" << pp(re, r1) << ")*";
 | 
			
		||||
        if (can_skip_parenth(r1)) {
 | 
			
		||||
            print(out, r1);
 | 
			
		||||
            out << "*";
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            out << "(";
 | 
			
		||||
            print(out, r1);
 | 
			
		||||
            out << ")*";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_loop(e, r1, lo)) {
 | 
			
		||||
        if (can_skip_parenth(r1))
 | 
			
		||||
            return out << pp(re, r1) << "{" << lo << ",}";
 | 
			
		||||
        else 
 | 
			
		||||
            return out << "(" << pp(re, r1) << "){" << lo << ",}";
 | 
			
		||||
        if (can_skip_parenth(r1)) 
 | 
			
		||||
            print(out, r1) << "{" << lo << ",}";
 | 
			
		||||
        else {
 | 
			
		||||
            out << "(";
 | 
			
		||||
            print(out, r1);
 | 
			
		||||
            out << "){" << lo << ",}";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_loop(e, r1, lo, hi)) {
 | 
			
		||||
        if (can_skip_parenth(r1)) {
 | 
			
		||||
            print(out, r1);
 | 
			
		||||
            if (lo == hi)
 | 
			
		||||
                return out << pp(re, r1) << "{" << lo << "}";
 | 
			
		||||
            else 
 | 
			
		||||
                return out << pp(re, r1) << "{" << lo << "," << hi << "}";
 | 
			
		||||
                out << "{" << lo << "}";
 | 
			
		||||
            else
 | 
			
		||||
                out << "{" << lo << "," << hi << "}";
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            out << "(";
 | 
			
		||||
            print(out, r1);
 | 
			
		||||
            if (lo == hi)
 | 
			
		||||
                return out << "(" << pp(re, r1) << "){" << lo << "}";
 | 
			
		||||
                out << "){" << lo << "}";
 | 
			
		||||
            else
 | 
			
		||||
                return out << "(" << pp(re, r1) << "){" << lo << "," << hi << "}";
 | 
			
		||||
                out << "){" << lo << "," << hi << "}";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_diff(e, r1, r2)) 
 | 
			
		||||
        return out << "(" << pp(re, r1) << ")\\(" << pp(re, r2) << ")";
 | 
			
		||||
    else if (re.m.is_ite(e, s, r1, r2)) 
 | 
			
		||||
        return out << "if(" << mk_pp(s, re.m) << "," << pp(re, r1) << "," << pp(re, r2) << ")";
 | 
			
		||||
    else if (re.is_diff(e, r1, r2)) {
 | 
			
		||||
        out << "(";
 | 
			
		||||
        print(out, r1);
 | 
			
		||||
        out << ")\\(";
 | 
			
		||||
        print(out, r2);
 | 
			
		||||
        out << ")";
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.m.is_ite(e, s, r1, r2)) {
 | 
			
		||||
        out << (html_encode ? "(𝐢𝐟 " : "(if ");
 | 
			
		||||
        print(out, s);
 | 
			
		||||
        out << (html_encode ? " 𝐭𝗵𝐞𝐧 " : " then ");
 | 
			
		||||
        print(out, r1);
 | 
			
		||||
        out << (html_encode ? " 𝐞𝐥𝘀𝐞 " : " else ");
 | 
			
		||||
        print(out, r2);
 | 
			
		||||
        out << ")";
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_opt(e, r1)) {
 | 
			
		||||
        if (can_skip_parenth(r1)) 
 | 
			
		||||
            return out << pp(re, r1) << "?";
 | 
			
		||||
        else 
 | 
			
		||||
            return out << "(" << pp(re, r1) << ")?";
 | 
			
		||||
            print(out, r1) << "?";
 | 
			
		||||
        else {
 | 
			
		||||
            out << "(";
 | 
			
		||||
            print(out, r1);
 | 
			
		||||
            out << ")?";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_reverse(e, r1)) {
 | 
			
		||||
        out << "(reverse ";
 | 
			
		||||
        print(out, r1);
 | 
			
		||||
        out << ")";
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.m.is_eq(e, r1, r2)) {
 | 
			
		||||
        out << "(";
 | 
			
		||||
        print(out, r1);
 | 
			
		||||
        out << " = ";
 | 
			
		||||
        print(out, r2);
 | 
			
		||||
        out << ")";
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.m.is_not(e, r1)) {
 | 
			
		||||
        out << "!";
 | 
			
		||||
        print(out, r1);
 | 
			
		||||
    }
 | 
			
		||||
    else if (a.is_add(e, s, s2) && a.is_numeral(s, v) && v < 0) 
 | 
			
		||||
        print(out, s2) << " - " << -v;
 | 
			
		||||
    else if (a.is_add(e, s, s2) && a.is_numeral(s2, v) && v < 0)
 | 
			
		||||
        print(out, s) << " - " << -v;
 | 
			
		||||
    else if (a.is_add(e, s, s2))
 | 
			
		||||
        print(out, s) << " + ", print(out, s2);
 | 
			
		||||
    else if (a.is_sub(e, s, s2) && a.is_numeral(s2, v) && v > 0)
 | 
			
		||||
        print(out, s) << " - " << v;
 | 
			
		||||
    else if (a.is_le(e, s, s2))
 | 
			
		||||
        print(out << "(", s) << " <= ", print(out, s2) << ")";
 | 
			
		||||
    else if (re.m.is_value(e))
 | 
			
		||||
        out << mk_pp(e, re.m);
 | 
			
		||||
    else if (is_app(e) && to_app(e)->get_num_args() == 0)
 | 
			
		||||
        out << mk_pp(e, re.m);
 | 
			
		||||
    else if (is_app(e)) {
 | 
			
		||||
        out << "(" << to_app(e)->get_decl()->get_name();
 | 
			
		||||
        for (expr* arg : *to_app(e))
 | 
			
		||||
            print(out << " ", arg);
 | 
			
		||||
        out << ")";
 | 
			
		||||
    }
 | 
			
		||||
    else if (re.is_reverse(e, r1)) 
 | 
			
		||||
        return out << "reverse(" << pp(re, r1) << ")";
 | 
			
		||||
    else
 | 
			
		||||
        // Else: derivative or is_of_pred
 | 
			
		||||
        return out << "{" << mk_pp(e, re.m) << "}";
 | 
			
		||||
        // for all remaining cases use the default pretty printer
 | 
			
		||||
        out << mk_pp(e, re.m);
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
 | 
			
		||||
    return print(out, ex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -1244,7 +1420,16 @@ std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
 | 
			
		|||
*/
 | 
			
		||||
std::string seq_util::rex::to_str(expr* r) const {
 | 
			
		||||
    std::ostringstream out;
 | 
			
		||||
    out << pp(u.re, r);
 | 
			
		||||
    pp(u.re, r, false).display(out);
 | 
			
		||||
    return out.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  Pretty prints the regex r into the output string that is htmlencoded 
 | 
			
		||||
*/
 | 
			
		||||
std::string seq_util::rex::to_strh(expr* r) const {
 | 
			
		||||
    std::ostringstream out;
 | 
			
		||||
    pp(u.re, r, true).display(out);
 | 
			
		||||
    return out.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1290,7 +1475,7 @@ seq_util::rex::info seq_util::rex::get_info_rec(expr* e) const {
 | 
			
		|||
    else 
 | 
			
		||||
        result = mk_info_rec(to_app(e));
 | 
			
		||||
    m_infos.setx(e->get_id(), result, invalid_info);
 | 
			
		||||
    STRACE("re_info", tout << "compute_info(" << pp(u.re, e) << ")=" << result << std::endl;);
 | 
			
		||||
    STRACE("re_info", tout << "compute_info(" << pp(u.re, e, false) << ")=" << result << std::endl;);
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1518,7 +1703,13 @@ seq_util::rex::info seq_util::rex::info::orelse(seq_util::rex::info const& i) co
 | 
			
		|||
            // unsigned ite_min_length = std::min(min_length, i.min_length);
 | 
			
		||||
            // lbool ite_nullable = (nullable == i.nullable ? nullable : l_undef);
 | 
			
		||||
            // TBD: whether ite is interpreted or not depends on whether the condition is interpreted and both branches are interpreted
 | 
			
		||||
            return info(false, false, false, false, normalized && i.normalized, monadic && i.monadic, singleton && i.singleton, nullable, std::min(min_length, i.min_length), std::max(star_height, i.star_height));
 | 
			
		||||
            return info(false, false, false, false, 
 | 
			
		||||
                normalized && i.normalized, 
 | 
			
		||||
                monadic && i.monadic, 
 | 
			
		||||
                singleton && i.singleton, 
 | 
			
		||||
                ((nullable == l_true && i.nullable == l_true) ? l_true : ((nullable == l_false && i.nullable == l_false) ? l_false : l_undef)),
 | 
			
		||||
                std::min(min_length, i.min_length), 
 | 
			
		||||
                std::max(star_height, i.star_height));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return i;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -286,7 +286,8 @@ public:
 | 
			
		|||
        app* mk_at(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_AT, 2, es); }
 | 
			
		||||
        app* mk_nth(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH, 2, es); }
 | 
			
		||||
        app* mk_nth_i(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_I, 2, es); }
 | 
			
		||||
        app* mk_nth_i(expr* s, unsigned i) const;
 | 
			
		||||
        app* mk_nth_u(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_U, 2, es); }
 | 
			
		||||
        app* mk_nth_c(expr* s, unsigned i) const;
 | 
			
		||||
 | 
			
		||||
        app* mk_substr(expr* a, expr* b, expr* c) const { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); }
 | 
			
		||||
        app* mk_contains(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONTAINS, 2, es); }
 | 
			
		||||
| 
						 | 
				
			
			@ -350,6 +351,13 @@ public:
 | 
			
		|||
        bool is_from_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_FROM_CODE); }
 | 
			
		||||
        bool is_to_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_TO_CODE); }
 | 
			
		||||
 | 
			
		||||
        bool is_len_sub(expr const* n, expr*& l, expr*& u, rational& k) const;
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        tests if s is a single character string(c) or a unit (c)
 | 
			
		||||
        */
 | 
			
		||||
        bool is_unit_string(expr const* s, expr_ref& c) const;
 | 
			
		||||
 | 
			
		||||
        bool is_string_term(expr const * n) const {
 | 
			
		||||
            return u.is_string(n->get_sort());
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -530,7 +538,20 @@ public:
 | 
			
		|||
        bool is_loop(expr const* n)    const { return is_app_of(n, m_fid, OP_RE_LOOP); }
 | 
			
		||||
        bool is_empty(expr const* n)  const { return is_app_of(n, m_fid, OP_RE_EMPTY_SET); }
 | 
			
		||||
        bool is_full_char(expr const* n)  const { return is_app_of(n, m_fid, OP_RE_FULL_CHAR_SET); }
 | 
			
		||||
        bool is_full_seq(expr const* n)  const { return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET); }
 | 
			
		||||
        bool is_full_seq(expr const* n)  const {
 | 
			
		||||
            expr* s;
 | 
			
		||||
            return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET) || (is_star(n, s) && is_full_char(s));
 | 
			
		||||
        }
 | 
			
		||||
        bool is_dot_plus(expr const* n)  const {
 | 
			
		||||
            expr* s, * t;
 | 
			
		||||
            if (is_plus(n, s) && is_full_char(s))
 | 
			
		||||
                return true;
 | 
			
		||||
            if (is_concat(n, s, t)) {
 | 
			
		||||
                if ((is_full_char(s) && is_full_seq(t)) || (is_full_char(t) && is_full_seq(s)))
 | 
			
		||||
                    return true;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        bool is_of_pred(expr const* n) const { return is_app_of(n, m_fid, OP_RE_OF_PRED); }
 | 
			
		||||
        bool is_reverse(expr const* n) const { return is_app_of(n, m_fid, OP_RE_REVERSE); }
 | 
			
		||||
        bool is_derivative(expr const* n) const { return is_app_of(n, m_fid, OP_RE_DERIVATIVE); }
 | 
			
		||||
| 
						 | 
				
			
			@ -559,18 +580,32 @@ public:
 | 
			
		|||
        app* mk_epsilon(sort* seq_sort);
 | 
			
		||||
        info get_info(expr* r) const;
 | 
			
		||||
        std::string to_str(expr* r) const;
 | 
			
		||||
        std::string to_strh(expr* r) const;
 | 
			
		||||
 | 
			
		||||
        expr_ref mk_ite_simplify(expr* c, expr* t, expr* e)
 | 
			
		||||
        {
 | 
			
		||||
            expr_ref result(m);
 | 
			
		||||
            if (m.is_true(c) || t == e)
 | 
			
		||||
                result = t;
 | 
			
		||||
            else if (m.is_false(c))
 | 
			
		||||
                result = e;
 | 
			
		||||
            else
 | 
			
		||||
                result = m.mk_ite(c, t, e);
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        class pp {
 | 
			
		||||
            seq_util::rex& re;
 | 
			
		||||
            expr* e;
 | 
			
		||||
            expr* ex;
 | 
			
		||||
            bool html_encode;
 | 
			
		||||
            bool can_skip_parenth(expr* r) const;
 | 
			
		||||
            std::ostream& seq_unit(std::ostream& out, expr* s) const;
 | 
			
		||||
            std::ostream& compact_helper_seq(std::ostream& out, expr* s) const;
 | 
			
		||||
            std::ostream& compact_helper_range(std::ostream& out, expr* s1, expr* s2) const;
 | 
			
		||||
            bool print_unit(std::ostream& out, expr* s) const;
 | 
			
		||||
            bool print_seq(std::ostream& out, expr* s) const;
 | 
			
		||||
            std::ostream& print_range(std::ostream& out, expr* s1, expr* s2) const;
 | 
			
		||||
            std::ostream& print(std::ostream& out, expr* e) const;
 | 
			
		||||
 | 
			
		||||
        public:
 | 
			
		||||
            pp(seq_util::rex& r, expr* e, bool html = false) : re(r), e(e), html_encode(html) {}
 | 
			
		||||
            pp(seq_util::rex& re, expr* ex, bool html) : re(re), ex(ex), html_encode(html) {}
 | 
			
		||||
            std::ostream& display(std::ostream&) const;
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -87,6 +87,7 @@ public:
 | 
			
		|||
    bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); }
 | 
			
		||||
    sr_property get_property(func_decl* f) const;
 | 
			
		||||
    sr_property get_property(app* e) const { return get_property(e->get_decl()); }
 | 
			
		||||
    func_decl* get_relation(func_decl* f) const { SASSERT(is_special_relation(f)); return to_func_decl(f->get_parameter(0).get_ast()); }
 | 
			
		||||
 | 
			
		||||
    func_decl* mk_to_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_TO); }
 | 
			
		||||
    func_decl* mk_po_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_PO); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1828,9 +1828,8 @@ void cmd_context::add_declared_functions(model& mdl) {
 | 
			
		|||
                mdl.register_decl(f, fi);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        mdl.add_rec_funs();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    mdl.add_rec_funs();    
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_context::display_sat_result(lbool r) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -270,6 +270,7 @@ public:
 | 
			
		|||
    class check_value {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual bool operator()(Value const& v) = 0;
 | 
			
		||||
        virtual ~check_value() = default;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    bool find_le(Key const* keys, check_value& check) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ Revision History:
 | 
			
		|||
namespace lp {
 | 
			
		||||
class column_namer {
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~column_namer() = default;
 | 
			
		||||
    virtual std::string get_variable_name(unsigned j) const = 0;
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    std::ostream & print_row(const row_strip<T> & row, std::ostream & out) const {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,6 +120,8 @@ struct factorization_factory {
 | 
			
		|||
        m_vars(vars), m_monic(m) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual ~factorization_factory() = default;
 | 
			
		||||
 | 
			
		||||
    bool_vector get_mask() const {
 | 
			
		||||
        // we keep the last element always in the first factor to avoid
 | 
			
		||||
        // repeating a pair twice, that is why m_mask is shorter by one then m_vars
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,9 @@ namespace lp {
 | 
			
		|||
 | 
			
		||||
    lp_settings const& lar_solver::settings() const { return m_settings; }
 | 
			
		||||
 | 
			
		||||
    statistics& lar_solver::stats() { return m_settings.stats(); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void lar_solver::updt_params(params_ref const& _p) {
 | 
			
		||||
        smt_params_helper p(_p);
 | 
			
		||||
        set_track_pivoted_rows(p.arith_bprop_on_pivoted_rows());
 | 
			
		||||
| 
						 | 
				
			
			@ -23,17 +26,9 @@ namespace lp {
 | 
			
		|||
        m_settings.updt_params(_p);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void clear() {
 | 
			
		||||
        lp_assert(false); // not implemented
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lar_solver::lar_solver() :
 | 
			
		||||
        m_status(lp_status::UNKNOWN),
 | 
			
		||||
        m_crossed_bounds_column(-1),
 | 
			
		||||
        m_mpq_lar_core_solver(m_settings, *this),
 | 
			
		||||
        m_int_solver(nullptr),
 | 
			
		||||
        m_need_register_terms(false),
 | 
			
		||||
        m_var_register(false),
 | 
			
		||||
        m_term_register(true),
 | 
			
		||||
        m_constraints(*this) {}
 | 
			
		||||
| 
						 | 
				
			
			@ -197,11 +192,11 @@ namespace lp {
 | 
			
		|||
    void lar_solver::set_status(lp_status s) { m_status = s; }
 | 
			
		||||
 | 
			
		||||
    lp_status lar_solver::find_feasible_solution() {
 | 
			
		||||
        m_settings.stats().m_make_feasible++;
 | 
			
		||||
        if (A_r().column_count() > m_settings.stats().m_max_cols)
 | 
			
		||||
            m_settings.stats().m_max_cols = A_r().column_count();
 | 
			
		||||
        if (A_r().row_count() > m_settings.stats().m_max_rows)
 | 
			
		||||
            m_settings.stats().m_max_rows = A_r().row_count();
 | 
			
		||||
        stats().m_make_feasible++;
 | 
			
		||||
        if (A_r().column_count() > stats().m_max_cols)
 | 
			
		||||
            stats().m_max_cols = A_r().column_count();
 | 
			
		||||
        if (A_r().row_count() > stats().m_max_rows)
 | 
			
		||||
            stats().m_max_rows = A_r().row_count();
 | 
			
		||||
        if (strategy_is_undecided())
 | 
			
		||||
            decide_on_strategy_and_adjust_initial_state();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +243,7 @@ namespace lp {
 | 
			
		|||
        m_constraints.push();
 | 
			
		||||
        m_usage_in_terms.push();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    void lar_solver::clean_popped_elements(unsigned n, u_set& set) {
 | 
			
		||||
        vector<int> to_remove;
 | 
			
		||||
        for (unsigned j : set)
 | 
			
		||||
| 
						 | 
				
			
			@ -269,9 +264,8 @@ namespace lp {
 | 
			
		|||
        m_crossed_bounds_column.pop(k);
 | 
			
		||||
        unsigned n = m_columns_to_ul_pairs.peek_size(k);
 | 
			
		||||
        m_var_register.shrink(n);
 | 
			
		||||
        if (m_settings.use_tableau()) {
 | 
			
		||||
        if (m_settings.use_tableau()) 
 | 
			
		||||
            pop_tableau();
 | 
			
		||||
        }
 | 
			
		||||
        lp_assert(A_r().column_count() == n);
 | 
			
		||||
        TRACE("lar_solver_details",
 | 
			
		||||
            for (unsigned j = 0; j < n; j++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -285,6 +279,10 @@ namespace lp {
 | 
			
		|||
        clean_popped_elements(n, m_columns_with_changed_bounds);
 | 
			
		||||
        clean_popped_elements(n, m_incorrect_columns);
 | 
			
		||||
 | 
			
		||||
        for (auto rid : m_row_bounds_to_replay)
 | 
			
		||||
            insert_row_with_changed_bounds(rid);
 | 
			
		||||
        m_row_bounds_to_replay.reset();
 | 
			
		||||
 | 
			
		||||
        unsigned m = A_r().row_count();
 | 
			
		||||
        clean_popped_elements(m, m_rows_with_changed_bounds);
 | 
			
		||||
        clean_inf_set_of_r_solver_after_pop();
 | 
			
		||||
| 
						 | 
				
			
			@ -633,6 +631,9 @@ namespace lp {
 | 
			
		|||
                left_side.push_back(std::make_pair(p.second, p.first));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void lar_solver::insert_row_with_changed_bounds(unsigned rid) {
 | 
			
		||||
        m_rows_with_changed_bounds.insert(rid);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) {
 | 
			
		||||
        if (A_r().row_count() != m_column_buffer.data_size())
 | 
			
		||||
| 
						 | 
				
			
			@ -643,14 +644,14 @@ namespace lp {
 | 
			
		|||
 | 
			
		||||
        m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer);
 | 
			
		||||
        for (unsigned i : m_column_buffer.m_index)
 | 
			
		||||
            m_rows_with_changed_bounds.insert(i);
 | 
			
		||||
            insert_row_with_changed_bounds(i);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) {
 | 
			
		||||
        for (auto& rc : m_mpq_lar_core_solver.m_r_A.m_columns[j])
 | 
			
		||||
            m_rows_with_changed_bounds.insert(rc.var());
 | 
			
		||||
            insert_row_with_changed_bounds(rc.var());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool lar_solver::use_tableau() const { return m_settings.use_tableau(); }
 | 
			
		||||
| 
						 | 
				
			
			@ -743,7 +744,7 @@ namespace lp {
 | 
			
		|||
 | 
			
		||||
    void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) {
 | 
			
		||||
        if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) {
 | 
			
		||||
            m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]);
 | 
			
		||||
            insert_row_with_changed_bounds(m_mpq_lar_core_solver.m_r_heading[j]);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -793,7 +794,7 @@ namespace lp {
 | 
			
		|||
            update_x_and_inf_costs_for_columns_with_changed_bounds();
 | 
			
		||||
        m_mpq_lar_core_solver.solve();
 | 
			
		||||
        set_status(m_mpq_lar_core_solver.m_r_solver.get_status());
 | 
			
		||||
        lp_assert(((m_settings.stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold());
 | 
			
		||||
        lp_assert(((stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -974,9 +975,7 @@ namespace lp {
 | 
			
		|||
 | 
			
		||||
    bool lar_solver::the_left_sides_sum_to_zero(const vector<std::pair<mpq, unsigned>>& evidence) const {
 | 
			
		||||
        std::unordered_map<var_index, mpq> coeff_map;
 | 
			
		||||
        for (auto& it : evidence) {
 | 
			
		||||
            mpq coeff = it.first;
 | 
			
		||||
            constraint_index con_ind = it.second;
 | 
			
		||||
        for (auto const & [coeff, con_ind] : evidence) {
 | 
			
		||||
            lp_assert(m_constraints.valid_index(con_ind));
 | 
			
		||||
            register_in_map(coeff_map, m_constraints[con_ind], coeff);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1337,7 +1336,7 @@ namespace lp {
 | 
			
		|||
    void lar_solver::mark_rows_for_bound_prop(lpvar j) {
 | 
			
		||||
        auto& column = A_r().m_columns[j];
 | 
			
		||||
        for (auto const& r : column) 
 | 
			
		||||
            m_rows_with_changed_bounds.insert(r.var());        
 | 
			
		||||
            insert_row_with_changed_bounds(r.var());        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1659,7 +1658,7 @@ namespace lp {
 | 
			
		|||
            m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size());
 | 
			
		||||
            m_mpq_lar_core_solver.m_r_basis.push_back(j);
 | 
			
		||||
            if (m_settings.bound_propagation())
 | 
			
		||||
                m_rows_with_changed_bounds.insert(A_r().row_count() - 1);
 | 
			
		||||
                insert_row_with_changed_bounds(A_r().row_count() - 1);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast<int>(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1);
 | 
			
		||||
| 
						 | 
				
			
			@ -1755,7 +1754,7 @@ namespace lp {
 | 
			
		|||
        if (use_tableau() && !coeffs.empty()) {
 | 
			
		||||
            add_row_from_term_no_constraint(m_terms.back(), ret);
 | 
			
		||||
            if (m_settings.bound_propagation())
 | 
			
		||||
                m_rows_with_changed_bounds.insert(A_r().row_count() - 1);
 | 
			
		||||
                insert_row_with_changed_bounds(A_r().row_count() - 1);
 | 
			
		||||
        }
 | 
			
		||||
        lp_assert(m_var_register.size() == A_r().column_count());
 | 
			
		||||
        if (m_need_register_terms) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -76,13 +76,13 @@ class lar_solver : public column_namer {
 | 
			
		|||
    
 | 
			
		||||
    //////////////////// fields //////////////////////////
 | 
			
		||||
    lp_settings                                         m_settings;
 | 
			
		||||
    lp_status                                           m_status;
 | 
			
		||||
    lp_status                                           m_status = lp_status::UNKNOWN;
 | 
			
		||||
    stacked_value<simplex_strategy_enum>                m_simplex_strategy;
 | 
			
		||||
    // such can be found at the initialization step: u < l
 | 
			
		||||
    stacked_value<int>                                  m_crossed_bounds_column; 
 | 
			
		||||
    lar_core_solver                                     m_mpq_lar_core_solver;
 | 
			
		||||
    int_solver *                                        m_int_solver;
 | 
			
		||||
    bool                                                m_need_register_terms;
 | 
			
		||||
    int_solver *                                        m_int_solver = nullptr;
 | 
			
		||||
    bool                                                m_need_register_terms = false;
 | 
			
		||||
    var_register                                        m_var_register;
 | 
			
		||||
    var_register                                        m_term_register;
 | 
			
		||||
    stacked_vector<ul_pair>                             m_columns_to_ul_pairs;
 | 
			
		||||
| 
						 | 
				
			
			@ -90,6 +90,8 @@ class lar_solver : public column_namer {
 | 
			
		|||
    // the set of column indices j such that bounds have changed for j
 | 
			
		||||
    u_set                                               m_columns_with_changed_bounds;
 | 
			
		||||
    u_set                                               m_rows_with_changed_bounds;
 | 
			
		||||
    unsigned_vector                                     m_row_bounds_to_replay;
 | 
			
		||||
    
 | 
			
		||||
    u_set                                               m_basic_columns_with_changed_cost;
 | 
			
		||||
    // these are basic columns with the value changed, so the the corresponding row in the tableau
 | 
			
		||||
    // does not sum to zero anymore
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +166,6 @@ class lar_solver : public column_namer {
 | 
			
		|||
    void adjust_initial_state_for_lu();
 | 
			
		||||
    void adjust_initial_state_for_tableau_rows();
 | 
			
		||||
    void fill_last_row_of_A_d(static_matrix<double, double> & A, const lar_term* ls);
 | 
			
		||||
    void clear();
 | 
			
		||||
    bool use_lu() const;
 | 
			
		||||
    bool sizes_are_correct() const;
 | 
			
		||||
    bool implied_bound_is_correctly_explained(implied_bound const & be, const vector<std::pair<mpq, unsigned>> & explanation) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -219,6 +220,7 @@ class lar_solver : public column_namer {
 | 
			
		|||
    void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair<mpq> & delta);
 | 
			
		||||
    void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j);
 | 
			
		||||
    unsigned num_changed_bounds() const { return m_rows_with_changed_bounds.size(); }
 | 
			
		||||
    void insert_row_with_changed_bounds(unsigned rid);
 | 
			
		||||
    void detect_rows_with_changed_bounds_for_column(unsigned j);
 | 
			
		||||
    void detect_rows_with_changed_bounds();
 | 
			
		||||
    void set_value_for_nbasic_column(unsigned j, const impq & new_val);
 | 
			
		||||
| 
						 | 
				
			
			@ -368,20 +370,19 @@ public:
 | 
			
		|||
        // these two loops should be run sequentially
 | 
			
		||||
        // since the first loop might change column bounds
 | 
			
		||||
        // and add fixed columns this way
 | 
			
		||||
        if (settings().cheap_eqs()) {
 | 
			
		||||
        if (settings().propagate_eqs()) {
 | 
			
		||||
            bp.clear_for_eq();
 | 
			
		||||
            for (unsigned i : m_rows_with_changed_bounds) {
 | 
			
		||||
                calculate_cheap_eqs_for_row(i, bp);
 | 
			
		||||
                unsigned offset_eqs = stats().m_offset_eqs;
 | 
			
		||||
                bp.cheap_eq_tree(i);                
 | 
			
		||||
                if (settings().get_cancel_flag())
 | 
			
		||||
                    return;
 | 
			
		||||
                if (stats().m_offset_eqs > offset_eqs)
 | 
			
		||||
                    m_row_bounds_to_replay.push_back(i);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        m_rows_with_changed_bounds.clear();
 | 
			
		||||
    }
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    void calculate_cheap_eqs_for_row(unsigned i, lp_bound_propagator<T> & bp) {
 | 
			
		||||
        bp.cheap_eq_tree(i);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    bool is_fixed(column_index const& j) const { return column_is_fixed(j); }
 | 
			
		||||
    inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); }
 | 
			
		||||
| 
						 | 
				
			
			@ -515,6 +516,8 @@ public:
 | 
			
		|||
    unsigned column_to_reported_index(unsigned j) const;
 | 
			
		||||
    lp_settings & settings();
 | 
			
		||||
    lp_settings const & settings() const;
 | 
			
		||||
    statistics& stats();
 | 
			
		||||
 
 | 
			
		||||
    void updt_params(params_ref const& p);
 | 
			
		||||
    column_type get_column_type(unsigned j) const { return m_mpq_lar_core_solver.m_column_types()[j]; }
 | 
			
		||||
    const impq & get_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_lower_bounds()[j]; }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -155,7 +155,7 @@ public:
 | 
			
		|||
    };
 | 
			
		||||
    
 | 
			
		||||
    class const_iterator {
 | 
			
		||||
        u_map< mpq>::iterator m_it;
 | 
			
		||||
        u_map<mpq>::iterator m_it;
 | 
			
		||||
    public:
 | 
			
		||||
        ival operator*() const { return ival(m_it->m_key, m_it->m_value); }        
 | 
			
		||||
        const_iterator operator++() { const_iterator i = *this; m_it++; return i;  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -72,14 +72,15 @@ class lp_bound_propagator {
 | 
			
		|||
    static int other(int x, int y, int z) { SASSERT(x == z || y == z); return x == z ? y : x; }
 | 
			
		||||
    std::ostream& print_vert(std::ostream & out, const vertex* v) const {
 | 
			
		||||
        out << "(c = " << v->column() << ", parent = {";
 | 
			
		||||
        if (v->parent()) { out << "(" << v->parent()->column() << ")";}
 | 
			
		||||
        else { out << "null"; }
 | 
			
		||||
        if (v->parent())
 | 
			
		||||
            out << "(" << v->parent()->column() << ")";
 | 
			
		||||
        else
 | 
			
		||||
            out << "null"; 
 | 
			
		||||
        out <<  "} , lvl = " << v->level();
 | 
			
		||||
        if (m_pol.contains(v->column())) {
 | 
			
		||||
        if (m_pol.contains(v->column())) 
 | 
			
		||||
            out << (pol(v) == -1? " -":" +");
 | 
			
		||||
        } else {
 | 
			
		||||
        else
 | 
			
		||||
            out << " not in m_pol";
 | 
			
		||||
        }
 | 
			
		||||
        out << ')';
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -87,13 +88,13 @@ class lp_bound_propagator {
 | 
			
		|||
    hashtable<unsigned, u_hash, u_eq>         m_visited_rows;
 | 
			
		||||
    hashtable<unsigned, u_hash, u_eq>         m_visited_columns;
 | 
			
		||||
    u_map<vertex*>                            m_vertices;
 | 
			
		||||
    vertex*                                   m_root;
 | 
			
		||||
    vertex*                                   m_root = nullptr;
 | 
			
		||||
    // At some point we can find a row with a single vertex non fixed vertex
 | 
			
		||||
    // then we can fix the whole tree,
 | 
			
		||||
    // by adjusting the vertices offsets, so they become absolute.
 | 
			
		||||
    // If the tree is fixed then in addition to checking with the m_vals_to_verts
 | 
			
		||||
    // we are going to check with the m_fixed_var_tables.
 | 
			
		||||
    const vertex*                             m_fixed_vertex;
 | 
			
		||||
    const vertex*                             m_fixed_vertex = nullptr;
 | 
			
		||||
    explanation                               m_fixed_vertex_explanation;
 | 
			
		||||
    // a pair (o, j) belongs to m_vals_to_verts iff x[j] = x[m_root->column()] + o
 | 
			
		||||
    map<mpq, const vertex*, obj_hash<mpq>, default_eq<mpq>>  m_vals_to_verts;
 | 
			
		||||
| 
						 | 
				
			
			@ -111,19 +112,199 @@ class lp_bound_propagator {
 | 
			
		|||
    
 | 
			
		||||
    T&                                        m_imp;
 | 
			
		||||
    vector<implied_bound>                     m_ibounds;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    map<mpq, unsigned, obj_hash<mpq>, default_eq<mpq>>  m_val2fixed_row;
 | 
			
		||||
    
 | 
			
		||||
    void try_add_equation_with_internal_fixed_tables(unsigned r1, vertex const* v) {
 | 
			
		||||
        SASSERT(m_fixed_vertex);
 | 
			
		||||
        if (v != m_root)
 | 
			
		||||
            return;
 | 
			
		||||
        unsigned v1 = v->column();
 | 
			
		||||
        unsigned r2 = UINT_MAX;
 | 
			
		||||
        if (!m_val2fixed_row.find(val(v1), r2) || r2 >= lp().row_count()) {
 | 
			
		||||
            m_val2fixed_row.insert(val(v1), r1);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        unsigned v2, v3;
 | 
			
		||||
        int polarity;
 | 
			
		||||
        if (!is_tree_offset_row(r2, v2, v3, polarity) || !not_set(v3) ||
 | 
			
		||||
            is_int(v1) != is_int(v2) || val(v1) != val(v2)) {
 | 
			
		||||
            m_val2fixed_row.insert(val(v1), r1);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        explanation ex;
 | 
			
		||||
        explain_fixed_in_row(r1, ex);
 | 
			
		||||
        explain_fixed_in_row(r2, ex);
 | 
			
		||||
        add_eq_on_columns(ex, v1, v2, true);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void try_add_equation_with_lp_fixed_tables(unsigned row_index, const vertex *v) {
 | 
			
		||||
        SASSERT(m_fixed_vertex);
 | 
			
		||||
        unsigned v_j = v->column();
 | 
			
		||||
        unsigned j = null_lpvar;
 | 
			
		||||
        if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j)) {
 | 
			
		||||
            // try_add_equation_with_internal_fixed_tables(row_index, v);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
       
 | 
			
		||||
        TRACE("cheap_eq",
 | 
			
		||||
              tout << "v_j = "; lp().print_column_info(v_j, tout) << std::endl;
 | 
			
		||||
              tout << "v = "; print_vert(tout, v) << std::endl;
 | 
			
		||||
              tout << "found j " << j << std::endl; lp().print_column_info(j, tout)<< std::endl;
 | 
			
		||||
              tout << "found j = " << j << std::endl;);
 | 
			
		||||
        vector<edge> path = connect_in_tree(v, m_fixed_vertex);
 | 
			
		||||
        explanation ex = get_explanation_from_path(path);
 | 
			
		||||
        ex.add_expl(m_fixed_vertex_explanation);
 | 
			
		||||
        explain_fixed_column(j, ex);
 | 
			
		||||
        add_eq_on_columns(ex, j, v_j, true);         
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void try_add_equation_with_val_table(const vertex *v) {
 | 
			
		||||
        SASSERT(m_fixed_vertex);
 | 
			
		||||
        unsigned v_j = v->column();
 | 
			
		||||
        const vertex *u = nullptr;
 | 
			
		||||
        if (!m_vals_to_verts.find(val(v_j), u)) {
 | 
			
		||||
            m_vals_to_verts.insert(val(v_j), v);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        unsigned j = u->column();
 | 
			
		||||
        if (j == v_j || is_int(j) != is_int(v_j))
 | 
			
		||||
            return;
 | 
			
		||||
    
 | 
			
		||||
        TRACE("cheap_eq", tout << "found j=" << j << " for v=";
 | 
			
		||||
              print_vert(tout, v) << "\n in m_vals_to_verts\n";);
 | 
			
		||||
        vector<edge> path = connect_in_tree(u, v);
 | 
			
		||||
        explanation ex = get_explanation_from_path(path);
 | 
			
		||||
        ex.add_expl(m_fixed_vertex_explanation);
 | 
			
		||||
        add_eq_on_columns(ex, j, v_j, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static bool not_set(unsigned j) { return j == UINT_MAX; }
 | 
			
		||||
    static bool is_set(unsigned j) { return j != UINT_MAX; }
 | 
			
		||||
    
 | 
			
		||||
    void create_root(unsigned row_index) {
 | 
			
		||||
        SASSERT(!m_root && !m_fixed_vertex);
 | 
			
		||||
        unsigned x, y;
 | 
			
		||||
        int polarity;
 | 
			
		||||
        TRACE("cheap_eq_det", print_row(tout, row_index););
 | 
			
		||||
        if (!is_tree_offset_row(row_index, x, y, polarity)) {
 | 
			
		||||
            TRACE("cheap_eq_det", tout << "not an offset row\n";);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        TRACE("cheap_eq", print_row(tout, row_index););
 | 
			
		||||
        m_root = alloc_v(x);
 | 
			
		||||
        set_polarity(m_root, 1); // keep m_root in the positive table
 | 
			
		||||
        if (not_set(y)) {
 | 
			
		||||
            set_fixed_vertex(m_root);
 | 
			
		||||
            explain_fixed_in_row(row_index, m_fixed_vertex_explanation);
 | 
			
		||||
        }
 | 
			
		||||
        else {            
 | 
			
		||||
            vertex *v = add_child_with_check(row_index, y, m_root, polarity);
 | 
			
		||||
            if (v)
 | 
			
		||||
                explore_under(v);
 | 
			
		||||
        }
 | 
			
		||||
        explore_under(m_root);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        void explore_under(vertex * v) {
 | 
			
		||||
        check_for_eq_and_add_to_val_tables(v);
 | 
			
		||||
        go_over_vertex_column(v);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // In case of only one non fixed column, and the function returns true,
 | 
			
		||||
    // this column would be represened by x.
 | 
			
		||||
    bool is_tree_offset_row(unsigned row_index, unsigned & x, unsigned & y, int & polarity) const {
 | 
			
		||||
        x = y =  UINT_MAX;
 | 
			
		||||
        const row_cell<mpq>* x_cell = nullptr;
 | 
			
		||||
        const row_cell<mpq>* y_cell = nullptr;
 | 
			
		||||
        const auto & row = lp().get_row(row_index);
 | 
			
		||||
        for (unsigned k = 0; k < row.size(); k++) {
 | 
			
		||||
            const auto& c = row[k];
 | 
			
		||||
            if (column_is_fixed(c.var()))
 | 
			
		||||
                continue;
 | 
			
		||||
            if (not_set(x)) {
 | 
			
		||||
                if (c.coeff().is_one() || c.coeff().is_minus_one()) {
 | 
			
		||||
                    x = c.var();
 | 
			
		||||
                    x_cell = & c;
 | 
			
		||||
                }
 | 
			
		||||
                else 
 | 
			
		||||
                    return false;
 | 
			
		||||
            }
 | 
			
		||||
            else if (not_set(y)) {
 | 
			
		||||
                if (c.coeff().is_one() || c.coeff().is_minus_one()) {
 | 
			
		||||
                    y = c.var();
 | 
			
		||||
                    y_cell = & c;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    return false;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (is_set(x)) {
 | 
			
		||||
            if (is_set(y))
 | 
			
		||||
                polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos()? -1 : 1;
 | 
			
		||||
            else
 | 
			
		||||
                polarity = 1;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void go_over_vertex_column(vertex * v) {
 | 
			
		||||
        lpvar j = v->column();
 | 
			
		||||
        if (!check_insert(m_visited_columns, j))
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        for (const auto & c : lp().get_column(j)) {
 | 
			
		||||
            unsigned row_index = c.var();
 | 
			
		||||
            if (!check_insert(m_visited_rows, row_index))
 | 
			
		||||
                continue;
 | 
			
		||||
            vertex* u = get_child_from_row(row_index, v);
 | 
			
		||||
            if (u) 
 | 
			
		||||
                explore_under(u);            
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void reset_cheap_eq_eh() {
 | 
			
		||||
        if (!m_root)
 | 
			
		||||
            return;
 | 
			
		||||
        delete_tree(m_root);
 | 
			
		||||
        m_root = nullptr;
 | 
			
		||||
        set_fixed_vertex(nullptr);
 | 
			
		||||
        m_fixed_vertex_explanation.clear();
 | 
			
		||||
        m_vals_to_verts.reset();
 | 
			
		||||
        m_vals_to_verts_neg.reset();
 | 
			
		||||
        m_pol.reset();
 | 
			
		||||
        m_vertices.reset();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    struct reset_cheap_eq {
 | 
			
		||||
        lp_bound_propagator& p;
 | 
			
		||||
        reset_cheap_eq(lp_bound_propagator& p):p(p) {}
 | 
			
		||||
        ~reset_cheap_eq() { p.reset_cheap_eq_eh(); }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    lp_bound_propagator(T& imp):
 | 
			
		||||
        m_imp(imp) {}
 | 
			
		||||
 | 
			
		||||
    const vector<implied_bound>& ibounds() const { return m_ibounds; }
 | 
			
		||||
    
 | 
			
		||||
    void init() {
 | 
			
		||||
        m_improved_upper_bounds.clear();
 | 
			
		||||
        m_improved_lower_bounds.clear();
 | 
			
		||||
        m_ibounds.reset();
 | 
			
		||||
    }
 | 
			
		||||
    lp_bound_propagator(T& imp): m_root(nullptr),
 | 
			
		||||
                                 m_fixed_vertex(nullptr),
 | 
			
		||||
                                 m_imp(imp) {}
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    const lar_solver& lp() const { return m_imp.lp(); }
 | 
			
		||||
    lar_solver& lp() { return m_imp.lp(); }
 | 
			
		||||
    
 | 
			
		||||
    column_type get_column_type(unsigned j) const {
 | 
			
		||||
        return m_imp.lp().get_column_type(j);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -133,9 +314,8 @@ public:
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    const mpq & get_lower_bound_rational(unsigned j) const {
 | 
			
		||||
            return m_imp.lp().get_lower_bound(j).x;
 | 
			
		||||
        return m_imp.lp().get_lower_bound(j).x;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    const impq & get_upper_bound(unsigned j) const {
 | 
			
		||||
        return m_imp.lp().get_upper_bound(j);
 | 
			
		||||
| 
						 | 
				
			
			@ -167,19 +347,22 @@ public:
 | 
			
		|||
                    found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
 | 
			
		||||
                    TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout););
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                m_improved_lower_bounds[j] = m_ibounds.size();
 | 
			
		||||
                m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
 | 
			
		||||
                TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout););
 | 
			
		||||
            }
 | 
			
		||||
        } else { // the upper bound case
 | 
			
		||||
        }
 | 
			
		||||
        else { // the upper bound case
 | 
			
		||||
            if (try_get_value(m_improved_upper_bounds, j, k)) {
 | 
			
		||||
                auto & found_bound = m_ibounds[k];
 | 
			
		||||
                if (v < found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) {
 | 
			
		||||
                    found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
 | 
			
		||||
                    TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout););
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                m_improved_upper_bounds[j] = m_ibounds.size();
 | 
			
		||||
                m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
 | 
			
		||||
                TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout););
 | 
			
		||||
| 
						 | 
				
			
			@ -199,54 +382,12 @@ public:
 | 
			
		|||
        return val(v->column());
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void try_add_equation_with_lp_fixed_tables(const vertex *v) {
 | 
			
		||||
        SASSERT(m_fixed_vertex);
 | 
			
		||||
        unsigned v_j = v->column();
 | 
			
		||||
        unsigned j = null_lpvar;
 | 
			
		||||
        if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j)) 
 | 
			
		||||
            return;
 | 
			
		||||
       
 | 
			
		||||
        TRACE("cheap_eq", tout << "v_j = "; lp().print_column_info(v_j, tout) << std::endl;);
 | 
			
		||||
        TRACE("cheap_eq", tout << "v = "; print_vert(tout, v) << std::endl;);        
 | 
			
		||||
        TRACE("cheap_eq", tout << "found j " << j << std::endl;
 | 
			
		||||
              lp().print_column_info(j, tout)<< std::endl;);
 | 
			
		||||
        TRACE("cheap_eq", tout << "found j = " << j << std::endl;);
 | 
			
		||||
        vector<edge> path = connect_in_tree(v, m_fixed_vertex);
 | 
			
		||||
        explanation ex = get_explanation_from_path(path);
 | 
			
		||||
        ex.add_expl(m_fixed_vertex_explanation);
 | 
			
		||||
        explain_fixed_column(j, ex);
 | 
			
		||||
        add_eq_on_columns(ex, j, v->column());
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void try_add_equation_with_val_table(const vertex *v) {
 | 
			
		||||
        SASSERT(m_fixed_vertex);
 | 
			
		||||
        unsigned v_j = v->column();
 | 
			
		||||
        const vertex *u = nullptr;
 | 
			
		||||
        if (!m_vals_to_verts.find(val(v_j), u)) {
 | 
			
		||||
            m_vals_to_verts.insert(val(v_j), v);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        unsigned j = u->column();
 | 
			
		||||
        if (j == v_j || is_int(j) != is_int(v_j))
 | 
			
		||||
            return;
 | 
			
		||||
    
 | 
			
		||||
        TRACE("cheap_eq", tout << "found j=" << j << " for v=";
 | 
			
		||||
              print_vert(tout, v) << "\n in m_vals_to_verts\n";);
 | 
			
		||||
        vector<edge> path = connect_in_tree(u, v);
 | 
			
		||||
        explanation ex = get_explanation_from_path(path);
 | 
			
		||||
        ex.add_expl(m_fixed_vertex_explanation);
 | 
			
		||||
        add_eq_on_columns(ex, j, v_j);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    bool tree_contains_r(vertex* root, vertex *v) const {
 | 
			
		||||
        if (*root == *v)
 | 
			
		||||
            return true;
 | 
			
		||||
        for (auto e : root->edges()) {
 | 
			
		||||
        for (auto e : root->edges()) 
 | 
			
		||||
            if (tree_contains_r(e.target(), v))
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -294,38 +435,12 @@ public:
 | 
			
		|||
        return v;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static bool not_set(unsigned j) { return j == UINT_MAX; }
 | 
			
		||||
    static bool is_set(unsigned j) { return j != UINT_MAX; }
 | 
			
		||||
    
 | 
			
		||||
    void create_root(unsigned row_index) {
 | 
			
		||||
        SASSERT(!m_root && !m_fixed_vertex);
 | 
			
		||||
        unsigned x, y;
 | 
			
		||||
        int polarity;
 | 
			
		||||
        TRACE("cheap_eq_det", print_row(tout, row_index););
 | 
			
		||||
        if (!is_tree_offset_row(row_index, x, y, polarity)) {
 | 
			
		||||
            TRACE("cheap_eq_det", tout << "not an offset row\n";);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        TRACE("cheap_eq", print_row(tout, row_index););
 | 
			
		||||
        m_root = alloc_v(x);
 | 
			
		||||
        set_polarity(m_root, 1); // keep m_root in the positive table
 | 
			
		||||
        if (not_set(y)) {
 | 
			
		||||
            set_fixed_vertex(m_root);
 | 
			
		||||
            explain_fixed_in_row(row_index, m_fixed_vertex_explanation);
 | 
			
		||||
        } else {            
 | 
			
		||||
            vertex *v = add_child_with_check(row_index, y, m_root, polarity);
 | 
			
		||||
            if (v)
 | 
			
		||||
                explore_under(v);
 | 
			
		||||
        }
 | 
			
		||||
        explore_under(m_root);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unsigned column(unsigned row, unsigned index) {
 | 
			
		||||
        return lp().get_row(row)[index].var();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool fixed_phase() const { return m_fixed_vertex; }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    // Returns the vertex to start exploration from, or nullptr.
 | 
			
		||||
| 
						 | 
				
			
			@ -379,10 +494,12 @@ public:
 | 
			
		|||
                is_int(k->column()) == is_int(v->column()) &&
 | 
			
		||||
                !is_equal(k->column(), v->column())) {
 | 
			
		||||
                report_eq(k, v);
 | 
			
		||||
            } else {
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                TRACE("cheap_eq", tout << "no report\n";);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            TRACE("cheap_eq", tout << "registered: " << val(v) << " -> { "; print_vert(tout, v) << "} \n";);
 | 
			
		||||
            table.insert(val(v), v);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -411,37 +528,31 @@ public:
 | 
			
		|||
 | 
			
		||||
    std::ostream& print_path(const vector<edge>& path, std::ostream& out) const {
 | 
			
		||||
        out << "path = \n";
 | 
			
		||||
        for (const edge& k : path) {
 | 
			
		||||
        for (const edge& k : path) 
 | 
			
		||||
            print_edge(k, out) << "\n";
 | 
			
		||||
        }
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    // we have v_i and v_j, indices of vertices at the same offsets
 | 
			
		||||
    void report_eq(const vertex* v_i, const vertex* v_j) {
 | 
			
		||||
        SASSERT(v_i != v_j);
 | 
			
		||||
        SASSERT(lp().get_column_value(v_i->column()) == lp().get_column_value(v_j->column()));
 | 
			
		||||
        TRACE("cheap_eq", tout << v_i->column() << " = " << v_j->column() << "\nu = ";
 | 
			
		||||
              print_vert(tout, v_i) << "\nv = "; print_vert(tout, v_j) <<"\n";
 | 
			
		||||
              );
 | 
			
		||||
              print_vert(tout, v_i) << "\nv = "; print_vert(tout, v_j) <<"\n");
 | 
			
		||||
        
 | 
			
		||||
        vector<edge> path = connect_in_tree(v_i, v_j);
 | 
			
		||||
        lp::explanation exp = get_explanation_from_path(path);
 | 
			
		||||
        add_eq_on_columns(exp, v_i->column(), v_j->column());
 | 
			
		||||
        add_eq_on_columns(exp, v_i->column(), v_j->column(), false);
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& print_expl(std::ostream & out, const explanation& exp) const {
 | 
			
		||||
        for (auto p : exp) {
 | 
			
		||||
        for (auto p : exp) 
 | 
			
		||||
            lp().constraints().display(out, [this](lpvar j) { return lp().get_variable_name(j);}, p.ci());
 | 
			
		||||
        }
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void add_eq_on_columns(const explanation& exp, lpvar j, lpvar k) {
 | 
			
		||||
    bool add_eq_on_columns(const explanation& exp, lpvar j, lpvar k, bool is_fixed) {
 | 
			
		||||
        SASSERT(j != k);
 | 
			
		||||
        unsigned je = lp().column_to_reported_index(j);
 | 
			
		||||
        unsigned ke = lp().column_to_reported_index(k);
 | 
			
		||||
| 
						 | 
				
			
			@ -452,8 +563,10 @@ public:
 | 
			
		|||
              tout << "theory_vars v" << lp().local_to_external(je) << " == v" << lp().local_to_external(ke) << "\n";
 | 
			
		||||
              );
 | 
			
		||||
        
 | 
			
		||||
        m_imp.add_eq(je, ke, exp);
 | 
			
		||||
        lp().settings().stats().m_cheap_eqs++;
 | 
			
		||||
        bool added = m_imp.add_eq(je, ke, exp, is_fixed);
 | 
			
		||||
        if (added)
 | 
			
		||||
            lp().stats().m_offset_eqs++;
 | 
			
		||||
        return added;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // column to theory_var
 | 
			
		||||
| 
						 | 
				
			
			@ -478,14 +591,10 @@ public:
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    void explain_fixed_in_row(unsigned row, explanation& ex) const {
 | 
			
		||||
                TRACE("cheap_eq",
 | 
			
		||||
        tout << lp().get_row(row) << std::endl;
 | 
			
		||||
              );
 | 
			
		||||
        for (const auto & c : lp().get_row(row)) {
 | 
			
		||||
            if (lp().is_fixed(c.var())) {
 | 
			
		||||
        TRACE("cheap_eq", tout << lp().get_row(row) << std::endl);
 | 
			
		||||
        for (const auto & c : lp().get_row(row)) 
 | 
			
		||||
            if (lp().is_fixed(c.var())) 
 | 
			
		||||
                explain_fixed_column(c.var(), ex);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void explain_fixed_column(unsigned j, explanation & ex) const {
 | 
			
		||||
| 
						 | 
				
			
			@ -536,10 +645,9 @@ public:
 | 
			
		|||
        if (visited_verts.find(v->column()) != visited_verts.end()) 
 | 
			
		||||
            return false;
 | 
			
		||||
        visited_verts.insert(v->column());
 | 
			
		||||
        for (auto e : v->edges()) {
 | 
			
		||||
        for (auto e : v->edges()) 
 | 
			
		||||
            if (!tree_is_correct(e.target(), visited_verts))
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    std::ostream& print_tree(std::ostream & out, vertex* v) const {
 | 
			
		||||
| 
						 | 
				
			
			@ -553,43 +661,37 @@ public:
 | 
			
		|||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void try_add_equation_with_fixed_tables(const vertex* v) {
 | 
			
		||||
        try_add_equation_with_lp_fixed_tables(v);
 | 
			
		||||
    void try_add_equation_with_fixed_tables(unsigned row_index, const vertex* v) {
 | 
			
		||||
        try_add_equation_with_lp_fixed_tables(row_index, v);
 | 
			
		||||
        try_add_equation_with_val_table(v);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void create_fixed_eqs(const vertex* v) {
 | 
			
		||||
        try_add_equation_with_fixed_tables(v);
 | 
			
		||||
    void handle_fixed_phase(unsigned row_index) {
 | 
			
		||||
        if (!fixed_phase())
 | 
			
		||||
            return;
 | 
			
		||||
        const vertex* v = m_root;
 | 
			
		||||
        try_add_equation_with_fixed_tables(row_index, v);
 | 
			
		||||
        for (auto e: v->edges())
 | 
			
		||||
            try_add_equation_with_fixed_tables(e.target());
 | 
			
		||||
            try_add_equation_with_fixed_tables(row_index, e.target());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handle_fixed_phase() {
 | 
			
		||||
        create_fixed_eqs(m_root);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void cheap_eq_tree(unsigned row_index) {
 | 
			
		||||
        TRACE("cheap_eq_det", tout << "row_index = " << row_index << "\n";);        
 | 
			
		||||
        if (!check_insert(m_visited_rows, row_index))
 | 
			
		||||
            return; // already explored
 | 
			
		||||
        create_root(row_index);
 | 
			
		||||
        if (m_root == nullptr) {
 | 
			
		||||
        reset_cheap_eq _reset(*this);
 | 
			
		||||
        TRACE("cheap_eq_det", tout << "row_index = " << row_index << "\n";);
 | 
			
		||||
        if (!check_insert(m_visited_rows, row_index)) 
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";);
 | 
			
		||||
        create_root(row_index);
 | 
			
		||||
        if (!m_root)
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";);        
 | 
			
		||||
        SASSERT(tree_is_correct());
 | 
			
		||||
        if (fixed_phase())
 | 
			
		||||
            handle_fixed_phase();
 | 
			
		||||
        TRACE("cheap_eq", tout << "done for row_index " << row_index << "\n";);
 | 
			
		||||
        TRACE("cheap_eq", tout << "tree size = " << verts_size(););
 | 
			
		||||
        delete_tree(m_root);
 | 
			
		||||
        m_root = nullptr;
 | 
			
		||||
        set_fixed_vertex(nullptr);
 | 
			
		||||
        m_fixed_vertex_explanation.clear();
 | 
			
		||||
        m_vals_to_verts.reset();
 | 
			
		||||
        m_vals_to_verts_neg.reset();
 | 
			
		||||
        m_pol.reset();
 | 
			
		||||
        m_vertices.reset();
 | 
			
		||||
        handle_fixed_phase(row_index);
 | 
			
		||||
        
 | 
			
		||||
        TRACE("cheap_eq",
 | 
			
		||||
              tout << "done for row_index " << row_index << "\n";
 | 
			
		||||
              tout << "tree size = " << verts_size(););
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& print_row(std::ostream & out, unsigned row_index) const  {
 | 
			
		||||
| 
						 | 
				
			
			@ -643,71 +745,7 @@ public:
 | 
			
		|||
            return false;
 | 
			
		||||
        table.insert(j);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void go_over_vertex_column(vertex * v) {
 | 
			
		||||
        lpvar j = v->column();
 | 
			
		||||
        if (!check_insert(m_visited_columns, j))
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        for (const auto & c : lp().get_column(j)) {
 | 
			
		||||
            unsigned row_index = c.var();
 | 
			
		||||
            if (!check_insert(m_visited_rows, row_index))
 | 
			
		||||
                continue;
 | 
			
		||||
            vertex *u = get_child_from_row(row_index, v);
 | 
			
		||||
            if (u) {
 | 
			
		||||
                // debug
 | 
			
		||||
                // if (verts_size() > 3) {
 | 
			
		||||
                //     std::cout << "big tree\n";
 | 
			
		||||
                //     TRACE("cheap_eq", print_tree(tout, m_root););
 | 
			
		||||
                //     exit(1);
 | 
			
		||||
                // } // end debug
 | 
			
		||||
                explore_under(u);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void explore_under(vertex * v) {
 | 
			
		||||
        check_for_eq_and_add_to_val_tables(v);
 | 
			
		||||
        go_over_vertex_column(v);
 | 
			
		||||
    }
 | 
			
		||||
    }        
 | 
			
		||||
 | 
			
		||||
    // In case of only one non fixed column, and the function returns true,
 | 
			
		||||
    // this column would be represened by x.
 | 
			
		||||
    bool is_tree_offset_row( unsigned row_index,
 | 
			
		||||
        unsigned & x, unsigned & y, int & polarity ) const {
 | 
			
		||||
        x = y =  UINT_MAX;
 | 
			
		||||
        const row_cell<mpq>* x_cell = nullptr;
 | 
			
		||||
        const row_cell<mpq>* y_cell = nullptr;
 | 
			
		||||
        const auto & row = lp().get_row(row_index);
 | 
			
		||||
        for (unsigned k = 0; k < row.size(); k++) {
 | 
			
		||||
            const auto& c = row[k];
 | 
			
		||||
            if (column_is_fixed(c.var()))
 | 
			
		||||
                continue;
 | 
			
		||||
            if (not_set(x)) {
 | 
			
		||||
                if (c.coeff().is_one() || c.coeff().is_minus_one()) {
 | 
			
		||||
                    x = c.var();
 | 
			
		||||
                    x_cell = & c;
 | 
			
		||||
                } else {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (not_set(y)) {
 | 
			
		||||
                if (c.coeff().is_one() || c.coeff().is_minus_one()) {
 | 
			
		||||
                    y = c.var();
 | 
			
		||||
                    y_cell = & c;
 | 
			
		||||
                } else
 | 
			
		||||
                    return false;
 | 
			
		||||
            } else
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (is_set(x)) {
 | 
			
		||||
            if (is_set(y))
 | 
			
		||||
                polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos()? -1 : 1;
 | 
			
		||||
            else
 | 
			
		||||
                polarity = 1;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,7 @@ template bool lp::vectors_are_equal<lp::mpq>(vector<lp::mpq > const&, vector<lp:
 | 
			
		|||
void lp::lp_settings::updt_params(params_ref const& _p) {
 | 
			
		||||
    smt_params_helper p(_p);
 | 
			
		||||
    m_enable_hnf = p.arith_enable_hnf();
 | 
			
		||||
    m_cheap_eqs = p.arith_propagate_eqs();
 | 
			
		||||
    m_propagate_eqs = p.arith_propagate_eqs();
 | 
			
		||||
    print_statistics = p.arith_print_stats();
 | 
			
		||||
    m_print_external_var_name = p.arith_print_ext_var_names();
 | 
			
		||||
    report_frequency = p.arith_rep_freq();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,6 +99,7 @@ template <typename X> bool is_epsilon_small(const X & v, const double& eps);
 | 
			
		|||
 | 
			
		||||
class lp_resource_limit {
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~lp_resource_limit() = default;
 | 
			
		||||
    virtual bool get_cancel_flag() = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -125,7 +126,7 @@ struct statistics {
 | 
			
		|||
    unsigned m_cross_nested_forms;
 | 
			
		||||
    unsigned m_grobner_calls;
 | 
			
		||||
    unsigned m_grobner_conflicts;
 | 
			
		||||
    unsigned m_cheap_eqs;
 | 
			
		||||
    unsigned m_offset_eqs;
 | 
			
		||||
    statistics() { reset(); }
 | 
			
		||||
    void reset() { memset(this, 0, sizeof(*this)); }
 | 
			
		||||
    void collect_statistics(::statistics& st) const {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +147,7 @@ struct statistics {
 | 
			
		|||
        st.update("arith-horner-cross-nested-forms", m_cross_nested_forms);
 | 
			
		||||
        st.update("arith-grobner-calls", m_grobner_calls);
 | 
			
		||||
        st.update("arith-grobner-conflicts", m_grobner_conflicts);
 | 
			
		||||
        st.update("arith-cheap-eqs", m_cheap_eqs);
 | 
			
		||||
        st.update("arith-offset-eqs", m_offset_eqs);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -241,10 +242,10 @@ private:
 | 
			
		|||
    unsigned         m_nlsat_delay;
 | 
			
		||||
    bool             m_enable_hnf { true };
 | 
			
		||||
    bool             m_print_external_var_name { false };
 | 
			
		||||
    bool             m_cheap_eqs { false };
 | 
			
		||||
    bool             m_propagate_eqs { false };
 | 
			
		||||
public:
 | 
			
		||||
    bool print_external_var_name() const { return m_print_external_var_name; }
 | 
			
		||||
    bool cheap_eqs() const { return m_cheap_eqs;}
 | 
			
		||||
    bool propagate_eqs() const { return m_propagate_eqs;}
 | 
			
		||||
    unsigned hnf_cut_period() const { return m_hnf_cut_period; }
 | 
			
		||||
    void set_hnf_cut_period(unsigned period) { m_hnf_cut_period = period;  }
 | 
			
		||||
    unsigned random_next() { return m_rand(); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,6 +71,7 @@ namespace polynomial {
 | 
			
		|||
    template<typename ValManager, typename Value = typename ValManager::numeral>
 | 
			
		||||
    class var2value {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~var2value() = default;
 | 
			
		||||
        virtual ValManager & m() const = 0;
 | 
			
		||||
        virtual bool contains(var x) const = 0;
 | 
			
		||||
        virtual Value const & operator()(var x) const = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +101,7 @@ namespace polynomial {
 | 
			
		|||
 | 
			
		||||
    struct display_var_proc {
 | 
			
		||||
        virtual std::ostream& operator()(std::ostream & out, var x) const { return out << "x" << x; }
 | 
			
		||||
        virtual ~display_var_proc() = default;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class polynomial;
 | 
			
		||||
| 
						 | 
				
			
			@ -228,6 +230,7 @@ namespace polynomial {
 | 
			
		|||
            del_eh * m_next;
 | 
			
		||||
        public:
 | 
			
		||||
            del_eh():m_next(nullptr) {}
 | 
			
		||||
            virtual ~del_eh() = default;
 | 
			
		||||
            virtual void operator()(polynomial * p) = 0;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -175,6 +175,8 @@ namespace upolynomial {
 | 
			
		|||
            m_current_size = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        virtual ~factorization_combination_iterator_base() = default;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
           \brief Returns the factors we are enumerating through.
 | 
			
		||||
        */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ namespace realclosure {
 | 
			
		|||
 | 
			
		||||
    class mk_interval {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~mk_interval() = default;
 | 
			
		||||
        virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ Notes:
 | 
			
		|||
 | 
			
		||||
#include "math/simplex/bit_matrix.h"
 | 
			
		||||
#include "util/stopwatch.h"
 | 
			
		||||
#include "util/trace.h"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,7 @@ public:
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
struct display_var_proc {
 | 
			
		||||
    virtual ~display_var_proc() = default;
 | 
			
		||||
    virtual void operator()(std::ostream & out, var x) const { out << "x" << x; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ array_factory::array_factory(ast_manager & m, model_core & md):
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   \brieft Return as-array[f] where f is a fresh function symbol with the right domain and range for the array sort s.
 | 
			
		||||
   \brief Return as-array[f] where f is a fresh function symbol with the right domain and range for the array sort s.
 | 
			
		||||
   Store in fi the function interpretation for f.
 | 
			
		||||
*/
 | 
			
		||||
expr * array_factory::mk_array_interp(sort * s, func_interp * & fi) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -280,6 +280,9 @@ void func_interp::compress() {
 | 
			
		|||
    }
 | 
			
		||||
    // other compression, if else is a default branch.
 | 
			
		||||
    // or function encode identity.
 | 
			
		||||
#if 0
 | 
			
		||||
    // breaks array interpretations
 | 
			
		||||
    // #5604
 | 
			
		||||
    if (m().is_false(m_else)) {
 | 
			
		||||
        expr_ref new_else(get_interp(), m());
 | 
			
		||||
        for (func_entry * curr : m_entries) {
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +294,9 @@ void func_interp::compress() {
 | 
			
		|||
        m().dec_ref(m_else);
 | 
			
		||||
        m_else = new_else;
 | 
			
		||||
    }
 | 
			
		||||
    else if (!m_entries.empty() && is_identity()) {
 | 
			
		||||
    else
 | 
			
		||||
#endif
 | 
			
		||||
        if (!m_entries.empty() && is_identity()) {
 | 
			
		||||
        for (func_entry * curr : m_entries) {
 | 
			
		||||
            curr->deallocate(m(), m_arity);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -335,14 +340,11 @@ expr * func_interp::get_interp_core() const {
 | 
			
		|||
    expr * r = m_else;
 | 
			
		||||
    ptr_buffer<expr> vars;
 | 
			
		||||
    for (func_entry * curr : m_entries) {
 | 
			
		||||
        if (m_else == curr->get_result()) {
 | 
			
		||||
        if (m_else == curr->get_result()) 
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (vars.empty()) {
 | 
			
		||||
            for (unsigned i = 0; i < m_arity; i++) {
 | 
			
		||||
        if (vars.empty()) 
 | 
			
		||||
            for (unsigned i = 0; i < m_arity; i++)                 
 | 
			
		||||
                vars.push_back(m().mk_var(i, curr->get_arg(i)->get_sort()));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ptr_buffer<expr> eqs;
 | 
			
		||||
        for (unsigned i = 0; i < m_arity; i++) {
 | 
			
		||||
            eqs.push_back(m().mk_eq(vars[i], curr->get_arg(i)));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -238,15 +238,13 @@ void model::compress(bool force_inline) {
 | 
			
		|||
        top_sort ts(m);
 | 
			
		||||
        collect_deps(ts);
 | 
			
		||||
        ts.topological_sort();
 | 
			
		||||
        for (func_decl * f : ts.top_sorted()) {
 | 
			
		||||
        for (func_decl * f : ts.top_sorted())
 | 
			
		||||
            cleanup_interp(ts, f, force_inline);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        func_decl_set removed;
 | 
			
		||||
        ts.m_occur_count.reset();
 | 
			
		||||
        for (func_decl * f : ts.top_sorted()) {
 | 
			
		||||
        for (func_decl * f : ts.top_sorted()) 
 | 
			
		||||
            collect_occs(ts, f);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // remove auxiliary declarations that are not used.
 | 
			
		||||
        for (func_decl * f : ts.top_sorted()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +254,8 @@ void model::compress(bool force_inline) {
 | 
			
		|||
                removed.insert(f);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (removed.empty()) break;
 | 
			
		||||
        if (removed.empty())
 | 
			
		||||
            break;
 | 
			
		||||
        TRACE("model", tout << "remove\n"; for (func_decl* f : removed) tout << f->get_name() << "\n";);
 | 
			
		||||
        remove_decls(m_decls, removed);
 | 
			
		||||
        remove_decls(m_func_decls, removed);
 | 
			
		||||
| 
						 | 
				
			
			@ -268,12 +267,14 @@ void model::compress(bool force_inline) {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
void model::collect_deps(top_sort& ts) {
 | 
			
		||||
    for (auto const& kv : m_finterp) {
 | 
			
		||||
        ts.insert(kv.m_key, collect_deps(ts, kv.m_value));
 | 
			
		||||
    }
 | 
			
		||||
    for (auto const& kv : m_interp) {
 | 
			
		||||
        ts.insert(kv.m_key, collect_deps(ts, kv.m_value.second));
 | 
			
		||||
    }
 | 
			
		||||
    recfun::util u(m);
 | 
			
		||||
    for (auto const& [f, v] : m_finterp) 
 | 
			
		||||
        if (!u.has_def(f))
 | 
			
		||||
            ts.insert(f, collect_deps(ts, v));
 | 
			
		||||
        
 | 
			
		||||
    for (auto const& [f,v] : m_interp)
 | 
			
		||||
        if (!u.has_def(f))
 | 
			
		||||
            ts.insert(f, collect_deps(ts, v.second));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct model::deps_collector {
 | 
			
		||||
| 
						 | 
				
			
			@ -334,6 +335,7 @@ model::func_decl_set* model::collect_deps(top_sort& ts, func_interp * fi) {
 | 
			
		|||
*/
 | 
			
		||||
 | 
			
		||||
void model::cleanup_interp(top_sort& ts, func_decl* f, bool force_inline) {
 | 
			
		||||
    
 | 
			
		||||
    unsigned pid = ts.partition_id(f);
 | 
			
		||||
    expr * e1 = get_const_interp(f);
 | 
			
		||||
    if (e1) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,7 +162,6 @@ struct evaluator_cfg : public default_rewriter_cfg {
 | 
			
		|||
        result_pr = nullptr;
 | 
			
		||||
        family_id fid = f->get_family_id();
 | 
			
		||||
        bool _is_uninterp = fid != null_family_id && m.get_plugin(fid)->is_considered_uninterpreted(f);
 | 
			
		||||
        func_decl* g = nullptr;
 | 
			
		||||
        br_status st = BR_FAILED;
 | 
			
		||||
#if 0
 | 
			
		||||
        struct pp {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -122,6 +122,7 @@ namespace datalog {
 | 
			
		|||
 | 
			
		||||
    class register_engine_base {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~register_engine_base() = default;
 | 
			
		||||
        virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0;
 | 
			
		||||
        virtual void set_context(context* ctx) = 0;
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ namespace datalog {
 | 
			
		|||
            /**
 | 
			
		||||
               \brief Number of rules longer than two that contain this pair.
 | 
			
		||||
 | 
			
		||||
               This number is being updated by \c add_rule and \remove rule. Even though between
 | 
			
		||||
               This number is being updated by \c add_rule and \c remove_rule. Even though between
 | 
			
		||||
               adding a rule and removing it, the length of a rule can decrease without this pair
 | 
			
		||||
               being notified about it, it will surely see the decrease from length 3 to 2 which
 | 
			
		||||
               the threshold for rule being counted in this counter.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ public:
 | 
			
		|||
    typedef obj_map<func_decl, unsigned> partition_map;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       \Brief Model Based Cartesian projection of lits
 | 
			
		||||
       \brief Model Based Cartesian projection of lits
 | 
			
		||||
     */
 | 
			
		||||
    void operator()(const partition_map &pmap, expr_ref_vector &lits, model &mdl,
 | 
			
		||||
                    vector<expr_ref_vector> &res);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,6 +32,7 @@ namespace nlsat {
 | 
			
		|||
 | 
			
		||||
    class display_assumption_proc {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~display_assumption_proc() = default;
 | 
			
		||||
        virtual std::ostream& operator()(std::ostream& out, assumption a) const = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -131,16 +131,18 @@ namespace opt {
 | 
			
		|||
                soft.set_value(l_undef);
 | 
			
		||||
            }
 | 
			
		||||
            model_ref mdl;
 | 
			
		||||
            s().get_model(mdl);
 | 
			
		||||
            s().get_model(mdl);            
 | 
			
		||||
            if (mdl) {
 | 
			
		||||
                TRACE("opt", tout << *mdl << "\n";);
 | 
			
		||||
                for (auto & soft : m_soft) {
 | 
			
		||||
                    if (!mdl->is_true(soft.s)) {
 | 
			
		||||
                        break;
 | 
			
		||||
                        update_bounds();
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    soft.set_value(l_true);
 | 
			
		||||
                    assert_value(soft);
 | 
			
		||||
                }
 | 
			
		||||
                update_bounds();
 | 
			
		||||
                update_assignment();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -151,9 +153,8 @@ namespace opt {
 | 
			
		|||
            unsigned sz = m_soft.size();
 | 
			
		||||
            for (unsigned i = 0; i < sz; ++i) {
 | 
			
		||||
                auto& soft = m_soft[i];
 | 
			
		||||
                if (soft.value != l_undef) {
 | 
			
		||||
                if (soft.value != l_undef) 
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                expr_ref_vector asms(m);
 | 
			
		||||
                asms.push_back(soft.s);
 | 
			
		||||
                lbool is_sat = s().check_sat(asms);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ Notes:
 | 
			
		|||
#include "ast/pb_decl_plugin.h"
 | 
			
		||||
#include "ast/ast_smt_pp.h"
 | 
			
		||||
#include "ast/ast_pp_util.h"
 | 
			
		||||
#include "ast/ast_ll_pp.h"
 | 
			
		||||
#include "ast/display_dimacs.h"
 | 
			
		||||
#include "model/model_smt2_pp.h"
 | 
			
		||||
#include "tactic/goal.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -966,12 +967,12 @@ namespace opt {
 | 
			
		|||
                  tout << "Convert minimization " << orig_term << "\n";
 | 
			
		||||
                  tout << "to maxsat: " << term << "\n";
 | 
			
		||||
                  for (unsigned i = 0; i < weights.size(); ++i) {
 | 
			
		||||
                      tout << mk_pp(terms[i].get(), m) << ": " << weights[i] << "\n";
 | 
			
		||||
                      tout << mk_pp(terms.get(i), m) << ": " << weights[i] << "\n";
 | 
			
		||||
                  }
 | 
			
		||||
                  tout << "offset: " << offset << "\n";
 | 
			
		||||
                  );
 | 
			
		||||
            std::ostringstream out;
 | 
			
		||||
            out << orig_term << ':' << index;
 | 
			
		||||
            out << mk_bounded_pp(orig_term, m, 2) << ':' << index;
 | 
			
		||||
            id = symbol(out.str());
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -994,7 +995,7 @@ namespace opt {
 | 
			
		|||
            }
 | 
			
		||||
            neg = true;
 | 
			
		||||
            std::ostringstream out;
 | 
			
		||||
            out << orig_term << ':' << index;
 | 
			
		||||
            out << mk_bounded_pp(orig_term, m) << ':' << index;
 | 
			
		||||
            id = symbol(out.str());
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1013,7 +1014,7 @@ namespace opt {
 | 
			
		|||
            }
 | 
			
		||||
            neg = is_max;
 | 
			
		||||
            std::ostringstream out;
 | 
			
		||||
            out << orig_term << ':' << index;
 | 
			
		||||
            out << mk_bounded_pp(orig_term, m, 2) << ':' << index;
 | 
			
		||||
            id = symbol(out.str());
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1200,7 +1201,7 @@ namespace opt {
 | 
			
		|||
 | 
			
		||||
    app* context::purify(generic_model_converter_ref& fm, expr* term) {
 | 
			
		||||
       std::ostringstream out;
 | 
			
		||||
       out << mk_pp(term, m);
 | 
			
		||||
       out << mk_bounded_pp(term, m, 3);
 | 
			
		||||
       app* q = m.mk_fresh_const(out.str(), term->get_sort());
 | 
			
		||||
       if (!fm) fm = alloc(generic_model_converter, m, "opt");
 | 
			
		||||
       if (m_arith.is_int_real(term)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,6 +45,7 @@ namespace opt {
 | 
			
		|||
 | 
			
		||||
    class maxsat_context {
 | 
			
		||||
    public:        
 | 
			
		||||
        virtual ~maxsat_context() = default;
 | 
			
		||||
        virtual generic_model_converter& fm() = 0;   // converter that removes fresh names introduced by simplification.
 | 
			
		||||
        virtual bool sat_enabled() const = 0;       // is using th SAT solver core enabled?
 | 
			
		||||
        virtual solver& get_solver() = 0;           // retrieve solver object (SAT or SMT solver)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@ namespace opt {
 | 
			
		|||
   
 | 
			
		||||
    class pareto_callback {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~pareto_callback() = default;
 | 
			
		||||
        virtual unsigned num_objectives() = 0;
 | 
			
		||||
        virtual expr_ref mk_gt(unsigned i, model_ref& model) = 0;
 | 
			
		||||
        virtual expr_ref mk_ge(unsigned i, model_ref& model) = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -989,6 +989,7 @@ namespace nlarith {
 | 
			
		|||
            imp& m_imp;
 | 
			
		||||
        public:
 | 
			
		||||
            isubst(imp& i) : m_imp(i) {}
 | 
			
		||||
            virtual ~isubst() = default;
 | 
			
		||||
            virtual void mk_lt(poly const& p, app_ref& r) = 0;
 | 
			
		||||
            virtual void mk_eq(poly const& p, app_ref& r) = 0;
 | 
			
		||||
            virtual void mk_le(poly const& p, app_ref& r) { 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,8 +96,6 @@ namespace nlarith {
 | 
			
		|||
        bool create_branches(app* x, unsigned nl, expr* const* lits, branch_conditions& bc);
 | 
			
		||||
        /**
 | 
			
		||||
           \brief Extract non-linear variables from ground formula.
 | 
			
		||||
           
 | 
			
		||||
           \requires a ground formula.
 | 
			
		||||
        */
 | 
			
		||||
        void extract_non_linear(expr* e, ptr_vector<app>& nl_vars);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,7 @@ namespace qe {
 | 
			
		|||
 | 
			
		||||
    class i_nnf_atom {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~i_nnf_atom() = default;
 | 
			
		||||
        virtual void operator()(expr* e, bool pol, expr_ref& result) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,6 +40,7 @@ namespace sat {
 | 
			
		|||
    class literal_occs_fun {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual double operator()(literal l) = 0;        
 | 
			
		||||
        virtual ~literal_occs_fun() = default;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1893,7 +1893,6 @@ namespace sat {
 | 
			
		|||
    void solver::init_ext_assumptions() {
 | 
			
		||||
        if (m_ext && m_ext->tracking_assumptions()) {
 | 
			
		||||
            m_ext_assumption_set.reset();
 | 
			
		||||
            unsigned trail_size = m_trail.size();
 | 
			
		||||
            if (!inconsistent())
 | 
			
		||||
                m_ext->add_assumptions(m_ext_assumption_set);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -301,26 +301,27 @@ namespace arith {
 | 
			
		|||
        m_explanation.add_pair(j, v);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void solver::add_eq(lpvar u, lpvar v, lp::explanation const& e) {
 | 
			
		||||
    bool solver::add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed) {
 | 
			
		||||
        if (s().inconsistent())
 | 
			
		||||
            return;
 | 
			
		||||
            return false;
 | 
			
		||||
        theory_var uv = lp().local_to_external(u); // variables that are returned should have external representations
 | 
			
		||||
        theory_var vv = lp().local_to_external(v); // so maybe better to have them already transformed to external form
 | 
			
		||||
        if (is_equal(uv, vv))
 | 
			
		||||
            return;
 | 
			
		||||
            return false;
 | 
			
		||||
        enode* n1 = var2enode(uv);
 | 
			
		||||
        enode* n2 = var2enode(vv);
 | 
			
		||||
        expr* e1 = n1->get_expr();
 | 
			
		||||
        expr* e2 = n2->get_expr();
 | 
			
		||||
        if (m.is_ite(e1) || m.is_ite(e2))
 | 
			
		||||
            return;
 | 
			
		||||
        if (!is_fixed && !a.is_numeral(e1) && !a.is_numeral(e2) && (m.is_ite(e1) || m.is_ite(e2)))
 | 
			
		||||
            return false;
 | 
			
		||||
        if (e1->get_sort() != e2->get_sort())
 | 
			
		||||
            return;
 | 
			
		||||
            return false;
 | 
			
		||||
        reset_evidence();
 | 
			
		||||
        for (auto ev : e)
 | 
			
		||||
            set_evidence(ev.ci(), m_core, m_eqs);
 | 
			
		||||
        auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, n1, n2);
 | 
			
		||||
        ctx.propagate(n1, n2, jst->to_index());
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool solver::bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const {
 | 
			
		||||
| 
						 | 
				
			
			@ -627,7 +628,7 @@ namespace arith {
 | 
			
		|||
            anum const& an = nl_value(v, *m_a1);
 | 
			
		||||
            if (a.is_int(o) && !m_nla->am().is_int(an))
 | 
			
		||||
                value = a.mk_numeral(rational::zero(), a.is_int(o));             
 | 
			
		||||
                value = a.mk_numeral(m_nla->am(), nl_value(v, *m_a1), a.is_int(o));
 | 
			
		||||
            //value = a.mk_numeral(m_nla->am(), nl_value(v, *m_a1), a.is_int(o));
 | 
			
		||||
        }
 | 
			
		||||
        else if (v != euf::null_theory_var) {
 | 
			
		||||
            rational r = get_value(v);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -450,7 +450,7 @@ namespace arith {
 | 
			
		|||
        lp::lar_solver& lp() { return *m_solver; }
 | 
			
		||||
        lp::lar_solver const& lp() const { return *m_solver; }
 | 
			
		||||
        bool is_equal(theory_var x, theory_var y) const;
 | 
			
		||||
        void add_eq(lpvar u, lpvar v, lp::explanation const& e);
 | 
			
		||||
        bool add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed);
 | 
			
		||||
        void consume(rational const& v, lp::constraint_index j);
 | 
			
		||||
        bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const;
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -621,8 +621,7 @@ namespace array {
 | 
			
		|||
                continue;            
 | 
			
		||||
            // arrays used as indices in other arrays have to be treated as shared issue #3532, #3529            
 | 
			
		||||
            if (ctx.is_shared(r) || is_shared_arg(r)) 
 | 
			
		||||
                roots.push_back(r->get_th_var(get_id()));
 | 
			
		||||
            
 | 
			
		||||
                roots.push_back(r->get_th_var(get_id()));           
 | 
			
		||||
            r->mark1();
 | 
			
		||||
            to_unmark.push_back(r);            
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,8 +120,8 @@ namespace array {
 | 
			
		|||
        SASSERT(!n || !n->is_attached_to(get_id()));
 | 
			
		||||
        if (!n) 
 | 
			
		||||
            n = mk_enode(e, false);
 | 
			
		||||
        SASSERT(!n->is_attached_to(get_id()));
 | 
			
		||||
        mk_var(n);
 | 
			
		||||
        if (!n->is_attached_to(get_id())) 
 | 
			
		||||
            mk_var(n);
 | 
			
		||||
        for (auto* arg : euf::enode_args(n))
 | 
			
		||||
            ensure_var(arg);  
 | 
			
		||||
        switch (a->get_decl_kind()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -119,7 +119,7 @@ namespace array {
 | 
			
		|||
 | 
			
		||||
    bool solver::must_have_different_model_values(theory_var v1, theory_var v2) {
 | 
			
		||||
        euf::enode* else1 = nullptr, * else2 = nullptr;
 | 
			
		||||
        euf::enode* n1 = var2enode(v1), *n2 = var2enode(v2);
 | 
			
		||||
        euf::enode* n1 = var2enode(v1);
 | 
			
		||||
        expr* e1 = n1->get_expr();
 | 
			
		||||
        if (!a.is_array(e1))
 | 
			
		||||
            return true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,7 @@ namespace euf {
 | 
			
		|||
                value = r->get_expr();
 | 
			
		||||
            else
 | 
			
		||||
                value = factory.get_fresh_value(srt);
 | 
			
		||||
            (void)s;
 | 
			
		||||
            TRACE("model", tout << s.bpp(r) << " := " << value << "\n";);
 | 
			
		||||
            values.set(id, value);
 | 
			
		||||
            expr_ref_vector* vals = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,32 @@ Author:
 | 
			
		|||
 | 
			
		||||
namespace euf {
 | 
			
		||||
 | 
			
		||||
    void solver::add_auto_relevant(expr* e) {
 | 
			
		||||
        if (!relevancy_enabled())
 | 
			
		||||
            return;
 | 
			
		||||
        for (; m_auto_relevant_scopes > 0; --m_auto_relevant_scopes) 
 | 
			
		||||
            m_auto_relevant_lim.push_back(m_auto_relevant.size());
 | 
			
		||||
        // std::cout << "add-auto " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n";
 | 
			
		||||
        m_auto_relevant.push_back(e);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void solver::pop_relevant(unsigned n) {
 | 
			
		||||
        if (m_auto_relevant_scopes >= n) {
 | 
			
		||||
            m_auto_relevant_scopes -= n;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        n -= m_auto_relevant_scopes;
 | 
			
		||||
        m_auto_relevant_scopes = 0;
 | 
			
		||||
        unsigned top = m_auto_relevant_lim.size() - n;
 | 
			
		||||
        unsigned lim = m_auto_relevant_lim[top];
 | 
			
		||||
        m_auto_relevant_lim.shrink(top);
 | 
			
		||||
        m_auto_relevant.shrink(lim);        
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void solver::push_relevant() {
 | 
			
		||||
        ++m_auto_relevant_scopes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool solver::is_relevant(expr* e) const { 
 | 
			
		||||
        return m_relevant_expr_ids.get(e->get_id(), true); 
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -31,11 +57,11 @@ namespace euf {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    void solver::ensure_dual_solver() {
 | 
			
		||||
        if (!m_dual_solver) {
 | 
			
		||||
            m_dual_solver = alloc(sat::dual_solver, s().rlimit());
 | 
			
		||||
            for (unsigned i = s().num_user_scopes(); i-- > 0; )
 | 
			
		||||
                m_dual_solver->push();
 | 
			
		||||
        }
 | 
			
		||||
        if (m_dual_solver)
 | 
			
		||||
            return;
 | 
			
		||||
        m_dual_solver = alloc(sat::dual_solver, s().rlimit());
 | 
			
		||||
        for (unsigned i = s().num_user_scopes(); i-- > 0; )
 | 
			
		||||
            m_dual_solver->push();        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -65,8 +91,6 @@ namespace euf {
 | 
			
		|||
 | 
			
		||||
    bool solver::init_relevancy() {
 | 
			
		||||
        m_relevant_expr_ids.reset();
 | 
			
		||||
        bool_vector visited;
 | 
			
		||||
        ptr_vector<expr> todo;
 | 
			
		||||
        if (!relevancy_enabled())
 | 
			
		||||
            return true;
 | 
			
		||||
        if (!m_dual_solver)
 | 
			
		||||
| 
						 | 
				
			
			@ -77,12 +101,21 @@ namespace euf {
 | 
			
		|||
        for (enode* n : m_egraph.nodes())
 | 
			
		||||
            max_id = std::max(max_id, n->get_expr_id());
 | 
			
		||||
        m_relevant_expr_ids.resize(max_id + 1, false);
 | 
			
		||||
        ptr_vector<expr>& todo = m_relevant_todo;
 | 
			
		||||
        bool_vector& visited = m_relevant_visited;
 | 
			
		||||
        auto const& core = m_dual_solver->core();
 | 
			
		||||
        todo.reset();
 | 
			
		||||
        for (auto lit : core) {
 | 
			
		||||
            expr* e = m_bool_var2expr.get(lit.var(), nullptr);
 | 
			
		||||
            if (e)
 | 
			
		||||
                todo.push_back(e);
 | 
			
		||||
        }
 | 
			
		||||
#if 0
 | 
			
		||||
        std::cout << "init-relevant\n";
 | 
			
		||||
        for (expr* e : m_auto_relevant)
 | 
			
		||||
            std::cout << "auto-relevant " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n";
 | 
			
		||||
#endif
 | 
			
		||||
        todo.append(m_auto_relevant);
 | 
			
		||||
        for (unsigned i = 0; i < todo.size(); ++i) {
 | 
			
		||||
            expr* e = todo[i];
 | 
			
		||||
            if (visited.get(e->get_id(), false))
 | 
			
		||||
| 
						 | 
				
			
			@ -114,6 +147,9 @@ namespace euf {
 | 
			
		|||
                todo.push_back(arg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (auto * e : todo)
 | 
			
		||||
            visited[e->get_id()] = false;
 | 
			
		||||
 | 
			
		||||
        TRACE("euf",
 | 
			
		||||
            for (enode* n : m_egraph.nodes())
 | 
			
		||||
                if (is_relevant(n)) 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -183,6 +183,7 @@ namespace euf {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    void solver::propagate(literal lit, ext_justification_idx idx) {
 | 
			
		||||
        add_auto_relevant(bool_var2expr(lit.var()));
 | 
			
		||||
        s().assign(lit, sat::justification::mk_ext_justification(s().scope_lvl(), idx));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -527,6 +528,7 @@ namespace euf {
 | 
			
		|||
        m_egraph.push();
 | 
			
		||||
        if (m_dual_solver)
 | 
			
		||||
            m_dual_solver->push();
 | 
			
		||||
        push_relevant();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void solver::pop(unsigned n) {
 | 
			
		||||
| 
						 | 
				
			
			@ -536,6 +538,7 @@ namespace euf {
 | 
			
		|||
            e->pop(n);
 | 
			
		||||
        si.pop(n);
 | 
			
		||||
        m_egraph.pop(n);
 | 
			
		||||
        pop_relevant(n);
 | 
			
		||||
        scope const & sc = m_scopes[m_scopes.size() - n];
 | 
			
		||||
        for (unsigned i = m_var_trail.size(); i-- > sc.m_var_lim; ) {
 | 
			
		||||
            bool_var v = m_var_trail[i];
 | 
			
		||||
| 
						 | 
				
			
			@ -994,7 +997,7 @@ namespace euf {
 | 
			
		|||
        ::solver::push_eh_t& push_eh,
 | 
			
		||||
        ::solver::pop_eh_t& pop_eh,
 | 
			
		||||
        ::solver::fresh_eh_t& fresh_eh) {
 | 
			
		||||
        m_user_propagator = alloc(user::solver, *this);
 | 
			
		||||
        m_user_propagator = alloc(user_solver::solver, *this);
 | 
			
		||||
        m_user_propagator->add(ctx, push_eh, pop_eh, fresh_eh);
 | 
			
		||||
        for (unsigned i = m_scopes.size(); i-- > 0; )
 | 
			
		||||
            m_user_propagator->push();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,9 +99,8 @@ namespace euf {
 | 
			
		|||
        sat::lookahead*        m_lookahead = nullptr;
 | 
			
		||||
        ast_manager*           m_to_m;
 | 
			
		||||
        sat::sat_internalizer* m_to_si;
 | 
			
		||||
        scoped_ptr<euf::ackerman>    m_ackerman;
 | 
			
		||||
        scoped_ptr<sat::dual_solver> m_dual_solver;
 | 
			
		||||
        user::solver*          m_user_propagator = nullptr;
 | 
			
		||||
        scoped_ptr<euf::ackerman>     m_ackerman;
 | 
			
		||||
        user_solver::solver*          m_user_propagator = nullptr;
 | 
			
		||||
        th_solver*             m_qsolver = nullptr;
 | 
			
		||||
        unsigned               m_generation = 0;
 | 
			
		||||
        mutable ptr_vector<expr> m_todo;
 | 
			
		||||
| 
						 | 
				
			
			@ -182,6 +181,8 @@ namespace euf {
 | 
			
		|||
 | 
			
		||||
        // relevancy
 | 
			
		||||
        bool_vector m_relevant_expr_ids;
 | 
			
		||||
        bool_vector m_relevant_visited;
 | 
			
		||||
        ptr_vector<expr> m_relevant_todo;
 | 
			
		||||
        void ensure_dual_solver();
 | 
			
		||||
        bool init_relevancy();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -363,6 +364,11 @@ namespace euf {
 | 
			
		|||
 | 
			
		||||
        // relevancy
 | 
			
		||||
        bool m_relevancy = true;
 | 
			
		||||
        scoped_ptr<sat::dual_solver>  m_dual_solver;
 | 
			
		||||
        ptr_vector<expr>              m_auto_relevant;
 | 
			
		||||
        unsigned_vector               m_auto_relevant_lim;
 | 
			
		||||
        unsigned                      m_auto_relevant_scopes = 0;
 | 
			
		||||
 | 
			
		||||
        bool relevancy_enabled() const { return m_relevancy && get_config().m_relevancy_lvl > 0; }
 | 
			
		||||
        void disable_relevancy(expr* e) { IF_VERBOSE(0, verbose_stream() << "disabling relevancy " << mk_pp(e, m) << "\n"); m_relevancy = false;  }
 | 
			
		||||
        void add_root(unsigned n, sat::literal const* lits);
 | 
			
		||||
| 
						 | 
				
			
			@ -377,6 +383,9 @@ namespace euf {
 | 
			
		|||
        void track_relevancy(sat::bool_var v);
 | 
			
		||||
        bool is_relevant(expr* e) const;
 | 
			
		||||
        bool is_relevant(enode* n) const;
 | 
			
		||||
        void add_auto_relevant(expr* e);
 | 
			
		||||
        void pop_relevant(unsigned n);
 | 
			
		||||
        void push_relevant();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // model construction
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,6 +52,9 @@ namespace pb {
 | 
			
		|||
        constraint(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k): 
 | 
			
		||||
            m_tag(t), m_lit(l), m_size(sz), m_obj_size(osz), m_id(id), m_k(k) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        virtual ~constraint() = default;
 | 
			
		||||
 | 
			
		||||
        sat::ext_constraint_idx cindex() const { return sat::constraint_base::mem2base(this); }
 | 
			
		||||
        void deallocate(small_object_allocator& a) { a.deallocate(obj_size(), sat::constraint_base::mem2base_ptr(this)); }
 | 
			
		||||
        unsigned id() const { return m_id; }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,7 @@ namespace pb {
 | 
			
		|||
 | 
			
		||||
    class solver_interface {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~solver_interface() = default;
 | 
			
		||||
        virtual lbool value(bool_var v) const = 0;
 | 
			
		||||
        virtual lbool value(literal lit) const = 0;
 | 
			
		||||
        virtual bool is_false(literal lit) const = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,8 +32,8 @@ namespace q {
 | 
			
		|||
                << mk_bounded_pp(rhs, m, 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& binding::display(euf::solver& ctx, unsigned num_nodes, std::ostream& out) const {
 | 
			
		||||
        for (unsigned i = 0; i < num_nodes; ++i) 
 | 
			
		||||
    std::ostream& binding::display(euf::solver& ctx, std::ostream& out) const {
 | 
			
		||||
        for (unsigned i = 0; i < size(); ++i) 
 | 
			
		||||
            out << ctx.bpp((*this)[i]) << " ";
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ namespace q {
 | 
			
		|||
        if (!b)
 | 
			
		||||
            return out;
 | 
			
		||||
        do {
 | 
			
		||||
            b->display(ctx, num_decls(), out) << "\n";
 | 
			
		||||
            b->display(ctx, out) << "\n";
 | 
			
		||||
            b = b->next();
 | 
			
		||||
        } 
 | 
			
		||||
        while (b != m_bindings);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue