mirror of
https://github.com/Z3Prover/z3
synced 2026-04-26 13:53:33 +00:00
Merge branch 'master' into nl2lin
This commit is contained in:
commit
8f9c527444
964 changed files with 54547 additions and 36359 deletions
|
|
@ -134,6 +134,10 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
|||
# Enable RPATH support
|
||||
MACOSX_RPATH TRUE
|
||||
)
|
||||
# Add header padding to allow install_name_tool to modify the dylib
|
||||
# This fixes issues where install_name_tool fails with "larger updated load commands do not fit"
|
||||
# See: https://github.com/Z3Prover/z3/issues/7623
|
||||
target_link_options(libz3 PRIVATE "-Wl,-headerpad_max_install_names")
|
||||
endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
|
|
@ -151,6 +155,11 @@ endif()
|
|||
# so that if those are also shared libraries they are referenced by `libz3.so`.
|
||||
target_link_libraries(libz3 PRIVATE ${Z3_DEPENDENT_LIBS})
|
||||
|
||||
# On macOS, add headerpad for install_name_tool compatibility
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND Z3_BUILD_LIBZ3_SHARED)
|
||||
target_link_options(libz3 PRIVATE "-Wl,-headerpad_max_install_names")
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Create include directory with headers for easier developer integration
|
||||
################################################################################
|
||||
|
|
@ -346,4 +355,16 @@ if (Z3_BUILD_JULIA_BINDINGS)
|
|||
add_subdirectory(api/julia)
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Go bindings
|
||||
################################################################################
|
||||
option(Z3_BUILD_GO_BINDINGS "Build Go bindings for Z3" OFF)
|
||||
if (Z3_BUILD_GO_BINDINGS)
|
||||
if (NOT Z3_BUILD_LIBZ3_SHARED)
|
||||
message(FATAL_ERROR "The Go bindings will not work with a static libz3. "
|
||||
"You either need to disable Z3_BUILD_GO_BINDINGS or enable Z3_BUILD_LIBZ3_SHARED")
|
||||
endif()
|
||||
add_subdirectory(api/go)
|
||||
endif()
|
||||
|
||||
# TODO: Implement support for other bindigns
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ public:
|
|||
TRACE(goal, g->display(tout << "in\n"););
|
||||
|
||||
ptr_vector<expr> flas;
|
||||
const unsigned sz = g->size();
|
||||
for (unsigned i = 0; i < sz; i++) flas.push_back(g->form(i));
|
||||
for (auto [f, dep, pr] : *g) flas.push_back(f);
|
||||
lackr lackr(m, m_p, m_st, flas, nullptr);
|
||||
|
||||
// mk result
|
||||
|
|
|
|||
|
|
@ -62,10 +62,9 @@ class ackr_bound_probe : public probe {
|
|||
public:
|
||||
result operator()(goal const & g) override {
|
||||
proc p(g.m());
|
||||
unsigned sz = g.size();
|
||||
expr_fast_mark1 visited;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for_each_expr_core<proc, expr_fast_mark1, true, true>(p, visited, g.form(i));
|
||||
for (auto [curr, dep, pr] : g) {
|
||||
for_each_expr_core<proc, expr_fast_mark1, true, true>(p, visited, curr);
|
||||
}
|
||||
p.prune_non_select();
|
||||
double total = ackr_helper::calculate_lemma_bound(p.m_fun2terms, p.m_sel2terms);
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ void ackr_model_converter::convert_constants(model * source, model * destination
|
|||
evaluator.set_model_completion(true);
|
||||
array_util autil(m);
|
||||
|
||||
for (unsigned i = 0; i < source->get_num_constants(); i++) {
|
||||
for (unsigned i = 0, n = source->get_num_constants(); i < n; ++i) {
|
||||
func_decl * const c = source->get_constant(i);
|
||||
app * const term = info->find_term(c);
|
||||
expr * value = source->get_const_interp(c);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public:
|
|||
//
|
||||
bool check() {
|
||||
bool retv = true;
|
||||
for (unsigned i = 0; i < m_abstr_model->get_num_constants(); i++) {
|
||||
for (unsigned i = 0; i < m_abstr_model->get_num_constants(); ++i) {
|
||||
func_decl * const c = m_abstr_model->get_constant(i);
|
||||
app * const _term = m_info->find_term(c);
|
||||
expr * const term = _term ? _term : m.mk_const(c);
|
||||
|
|
@ -58,13 +58,13 @@ public:
|
|||
|
||||
|
||||
void make_model(model_ref& destination) {
|
||||
for (unsigned i = 0; i < m_abstr_model->get_num_uninterpreted_sorts(); i++) {
|
||||
for (unsigned i = 0; i < m_abstr_model->get_num_uninterpreted_sorts(); ++i) {
|
||||
sort * const s = m_abstr_model->get_uninterpreted_sort(i);
|
||||
ptr_vector<expr> u = m_abstr_model->get_universe(s);
|
||||
destination->register_usort(s, u.size(), u.data());
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_abstr_model->get_num_functions(); i++) {
|
||||
for (unsigned i = 0; i < m_abstr_model->get_num_functions(); ++i) {
|
||||
func_decl * const fd = m_abstr_model->get_function(i);
|
||||
func_interp * const fi = m_abstr_model->get_func_interp(fd);
|
||||
destination->register_decl(fd, fi);
|
||||
|
|
@ -247,7 +247,7 @@ private:
|
|||
);
|
||||
m_app2val.insert(a, result.get()); // memoize
|
||||
m_pinned.push_back(a);
|
||||
m_pinned.push_back(result);
|
||||
m_pinned.push_back(std::move(result));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ z3_add_component(api
|
|||
api_context.cpp
|
||||
api_datalog.cpp
|
||||
api_datatype.cpp
|
||||
api_finite_set.cpp
|
||||
api_fpa.cpp
|
||||
api_goal.cpp
|
||||
api_log.cpp
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ extern "C" {
|
|||
static bool to_anum_vector(Z3_context c, unsigned n, Z3_ast a[], scoped_anum_vector & as) {
|
||||
algebraic_numbers::manager & _am = am(c);
|
||||
scoped_anum tmp(_am);
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
if (is_rational(c, a[i])) {
|
||||
_am.set(tmp, get_rational(c, a[i]).to_mpq());
|
||||
as.push_back(tmp);
|
||||
|
|
@ -378,7 +378,7 @@ extern "C" {
|
|||
}
|
||||
Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
|
||||
mk_c(c)->save_object(result);
|
||||
for (unsigned i = 0; i < roots.size(); i++) {
|
||||
for (unsigned i = 0; i < roots.size(); ++i) {
|
||||
result->m_ast_vector.push_back(au(c).mk_numeral(_am, roots.get(i), false));
|
||||
}
|
||||
RETURN_Z3(of_ast_vector(result));
|
||||
|
|
|
|||
|
|
@ -901,7 +901,7 @@ extern "C" {
|
|||
expr * const * from = to_exprs(num_exprs, _from);
|
||||
expr * const * to = to_exprs(num_exprs, _to);
|
||||
expr * r = nullptr;
|
||||
for (unsigned i = 0; i < num_exprs; i++) {
|
||||
for (unsigned i = 0; i < num_exprs; ++i) {
|
||||
if (from[i]->get_sort() != to[i]->get_sort()) {
|
||||
SET_ERROR_CODE(Z3_SORT_ERROR, nullptr);
|
||||
RETURN_Z3(of_expr(nullptr));
|
||||
|
|
@ -910,7 +910,7 @@ extern "C" {
|
|||
SASSERT(to[i]->get_ref_count() > 0);
|
||||
}
|
||||
expr_safe_replace subst(m);
|
||||
for (unsigned i = 0; i < num_exprs; i++) {
|
||||
for (unsigned i = 0; i < num_exprs; ++i) {
|
||||
subst.insert(from[i], to[i]);
|
||||
}
|
||||
expr_ref new_a(m);
|
||||
|
|
@ -940,7 +940,7 @@ extern "C" {
|
|||
obj_map<func_decl, expr*> rep;
|
||||
obj_map<expr, expr*> cache;
|
||||
|
||||
for (unsigned i = 0; i < num_funs; i++) {
|
||||
for (unsigned i = 0; i < num_funs; ++i) {
|
||||
if (from[i]->get_range() != to[i]->get_sort()) {
|
||||
SET_ERROR_CODE(Z3_SORT_ERROR, nullptr);
|
||||
RETURN_Z3(of_expr(nullptr));
|
||||
|
|
@ -1465,6 +1465,25 @@ extern "C" {
|
|||
}
|
||||
}
|
||||
|
||||
if (mk_c(c)->fsutil().get_family_id() == _d->get_family_id()) {
|
||||
switch(_d->get_decl_kind()) {
|
||||
case OP_FINITE_SET_EMPTY: return Z3_OP_FINITE_SET_EMPTY;
|
||||
case OP_FINITE_SET_SINGLETON: return Z3_OP_FINITE_SET_SINGLETON;
|
||||
case OP_FINITE_SET_UNION: return Z3_OP_FINITE_SET_UNION;
|
||||
case OP_FINITE_SET_INTERSECT: return Z3_OP_FINITE_SET_INTERSECT;
|
||||
case OP_FINITE_SET_DIFFERENCE: return Z3_OP_FINITE_SET_DIFFERENCE;
|
||||
case OP_FINITE_SET_IN: return Z3_OP_FINITE_SET_IN;
|
||||
case OP_FINITE_SET_SIZE: return Z3_OP_FINITE_SET_SIZE;
|
||||
case OP_FINITE_SET_SUBSET: return Z3_OP_FINITE_SET_SUBSET;
|
||||
case OP_FINITE_SET_MAP: return Z3_OP_FINITE_SET_MAP;
|
||||
case OP_FINITE_SET_FILTER: return Z3_OP_FINITE_SET_FILTER;
|
||||
case OP_FINITE_SET_RANGE: return Z3_OP_FINITE_SET_RANGE;
|
||||
case OP_FINITE_SET_EXT: return Z3_OP_FINITE_SET_EXT;
|
||||
case OP_FINITE_SET_MAP_INVERSE: return Z3_OP_FINITE_SET_MAP_INVERSE;
|
||||
default: return Z3_OP_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (mk_c(c)->recfun().get_family_id() == _d->get_family_id())
|
||||
return Z3_OP_RECURSIVE;
|
||||
|
||||
|
|
|
|||
|
|
@ -138,10 +138,8 @@ extern "C" {
|
|||
RESET_ERROR_CODE();
|
||||
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), to_ast_map(m)->m);
|
||||
mk_c(c)->save_object(v);
|
||||
obj_map<ast, ast*>::iterator it = to_ast_map_ref(m).begin();
|
||||
obj_map<ast, ast*>::iterator end = to_ast_map_ref(m).end();
|
||||
for (; it != end; ++it) {
|
||||
v->m_ast_vector.push_back(it->m_key);
|
||||
for (auto const& kv : to_ast_map_ref(m)) {
|
||||
v->m_ast_vector.push_back(kv.m_key);
|
||||
}
|
||||
Z3_ast_vector r = of_ast_vector(v);
|
||||
RETURN_Z3(r);
|
||||
|
|
@ -155,10 +153,8 @@ extern "C" {
|
|||
std::ostringstream buffer;
|
||||
ast_manager & mng = to_ast_map(m)->m;
|
||||
buffer << "(ast-map";
|
||||
obj_map<ast, ast*>::iterator it = to_ast_map_ref(m).begin();
|
||||
obj_map<ast, ast*>::iterator end = to_ast_map_ref(m).end();
|
||||
for (; it != end; ++it) {
|
||||
buffer << "\n (" << mk_ismt2_pp(it->m_key, mng, 3) << "\n " << mk_ismt2_pp(it->m_value, mng, 3) << ")";
|
||||
for (auto const& kv : to_ast_map_ref(m)) {
|
||||
buffer << "\n (" << mk_ismt2_pp(kv.m_key, mng, 3) << "\n " << mk_ismt2_pp(kv.m_value, mng, 3) << ")";
|
||||
}
|
||||
buffer << ')';
|
||||
return mk_c(c)->mk_external_string(std::move(buffer).str());
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ extern "C" {
|
|||
Z3_ast_vector_ref * new_v = alloc(Z3_ast_vector_ref, *mk_c(t), mk_c(t)->m());
|
||||
mk_c(t)->save_object(new_v);
|
||||
unsigned sz = to_ast_vector_ref(v).size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
ast * new_ast = translator(to_ast_vector_ref(v).get(i));
|
||||
new_v->m_ast_vector.push_back(new_ast);
|
||||
}
|
||||
|
|
@ -127,10 +127,10 @@ extern "C" {
|
|||
std::ostringstream buffer;
|
||||
buffer << "(ast-vector";
|
||||
unsigned sz = to_ast_vector_ref(v).size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
buffer << "\n " << mk_ismt2_pp(to_ast_vector_ref(v).get(i), mk_c(c)->m(), 2);
|
||||
}
|
||||
buffer << ")";
|
||||
buffer << ')';
|
||||
return mk_c(c)->mk_external_string(buffer.str());
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ namespace api {
|
|||
m_fpa_util(m()),
|
||||
m_sutil(m()),
|
||||
m_recfun(m()),
|
||||
m_finite_set_util(m()),
|
||||
m_ast_trail(m()),
|
||||
m_pmanager(m_limit) {
|
||||
|
||||
|
|
@ -225,9 +226,8 @@ namespace api {
|
|||
e = m_bv_util.mk_numeral(n, s);
|
||||
}
|
||||
else if (fid == get_datalog_fid() && n.is_uint64()) {
|
||||
uint64_t sz;
|
||||
if (m_datalog_util.try_get_size(s, sz) &&
|
||||
sz <= n.get_uint64()) {
|
||||
if (auto size_opt = m_datalog_util.try_get_size(s);
|
||||
size_opt.has_value() && *size_opt <= n.get_uint64()) {
|
||||
invoke_error_handler(Z3_INVALID_ARG);
|
||||
}
|
||||
e = m_datalog_util.mk_numeral(n.get_uint64(), s);
|
||||
|
|
@ -252,7 +252,7 @@ namespace api {
|
|||
save_ast_trail(exprs[0]);
|
||||
return exprs[0];
|
||||
default: {
|
||||
expr * a = m().mk_and(num_exprs, exprs);
|
||||
expr * a = m().mk_and(std::span<expr* const>(exprs, num_exprs));
|
||||
save_ast_trail(a);
|
||||
return a;
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ Revision History:
|
|||
#include "ast/fpa_decl_plugin.h"
|
||||
#include "ast/recfun_decl_plugin.h"
|
||||
#include "ast/special_relations_decl_plugin.h"
|
||||
#include "ast/finite_set_decl_plugin.h"
|
||||
#include "ast/rewriter/seq_rewriter.h"
|
||||
#include "params/smt_params.h"
|
||||
#include "smt/smt_kernel.h"
|
||||
|
|
@ -77,6 +78,7 @@ namespace api {
|
|||
fpa_util m_fpa_util;
|
||||
seq_util m_sutil;
|
||||
recfun::util m_recfun;
|
||||
finite_set_util m_finite_set_util;
|
||||
|
||||
// Support for old solver API
|
||||
smt_params m_fparams;
|
||||
|
|
@ -146,6 +148,7 @@ namespace api {
|
|||
datatype_util& dtutil() { return m_dt_plugin->u(); }
|
||||
seq_util& sutil() { return m_sutil; }
|
||||
recfun::util& recfun() { return m_recfun; }
|
||||
finite_set_util& fsutil() { return m_finite_set_util; }
|
||||
family_id get_basic_fid() const { return basic_family_id; }
|
||||
family_id get_array_fid() const { return m_array_fid; }
|
||||
family_id get_arith_fid() const { return arith_family_id; }
|
||||
|
|
|
|||
|
|
@ -213,8 +213,11 @@ extern "C" {
|
|||
// must start logging here, since function uses Z3_get_sort_kind above
|
||||
LOG_Z3_get_finite_domain_sort_size(c, s, out);
|
||||
RESET_ERROR_CODE();
|
||||
VERIFY(mk_c(c)->datalog_util().try_get_size(to_sort(s), *out));
|
||||
return true;
|
||||
if (auto size = mk_c(c)->datalog_util().try_get_size(to_sort(s)); size) {
|
||||
*out = *size;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
Z3_CATCH_RETURN(false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ extern "C" {
|
|||
// Create projections
|
||||
ptr_vector<func_decl> const & _accs = *dt_util.get_constructor_accessors(decl);
|
||||
SASSERT(_accs.size() == num_fields);
|
||||
for (unsigned i = 0; i < _accs.size(); i++) {
|
||||
for (unsigned i = 0; i < _accs.size(); ++i) {
|
||||
mk_c(c)->save_multiple_ast_trail(_accs[i]);
|
||||
proj_decls[i] = of_func_decl(_accs[i]);
|
||||
}
|
||||
|
|
|
|||
187
src/api/api_finite_set.cpp
Normal file
187
src/api/api_finite_set.cpp
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/*++
|
||||
Copyright (c) 2025 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
api_finite_set.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
API for finite sets.
|
||||
|
||||
Author:
|
||||
|
||||
Copilot 2025-01-21
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include "api/z3.h"
|
||||
#include "api/api_log_macros.h"
|
||||
#include "api/api_context.h"
|
||||
#include "api/api_util.h"
|
||||
#include "ast/ast_pp.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
Z3_sort Z3_API Z3_mk_finite_set_sort(Z3_context c, Z3_sort elem_sort) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_sort(c, elem_sort);
|
||||
RESET_ERROR_CODE();
|
||||
parameter param(to_sort(elem_sort));
|
||||
sort* ty = mk_c(c)->m().mk_sort(mk_c(c)->fsutil().get_family_id(), FINITE_SET_SORT, 1, ¶m);
|
||||
mk_c(c)->save_ast_trail(ty);
|
||||
RETURN_Z3(of_sort(ty));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
bool Z3_API Z3_is_finite_set_sort(Z3_context c, Z3_sort s) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_is_finite_set_sort(c, s);
|
||||
RESET_ERROR_CODE();
|
||||
return mk_c(c)->fsutil().is_finite_set(to_sort(s));
|
||||
Z3_CATCH_RETURN(false);
|
||||
}
|
||||
|
||||
Z3_sort Z3_API Z3_get_finite_set_sort_basis(Z3_context c, Z3_sort s) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_get_finite_set_sort_basis(c, s);
|
||||
RESET_ERROR_CODE();
|
||||
sort* elem_sort = nullptr;
|
||||
if (!mk_c(c)->fsutil().is_finite_set(to_sort(s), elem_sort)) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, "expected finite set sort");
|
||||
RETURN_Z3(nullptr);
|
||||
}
|
||||
RETURN_Z3(of_sort(elem_sort));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_empty(Z3_context c, Z3_sort set_sort) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_empty(c, set_sort);
|
||||
RESET_ERROR_CODE();
|
||||
app* a = mk_c(c)->fsutil().mk_empty(to_sort(set_sort));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_singleton(Z3_context c, Z3_ast elem) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_singleton(c, elem);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_EXPR(elem, nullptr);
|
||||
app* a = mk_c(c)->fsutil().mk_singleton(to_expr(elem));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_union(Z3_context c, Z3_ast s1, Z3_ast s2) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_union(c, s1, s2);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_EXPR(s1, nullptr);
|
||||
CHECK_IS_EXPR(s2, nullptr);
|
||||
app* a = mk_c(c)->fsutil().mk_union(to_expr(s1), to_expr(s2));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_intersect(Z3_context c, Z3_ast s1, Z3_ast s2) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_intersect(c, s1, s2);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_EXPR(s1, nullptr);
|
||||
CHECK_IS_EXPR(s2, nullptr);
|
||||
app* a = mk_c(c)->fsutil().mk_intersect(to_expr(s1), to_expr(s2));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_difference(Z3_context c, Z3_ast s1, Z3_ast s2) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_difference(c, s1, s2);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_EXPR(s1, nullptr);
|
||||
CHECK_IS_EXPR(s2, nullptr);
|
||||
app* a = mk_c(c)->fsutil().mk_difference(to_expr(s1), to_expr(s2));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_member(Z3_context c, Z3_ast elem, Z3_ast set) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_member(c, elem, set);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_EXPR(elem, nullptr);
|
||||
CHECK_IS_EXPR(set, nullptr);
|
||||
app* a = mk_c(c)->fsutil().mk_in(to_expr(elem), to_expr(set));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_size(Z3_context c, Z3_ast set) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_size(c, set);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_EXPR(set, nullptr);
|
||||
app* a = mk_c(c)->fsutil().mk_size(to_expr(set));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_subset(Z3_context c, Z3_ast s1, Z3_ast s2) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_subset(c, s1, s2);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_EXPR(s1, nullptr);
|
||||
CHECK_IS_EXPR(s2, nullptr);
|
||||
app* a = mk_c(c)->fsutil().mk_subset(to_expr(s1), to_expr(s2));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_map(Z3_context c, Z3_ast f, Z3_ast set) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_map(c, f, set);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_EXPR(f, nullptr);
|
||||
CHECK_IS_EXPR(set, nullptr);
|
||||
app* a = mk_c(c)->fsutil().mk_map(to_expr(f), to_expr(set));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_filter(Z3_context c, Z3_ast f, Z3_ast set) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_filter(c, f, set);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_EXPR(f, nullptr);
|
||||
CHECK_IS_EXPR(set, nullptr);
|
||||
app* a = mk_c(c)->fsutil().mk_filter(to_expr(f), to_expr(set));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_mk_finite_set_range(Z3_context c, Z3_ast low, Z3_ast high) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_finite_set_range(c, low, high);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_EXPR(low, nullptr);
|
||||
CHECK_IS_EXPR(high, nullptr);
|
||||
app* a = mk_c(c)->fsutil().mk_range(to_expr(low), to_expr(high));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -141,7 +141,7 @@ extern "C" {
|
|||
Z3_TRY;
|
||||
LOG_Z3_optimize_check(c, o, num_assumptions, assumptions);
|
||||
RESET_ERROR_CODE();
|
||||
for (unsigned i = 0; i < num_assumptions; i++) {
|
||||
for (unsigned i = 0; i < num_assumptions; ++i) {
|
||||
if (!is_expr(to_ast(assumptions[i]))) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, "assumption is not an expression");
|
||||
return Z3_L_UNDEF;
|
||||
|
|
@ -432,7 +432,7 @@ extern "C" {
|
|||
unsigned n = to_optimize_ptr(o)->num_objectives();
|
||||
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
|
||||
mk_c(c)->save_object(v);
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
v->m_ast_vector.push_back(to_optimize_ptr(o)->get_objective(i));
|
||||
}
|
||||
RETURN_Z3(of_ast_vector(v));
|
||||
|
|
|
|||
|
|
@ -200,14 +200,14 @@ extern "C" {
|
|||
LOG_Z3_param_descrs_to_string(c, p);
|
||||
RESET_ERROR_CODE();
|
||||
std::ostringstream buffer;
|
||||
buffer << "(";
|
||||
buffer << '(';
|
||||
unsigned sz = to_param_descrs_ptr(p)->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (i > 0)
|
||||
buffer << ", ";
|
||||
buffer << to_param_descrs_ptr(p)->get_param_name(i);
|
||||
}
|
||||
buffer << ")";
|
||||
buffer << ')';
|
||||
return mk_c(c)->mk_external_string(std::move(buffer).str());
|
||||
Z3_CATCH_RETURN("");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,34 +33,48 @@ extern "C" {
|
|||
LOG_Z3_polynomial_subresultants(c, p, q, x);
|
||||
RESET_ERROR_CODE();
|
||||
polynomial::manager & pm = mk_c(c)->pm();
|
||||
polynomial_ref _p(pm), _q(pm);
|
||||
polynomial::scoped_numeral d(pm.m());
|
||||
default_expr2polynomial converter(mk_c(c)->m(), pm);
|
||||
if (!converter.to_polynomial(to_expr(p), _p, d) ||
|
||||
!converter.to_polynomial(to_expr(q), _q, d)) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
|
||||
return nullptr;
|
||||
|
||||
// Compute all polynomial results BEFORE allocating the result vector.
|
||||
// This avoids a memory corruption issue where allocating API objects
|
||||
// can interfere with the converter's internal state.
|
||||
expr_ref_vector results(mk_c(c)->m());
|
||||
{
|
||||
polynomial_ref _p(pm), _q(pm);
|
||||
polynomial::scoped_numeral d(pm.m());
|
||||
default_expr2polynomial converter(mk_c(c)->m(), pm);
|
||||
if (!converter.to_polynomial(to_expr(p), _p, d) ||
|
||||
!converter.to_polynomial(to_expr(q), _q, d)) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (converter.is_var(to_expr(x))) {
|
||||
expr2var const & mapping = converter.get_mapping();
|
||||
unsigned v_x = mapping.to_var(to_expr(x));
|
||||
if (v_x != UINT_MAX) {
|
||||
polynomial_ref_vector rs(pm);
|
||||
polynomial_ref r(pm);
|
||||
expr_ref _r(mk_c(c)->m());
|
||||
|
||||
{
|
||||
cancel_eh<reslimit> eh(mk_c(c)->poly_limit());
|
||||
api::context::set_interruptable si(*(mk_c(c)), eh);
|
||||
scoped_timer timer(mk_c(c)->params().m_timeout, &eh);
|
||||
pm.psc_chain(_p, _q, v_x, rs);
|
||||
}
|
||||
for (unsigned i = 0; i < rs.size(); ++i) {
|
||||
r = rs.get(i);
|
||||
converter.to_expr(r, true, _r);
|
||||
results.push_back(_r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Converter is now out of scope - safe to allocate result vector
|
||||
Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
|
||||
mk_c(c)->save_object(result);
|
||||
if (converter.is_var(to_expr(x))) {
|
||||
expr2var const & mapping = converter.get_mapping();
|
||||
unsigned v_x = mapping.to_var(to_expr(x));
|
||||
polynomial_ref_vector rs(pm);
|
||||
polynomial_ref r(pm);
|
||||
expr_ref _r(mk_c(c)->m());
|
||||
|
||||
{
|
||||
cancel_eh<reslimit> eh(mk_c(c)->poly_limit());
|
||||
api::context::set_interruptable si(*(mk_c(c)), eh);
|
||||
scoped_timer timer(mk_c(c)->params().m_timeout, &eh);
|
||||
pm.psc_chain(_p, _q, v_x, rs);
|
||||
}
|
||||
for (unsigned i = 0; i < rs.size(); i++) {
|
||||
r = rs.get(i);
|
||||
converter.to_expr(r, true, _r);
|
||||
result->m_ast_vector.push_back(_r);
|
||||
}
|
||||
for (expr* e : results) {
|
||||
result->m_ast_vector.push_back(e);
|
||||
}
|
||||
RETURN_Z3(of_ast_vector(result));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ extern "C" {
|
|||
expr * const* no_ps = reinterpret_cast<expr * const*>(no_patterns);
|
||||
symbol qid = to_symbol(quantifier_id);
|
||||
pattern_validator v(mk_c(c)->m());
|
||||
for (unsigned i = 0; i < num_patterns; i++) {
|
||||
for (unsigned i = 0; i < num_patterns; ++i) {
|
||||
if (!v(num_decls, ps[i], 0, 0)) {
|
||||
SET_ERROR_CODE(Z3_INVALID_PATTERN, nullptr);
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ extern "C" {
|
|||
reset_rcf_cancel(c);
|
||||
rcnumeral_vector av;
|
||||
unsigned rz = 0;
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
if (!rcfm(c).is_zero(to_rcnumeral(a[i])))
|
||||
rz = i + 1;
|
||||
av.push_back(to_rcnumeral(a[i]));
|
||||
|
|
@ -129,7 +129,7 @@ extern "C" {
|
|||
rcnumeral_vector rs;
|
||||
rcfm(c).isolate_roots(av.size(), av.data(), rs);
|
||||
unsigned num_roots = rs.size();
|
||||
for (unsigned i = 0; i < num_roots; i++) {
|
||||
for (unsigned i = 0; i < num_roots; ++i) {
|
||||
roots[i] = from_rcnumeral(rs[i]);
|
||||
}
|
||||
RETURN_Z3_rcf_mk_roots num_roots;
|
||||
|
|
|
|||
|
|
@ -558,7 +558,7 @@ extern "C" {
|
|||
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
|
||||
mk_c(c)->save_object(v);
|
||||
unsigned sz = to_solver_ref(s)->get_num_assertions();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
v->m_ast_vector.push_back(to_solver_ref(s)->get_assertion(i));
|
||||
}
|
||||
RETURN_Z3(of_ast_vector(v));
|
||||
|
|
@ -638,7 +638,7 @@ extern "C" {
|
|||
#define TOSTRING(x) STRINGIFY(x)
|
||||
|
||||
static Z3_lbool _solver_check(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) {
|
||||
for (unsigned i = 0; i < num_assumptions; i++) {
|
||||
for (unsigned i = 0; i < num_assumptions; ++i) {
|
||||
if (!is_expr(to_ast(assumptions[i]))) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG, "assumption is not an expression");
|
||||
return Z3_L_UNDEF;
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ extern "C" {
|
|||
LOG_Z3_tactic_par_or(c, num, ts);
|
||||
RESET_ERROR_CODE();
|
||||
ptr_buffer<tactic> _ts;
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
_ts.push_back(to_tactic_ref(ts[i]));
|
||||
}
|
||||
tactic * new_t = par(num, _ts.data());
|
||||
|
|
@ -496,7 +496,7 @@ extern "C" {
|
|||
std::ostringstream buffer;
|
||||
buffer << "(goals\n";
|
||||
unsigned sz = to_apply_result(r)->m_subgoals.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
to_apply_result(r)->m_subgoals[i]->display(buffer);
|
||||
}
|
||||
buffer << ')';
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ Notes:
|
|||
#include<memory>
|
||||
#include<vector>
|
||||
#include<z3.h>
|
||||
#include<z3_rcf.h>
|
||||
#include<limits.h>
|
||||
#include<functional>
|
||||
|
||||
|
|
@ -82,6 +83,36 @@ namespace z3 {
|
|||
inline void set_param(char const * param, int value) { auto str = std::to_string(value); Z3_global_param_set(param, str.c_str()); }
|
||||
inline void reset_params() { Z3_global_param_reset_all(); }
|
||||
|
||||
/**
|
||||
\brief Return Z3 version number information.
|
||||
*/
|
||||
inline void get_version(unsigned& major, unsigned& minor, unsigned& build_number, unsigned& revision_number) {
|
||||
Z3_get_version(&major, &minor, &build_number, &revision_number);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return a string that fully describes the version of Z3 in use.
|
||||
*/
|
||||
inline std::string get_full_version() {
|
||||
return std::string(Z3_get_full_version());
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Enable tracing messages tagged as \c tag when Z3 is compiled in debug mode.
|
||||
It is a NOOP otherwise.
|
||||
*/
|
||||
inline void enable_trace(char const * tag) {
|
||||
Z3_enable_trace(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Disable tracing messages tagged as \c tag when Z3 is compiled in debug mode.
|
||||
It is a NOOP otherwise.
|
||||
*/
|
||||
inline void disable_trace(char const * tag) {
|
||||
Z3_disable_trace(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Exception used to sign API usage errors.
|
||||
*/
|
||||
|
|
@ -183,6 +214,24 @@ namespace z3 {
|
|||
public:
|
||||
context() { config c; init(c); }
|
||||
context(config & c) { init(c); }
|
||||
|
||||
context(context && other) noexcept
|
||||
: m_enable_exceptions(other.m_enable_exceptions),
|
||||
m_rounding_mode(other.m_rounding_mode),
|
||||
m_ctx(other.m_ctx) {
|
||||
other.m_ctx = nullptr;
|
||||
}
|
||||
|
||||
context & operator=(context && other) noexcept {
|
||||
if (this != &other) {
|
||||
if (m_ctx) Z3_del_context(m_ctx);
|
||||
m_enable_exceptions = other.m_enable_exceptions;
|
||||
m_rounding_mode = other.m_rounding_mode;
|
||||
m_ctx = other.m_ctx;
|
||||
other.m_ctx = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
~context() { if (m_ctx) Z3_del_context(m_ctx); }
|
||||
operator Z3_context() const { return m_ctx; }
|
||||
|
||||
|
|
@ -274,6 +323,10 @@ namespace z3 {
|
|||
\brief Return a regular expression sort over sequences \c seq_sort.
|
||||
*/
|
||||
sort re_sort(sort& seq_sort);
|
||||
/**
|
||||
\brief Return a finite set sort over element sort \c s.
|
||||
*/
|
||||
sort finite_set_sort(sort& s);
|
||||
/**
|
||||
\brief Return an array sort for arrays from \c d to \c r.
|
||||
|
||||
|
|
@ -1232,11 +1285,32 @@ namespace z3 {
|
|||
expr_vector args() const {
|
||||
expr_vector vec(ctx());
|
||||
unsigned argCnt = num_args();
|
||||
for (unsigned i = 0; i < argCnt; i++)
|
||||
for (unsigned i = 0; i < argCnt; ++i)
|
||||
vec.push_back(arg(i));
|
||||
return vec;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Update the arguments of this application.
|
||||
Return a new expression with the same function declaration and updated arguments.
|
||||
The number of new arguments must match the current number of arguments.
|
||||
|
||||
\pre is_app()
|
||||
\pre args.size() == num_args()
|
||||
*/
|
||||
expr update(expr_vector const& args) const;
|
||||
|
||||
/**
|
||||
\brief Update a datatype field.
|
||||
Return a new datatype expression with the specified field updated to the new value.
|
||||
The remaining fields are unchanged.
|
||||
|
||||
\pre is_datatype()
|
||||
\param field_access The accessor function declaration for the field to update
|
||||
\param new_value The new value for the field
|
||||
*/
|
||||
expr update_field(func_decl const& field_access, expr const& new_value) const;
|
||||
|
||||
/**
|
||||
\brief Return the 'body' of this quantifier.
|
||||
|
||||
|
|
@ -2305,6 +2379,19 @@ namespace z3 {
|
|||
return to_func_decl(a.ctx(), Z3_mk_tree_order(a.ctx(), a, index));
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return the nonzero subresultants of p and q with respect to the "variable" x.
|
||||
|
||||
\pre p, q and x are Z3 expressions where p and q are arithmetic terms.
|
||||
Note that, any subterm that cannot be viewed as a polynomial is assumed to be a variable.
|
||||
*/
|
||||
inline expr_vector polynomial_subresultants(expr const& p, expr const& q, expr const& x) {
|
||||
check_context(p, q); check_context(p, x);
|
||||
Z3_ast_vector r = Z3_polynomial_subresultants(p.ctx(), p, q, x);
|
||||
p.check_error();
|
||||
return expr_vector(p.ctx(), r);
|
||||
}
|
||||
|
||||
template<> class cast_ast<ast> {
|
||||
public:
|
||||
ast operator()(context & c, Z3_ast a) { return ast(c, a); }
|
||||
|
|
@ -2340,7 +2427,7 @@ namespace z3 {
|
|||
template<typename T>
|
||||
template<typename T2>
|
||||
array<T>::array(ast_vector_tpl<T2> const & v):m_array(new T[v.size()]), m_size(v.size()) {
|
||||
for (unsigned i = 0; i < m_size; i++) {
|
||||
for (unsigned i = 0; i < m_size; ++i) {
|
||||
m_array[i] = v[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -2702,6 +2789,29 @@ namespace z3 {
|
|||
check_error();
|
||||
}
|
||||
|
||||
unsigned num_sorts() const {
|
||||
unsigned r = Z3_model_get_num_sorts(ctx(), m_model);
|
||||
check_error();
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return the uninterpreted sort at position \c i.
|
||||
\pre i < num_sorts()
|
||||
*/
|
||||
sort get_sort(unsigned i) const {
|
||||
Z3_sort s = Z3_model_get_sort(ctx(), m_model, i);
|
||||
check_error();
|
||||
return sort(ctx(), s);
|
||||
}
|
||||
|
||||
expr_vector sort_universe(sort const& s) const {
|
||||
check_context(*this, s);
|
||||
Z3_ast_vector r = Z3_model_get_sort_universe(ctx(), m_model, s);
|
||||
check_error();
|
||||
return expr_vector(ctx(), r);
|
||||
}
|
||||
|
||||
friend std::ostream & operator<<(std::ostream & out, model const & m);
|
||||
|
||||
std::string to_string() const { return m_model ? std::string(Z3_model_to_string(ctx(), m_model)) : "null"; }
|
||||
|
|
@ -2848,7 +2958,7 @@ namespace z3 {
|
|||
check_result check() { Z3_lbool r = Z3_solver_check(ctx(), m_solver); check_error(); return to_check_result(r); }
|
||||
check_result check(unsigned n, expr * const assumptions) {
|
||||
array<Z3_ast> _assumptions(n);
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
check_context(*this, assumptions[i]);
|
||||
_assumptions[i] = assumptions[i];
|
||||
}
|
||||
|
|
@ -2859,7 +2969,7 @@ namespace z3 {
|
|||
check_result check(expr_vector const& assumptions) {
|
||||
unsigned n = assumptions.size();
|
||||
array<Z3_ast> _assumptions(n);
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
check_context(*this, assumptions[i]);
|
||||
_assumptions[i] = assumptions[i];
|
||||
}
|
||||
|
|
@ -2890,6 +3000,25 @@ namespace z3 {
|
|||
check_error();
|
||||
return result;
|
||||
}
|
||||
expr congruence_root(expr const& t) const {
|
||||
check_context(*this, t);
|
||||
Z3_ast r = Z3_solver_congruence_root(ctx(), m_solver, t);
|
||||
check_error();
|
||||
return expr(ctx(), r);
|
||||
}
|
||||
expr congruence_next(expr const& t) const {
|
||||
check_context(*this, t);
|
||||
Z3_ast r = Z3_solver_congruence_next(ctx(), m_solver, t);
|
||||
check_error();
|
||||
return expr(ctx(), r);
|
||||
}
|
||||
expr congruence_explain(expr const& a, expr const& b) const {
|
||||
check_context(*this, a);
|
||||
check_context(*this, b);
|
||||
Z3_ast r = Z3_solver_congruence_explain(ctx(), m_solver, a, b);
|
||||
check_error();
|
||||
return expr(ctx(), r);
|
||||
}
|
||||
void set_initial_value(expr const& var, expr const& value) {
|
||||
Z3_solver_set_initial_value(ctx(), m_solver, var, value);
|
||||
check_error();
|
||||
|
|
@ -2901,6 +3030,26 @@ namespace z3 {
|
|||
set_initial_value(var, ctx().bool_val(b));
|
||||
}
|
||||
|
||||
void solve_for(expr_vector const& vars, expr_vector& terms, expr_vector& guards) {
|
||||
// Create a copy of vars since the C API modifies the variables vector
|
||||
expr_vector variables(ctx());
|
||||
for (unsigned i = 0; i < vars.size(); ++i) {
|
||||
check_context(*this, vars[i]);
|
||||
variables.push_back(vars[i]);
|
||||
}
|
||||
// Clear output vectors before calling C API
|
||||
terms = expr_vector(ctx());
|
||||
guards = expr_vector(ctx());
|
||||
Z3_solver_solve_for(ctx(), m_solver, variables, terms, guards);
|
||||
check_error();
|
||||
}
|
||||
|
||||
void import_model_converter(solver const& src) {
|
||||
check_context(*this, src);
|
||||
Z3_solver_import_model_converter(ctx(), src.m_solver, m_solver);
|
||||
check_error();
|
||||
}
|
||||
|
||||
expr proof() const { Z3_ast r = Z3_solver_get_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); }
|
||||
friend std::ostream & operator<<(std::ostream & out, solver const & s);
|
||||
|
||||
|
|
@ -3071,7 +3220,7 @@ namespace z3 {
|
|||
return operator[](0u);
|
||||
else {
|
||||
array<Z3_ast> args(n);
|
||||
for (unsigned i = 0; i < n; i++)
|
||||
for (unsigned i = 0; i < n; ++i)
|
||||
args[i] = operator[](i);
|
||||
return expr(ctx(), Z3_mk_and(ctx(), n, args.ptr()));
|
||||
}
|
||||
|
|
@ -3400,7 +3549,7 @@ namespace z3 {
|
|||
check_result check(expr_vector const& asms) {
|
||||
unsigned n = asms.size();
|
||||
array<Z3_ast> _asms(n);
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
check_context(*this, asms[i]);
|
||||
_asms[i] = asms[i];
|
||||
}
|
||||
|
|
@ -3518,6 +3667,7 @@ namespace z3 {
|
|||
inline sort context::char_sort() { Z3_sort s = Z3_mk_char_sort(m_ctx); check_error(); return sort(*this, s); }
|
||||
inline sort context::seq_sort(sort& s) { Z3_sort r = Z3_mk_seq_sort(m_ctx, s); check_error(); return sort(*this, r); }
|
||||
inline sort context::re_sort(sort& s) { Z3_sort r = Z3_mk_re_sort(m_ctx, s); check_error(); return sort(*this, r); }
|
||||
inline sort context::finite_set_sort(sort& s) { Z3_sort r = Z3_mk_finite_set_sort(m_ctx, s); check_error(); return sort(*this, r); }
|
||||
inline sort context::fpa_sort(unsigned ebits, unsigned sbits) { Z3_sort s = Z3_mk_fpa_sort(m_ctx, ebits, sbits); check_error(); return sort(*this, s); }
|
||||
|
||||
template<>
|
||||
|
|
@ -3541,25 +3691,25 @@ namespace z3 {
|
|||
}
|
||||
inline sort context::enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts) {
|
||||
array<Z3_symbol> _enum_names(n);
|
||||
for (unsigned i = 0; i < n; i++) { _enum_names[i] = Z3_mk_string_symbol(*this, enum_names[i]); }
|
||||
for (unsigned i = 0; i < n; ++i) { _enum_names[i] = Z3_mk_string_symbol(*this, enum_names[i]); }
|
||||
array<Z3_func_decl> _cs(n);
|
||||
array<Z3_func_decl> _ts(n);
|
||||
Z3_symbol _name = Z3_mk_string_symbol(*this, name);
|
||||
sort s = to_sort(*this, Z3_mk_enumeration_sort(*this, _name, n, _enum_names.ptr(), _cs.ptr(), _ts.ptr()));
|
||||
check_error();
|
||||
for (unsigned i = 0; i < n; i++) { cs.push_back(func_decl(*this, _cs[i])); ts.push_back(func_decl(*this, _ts[i])); }
|
||||
for (unsigned i = 0; i < n; ++i) { cs.push_back(func_decl(*this, _cs[i])); ts.push_back(func_decl(*this, _ts[i])); }
|
||||
return s;
|
||||
}
|
||||
inline func_decl context::tuple_sort(char const * name, unsigned n, char const * const * names, sort const* sorts, func_decl_vector & projs) {
|
||||
array<Z3_symbol> _names(n);
|
||||
array<Z3_sort> _sorts(n);
|
||||
for (unsigned i = 0; i < n; i++) { _names[i] = Z3_mk_string_symbol(*this, names[i]); _sorts[i] = sorts[i]; }
|
||||
for (unsigned i = 0; i < n; ++i) { _names[i] = Z3_mk_string_symbol(*this, names[i]); _sorts[i] = sorts[i]; }
|
||||
array<Z3_func_decl> _projs(n);
|
||||
Z3_symbol _name = Z3_mk_string_symbol(*this, name);
|
||||
Z3_func_decl tuple;
|
||||
sort _ignore_s = to_sort(*this, Z3_mk_tuple_sort(*this, _name, n, _names.ptr(), _sorts.ptr(), &tuple, _projs.ptr()));
|
||||
check_error();
|
||||
for (unsigned i = 0; i < n; i++) { projs.push_back(func_decl(*this, _projs[i])); }
|
||||
for (unsigned i = 0; i < n; ++i) { projs.push_back(func_decl(*this, _projs[i])); }
|
||||
return func_decl(*this, tuple);
|
||||
}
|
||||
|
||||
|
|
@ -3682,7 +3832,7 @@ namespace z3 {
|
|||
|
||||
inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) {
|
||||
array<Z3_sort> args(arity);
|
||||
for (unsigned i = 0; i < arity; i++) {
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
check_context(domain[i], range);
|
||||
args[i] = domain[i];
|
||||
}
|
||||
|
|
@ -3697,7 +3847,7 @@ namespace z3 {
|
|||
|
||||
inline func_decl context::function(symbol const& name, sort_vector const& domain, sort const& range) {
|
||||
array<Z3_sort> args(domain.size());
|
||||
for (unsigned i = 0; i < domain.size(); i++) {
|
||||
for (unsigned i = 0; i < domain.size(); ++i) {
|
||||
check_context(domain[i], range);
|
||||
args[i] = domain[i];
|
||||
}
|
||||
|
|
@ -3753,7 +3903,7 @@ namespace z3 {
|
|||
|
||||
inline func_decl context::recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range) {
|
||||
array<Z3_sort> args(arity);
|
||||
for (unsigned i = 0; i < arity; i++) {
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
check_context(domain[i], range);
|
||||
args[i] = domain[i];
|
||||
}
|
||||
|
|
@ -3877,7 +4027,7 @@ namespace z3 {
|
|||
|
||||
inline expr func_decl::operator()(unsigned n, expr const * args) const {
|
||||
array<Z3_ast> _args(n);
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
check_context(*this, args[i]);
|
||||
_args[i] = args[i];
|
||||
}
|
||||
|
|
@ -3888,7 +4038,7 @@ namespace z3 {
|
|||
}
|
||||
inline expr func_decl::operator()(expr_vector const& args) const {
|
||||
array<Z3_ast> _args(args.size());
|
||||
for (unsigned i = 0; i < args.size(); i++) {
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
check_context(*this, args[i]);
|
||||
_args[i] = args[i];
|
||||
}
|
||||
|
|
@ -4043,6 +4193,19 @@ namespace z3 {
|
|||
return expr(f.ctx(), r);
|
||||
}
|
||||
|
||||
inline expr array_default(expr const & a) {
|
||||
Z3_ast r = Z3_mk_array_default(a.ctx(), a);
|
||||
a.check_error();
|
||||
return expr(a.ctx(), r);
|
||||
}
|
||||
|
||||
inline expr array_ext(expr const & a, expr const & b) {
|
||||
check_context(a, b);
|
||||
Z3_ast r = Z3_mk_array_ext(a.ctx(), a, b);
|
||||
a.check_error();
|
||||
return expr(a.ctx(), r);
|
||||
}
|
||||
|
||||
#define MK_EXPR1(_fn, _arg) \
|
||||
Z3_ast r = _fn(_arg.ctx(), _arg); \
|
||||
_arg.check_error(); \
|
||||
|
|
@ -4106,6 +4269,54 @@ namespace z3 {
|
|||
MK_EXPR2(Z3_mk_set_subset, a, b);
|
||||
}
|
||||
|
||||
// finite set operations
|
||||
|
||||
inline expr finite_set_empty(sort const& s) {
|
||||
Z3_ast r = Z3_mk_finite_set_empty(s.ctx(), s);
|
||||
s.check_error();
|
||||
return expr(s.ctx(), r);
|
||||
}
|
||||
|
||||
inline expr finite_set_singleton(expr const& e) {
|
||||
MK_EXPR1(Z3_mk_finite_set_singleton, e);
|
||||
}
|
||||
|
||||
inline expr finite_set_union(expr const& a, expr const& b) {
|
||||
MK_EXPR2(Z3_mk_finite_set_union, a, b);
|
||||
}
|
||||
|
||||
inline expr finite_set_intersect(expr const& a, expr const& b) {
|
||||
MK_EXPR2(Z3_mk_finite_set_intersect, a, b);
|
||||
}
|
||||
|
||||
inline expr finite_set_difference(expr const& a, expr const& b) {
|
||||
MK_EXPR2(Z3_mk_finite_set_difference, a, b);
|
||||
}
|
||||
|
||||
inline expr finite_set_member(expr const& e, expr const& s) {
|
||||
MK_EXPR2(Z3_mk_finite_set_member, e, s);
|
||||
}
|
||||
|
||||
inline expr finite_set_size(expr const& s) {
|
||||
MK_EXPR1(Z3_mk_finite_set_size, s);
|
||||
}
|
||||
|
||||
inline expr finite_set_subset(expr const& a, expr const& b) {
|
||||
MK_EXPR2(Z3_mk_finite_set_subset, a, b);
|
||||
}
|
||||
|
||||
inline expr finite_set_map(expr const& f, expr const& s) {
|
||||
MK_EXPR2(Z3_mk_finite_set_map, f, s);
|
||||
}
|
||||
|
||||
inline expr finite_set_filter(expr const& f, expr const& s) {
|
||||
MK_EXPR2(Z3_mk_finite_set_filter, f, s);
|
||||
}
|
||||
|
||||
inline expr finite_set_range(expr const& low, expr const& high) {
|
||||
MK_EXPR2(Z3_mk_finite_set_range, low, high);
|
||||
}
|
||||
|
||||
// sequence and regular expression operations.
|
||||
// union is +
|
||||
// concat is overloaded to handle sequences and regular expressions
|
||||
|
|
@ -4314,6 +4525,23 @@ namespace z3 {
|
|||
return expr(ctx(), r);
|
||||
}
|
||||
|
||||
inline expr expr::update(expr_vector const& args) const {
|
||||
array<Z3_ast> _args(args.size());
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
_args[i] = args[i];
|
||||
}
|
||||
Z3_ast r = Z3_update_term(ctx(), m_ast, args.size(), _args.ptr());
|
||||
check_error();
|
||||
return expr(ctx(), r);
|
||||
}
|
||||
|
||||
inline expr expr::update_field(func_decl const& field_access, expr const& new_value) const {
|
||||
assert(is_datatype());
|
||||
Z3_ast r = Z3_datatype_update_field(ctx(), field_access, m_ast, new_value);
|
||||
check_error();
|
||||
return expr(ctx(), r);
|
||||
}
|
||||
|
||||
typedef std::function<void(expr const& proof, std::vector<unsigned> const& deps, expr_vector const& clause)> on_clause_eh_t;
|
||||
|
||||
class on_clause {
|
||||
|
|
@ -4642,6 +4870,223 @@ namespace z3 {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Wrapper for Z3 Real Closed Field (RCF) numerals.
|
||||
|
||||
RCF numerals can represent:
|
||||
- Rational numbers
|
||||
- Algebraic numbers (roots of polynomials)
|
||||
- Transcendental extensions (e.g., pi, e)
|
||||
- Infinitesimal extensions
|
||||
*/
|
||||
class rcf_num {
|
||||
Z3_context m_ctx;
|
||||
Z3_rcf_num m_num;
|
||||
|
||||
void check_context(rcf_num const& other) const {
|
||||
if (m_ctx != other.m_ctx) {
|
||||
throw exception("rcf_num objects from different contexts");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
rcf_num(context& c, Z3_rcf_num n): m_ctx(c), m_num(n) {}
|
||||
|
||||
rcf_num(context& c, int val): m_ctx(c) {
|
||||
m_num = Z3_rcf_mk_small_int(c, val);
|
||||
}
|
||||
|
||||
rcf_num(context& c, char const* val): m_ctx(c) {
|
||||
m_num = Z3_rcf_mk_rational(c, val);
|
||||
}
|
||||
|
||||
rcf_num(rcf_num const& other): m_ctx(other.m_ctx) {
|
||||
// Create a copy by converting to string and back
|
||||
std::string str = Z3_rcf_num_to_string(m_ctx, other.m_num, false, false);
|
||||
m_num = Z3_rcf_mk_rational(m_ctx, str.c_str());
|
||||
}
|
||||
|
||||
rcf_num& operator=(rcf_num const& other) {
|
||||
if (this != &other) {
|
||||
Z3_rcf_del(m_ctx, m_num);
|
||||
m_ctx = other.m_ctx;
|
||||
std::string str = Z3_rcf_num_to_string(m_ctx, other.m_num, false, false);
|
||||
m_num = Z3_rcf_mk_rational(m_ctx, str.c_str());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~rcf_num() {
|
||||
Z3_rcf_del(m_ctx, m_num);
|
||||
}
|
||||
|
||||
operator Z3_rcf_num() const { return m_num; }
|
||||
Z3_context ctx() const { return m_ctx; }
|
||||
|
||||
/**
|
||||
\brief Return string representation of the RCF numeral.
|
||||
*/
|
||||
std::string to_string(bool compact = false) const {
|
||||
return std::string(Z3_rcf_num_to_string(m_ctx, m_num, compact, false));
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return decimal string representation with given precision.
|
||||
*/
|
||||
std::string to_decimal(unsigned precision = 10) const {
|
||||
return std::string(Z3_rcf_num_to_decimal_string(m_ctx, m_num, precision));
|
||||
}
|
||||
|
||||
// Arithmetic operations
|
||||
rcf_num operator+(rcf_num const& other) const {
|
||||
check_context(other);
|
||||
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
|
||||
Z3_rcf_add(m_ctx, m_num, other.m_num));
|
||||
}
|
||||
|
||||
rcf_num operator-(rcf_num const& other) const {
|
||||
check_context(other);
|
||||
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
|
||||
Z3_rcf_sub(m_ctx, m_num, other.m_num));
|
||||
}
|
||||
|
||||
rcf_num operator*(rcf_num const& other) const {
|
||||
check_context(other);
|
||||
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
|
||||
Z3_rcf_mul(m_ctx, m_num, other.m_num));
|
||||
}
|
||||
|
||||
rcf_num operator/(rcf_num const& other) const {
|
||||
check_context(other);
|
||||
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
|
||||
Z3_rcf_div(m_ctx, m_num, other.m_num));
|
||||
}
|
||||
|
||||
rcf_num operator-() const {
|
||||
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
|
||||
Z3_rcf_neg(m_ctx, m_num));
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return the power of this number raised to k.
|
||||
*/
|
||||
rcf_num power(unsigned k) const {
|
||||
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
|
||||
Z3_rcf_power(m_ctx, m_num, k));
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return the multiplicative inverse (1/this).
|
||||
*/
|
||||
rcf_num inv() const {
|
||||
return rcf_num(*const_cast<context*>(reinterpret_cast<context const*>(&m_ctx)),
|
||||
Z3_rcf_inv(m_ctx, m_num));
|
||||
}
|
||||
|
||||
// Comparison operations
|
||||
bool operator<(rcf_num const& other) const {
|
||||
check_context(other);
|
||||
return Z3_rcf_lt(m_ctx, m_num, other.m_num);
|
||||
}
|
||||
|
||||
bool operator>(rcf_num const& other) const {
|
||||
check_context(other);
|
||||
return Z3_rcf_gt(m_ctx, m_num, other.m_num);
|
||||
}
|
||||
|
||||
bool operator<=(rcf_num const& other) const {
|
||||
check_context(other);
|
||||
return Z3_rcf_le(m_ctx, m_num, other.m_num);
|
||||
}
|
||||
|
||||
bool operator>=(rcf_num const& other) const {
|
||||
check_context(other);
|
||||
return Z3_rcf_ge(m_ctx, m_num, other.m_num);
|
||||
}
|
||||
|
||||
bool operator==(rcf_num const& other) const {
|
||||
check_context(other);
|
||||
return Z3_rcf_eq(m_ctx, m_num, other.m_num);
|
||||
}
|
||||
|
||||
bool operator!=(rcf_num const& other) const {
|
||||
check_context(other);
|
||||
return Z3_rcf_neq(m_ctx, m_num, other.m_num);
|
||||
}
|
||||
|
||||
// Type queries
|
||||
bool is_rational() const {
|
||||
return Z3_rcf_is_rational(m_ctx, m_num);
|
||||
}
|
||||
|
||||
bool is_algebraic() const {
|
||||
return Z3_rcf_is_algebraic(m_ctx, m_num);
|
||||
}
|
||||
|
||||
bool is_infinitesimal() const {
|
||||
return Z3_rcf_is_infinitesimal(m_ctx, m_num);
|
||||
}
|
||||
|
||||
bool is_transcendental() const {
|
||||
return Z3_rcf_is_transcendental(m_ctx, m_num);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, rcf_num const& n) {
|
||||
return out << n.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Create an RCF numeral representing pi.
|
||||
*/
|
||||
inline rcf_num rcf_pi(context& c) {
|
||||
return rcf_num(c, Z3_rcf_mk_pi(c));
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Create an RCF numeral representing e (Euler's constant).
|
||||
*/
|
||||
inline rcf_num rcf_e(context& c) {
|
||||
return rcf_num(c, Z3_rcf_mk_e(c));
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Create an RCF numeral representing an infinitesimal.
|
||||
*/
|
||||
inline rcf_num rcf_infinitesimal(context& c) {
|
||||
return rcf_num(c, Z3_rcf_mk_infinitesimal(c));
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Find roots of a polynomial with given coefficients.
|
||||
|
||||
The polynomial is a[n-1]*x^(n-1) + ... + a[1]*x + a[0].
|
||||
Returns a vector of RCF numerals representing the roots.
|
||||
*/
|
||||
inline std::vector<rcf_num> rcf_roots(context& c, std::vector<rcf_num> const& coeffs) {
|
||||
if (coeffs.empty()) {
|
||||
throw exception("polynomial coefficients cannot be empty");
|
||||
}
|
||||
|
||||
unsigned n = static_cast<unsigned>(coeffs.size());
|
||||
std::vector<Z3_rcf_num> a(n);
|
||||
std::vector<Z3_rcf_num> roots(n);
|
||||
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
a[i] = coeffs[i];
|
||||
}
|
||||
|
||||
unsigned num_roots = Z3_rcf_mk_roots(c, n, a.data(), roots.data());
|
||||
|
||||
std::vector<rcf_num> result;
|
||||
result.reserve(num_roots);
|
||||
for (unsigned i = 0; i < num_roots; ++i) {
|
||||
result.push_back(rcf_num(c, roots[i]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**@}*/
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE
|
|||
FiniteDomainExpr.cs
|
||||
FiniteDomainNum.cs
|
||||
FiniteDomainSort.cs
|
||||
FiniteSetSort.cs
|
||||
Fixedpoint.cs
|
||||
FPExpr.cs
|
||||
FPNum.cs
|
||||
|
|
|
|||
|
|
@ -2442,6 +2442,180 @@ namespace Microsoft.Z3
|
|||
|
||||
#endregion
|
||||
|
||||
#region Finite Sets
|
||||
|
||||
/// <summary>
|
||||
/// Create a finite set sort over the given element sort.
|
||||
/// </summary>
|
||||
public FiniteSetSort MkFiniteSetSort(Sort elemSort)
|
||||
{
|
||||
Debug.Assert(elemSort != null);
|
||||
|
||||
CheckContextMatch(elemSort);
|
||||
return new FiniteSetSort(this, elemSort);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a sort is a finite set sort.
|
||||
/// </summary>
|
||||
public bool IsFiniteSetSort(Sort s)
|
||||
{
|
||||
Debug.Assert(s != null);
|
||||
|
||||
CheckContextMatch(s);
|
||||
return Native.Z3_is_finite_set_sort(nCtx, s.NativeObject) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the element sort (basis) of a finite set sort.
|
||||
/// </summary>
|
||||
public Sort GetFiniteSetSortBasis(Sort s)
|
||||
{
|
||||
Debug.Assert(s != null);
|
||||
|
||||
CheckContextMatch(s);
|
||||
return Sort.Create(this, Native.Z3_get_finite_set_sort_basis(nCtx, s.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty finite set.
|
||||
/// </summary>
|
||||
public Expr MkFiniteSetEmpty(Sort setSort)
|
||||
{
|
||||
Debug.Assert(setSort != null);
|
||||
|
||||
CheckContextMatch(setSort);
|
||||
return Expr.Create(this, Native.Z3_mk_finite_set_empty(nCtx, setSort.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a singleton finite set.
|
||||
/// </summary>
|
||||
public Expr MkFiniteSetSingleton(Expr elem)
|
||||
{
|
||||
Debug.Assert(elem != null);
|
||||
|
||||
CheckContextMatch(elem);
|
||||
return Expr.Create(this, Native.Z3_mk_finite_set_singleton(nCtx, elem.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the union of two finite sets.
|
||||
/// </summary>
|
||||
public Expr MkFiniteSetUnion(Expr s1, Expr s2)
|
||||
{
|
||||
Debug.Assert(s1 != null);
|
||||
Debug.Assert(s2 != null);
|
||||
|
||||
CheckContextMatch(s1);
|
||||
CheckContextMatch(s2);
|
||||
return Expr.Create(this, Native.Z3_mk_finite_set_union(nCtx, s1.NativeObject, s2.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the intersection of two finite sets.
|
||||
/// </summary>
|
||||
public Expr MkFiniteSetIntersect(Expr s1, Expr s2)
|
||||
{
|
||||
Debug.Assert(s1 != null);
|
||||
Debug.Assert(s2 != null);
|
||||
|
||||
CheckContextMatch(s1);
|
||||
CheckContextMatch(s2);
|
||||
return Expr.Create(this, Native.Z3_mk_finite_set_intersect(nCtx, s1.NativeObject, s2.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the difference of two finite sets.
|
||||
/// </summary>
|
||||
public Expr MkFiniteSetDifference(Expr s1, Expr s2)
|
||||
{
|
||||
Debug.Assert(s1 != null);
|
||||
Debug.Assert(s2 != null);
|
||||
|
||||
CheckContextMatch(s1);
|
||||
CheckContextMatch(s2);
|
||||
return Expr.Create(this, Native.Z3_mk_finite_set_difference(nCtx, s1.NativeObject, s2.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for membership in a finite set.
|
||||
/// </summary>
|
||||
public BoolExpr MkFiniteSetMember(Expr elem, Expr set)
|
||||
{
|
||||
Debug.Assert(elem != null);
|
||||
Debug.Assert(set != null);
|
||||
|
||||
CheckContextMatch(elem);
|
||||
CheckContextMatch(set);
|
||||
return (BoolExpr)Expr.Create(this, Native.Z3_mk_finite_set_member(nCtx, elem.NativeObject, set.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the cardinality of a finite set.
|
||||
/// </summary>
|
||||
public Expr MkFiniteSetSize(Expr set)
|
||||
{
|
||||
Debug.Assert(set != null);
|
||||
|
||||
CheckContextMatch(set);
|
||||
return Expr.Create(this, Native.Z3_mk_finite_set_size(nCtx, set.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if one finite set is a subset of another.
|
||||
/// </summary>
|
||||
public BoolExpr MkFiniteSetSubset(Expr s1, Expr s2)
|
||||
{
|
||||
Debug.Assert(s1 != null);
|
||||
Debug.Assert(s2 != null);
|
||||
|
||||
CheckContextMatch(s1);
|
||||
CheckContextMatch(s2);
|
||||
return (BoolExpr)Expr.Create(this, Native.Z3_mk_finite_set_subset(nCtx, s1.NativeObject, s2.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map a function over all elements in a finite set.
|
||||
/// </summary>
|
||||
public Expr MkFiniteSetMap(Expr f, Expr set)
|
||||
{
|
||||
Debug.Assert(f != null);
|
||||
Debug.Assert(set != null);
|
||||
|
||||
CheckContextMatch(f);
|
||||
CheckContextMatch(set);
|
||||
return Expr.Create(this, Native.Z3_mk_finite_set_map(nCtx, f.NativeObject, set.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filter a finite set with a predicate.
|
||||
/// </summary>
|
||||
public Expr MkFiniteSetFilter(Expr f, Expr set)
|
||||
{
|
||||
Debug.Assert(f != null);
|
||||
Debug.Assert(set != null);
|
||||
|
||||
CheckContextMatch(f);
|
||||
CheckContextMatch(set);
|
||||
return Expr.Create(this, Native.Z3_mk_finite_set_filter(nCtx, f.NativeObject, set.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a finite set containing integers in the range [low, high].
|
||||
/// </summary>
|
||||
public Expr MkFiniteSetRange(Expr low, Expr high)
|
||||
{
|
||||
Debug.Assert(low != null);
|
||||
Debug.Assert(high != null);
|
||||
|
||||
CheckContextMatch(low);
|
||||
CheckContextMatch(high);
|
||||
return Expr.Create(this, Native.Z3_mk_finite_set_range(nCtx, low.NativeObject, high.NativeObject));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sequence, string and regular expressions
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -2647,6 +2821,55 @@ namespace Microsoft.Z3
|
|||
return new SeqExpr(this, Native.Z3_mk_seq_replace(nCtx, s.NativeObject, src.NativeObject, dst.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map function f over the sequence s.
|
||||
/// </summary>
|
||||
public Expr MkSeqMap(Expr f, SeqExpr s)
|
||||
{
|
||||
Debug.Assert(f != null);
|
||||
Debug.Assert(s != null);
|
||||
CheckContextMatch(f, s);
|
||||
return Expr.Create(this, Native.Z3_mk_seq_map(nCtx, f.NativeObject, s.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map function f over the sequence s at index i.
|
||||
/// </summary>
|
||||
public Expr MkSeqMapi(Expr f, Expr i, SeqExpr s)
|
||||
{
|
||||
Debug.Assert(f != null);
|
||||
Debug.Assert(i != null);
|
||||
Debug.Assert(s != null);
|
||||
CheckContextMatch(f, i, s);
|
||||
return Expr.Create(this, Native.Z3_mk_seq_mapi(nCtx, f.NativeObject, i.NativeObject, s.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fold left the function f over the sequence s with initial value a.
|
||||
/// </summary>
|
||||
public Expr MkSeqFoldLeft(Expr f, Expr a, SeqExpr s)
|
||||
{
|
||||
Debug.Assert(f != null);
|
||||
Debug.Assert(a != null);
|
||||
Debug.Assert(s != null);
|
||||
CheckContextMatch(f, a, s);
|
||||
return Expr.Create(this, Native.Z3_mk_seq_foldl(nCtx, f.NativeObject, a.NativeObject, s.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fold left with index the function f over the sequence s with initial value a starting at index i.
|
||||
/// </summary>
|
||||
public Expr MkSeqFoldLeftI(Expr f, Expr i, Expr a, SeqExpr s)
|
||||
{
|
||||
Debug.Assert(f != null);
|
||||
Debug.Assert(i != null);
|
||||
Debug.Assert(a != null);
|
||||
Debug.Assert(s != null);
|
||||
CheckContextMatch(f, i, a);
|
||||
CheckContextMatch(s, a);
|
||||
return Expr.Create(this, Native.Z3_mk_seq_foldli(nCtx, f.NativeObject, i.NativeObject, a.NativeObject, s.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a regular expression that accepts sequence s.
|
||||
/// </summary>
|
||||
|
|
@ -3395,7 +3618,12 @@ namespace Microsoft.Z3
|
|||
/// <seealso cref="Sort.ToString()"/>
|
||||
public Z3_ast_print_mode PrintMode
|
||||
{
|
||||
set { Native.Z3_set_ast_print_mode(nCtx, (uint)value); }
|
||||
get { return m_print_mode; }
|
||||
set
|
||||
{
|
||||
Native.Z3_set_ast_print_mode(nCtx, (uint)value);
|
||||
m_print_mode = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
@ -3438,6 +3666,32 @@ namespace Microsoft.Z3
|
|||
AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls)));
|
||||
return assertions.ToBoolExprArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a benchmark into SMT-LIB2 formatted string.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the benchmark. May be null.</param>
|
||||
/// <param name="logic">The benchmark logic. May be null.</param>
|
||||
/// <param name="status">Status string, such as "sat", "unsat", or "unknown".</param>
|
||||
/// <param name="attributes">Other attributes, such as source, difficulty or category. May be null.</param>
|
||||
/// <param name="assumptions">Auxiliary assumptions.</param>
|
||||
/// <param name="formula">Formula to be checked for consistency in conjunction with assumptions.</param>
|
||||
/// <returns>A string representation of the benchmark in SMT-LIB2 format.</returns>
|
||||
public string BenchmarkToSmtlibString(string name, string logic, string status, string attributes, BoolExpr[] assumptions, BoolExpr formula)
|
||||
{
|
||||
Debug.Assert(assumptions != null);
|
||||
Debug.Assert(formula != null);
|
||||
|
||||
return Native.Z3_benchmark_to_smtlib_string(
|
||||
nCtx,
|
||||
name,
|
||||
logic,
|
||||
status,
|
||||
attributes,
|
||||
(uint)(assumptions?.Length ?? 0),
|
||||
AST.ArrayToNative(assumptions),
|
||||
formula.NativeObject);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Goals
|
||||
|
|
@ -4849,6 +5103,44 @@ namespace Microsoft.Z3
|
|||
return a.NativeObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a partial order relation over a sort.
|
||||
/// </summary>
|
||||
/// <param name="a">The sort of the relation.</param>
|
||||
/// <param name="index">The index of the relation.</param>
|
||||
public FuncDecl MkPartialOrder(Sort a, uint index)
|
||||
{
|
||||
return new FuncDecl(this, Native.Z3_mk_partial_order(this.nCtx, a.NativeObject, index));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the transitive closure of a binary relation.
|
||||
/// </summary>
|
||||
/// <remarks>The resulting relation is recursive.</remarks>
|
||||
/// <param name="f">A binary relation represented as a function declaration.</param>
|
||||
public FuncDecl MkTransitiveClosure(FuncDecl f)
|
||||
{
|
||||
return new FuncDecl(this, Native.Z3_mk_transitive_closure(this.nCtx, f.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the nonzero subresultants of p and q with respect to the "variable" x.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// p, q and x are Z3 expressions where p and q are arithmetic terms.
|
||||
/// Note that any subterm that cannot be viewed as a polynomial is assumed to be a variable.
|
||||
/// </remarks>
|
||||
/// <param name="p">First arithmetic term.</param>
|
||||
/// <param name="q">Second arithmetic term.</param>
|
||||
/// <param name="x">The variable with respect to which subresultants are computed.</param>
|
||||
public ASTVector PolynomialSubresultants(Expr p, Expr q, Expr x)
|
||||
{
|
||||
CheckContextMatch(p);
|
||||
CheckContextMatch(q);
|
||||
CheckContextMatch(x);
|
||||
return new ASTVector(this, Native.Z3_polynomial_subresultants(this.nCtx, p.NativeObject, q.NativeObject, x.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a string describing all available parameters to <c>Expr.Simplify</c>.
|
||||
/// </summary>
|
||||
|
|
@ -4905,6 +5197,7 @@ namespace Microsoft.Z3
|
|||
internal Native.Z3_error_handler m_n_err_handler = null;
|
||||
internal static Object creation_lock = new Object();
|
||||
internal IntPtr nCtx { get { return m_ctx; } }
|
||||
private Z3_ast_print_mode m_print_mode = Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT;
|
||||
|
||||
internal void NativeErrorHandler(IntPtr ctx, Z3_error_code errorCode)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -159,6 +159,28 @@ namespace Microsoft.Z3
|
|||
return Expr.Create(Context, Native.Z3_substitute_vars(Context.nCtx, NativeObject, (uint)to.Length, Expr.ArrayToNative(to)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Substitute functions in <paramref name="from"/> with the expressions in <paramref name="to"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The expressions in <paramref name="to"/> can have free variables. The free variable in <c>to[i]</c> at de-Bruijn index 0
|
||||
/// refers to the first argument of <c>from[i]</c>, the free variable at index 1 corresponds to the second argument, and so on.
|
||||
/// The arrays <paramref name="from"/> and <paramref name="to"/> must have the same size.
|
||||
/// </remarks>
|
||||
public Expr SubstituteFuns(FuncDecl[] from, Expr[] to)
|
||||
{
|
||||
Debug.Assert(from != null);
|
||||
Debug.Assert(to != null);
|
||||
Debug.Assert(from.All(f => f != null));
|
||||
Debug.Assert(to.All(t => t != null));
|
||||
|
||||
Context.CheckContextMatch<FuncDecl>(from);
|
||||
Context.CheckContextMatch<Expr>(to);
|
||||
if (from.Length != to.Length)
|
||||
throw new Z3Exception("Arrays 'from' and 'to' must have the same length");
|
||||
return Expr.Create(Context, Native.Z3_substitute_funs(Context.nCtx, NativeObject, (uint)from.Length, FuncDecl.ArrayToNative(from), Expr.ArrayToNative(to)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates (copies) the term to the Context <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
|
|
|
|||
53
src/api/dotnet/FiniteSetSort.cs
Normal file
53
src/api/dotnet/FiniteSetSort.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*++
|
||||
Copyright (c) 2024 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
FiniteSetSort.cs
|
||||
|
||||
Abstract:
|
||||
|
||||
Z3 Managed API: Finite Set Sorts
|
||||
|
||||
Author:
|
||||
|
||||
GitHub Copilot
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Z3
|
||||
{
|
||||
/// <summary>
|
||||
/// Finite set sorts.
|
||||
/// </summary>
|
||||
public class FiniteSetSort : Sort
|
||||
{
|
||||
#region Internal
|
||||
internal FiniteSetSort(Context ctx, IntPtr obj)
|
||||
: base(ctx, obj)
|
||||
{
|
||||
Debug.Assert(ctx != null);
|
||||
}
|
||||
|
||||
internal FiniteSetSort(Context ctx, Sort elemSort)
|
||||
: base(ctx, Native.Z3_mk_finite_set_sort(ctx.nCtx, elemSort.NativeObject))
|
||||
{
|
||||
Debug.Assert(ctx != null);
|
||||
Debug.Assert(elemSort != null);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Get the element sort (basis) of this finite set sort.
|
||||
/// </summary>
|
||||
public Sort Basis
|
||||
{
|
||||
get { return Sort.Create(Context, Native.Z3_get_finite_set_sort_basis(Context.nCtx, NativeObject)); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -277,6 +277,50 @@ namespace Microsoft.Z3
|
|||
public Z3_ast[] Units
|
||||
=> ntvContext.ToArray(Native.Z3_solver_get_units(nCtx, z3solver));
|
||||
|
||||
/// <summary>
|
||||
/// Non-unit literals in the solver state.
|
||||
/// </summary>
|
||||
public Z3_ast[] NonUnits
|
||||
=> ntvContext.ToArray(Native.Z3_solver_get_non_units(nCtx, z3solver));
|
||||
|
||||
/// <summary>
|
||||
/// Trail of the solver state after a check() call.
|
||||
/// </summary>
|
||||
public Z3_ast[] Trail
|
||||
=> ntvContext.ToArray(Native.Z3_solver_get_trail(nCtx, z3solver));
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve congruence closure root of the term t relative to the current search state.
|
||||
/// The function primarily works for SimpleSolver.
|
||||
/// Terms and variables that are eliminated during pre-processing are not visible to the congruence closure.
|
||||
/// </summary>
|
||||
public Z3_ast CongruenceRoot(Z3_ast t)
|
||||
{
|
||||
Debug.Assert(t != IntPtr.Zero);
|
||||
return Native.Z3_solver_congruence_root(nCtx, z3solver, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve congruence closure sibling of the term t relative to the current search state.
|
||||
/// The function primarily works for SimpleSolver.
|
||||
/// Terms and variables that are eliminated during pre-processing are not visible to the congruence closure.
|
||||
/// </summary>
|
||||
public Z3_ast CongruenceNext(Z3_ast t)
|
||||
{
|
||||
Debug.Assert(t != IntPtr.Zero);
|
||||
return Native.Z3_solver_congruence_next(nCtx, z3solver, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explain congruence of a and b relative to the current search state.
|
||||
/// </summary>
|
||||
public Z3_ast CongruenceExplain(Z3_ast a, Z3_ast b)
|
||||
{
|
||||
Debug.Assert(a != IntPtr.Zero);
|
||||
Debug.Assert(b != IntPtr.Zero);
|
||||
return Native.Z3_solver_congruence_explain(nCtx, z3solver, a, b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the assertions in the solver are consistent or not.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ namespace Microsoft.Z3
|
|||
/// <remarks>
|
||||
/// This API is an alternative to <see cref="Check(Expr[])"/> with assumptions for extracting unsat cores.
|
||||
/// Both APIs can be used in the same solver. The unsat core will contain a combination
|
||||
/// of the Boolean variables provided using <see cref="AssertAndTrack(BoolExpr[],BoolExpr[])"/>
|
||||
/// of the Boolean variables provided using <see cref="AssertAndTrack(BoolExpr,BoolExpr)"/>
|
||||
/// and the Boolean literals
|
||||
/// provided using <see cref="Check(Expr[])"/> with assumptions.
|
||||
/// </remarks>
|
||||
|
|
|
|||
459
src/api/dotnet/RCFNum.cs
Normal file
459
src/api/dotnet/RCFNum.cs
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
/*++
|
||||
Copyright (c) 2024 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
RCFNum.cs
|
||||
|
||||
Abstract:
|
||||
|
||||
Z3 Managed API: Real Closed Field (RCF) Numerals
|
||||
|
||||
Author:
|
||||
|
||||
GitHub Copilot 2024-01-12
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Z3
|
||||
{
|
||||
/// <summary>
|
||||
/// Real Closed Field (RCF) numerals.
|
||||
///
|
||||
/// RCF numerals can represent:
|
||||
/// - Rational numbers
|
||||
/// - Algebraic numbers (roots of polynomials)
|
||||
/// - Transcendental extensions (e.g., pi, e)
|
||||
/// - Infinitesimal extensions
|
||||
/// </summary>
|
||||
public class RCFNum : Z3Object
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an RCF numeral from a rational string.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Z3 context</param>
|
||||
/// <param name="value">String representation of a rational number (e.g., "3/2", "0.5", "42")</param>
|
||||
public RCFNum(Context ctx, string value)
|
||||
: base(ctx, Native.Z3_rcf_mk_rational(ctx.nCtx, value))
|
||||
{
|
||||
Debug.Assert(ctx != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an RCF numeral from a small integer.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Z3 context</param>
|
||||
/// <param name="value">Integer value</param>
|
||||
public RCFNum(Context ctx, int value)
|
||||
: base(ctx, Native.Z3_rcf_mk_small_int(ctx.nCtx, value))
|
||||
{
|
||||
Debug.Assert(ctx != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal constructor for wrapping native RCF numeral pointers.
|
||||
/// </summary>
|
||||
internal RCFNum(Context ctx, IntPtr obj)
|
||||
: base(ctx, obj)
|
||||
{
|
||||
Debug.Assert(ctx != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an RCF numeral representing pi.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Z3 context</param>
|
||||
/// <returns>RCF numeral for pi</returns>
|
||||
public static RCFNum MkPi(Context ctx)
|
||||
{
|
||||
return new RCFNum(ctx, Native.Z3_rcf_mk_pi(ctx.nCtx));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an RCF numeral representing e (Euler's constant).
|
||||
/// </summary>
|
||||
/// <param name="ctx">Z3 context</param>
|
||||
/// <returns>RCF numeral for e</returns>
|
||||
public static RCFNum MkE(Context ctx)
|
||||
{
|
||||
return new RCFNum(ctx, Native.Z3_rcf_mk_e(ctx.nCtx));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an RCF numeral representing an infinitesimal.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Z3 context</param>
|
||||
/// <returns>RCF numeral for an infinitesimal</returns>
|
||||
public static RCFNum MkInfinitesimal(Context ctx)
|
||||
{
|
||||
return new RCFNum(ctx, Native.Z3_rcf_mk_infinitesimal(ctx.nCtx));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find roots of a polynomial.
|
||||
///
|
||||
/// The polynomial is a[n-1]*x^(n-1) + ... + a[1]*x + a[0].
|
||||
/// </summary>
|
||||
/// <param name="ctx">Z3 context</param>
|
||||
/// <param name="coefficients">Polynomial coefficients (constant term first)</param>
|
||||
/// <returns>Array of RCF numerals representing the roots</returns>
|
||||
public static RCFNum[] MkRoots(Context ctx, RCFNum[] coefficients)
|
||||
{
|
||||
if (coefficients == null || coefficients.Length == 0)
|
||||
{
|
||||
throw new Z3Exception("Polynomial coefficients cannot be empty");
|
||||
}
|
||||
|
||||
uint n = (uint)coefficients.Length;
|
||||
IntPtr[] a = new IntPtr[n];
|
||||
IntPtr[] roots = new IntPtr[n];
|
||||
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
a[i] = coefficients[i].NativeObject;
|
||||
}
|
||||
|
||||
uint numRoots = Native.Z3_rcf_mk_roots(ctx.nCtx, n, a, roots);
|
||||
|
||||
RCFNum[] result = new RCFNum[numRoots];
|
||||
for (uint i = 0; i < numRoots; i++)
|
||||
{
|
||||
result[i] = new RCFNum(ctx, roots[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add two RCF numerals.
|
||||
/// </summary>
|
||||
/// <param name="other">The RCF numeral to add</param>
|
||||
/// <returns>this + other</returns>
|
||||
public RCFNum Add(RCFNum other)
|
||||
{
|
||||
CheckContext(other);
|
||||
return new RCFNum(Context, Native.Z3_rcf_add(Context.nCtx, NativeObject, other.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtract two RCF numerals.
|
||||
/// </summary>
|
||||
/// <param name="other">The RCF numeral to subtract</param>
|
||||
/// <returns>this - other</returns>
|
||||
public RCFNum Sub(RCFNum other)
|
||||
{
|
||||
CheckContext(other);
|
||||
return new RCFNum(Context, Native.Z3_rcf_sub(Context.nCtx, NativeObject, other.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply two RCF numerals.
|
||||
/// </summary>
|
||||
/// <param name="other">The RCF numeral to multiply</param>
|
||||
/// <returns>this * other</returns>
|
||||
public RCFNum Mul(RCFNum other)
|
||||
{
|
||||
CheckContext(other);
|
||||
return new RCFNum(Context, Native.Z3_rcf_mul(Context.nCtx, NativeObject, other.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divide two RCF numerals.
|
||||
/// </summary>
|
||||
/// <param name="other">The RCF numeral to divide by</param>
|
||||
/// <returns>this / other</returns>
|
||||
public RCFNum Div(RCFNum other)
|
||||
{
|
||||
CheckContext(other);
|
||||
return new RCFNum(Context, Native.Z3_rcf_div(Context.nCtx, NativeObject, other.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Negate this RCF numeral.
|
||||
/// </summary>
|
||||
/// <returns>-this</returns>
|
||||
public RCFNum Neg()
|
||||
{
|
||||
return new RCFNum(Context, Native.Z3_rcf_neg(Context.nCtx, NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the multiplicative inverse.
|
||||
/// </summary>
|
||||
/// <returns>1/this</returns>
|
||||
public RCFNum Inv()
|
||||
{
|
||||
return new RCFNum(Context, Native.Z3_rcf_inv(Context.nCtx, NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise this RCF numeral to a power.
|
||||
/// </summary>
|
||||
/// <param name="k">The exponent</param>
|
||||
/// <returns>this^k</returns>
|
||||
public RCFNum Power(uint k)
|
||||
{
|
||||
return new RCFNum(Context, Native.Z3_rcf_power(Context.nCtx, NativeObject, k));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for addition.
|
||||
/// </summary>
|
||||
public static RCFNum operator +(RCFNum a, RCFNum b)
|
||||
{
|
||||
return a.Add(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for subtraction.
|
||||
/// </summary>
|
||||
public static RCFNum operator -(RCFNum a, RCFNum b)
|
||||
{
|
||||
return a.Sub(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for multiplication.
|
||||
/// </summary>
|
||||
public static RCFNum operator *(RCFNum a, RCFNum b)
|
||||
{
|
||||
return a.Mul(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for division.
|
||||
/// </summary>
|
||||
public static RCFNum operator /(RCFNum a, RCFNum b)
|
||||
{
|
||||
return a.Div(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for negation.
|
||||
/// </summary>
|
||||
public static RCFNum operator -(RCFNum a)
|
||||
{
|
||||
return a.Neg();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this RCF numeral is less than another.
|
||||
/// </summary>
|
||||
/// <param name="other">The RCF numeral to compare with</param>
|
||||
/// <returns>true if this < other</returns>
|
||||
public bool Lt(RCFNum other)
|
||||
{
|
||||
CheckContext(other);
|
||||
return 0 != Native.Z3_rcf_lt(Context.nCtx, NativeObject, other.NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this RCF numeral is greater than another.
|
||||
/// </summary>
|
||||
/// <param name="other">The RCF numeral to compare with</param>
|
||||
/// <returns>true if this > other</returns>
|
||||
public bool Gt(RCFNum other)
|
||||
{
|
||||
CheckContext(other);
|
||||
return 0 != Native.Z3_rcf_gt(Context.nCtx, NativeObject, other.NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this RCF numeral is less than or equal to another.
|
||||
/// </summary>
|
||||
/// <param name="other">The RCF numeral to compare with</param>
|
||||
/// <returns>true if this <= other</returns>
|
||||
public bool Le(RCFNum other)
|
||||
{
|
||||
CheckContext(other);
|
||||
return 0 != Native.Z3_rcf_le(Context.nCtx, NativeObject, other.NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this RCF numeral is greater than or equal to another.
|
||||
/// </summary>
|
||||
/// <param name="other">The RCF numeral to compare with</param>
|
||||
/// <returns>true if this >= other</returns>
|
||||
public bool Ge(RCFNum other)
|
||||
{
|
||||
CheckContext(other);
|
||||
return 0 != Native.Z3_rcf_ge(Context.nCtx, NativeObject, other.NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this RCF numeral is equal to another.
|
||||
/// </summary>
|
||||
/// <param name="other">The RCF numeral to compare with</param>
|
||||
/// <returns>true if this == other</returns>
|
||||
public bool Eq(RCFNum other)
|
||||
{
|
||||
CheckContext(other);
|
||||
return 0 != Native.Z3_rcf_eq(Context.nCtx, NativeObject, other.NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this RCF numeral is not equal to another.
|
||||
/// </summary>
|
||||
/// <param name="other">The RCF numeral to compare with</param>
|
||||
/// <returns>true if this != other</returns>
|
||||
public bool Neq(RCFNum other)
|
||||
{
|
||||
CheckContext(other);
|
||||
return 0 != Native.Z3_rcf_neq(Context.nCtx, NativeObject, other.NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for less than.
|
||||
/// </summary>
|
||||
public static bool operator <(RCFNum a, RCFNum b)
|
||||
{
|
||||
return a.Lt(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for greater than.
|
||||
/// </summary>
|
||||
public static bool operator >(RCFNum a, RCFNum b)
|
||||
{
|
||||
return a.Gt(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for less than or equal.
|
||||
/// </summary>
|
||||
public static bool operator <=(RCFNum a, RCFNum b)
|
||||
{
|
||||
return a.Le(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for greater than or equal.
|
||||
/// </summary>
|
||||
public static bool operator >=(RCFNum a, RCFNum b)
|
||||
{
|
||||
return a.Ge(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for equality.
|
||||
/// </summary>
|
||||
public static bool operator ==(RCFNum a, RCFNum b)
|
||||
{
|
||||
if (ReferenceEquals(a, b)) return true;
|
||||
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false;
|
||||
return a.Eq(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator overload for inequality.
|
||||
/// </summary>
|
||||
public static bool operator !=(RCFNum a, RCFNum b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this RCF numeral is a rational number.
|
||||
/// </summary>
|
||||
/// <returns>true if this is rational</returns>
|
||||
public bool IsRational()
|
||||
{
|
||||
return 0 != Native.Z3_rcf_is_rational(Context.nCtx, NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this RCF numeral is an algebraic number.
|
||||
/// </summary>
|
||||
/// <returns>true if this is algebraic</returns>
|
||||
public bool IsAlgebraic()
|
||||
{
|
||||
return 0 != Native.Z3_rcf_is_algebraic(Context.nCtx, NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this RCF numeral is an infinitesimal.
|
||||
/// </summary>
|
||||
/// <returns>true if this is infinitesimal</returns>
|
||||
public bool IsInfinitesimal()
|
||||
{
|
||||
return 0 != Native.Z3_rcf_is_infinitesimal(Context.nCtx, NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this RCF numeral is a transcendental number.
|
||||
/// </summary>
|
||||
/// <returns>true if this is transcendental</returns>
|
||||
public bool IsTranscendental()
|
||||
{
|
||||
return 0 != Native.Z3_rcf_is_transcendental(Context.nCtx, NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert this RCF numeral to a string.
|
||||
/// </summary>
|
||||
/// <param name="compact">If true, use compact representation</param>
|
||||
/// <returns>String representation</returns>
|
||||
public string ToString(bool compact)
|
||||
{
|
||||
return Native.Z3_rcf_num_to_string(Context.nCtx, NativeObject, compact ? (byte)1 : (byte)0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert this RCF numeral to a string (non-compact).
|
||||
/// </summary>
|
||||
/// <returns>String representation</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return ToString(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert this RCF numeral to a decimal string.
|
||||
/// </summary>
|
||||
/// <param name="precision">Number of decimal places</param>
|
||||
/// <returns>Decimal string representation</returns>
|
||||
public string ToDecimal(uint precision)
|
||||
{
|
||||
return Native.Z3_rcf_num_to_decimal_string(Context.nCtx, NativeObject, precision);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override Equals for proper equality semantics.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is RCFNum other)
|
||||
{
|
||||
return this == other;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override GetHashCode for proper equality semantics.
|
||||
/// </summary>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return NativeObject.GetHashCode();
|
||||
}
|
||||
|
||||
#region Internal
|
||||
internal override void DecRef(IntPtr o)
|
||||
{
|
||||
Native.Z3_rcf_del(Context.nCtx, o);
|
||||
}
|
||||
|
||||
private void CheckContext(RCFNum other)
|
||||
{
|
||||
if (Context != other.Context)
|
||||
{
|
||||
throw new Z3Exception("RCF numerals from different contexts");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -325,6 +325,66 @@ namespace Microsoft.Z3
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non-unit literals in the solver state.
|
||||
/// </summary>
|
||||
public BoolExpr[] NonUnits
|
||||
{
|
||||
get
|
||||
{
|
||||
using ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_non_units(Context.nCtx, NativeObject));
|
||||
return assertions.ToBoolExprArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trail of the solver state after a check() call.
|
||||
/// </summary>
|
||||
public BoolExpr[] Trail
|
||||
{
|
||||
get
|
||||
{
|
||||
using ASTVector trail = new ASTVector(Context, Native.Z3_solver_get_trail(Context.nCtx, NativeObject));
|
||||
return trail.ToBoolExprArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve congruence closure root of the term t relative to the current search state.
|
||||
/// The function primarily works for SimpleSolver.
|
||||
/// Terms and variables that are eliminated during pre-processing are not visible to the congruence closure.
|
||||
/// </summary>
|
||||
public Expr CongruenceRoot(Expr t)
|
||||
{
|
||||
Debug.Assert(t != null);
|
||||
Context.CheckContextMatch(t);
|
||||
return Expr.Create(Context, Native.Z3_solver_congruence_root(Context.nCtx, NativeObject, t.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve congruence closure sibling of the term t relative to the current search state.
|
||||
/// The function primarily works for SimpleSolver.
|
||||
/// Terms and variables that are eliminated during pre-processing are not visible to the congruence closure.
|
||||
/// </summary>
|
||||
public Expr CongruenceNext(Expr t)
|
||||
{
|
||||
Debug.Assert(t != null);
|
||||
Context.CheckContextMatch(t);
|
||||
return Expr.Create(Context, Native.Z3_solver_congruence_next(Context.nCtx, NativeObject, t.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explain congruence of a and b relative to the current search state.
|
||||
/// </summary>
|
||||
public Expr CongruenceExplain(Expr a, Expr b)
|
||||
{
|
||||
Debug.Assert(a != null);
|
||||
Debug.Assert(b != null);
|
||||
Context.CheckContextMatch(a);
|
||||
Context.CheckContextMatch(b);
|
||||
return Expr.Create(Context, Native.Z3_solver_congruence_explain(Context.nCtx, NativeObject, a.NativeObject, b.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the assertions in the solver are consistent or not.
|
||||
/// </summary>
|
||||
|
|
@ -493,6 +553,38 @@ namespace Microsoft.Z3
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the trail and their associated decision levels after a <c>Check</c> call.
|
||||
/// The trail contains Boolean literals (decisions and propagations), and the levels
|
||||
/// array contains the corresponding decision levels at which each literal was assigned.
|
||||
/// </summary>
|
||||
public uint[] TrailLevels
|
||||
{
|
||||
get
|
||||
{
|
||||
using ASTVector trail = new ASTVector(Context, Native.Z3_solver_get_trail(Context.nCtx, NativeObject));
|
||||
uint[] levels = new uint[trail.Size];
|
||||
Native.Z3_solver_get_levels(Context.nCtx, NativeObject, trail.NativeObject, (uint)trail.Size, levels);
|
||||
return levels;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set an initial value for a variable to guide the solver's search heuristics.
|
||||
/// This can improve performance when good initial values are known for the problem domain.
|
||||
/// </summary>
|
||||
/// <param name="var">The variable to set an initial value for</param>
|
||||
/// <param name="value">The initial value for the variable</param>
|
||||
public void SetInitialValue(Expr var, Expr value)
|
||||
{
|
||||
Debug.Assert(var != null);
|
||||
Debug.Assert(value != null);
|
||||
|
||||
Context.CheckContextMatch(var);
|
||||
Context.CheckContextMatch(value);
|
||||
Native.Z3_solver_set_initial_value(Context.nCtx, NativeObject, var.NativeObject, value.NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a clone of the current solver with respect to <c>ctx</c>.
|
||||
/// </summary>
|
||||
|
|
@ -530,6 +622,34 @@ namespace Microsoft.Z3
|
|||
return Native.Z3_solver_to_string(Context.nCtx, NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the solver assertions to SMT-LIB2 format as a benchmark.
|
||||
/// </summary>
|
||||
/// <param name="status">Status string, such as "sat", "unsat", or "unknown". Default is "unknown".</param>
|
||||
/// <returns>A string representation of the solver's assertions in SMT-LIB2 format.</returns>
|
||||
public string ToSmt2(string status = "unknown")
|
||||
{
|
||||
BoolExpr[] assertions = Assertions;
|
||||
BoolExpr formula;
|
||||
BoolExpr[] assumptions;
|
||||
|
||||
if (assertions.Length > 0)
|
||||
{
|
||||
// Use last assertion as formula and rest as assumptions
|
||||
assumptions = new BoolExpr[assertions.Length - 1];
|
||||
Array.Copy(assertions, assumptions, assertions.Length - 1);
|
||||
formula = assertions[assertions.Length - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// No assertions, use true
|
||||
assumptions = new BoolExpr[0];
|
||||
formula = Context.MkTrue();
|
||||
}
|
||||
|
||||
return Context.BenchmarkToSmtlibString("", "", status, "", assumptions, formula);
|
||||
}
|
||||
|
||||
#region Internal
|
||||
internal Solver(Context ctx, IntPtr obj)
|
||||
: base(ctx, obj)
|
||||
|
|
|
|||
58
src/api/go/CMakeLists.txt
Normal file
58
src/api/go/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Z3 Go API
|
||||
|
||||
# Note: This CMakeLists.txt is a placeholder for Go binding integration.
|
||||
# Go bindings use CGO and are typically built using the Go toolchain directly.
|
||||
# However, we can set up installation targets here.
|
||||
|
||||
if(Z3_BUILD_GO_BINDINGS)
|
||||
message(STATUS "Z3 Go bindings will be installed")
|
||||
|
||||
# Install Go source files
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/z3.go
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/solver.go
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/go.mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/README.md
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/go/src/github.com/Z3Prover/z3/go"
|
||||
)
|
||||
|
||||
# On Windows, we need to ensure the DLL is accessible
|
||||
if(WIN32)
|
||||
message(STATUS "Go bindings on Windows require libz3.dll in PATH")
|
||||
endif()
|
||||
|
||||
# Add a custom target to test Go bindings if Go is available
|
||||
find_program(GO_EXECUTABLE go)
|
||||
if(GO_EXECUTABLE)
|
||||
message(STATUS "Found Go: ${GO_EXECUTABLE}")
|
||||
|
||||
# Custom target to build Go bindings
|
||||
set(CGO_LDFLAGS_VALUE "-L${CMAKE_BINARY_DIR} -lz3")
|
||||
add_custom_target(go-bindings
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
CGO_CFLAGS=-I${CMAKE_SOURCE_DIR}/src/api
|
||||
CGO_LDFLAGS=${CGO_LDFLAGS_VALUE}
|
||||
${GO_EXECUTABLE} build -v
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Building Go bindings"
|
||||
DEPENDS libz3
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
# Custom target to test Go examples
|
||||
add_custom_target(test-go-examples
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
CGO_CFLAGS=-I${CMAKE_SOURCE_DIR}/src/api
|
||||
CGO_LDFLAGS=${CGO_LDFLAGS_VALUE}
|
||||
LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{LD_LIBRARY_PATH}
|
||||
PATH=${CMAKE_BINARY_DIR}$<SEMICOLON>$ENV{PATH}
|
||||
${GO_EXECUTABLE} run basic_example.go
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/examples/go
|
||||
COMMENT "Running Go examples"
|
||||
DEPENDS libz3
|
||||
VERBATIM
|
||||
)
|
||||
else()
|
||||
message(STATUS "Go not found - Go bindings can be built manually")
|
||||
endif()
|
||||
endif()
|
||||
331
src/api/go/README.md
Normal file
331
src/api/go/README.md
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
# Z3 Go Bindings
|
||||
|
||||
This directory contains Go language bindings for the Z3 theorem prover.
|
||||
|
||||
## Overview
|
||||
|
||||
The Go bindings provide a comprehensive interface to Z3's C API using CGO. The bindings support:
|
||||
|
||||
- **Core Z3 Types**: Context, Config, Symbol, AST, Sort, Expr, FuncDecl
|
||||
- **Solver Operations**: Creating solvers, asserting constraints, checking satisfiability
|
||||
- **Model Manipulation**: Extracting and evaluating models
|
||||
- **Boolean Logic**: And, Or, Not, Implies, Iff, Xor
|
||||
- **Arithmetic**: Add, Sub, Mul, Div, Mod, comparison operators
|
||||
- **Bit-vectors**: Full bit-vector arithmetic, bitwise operations, shifts, comparisons
|
||||
- **Floating Point**: IEEE 754 floating-point arithmetic with rounding modes
|
||||
- **Arrays**: Select, Store, constant arrays
|
||||
- **Sequences/Strings**: String operations, concatenation, contains, indexing
|
||||
- **Regular Expressions**: Pattern matching, Kleene star/plus, regex operations
|
||||
- **Quantifiers**: Forall, Exists
|
||||
- **Functions**: Function declarations and applications
|
||||
- **Tactics & Goals**: Goal-based solving and tactic combinators
|
||||
- **Probes**: Goal property checking
|
||||
- **Datatypes**: Algebraic datatypes, tuples, enumerations, lists
|
||||
- **Parameters**: Solver and tactic configuration
|
||||
- **Optimize**: Optimization problems with maximize/minimize objectives
|
||||
|
||||
## Building
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Go 1.20 or later
|
||||
- Z3 library built and installed
|
||||
- CGO enabled
|
||||
|
||||
### With CMake
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake -DBUILD_GO_BINDINGS=ON ..
|
||||
make
|
||||
```
|
||||
|
||||
### With Python Build System
|
||||
|
||||
```bash
|
||||
python scripts/mk_make.py --go
|
||||
cd build
|
||||
make
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Z3Prover/z3/src/api/go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a context
|
||||
ctx := z3.NewContext()
|
||||
|
||||
// Create variables
|
||||
x := ctx.MkIntConst("x")
|
||||
y := ctx.MkIntConst("y")
|
||||
|
||||
// Create constraints: x + y == 10 && x > y
|
||||
ten := ctx.MkInt(10, ctx.MkIntSort())
|
||||
eq := ctx.MkEq(ctx.MkAdd(x, y), ten)
|
||||
gt := ctx.MkGt(x, y)
|
||||
|
||||
// Create solver and check
|
||||
solver := ctx.NewSolver()
|
||||
solver.Assert(eq)
|
||||
solver.Assert(gt)
|
||||
|
||||
if solver.Check() == z3.Satisfiable {
|
||||
model := solver.Model()
|
||||
if xVal, ok := model.Eval(x, true); ok {
|
||||
fmt.Println("x =", xVal.String())
|
||||
}
|
||||
if yVal, ok := model.Eval(y, true); ok {
|
||||
fmt.Println("y =", yVal.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Running Examples
|
||||
|
||||
```bash
|
||||
cd examples/go
|
||||
|
||||
# Set library path (Linux/Mac)
|
||||
export LD_LIBRARY_PATH=../../build:$LD_LIBRARY_PATH
|
||||
export CGO_CFLAGS="-I../../src/api"
|
||||
export CGO_LDFLAGS="-L../../build -lz3"
|
||||
|
||||
# Set library path (Windows)
|
||||
set PATH=..\..\build;%PATH%
|
||||
set CGO_CFLAGS=-I../../src/api
|
||||
set CGO_LDFLAGS=-L../../build -lz3
|
||||
|
||||
# Run example
|
||||
go run basic_example.go
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Context
|
||||
|
||||
- `NewContext()` - Create a new Z3 context
|
||||
- `NewContextWithConfig(cfg *Config)` - Create context with configuration
|
||||
- `SetParam(key, value string)` - Set context parameters
|
||||
|
||||
### Creating Expressions
|
||||
|
||||
- `MkBoolConst(name string)` - Create Boolean variable
|
||||
- `MkIntConst(name string)` - Create integer variable
|
||||
- `MkRealConst(name string)` - Create real variable
|
||||
- `MkInt(value int, sort *Sort)` - Create integer constant
|
||||
- `MkReal(num, den int)` - Create rational constant
|
||||
|
||||
### Boolean Operations
|
||||
|
||||
- `MkAnd(exprs ...*Expr)` - Conjunction
|
||||
- `MkOr(exprs ...*Expr)` - Disjunction
|
||||
- `MkNot(expr *Expr)` - Negation
|
||||
- `MkImplies(lhs, rhs *Expr)` - Implication
|
||||
- `MkIff(lhs, rhs *Expr)` - If-and-only-if
|
||||
- `MkXor(lhs, rhs *Expr)` - Exclusive or
|
||||
|
||||
### Arithmetic Operations
|
||||
|
||||
- `MkAdd(exprs ...*Expr)` - Addition
|
||||
- `MkSub(exprs ...*Expr)` - Subtraction
|
||||
- `MkMul(exprs ...*Expr)` - Multiplication
|
||||
- `MkDiv(lhs, rhs *Expr)` - Division
|
||||
- `MkMod(lhs, rhs *Expr)` - Modulo
|
||||
- `MkRem(lhs, rhs *Expr)` - Remainder
|
||||
|
||||
### Comparison Operations
|
||||
|
||||
- `MkEq(lhs, rhs *Expr)` - Equality
|
||||
- `MkDistinct(exprs ...*Expr)` - Distinct
|
||||
- `MkLt(lhs, rhs *Expr)` - Less than
|
||||
- `MkLe(lhs, rhs *Expr)` - Less than or equal
|
||||
- `MkGt(lhs, rhs *Expr)` - Greater than
|
||||
- `MkGe(lhs, rhs *Expr)` - Greater than or equal
|
||||
|
||||
### Solver Operations
|
||||
|
||||
- `NewSolver()` - Create a new solver
|
||||
- `Assert(constraint *Expr)` - Add constraint
|
||||
- `Check()` - Check satisfiability (returns Satisfiable, Unsatisfiable, or Unknown)
|
||||
- `Model()` - Get model (if satisfiable)
|
||||
- `Push()` - Create backtracking point
|
||||
- `Pop(n uint)` - Remove backtracking points
|
||||
- `Reset()` - Remove all assertions
|
||||
|
||||
### Model Operations
|
||||
|
||||
- `Eval(expr *Expr, modelCompletion bool)` - Evaluate expression in model
|
||||
- `NumConsts()` - Number of constants in model
|
||||
- `NumFuncs()` - Number of functions in model
|
||||
- `String()` - Get string representation
|
||||
|
||||
### Bit-vector Operations
|
||||
|
||||
- `MkBvSort(sz uint)` - Create bit-vector sort
|
||||
- `MkBVConst(name string, size uint)` - Create bit-vector variable
|
||||
- `MkBVAdd/Sub/Mul/UDiv/SDiv(lhs, rhs *Expr)` - Arithmetic operations
|
||||
- `MkBVAnd/Or/Xor/Not(...)` - Bitwise operations
|
||||
- `MkBVShl/LShr/AShr(lhs, rhs *Expr)` - Shift operations
|
||||
- `MkBVULT/SLT/ULE/SLE/UGE/SGE/UGT/SGT(...)` - Comparisons
|
||||
- `MkConcat(lhs, rhs *Expr)` - Bit-vector concatenation
|
||||
- `MkExtract(high, low uint, expr *Expr)` - Extract bits
|
||||
- `MkSignExt/ZeroExt(i uint, expr *Expr)` - Extend bit-vectors
|
||||
|
||||
### Floating-Point Operations
|
||||
|
||||
- `MkFPSort(ebits, sbits uint)` - Create floating-point sort
|
||||
- `MkFPSort16/32/64/128()` - Standard IEEE 754 sorts
|
||||
- `MkFPInf/NaN/Zero(sort *Sort, ...)` - Special values
|
||||
- `MkFPAdd/Sub/Mul/Div(rm, lhs, rhs *Expr)` - Arithmetic with rounding
|
||||
- `MkFPNeg/Abs/Sqrt(...)` - Unary operations
|
||||
- `MkFPLT/GT/LE/GE/Eq(lhs, rhs *Expr)` - Comparisons
|
||||
- `MkFPIsNaN/IsInf/IsZero(expr *Expr)` - Predicates
|
||||
|
||||
### Sequence/String Operations
|
||||
|
||||
- `MkStringSort()` - Create string sort
|
||||
- `MkSeqSort(elemSort *Sort)` - Create sequence sort
|
||||
- `MkString(value string)` - Create string constant
|
||||
- `MkSeqConcat(exprs ...*Expr)` - Concatenation
|
||||
- `MkSeqLength(seq *Expr)` - Length
|
||||
- `MkSeqPrefix/Suffix/Contains(...)` - Predicates
|
||||
- `MkSeqAt(seq, index *Expr)` - Element access
|
||||
- `MkSeqExtract(seq, offset, length *Expr)` - Substring
|
||||
- `MkStrToInt/IntToStr(...)` - Conversions
|
||||
|
||||
### Regular Expression Operations
|
||||
|
||||
- `MkReSort(seqSort *Sort)` - Create regex sort
|
||||
- `MkToRe(seq *Expr)` - Convert string to regex
|
||||
- `MkInRe(seq, re *Expr)` - String matches regex predicate
|
||||
- `MkReStar(re *Expr)` - Kleene star (zero or more)
|
||||
- `MkRePlus(re *Expr)` - Kleene plus (one or more)
|
||||
- `MkReOption(re *Expr)` - Optional (zero or one)
|
||||
- `MkRePower(re *Expr, n uint)` - Exactly n repetitions
|
||||
- `MkReLoop(re *Expr, lo, hi uint)` - Bounded repetition
|
||||
- `MkReConcat(regexes ...*Expr)` - Concatenation
|
||||
- `MkReUnion(regexes ...*Expr)` - Alternation (OR)
|
||||
- `MkReIntersect(regexes ...*Expr)` - Intersection
|
||||
- `MkReComplement(re *Expr)` - Complement
|
||||
- `MkReDiff(a, b *Expr)` - Difference
|
||||
- `MkReEmpty/Full/Allchar(sort *Sort)` - Special regexes
|
||||
- `MkReRange(lo, hi *Expr)` - Character range
|
||||
- `MkSeqReplaceRe/ReAll(seq, re, replacement *Expr)` - Regex replace
|
||||
|
||||
### Datatype Operations
|
||||
|
||||
- `MkConstructor(name, recognizer string, ...)` - Create constructor
|
||||
- `MkDatatypeSort(name string, constructors []*Constructor)` - Create datatype
|
||||
- `MkDatatypeSorts(names []string, ...)` - Mutually recursive datatypes
|
||||
- `MkTupleSort(name string, fieldNames []string, fieldSorts []*Sort)` - Tuples
|
||||
- `MkEnumSort(name string, enumNames []string)` - Enumerations
|
||||
- `MkListSort(name string, elemSort *Sort)` - Lists
|
||||
|
||||
### Tactic Operations
|
||||
|
||||
- `MkTactic(name string)` - Create tactic by name
|
||||
- `MkGoal(models, unsatCores, proofs bool)` - Create goal
|
||||
- `Apply(g *Goal)` - Apply tactic to goal
|
||||
- `AndThen(t2 *Tactic)` - Sequential composition
|
||||
- `OrElse(t2 *Tactic)` - Try first, fallback to second
|
||||
- `Repeat(max uint)` - Repeat tactic
|
||||
- `TacticWhen/Cond(...)` - Conditional tactics
|
||||
|
||||
### Probe Operations
|
||||
|
||||
- `MkProbe(name string)` - Create probe by name
|
||||
- `Apply(g *Goal)` - Evaluate probe on goal
|
||||
- `Lt/Gt/Le/Ge/Eq(p2 *Probe)` - Probe comparisons
|
||||
- `And/Or/Not(...)` - Probe combinators
|
||||
|
||||
### Parameter Operations
|
||||
|
||||
- `MkParams()` - Create parameter set
|
||||
- `SetBool/Uint/Double/Symbol(key string, value ...)` - Set parameters
|
||||
|
||||
### Optimize Operations
|
||||
|
||||
- `NewOptimize()` - Create optimization context
|
||||
- `Assert(constraint *Expr)` - Add constraint
|
||||
- `AssertSoft(constraint *Expr, weight, group string)` - Add soft constraint
|
||||
- `Maximize(expr *Expr)` - Add maximization objective
|
||||
- `Minimize(expr *Expr)` - Add minimization objective
|
||||
- `Check(assumptions ...*Expr)` - Check and optimize
|
||||
- `Model()` - Get optimal model
|
||||
- `GetLower/Upper(index uint)` - Get objective bounds
|
||||
- `Push/Pop()` - Backtracking
|
||||
- `Assertions/Objectives()` - Get assertions and objectives
|
||||
- `UnsatCore()` - Get unsat core
|
||||
|
||||
### Fixedpoint Operations (Datalog/CHC)
|
||||
|
||||
- `NewFixedpoint()` - Create fixedpoint solver
|
||||
- `RegisterRelation(funcDecl *FuncDecl)` - Register predicate
|
||||
- `AddRule(rule *Expr, name *Symbol)` - Add Horn clause
|
||||
- `AddFact(pred *FuncDecl, args []int)` - Add table fact
|
||||
- `Query(query *Expr)` - Query constraints
|
||||
- `QueryRelations(relations []*FuncDecl)` - Query relations
|
||||
- `GetAnswer()` - Get satisfying instance or proof
|
||||
- `Push/Pop()` - Backtracking
|
||||
|
||||
### Quantifier Operations
|
||||
|
||||
- `MkQuantifier(isForall bool, weight int, sorts, names, body, patterns)` - Create quantifier
|
||||
- `MkQuantifierConst(isForall bool, weight int, bound, body, patterns)` - Create with bound vars
|
||||
- `IsUniversal/IsExistential()` - Check quantifier type
|
||||
- `GetNumBound()` - Number of bound variables
|
||||
- `GetBoundName/Sort(idx int)` - Get bound variable info
|
||||
- `GetBody()` - Get quantifier body
|
||||
- `GetNumPatterns()` - Number of patterns
|
||||
- `GetPattern(idx int)` - Get pattern
|
||||
|
||||
### Lambda Operations
|
||||
|
||||
- `MkLambda(sorts, names, body)` - Create lambda expression
|
||||
- `MkLambdaConst(bound, body)` - Create lambda with bound variables
|
||||
- `GetNumBound()` - Number of bound variables
|
||||
- `GetBoundName/Sort(idx int)` - Get bound variable info
|
||||
- `GetBody()` - Get lambda body
|
||||
|
||||
### Type Variables
|
||||
|
||||
- `MkTypeVariable(name *Symbol)` - Create polymorphic type variable sort
|
||||
|
||||
### Logging
|
||||
|
||||
- `OpenLog(filename string)` - Open interaction log
|
||||
- `CloseLog()` - Close log
|
||||
- `AppendLog(s string)` - Append to log
|
||||
- `IsLogOpen()` - Check if log is open
|
||||
|
||||
## Memory Management
|
||||
|
||||
The Go bindings use `runtime.SetFinalizer` to automatically manage Z3 reference counts. You don't need to manually call inc_ref/dec_ref. However, be aware that finalizers run during garbage collection, so resources may not be freed immediately.
|
||||
|
||||
## Thread Safety
|
||||
|
||||
Z3 contexts are not thread-safe. Each goroutine should use its own context, or use appropriate synchronization when sharing a context.
|
||||
|
||||
## License
|
||||
|
||||
Z3 is licensed under the MIT License. See LICENSE.txt in the repository root.
|
||||
|
||||
## Contributing
|
||||
|
||||
Bug reports and contributions are welcome! Please submit issues and pull requests to the main Z3 repository.
|
||||
|
||||
## References
|
||||
|
||||
- [Z3 GitHub Repository](https://github.com/Z3Prover/z3)
|
||||
- [Z3 API Documentation](https://z3prover.github.io/api/html/index.html)
|
||||
- [Z3 Guide](https://microsoft.github.io/z3guide/)
|
||||
89
src/api/go/add_godoc.py
Normal file
89
src/api/go/add_godoc.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Add godoc comments to Z3 Go bindings systematically.
|
||||
This script adds proper godoc documentation to all exported types and functions.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
# Godoc comment templates for common patterns
|
||||
TYPE_COMMENTS = {
|
||||
'Config': '// Config represents a Z3 configuration object used to customize solver behavior.\n// Create with NewConfig and configure using SetParamValue before creating a Context.',
|
||||
'Context': '// Context represents a Z3 logical context.\n// All Z3 objects (sorts, expressions, solvers) are tied to the context that created them.\n// Contexts are not thread-safe - use separate contexts for concurrent operations.',
|
||||
'Symbol': '// Symbol represents a Z3 symbol, which can be either a string or integer identifier.\n// Symbols are used to name sorts, constants, and functions.',
|
||||
'AST': '// AST represents an Abstract Syntax Tree node in Z3.\n// This is the base type for all Z3 expressions, sorts, and function declarations.',
|
||||
'Sort': '// Sort represents a type in Z3\'s type system.\n// Common sorts include Bool, Int, Real, BitVec, Array, and user-defined datatypes.',
|
||||
'Expr': '// Expr represents a Z3 expression (term).\n// Expressions are typed AST nodes that can be evaluated, simplified, or used in constraints.',
|
||||
'FuncDecl': '// FuncDecl represents a function declaration in Z3.\n// Function declarations define the signature (domain and range sorts) of functions.',
|
||||
'Pattern': '// Pattern represents a pattern for quantifier instantiation.\n// Patterns guide Z3\'s E-matching algorithm for quantifier instantiation.',
|
||||
'Quantifier': '// Quantifier represents a quantified formula (forall or exists).\n// Quantifiers bind variables and include optional patterns for instantiation.',
|
||||
'Lambda': '// Lambda represents a lambda expression (anonymous function).\n// Lambda expressions can be used as array values or in higher-order reasoning.',
|
||||
'Statistics': '// Statistics holds performance and diagnostic information from Z3 solvers.\n// Use GetKey, GetUintValue, and GetDoubleValue to access individual statistics.',
|
||||
}
|
||||
|
||||
FUNCTION_COMMENTS = {
|
||||
'NewConfig': '// NewConfig creates a new Z3 configuration object.\n// Use SetParamValue to configure parameters before creating a context.',
|
||||
'NewContext': '// NewContext creates a new Z3 context with default configuration.\n// The context manages memory for all Z3 objects and must outlive any objects it creates.',
|
||||
'NewContextWithConfig': '// NewContextWithConfig creates a new Z3 context with the given configuration.\n// The configuration is consumed and should not be reused.',
|
||||
}
|
||||
|
||||
def add_godoc_comment(content, pattern, comment):
|
||||
"""Add godoc comment before a type or function declaration."""
|
||||
# Check if comment already exists
|
||||
lines = content.split('\n')
|
||||
result = []
|
||||
i = 0
|
||||
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
|
||||
# Check if this line matches our pattern
|
||||
if re.match(pattern, line):
|
||||
# Check if previous line is already a comment
|
||||
if i > 0 and (result[-1].strip().startswith('//') or result[-1].strip().startswith('/*')):
|
||||
# Comment exists, skip
|
||||
result.append(line)
|
||||
else:
|
||||
# Add comment
|
||||
result.append(comment)
|
||||
result.append(line)
|
||||
else:
|
||||
result.append(line)
|
||||
|
||||
i += 1
|
||||
|
||||
return '\n'.join(result)
|
||||
|
||||
def process_file(filepath, type_comments, func_comments):
|
||||
"""Process a single Go file and add godoc comments."""
|
||||
print(f"Processing {filepath}...")
|
||||
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Add type comments
|
||||
for type_name, comment in type_comments.items():
|
||||
pattern = f'^type {type_name} struct'
|
||||
content = add_godoc_comment(content, pattern, comment)
|
||||
|
||||
# Add function comments
|
||||
for func_name, comment in func_comments.items():
|
||||
pattern = f'^func (\\([^)]+\\) )?{func_name}\\('
|
||||
content = add_godoc_comment(content, pattern, comment)
|
||||
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"Updated {filepath}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
go_api_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Process z3.go with core types
|
||||
z3_go = os.path.join(go_api_dir, 'z3.go')
|
||||
if os.path.exists(z3_go):
|
||||
process_file(z3_go, TYPE_COMMENTS, FUNCTION_COMMENTS)
|
||||
|
||||
print("\nGodoc comments added successfully!")
|
||||
print("Run 'go doc' to verify documentation.")
|
||||
126
src/api/go/arith.go
Normal file
126
src/api/go/arith.go
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Arithmetic operations and sorts
|
||||
|
||||
// MkIntSort creates the integer sort.
|
||||
func (c *Context) MkIntSort() *Sort {
|
||||
return newSort(c, C.Z3_mk_int_sort(c.ptr))
|
||||
}
|
||||
|
||||
// MkRealSort creates the real number sort.
|
||||
func (c *Context) MkRealSort() *Sort {
|
||||
return newSort(c, C.Z3_mk_real_sort(c.ptr))
|
||||
}
|
||||
|
||||
// MkInt creates an integer constant from an int.
|
||||
func (c *Context) MkInt(value int, sort *Sort) *Expr {
|
||||
return newExpr(c, C.Z3_mk_int(c.ptr, C.int(value), sort.ptr))
|
||||
}
|
||||
|
||||
// MkInt64 creates an integer constant from an int64.
|
||||
func (c *Context) MkInt64(value int64, sort *Sort) *Expr {
|
||||
return newExpr(c, C.Z3_mk_int64(c.ptr, C.int64_t(value), sort.ptr))
|
||||
}
|
||||
|
||||
// MkReal creates a real constant from numerator and denominator.
|
||||
func (c *Context) MkReal(num, den int) *Expr {
|
||||
return newExpr(c, C.Z3_mk_real(c.ptr, C.int(num), C.int(den)))
|
||||
}
|
||||
|
||||
// MkIntConst creates an integer constant (variable) with the given name.
|
||||
func (c *Context) MkIntConst(name string) *Expr {
|
||||
sym := c.MkStringSymbol(name)
|
||||
return c.MkConst(sym, c.MkIntSort())
|
||||
}
|
||||
|
||||
// MkRealConst creates a real constant (variable) with the given name.
|
||||
func (c *Context) MkRealConst(name string) *Expr {
|
||||
sym := c.MkStringSymbol(name)
|
||||
return c.MkConst(sym, c.MkRealSort())
|
||||
}
|
||||
|
||||
// MkAdd creates an addition.
|
||||
func (c *Context) MkAdd(exprs ...*Expr) *Expr {
|
||||
if len(exprs) == 0 {
|
||||
return c.MkInt(0, c.MkIntSort())
|
||||
}
|
||||
if len(exprs) == 1 {
|
||||
return exprs[0]
|
||||
}
|
||||
cExprs := make([]C.Z3_ast, len(exprs))
|
||||
for i, e := range exprs {
|
||||
cExprs[i] = e.ptr
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_add(c.ptr, C.uint(len(exprs)), &cExprs[0]))
|
||||
}
|
||||
|
||||
// MkSub creates a subtraction.
|
||||
func (c *Context) MkSub(exprs ...*Expr) *Expr {
|
||||
if len(exprs) == 0 {
|
||||
return c.MkInt(0, c.MkIntSort())
|
||||
}
|
||||
if len(exprs) == 1 {
|
||||
return newExpr(c, C.Z3_mk_unary_minus(c.ptr, exprs[0].ptr))
|
||||
}
|
||||
cExprs := make([]C.Z3_ast, len(exprs))
|
||||
for i, e := range exprs {
|
||||
cExprs[i] = e.ptr
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_sub(c.ptr, C.uint(len(exprs)), &cExprs[0]))
|
||||
}
|
||||
|
||||
// MkMul creates a multiplication.
|
||||
func (c *Context) MkMul(exprs ...*Expr) *Expr {
|
||||
if len(exprs) == 0 {
|
||||
return c.MkInt(1, c.MkIntSort())
|
||||
}
|
||||
if len(exprs) == 1 {
|
||||
return exprs[0]
|
||||
}
|
||||
cExprs := make([]C.Z3_ast, len(exprs))
|
||||
for i, e := range exprs {
|
||||
cExprs[i] = e.ptr
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_mul(c.ptr, C.uint(len(exprs)), &cExprs[0]))
|
||||
}
|
||||
|
||||
// MkDiv creates a division.
|
||||
func (c *Context) MkDiv(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_div(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkMod creates a modulo operation.
|
||||
func (c *Context) MkMod(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_mod(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkRem creates a remainder operation.
|
||||
func (c *Context) MkRem(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_rem(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkLt creates a less-than constraint.
|
||||
func (c *Context) MkLt(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_lt(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkLe creates a less-than-or-equal constraint.
|
||||
func (c *Context) MkLe(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_le(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkGt creates a greater-than constraint.
|
||||
func (c *Context) MkGt(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_gt(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkGe creates a greater-than-or-equal constraint.
|
||||
func (c *Context) MkGe(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_ge(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
29
src/api/go/array.go
Normal file
29
src/api/go/array.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Array operations and sorts
|
||||
|
||||
// MkArraySort creates an array sort.
|
||||
func (c *Context) MkArraySort(domain, range_ *Sort) *Sort {
|
||||
return newSort(c, C.Z3_mk_array_sort(c.ptr, domain.ptr, range_.ptr))
|
||||
}
|
||||
|
||||
// MkSelect creates an array read (select) operation.
|
||||
func (c *Context) MkSelect(array, index *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_select(c.ptr, array.ptr, index.ptr))
|
||||
}
|
||||
|
||||
// MkStore creates an array write (store) operation.
|
||||
func (c *Context) MkStore(array, index, value *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_store(c.ptr, array.ptr, index.ptr, value.ptr))
|
||||
}
|
||||
|
||||
// MkConstArray creates a constant array.
|
||||
func (c *Context) MkConstArray(sort *Sort, value *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_const_array(c.ptr, sort.ptr, value.ptr))
|
||||
}
|
||||
160
src/api/go/bitvec.go
Normal file
160
src/api/go/bitvec.go
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Bit-vector operations
|
||||
|
||||
// MkBVConst creates a bit-vector constant with the given name and size.
|
||||
func (c *Context) MkBVConst(name string, size uint) *Expr {
|
||||
sym := c.MkStringSymbol(name)
|
||||
return c.MkConst(sym, c.MkBvSort(size))
|
||||
}
|
||||
|
||||
// MkBV creates a bit-vector numeral from an integer.
|
||||
func (c *Context) MkBV(value int, size uint) *Expr {
|
||||
return newExpr(c, C.Z3_mk_int(c.ptr, C.int(value), c.MkBvSort(size).ptr))
|
||||
}
|
||||
|
||||
// MkBVFromInt64 creates a bit-vector from an int64.
|
||||
func (c *Context) MkBVFromInt64(value int64, size uint) *Expr {
|
||||
return newExpr(c, C.Z3_mk_int64(c.ptr, C.int64_t(value), c.MkBvSort(size).ptr))
|
||||
}
|
||||
|
||||
// MkBVAdd creates a bit-vector addition.
|
||||
func (c *Context) MkBVAdd(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvadd(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVSub creates a bit-vector subtraction.
|
||||
func (c *Context) MkBVSub(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvsub(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVMul creates a bit-vector multiplication.
|
||||
func (c *Context) MkBVMul(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvmul(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVUDiv creates an unsigned bit-vector division.
|
||||
func (c *Context) MkBVUDiv(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvudiv(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVSDiv creates a signed bit-vector division.
|
||||
func (c *Context) MkBVSDiv(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvsdiv(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVURem creates an unsigned bit-vector remainder.
|
||||
func (c *Context) MkBVURem(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvurem(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVSRem creates a signed bit-vector remainder.
|
||||
func (c *Context) MkBVSRem(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvsrem(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVNeg creates a bit-vector negation.
|
||||
func (c *Context) MkBVNeg(expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvneg(c.ptr, expr.ptr))
|
||||
}
|
||||
|
||||
// MkBVAnd creates a bit-vector bitwise AND.
|
||||
func (c *Context) MkBVAnd(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvand(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVOr creates a bit-vector bitwise OR.
|
||||
func (c *Context) MkBVOr(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvor(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVXor creates a bit-vector bitwise XOR.
|
||||
func (c *Context) MkBVXor(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvxor(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVNot creates a bit-vector bitwise NOT.
|
||||
func (c *Context) MkBVNot(expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvnot(c.ptr, expr.ptr))
|
||||
}
|
||||
|
||||
// MkBVShl creates a bit-vector shift left.
|
||||
func (c *Context) MkBVShl(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvshl(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVLShr creates a bit-vector logical shift right.
|
||||
func (c *Context) MkBVLShr(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvlshr(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVAShr creates a bit-vector arithmetic shift right.
|
||||
func (c *Context) MkBVAShr(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvashr(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVULT creates an unsigned bit-vector less-than.
|
||||
func (c *Context) MkBVULT(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvult(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVSLT creates a signed bit-vector less-than.
|
||||
func (c *Context) MkBVSLT(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvslt(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVULE creates an unsigned bit-vector less-than-or-equal.
|
||||
func (c *Context) MkBVULE(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvule(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVSLE creates a signed bit-vector less-than-or-equal.
|
||||
func (c *Context) MkBVSLE(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvsle(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVUGE creates an unsigned bit-vector greater-than-or-equal.
|
||||
func (c *Context) MkBVUGE(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvuge(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVSGE creates a signed bit-vector greater-than-or-equal.
|
||||
func (c *Context) MkBVSGE(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvsge(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVUGT creates an unsigned bit-vector greater-than.
|
||||
func (c *Context) MkBVUGT(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvugt(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkBVSGT creates a signed bit-vector greater-than.
|
||||
func (c *Context) MkBVSGT(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_bvsgt(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkConcat creates a bit-vector concatenation.
|
||||
func (c *Context) MkConcat(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_concat(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkExtract creates a bit-vector extraction.
|
||||
func (c *Context) MkExtract(high, low uint, expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_extract(c.ptr, C.uint(high), C.uint(low), expr.ptr))
|
||||
}
|
||||
|
||||
// MkSignExt creates a bit-vector sign extension.
|
||||
func (c *Context) MkSignExt(i uint, expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_sign_ext(c.ptr, C.uint(i), expr.ptr))
|
||||
}
|
||||
|
||||
// MkZeroExt creates a bit-vector zero extension.
|
||||
func (c *Context) MkZeroExt(i uint, expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_zero_ext(c.ptr, C.uint(i), expr.ptr))
|
||||
}
|
||||
295
src/api/go/datatype.go
Normal file
295
src/api/go/datatype.go
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Constructor represents a datatype constructor.
|
||||
type Constructor struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_constructor
|
||||
}
|
||||
|
||||
// newConstructor creates a new Constructor and manages its reference count.
|
||||
func newConstructor(ctx *Context, ptr C.Z3_constructor) *Constructor {
|
||||
c := &Constructor{ctx: ctx, ptr: ptr}
|
||||
// Note: Z3_constructor doesn't use inc_ref/dec_ref pattern
|
||||
// It uses Z3_del_constructor for cleanup
|
||||
runtime.SetFinalizer(c, func(cons *Constructor) {
|
||||
C.Z3_del_constructor(cons.ctx.ptr, cons.ptr)
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
// MkConstructor creates a constructor for a datatype.
|
||||
// name is the constructor name, recognizer is the recognizer name,
|
||||
// fieldNames are the names of the fields, and fieldSorts are the sorts of the fields.
|
||||
// fieldSortRefs can be 0 for non-recursive fields or the datatype index for recursive fields.
|
||||
func (c *Context) MkConstructor(name, recognizer string, fieldNames []string, fieldSorts []*Sort, fieldSortRefs []uint) *Constructor {
|
||||
cName := C.CString(name)
|
||||
cRecognizer := C.CString(recognizer)
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
defer C.free(unsafe.Pointer(cRecognizer))
|
||||
|
||||
numFields := uint(len(fieldNames))
|
||||
if numFields != uint(len(fieldSorts)) || numFields != uint(len(fieldSortRefs)) {
|
||||
panic("fieldNames, fieldSorts, and fieldSortRefs must have the same length")
|
||||
}
|
||||
|
||||
var cFieldNames *C.Z3_symbol
|
||||
var cSorts *C.Z3_sort
|
||||
var cSortRefs *C.uint
|
||||
|
||||
if numFields > 0 {
|
||||
fieldSyms := make([]C.Z3_symbol, numFields)
|
||||
for i, fname := range fieldNames {
|
||||
fieldSyms[i] = c.MkStringSymbol(fname).ptr
|
||||
}
|
||||
cFieldNames = &fieldSyms[0]
|
||||
|
||||
sorts := make([]C.Z3_sort, numFields)
|
||||
for i, s := range fieldSorts {
|
||||
if s != nil {
|
||||
sorts[i] = s.ptr
|
||||
}
|
||||
}
|
||||
cSorts = &sorts[0]
|
||||
|
||||
refs := make([]C.uint, numFields)
|
||||
for i, r := range fieldSortRefs {
|
||||
refs[i] = C.uint(r)
|
||||
}
|
||||
cSortRefs = &refs[0]
|
||||
}
|
||||
|
||||
sym := c.MkStringSymbol(name)
|
||||
recSym := c.MkStringSymbol(recognizer)
|
||||
|
||||
return newConstructor(c, C.Z3_mk_constructor(
|
||||
c.ptr,
|
||||
sym.ptr,
|
||||
recSym.ptr,
|
||||
C.uint(numFields),
|
||||
cFieldNames,
|
||||
cSorts,
|
||||
cSortRefs,
|
||||
))
|
||||
}
|
||||
|
||||
// ConstructorList represents a list of datatype constructors.
|
||||
type ConstructorList struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_constructor_list
|
||||
}
|
||||
|
||||
// newConstructorList creates a new ConstructorList and manages its reference count.
|
||||
func newConstructorList(ctx *Context, ptr C.Z3_constructor_list) *ConstructorList {
|
||||
cl := &ConstructorList{ctx: ctx, ptr: ptr}
|
||||
// Note: Z3_constructor_list doesn't use inc_ref/dec_ref pattern
|
||||
// It uses Z3_del_constructor_list for cleanup
|
||||
runtime.SetFinalizer(cl, func(list *ConstructorList) {
|
||||
C.Z3_del_constructor_list(list.ctx.ptr, list.ptr)
|
||||
})
|
||||
return cl
|
||||
}
|
||||
|
||||
// MkConstructorList creates a list of constructors for a datatype.
|
||||
func (c *Context) MkConstructorList(constructors []*Constructor) *ConstructorList {
|
||||
numCons := uint(len(constructors))
|
||||
if numCons == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
cons := make([]C.Z3_constructor, numCons)
|
||||
for i, constr := range constructors {
|
||||
cons[i] = constr.ptr
|
||||
}
|
||||
|
||||
return newConstructorList(c, C.Z3_mk_constructor_list(c.ptr, C.uint(numCons), &cons[0]))
|
||||
}
|
||||
|
||||
// MkDatatypeSort creates a datatype sort from a constructor list.
|
||||
func (c *Context) MkDatatypeSort(name string, constructors []*Constructor) *Sort {
|
||||
sym := c.MkStringSymbol(name)
|
||||
|
||||
numCons := uint(len(constructors))
|
||||
cons := make([]C.Z3_constructor, numCons)
|
||||
for i, constr := range constructors {
|
||||
cons[i] = constr.ptr
|
||||
}
|
||||
|
||||
return newSort(c, C.Z3_mk_datatype(c.ptr, sym.ptr, C.uint(numCons), &cons[0]))
|
||||
}
|
||||
|
||||
// MkDatatypeSorts creates multiple mutually recursive datatype sorts.
|
||||
func (c *Context) MkDatatypeSorts(names []string, constructorLists [][]*Constructor) []*Sort {
|
||||
numTypes := uint(len(names))
|
||||
if numTypes != uint(len(constructorLists)) {
|
||||
panic("names and constructorLists must have the same length")
|
||||
}
|
||||
|
||||
syms := make([]C.Z3_symbol, numTypes)
|
||||
for i, name := range names {
|
||||
syms[i] = c.MkStringSymbol(name).ptr
|
||||
}
|
||||
|
||||
cLists := make([]C.Z3_constructor_list, numTypes)
|
||||
for i, constrs := range constructorLists {
|
||||
cons := make([]C.Z3_constructor, len(constrs))
|
||||
for j, constr := range constrs {
|
||||
cons[j] = constr.ptr
|
||||
}
|
||||
cLists[i] = C.Z3_mk_constructor_list(c.ptr, C.uint(len(constrs)), &cons[0])
|
||||
}
|
||||
|
||||
resultSorts := make([]C.Z3_sort, numTypes)
|
||||
|
||||
C.Z3_mk_datatypes(c.ptr, C.uint(numTypes), &syms[0], &resultSorts[0], &cLists[0])
|
||||
|
||||
// Clean up constructor lists
|
||||
for i := range cLists {
|
||||
C.Z3_del_constructor_list(c.ptr, cLists[i])
|
||||
}
|
||||
|
||||
sorts := make([]*Sort, numTypes)
|
||||
for i := range resultSorts {
|
||||
sorts[i] = newSort(c, resultSorts[i])
|
||||
}
|
||||
|
||||
return sorts
|
||||
}
|
||||
|
||||
// GetDatatypeSortConstructor returns the i-th constructor of a datatype sort.
|
||||
func (c *Context) GetDatatypeSortConstructor(sort *Sort, i uint) *FuncDecl {
|
||||
return newFuncDecl(c, C.Z3_get_datatype_sort_constructor(c.ptr, sort.ptr, C.uint(i)))
|
||||
}
|
||||
|
||||
// GetDatatypeSortRecognizer returns the i-th recognizer of a datatype sort.
|
||||
func (c *Context) GetDatatypeSortRecognizer(sort *Sort, i uint) *FuncDecl {
|
||||
return newFuncDecl(c, C.Z3_get_datatype_sort_recognizer(c.ptr, sort.ptr, C.uint(i)))
|
||||
}
|
||||
|
||||
// GetDatatypeSortConstructorAccessor returns the accessor for the i-th field of the j-th constructor.
|
||||
func (c *Context) GetDatatypeSortConstructorAccessor(sort *Sort, constructorIdx, accessorIdx uint) *FuncDecl {
|
||||
return newFuncDecl(c, C.Z3_get_datatype_sort_constructor_accessor(
|
||||
c.ptr, sort.ptr, C.uint(constructorIdx), C.uint(accessorIdx)))
|
||||
}
|
||||
|
||||
// GetDatatypeSortNumConstructors returns the number of constructors in a datatype sort.
|
||||
func (c *Context) GetDatatypeSortNumConstructors(sort *Sort) uint {
|
||||
return uint(C.Z3_get_datatype_sort_num_constructors(c.ptr, sort.ptr))
|
||||
}
|
||||
|
||||
// Tuple sorts (special case of datatypes)
|
||||
|
||||
// MkTupleSort creates a tuple sort with the given field sorts.
|
||||
func (c *Context) MkTupleSort(name string, fieldNames []string, fieldSorts []*Sort) (*Sort, *FuncDecl, []*FuncDecl) {
|
||||
sym := c.MkStringSymbol(name)
|
||||
|
||||
numFields := uint(len(fieldNames))
|
||||
if numFields != uint(len(fieldSorts)) {
|
||||
panic("fieldNames and fieldSorts must have the same length")
|
||||
}
|
||||
|
||||
fieldSyms := make([]C.Z3_symbol, numFields)
|
||||
for i, fname := range fieldNames {
|
||||
fieldSyms[i] = c.MkStringSymbol(fname).ptr
|
||||
}
|
||||
|
||||
sorts := make([]C.Z3_sort, numFields)
|
||||
for i, s := range fieldSorts {
|
||||
sorts[i] = s.ptr
|
||||
}
|
||||
|
||||
var mkTupleDecl C.Z3_func_decl
|
||||
projDecls := make([]C.Z3_func_decl, numFields)
|
||||
|
||||
tupleSort := C.Z3_mk_tuple_sort(
|
||||
c.ptr,
|
||||
sym.ptr,
|
||||
C.uint(numFields),
|
||||
&fieldSyms[0],
|
||||
&sorts[0],
|
||||
&mkTupleDecl,
|
||||
&projDecls[0],
|
||||
)
|
||||
|
||||
projections := make([]*FuncDecl, numFields)
|
||||
for i := range projDecls {
|
||||
projections[i] = newFuncDecl(c, projDecls[i])
|
||||
}
|
||||
|
||||
return newSort(c, tupleSort), newFuncDecl(c, mkTupleDecl), projections
|
||||
}
|
||||
|
||||
// Enumeration sorts (special case of datatypes)
|
||||
|
||||
// MkEnumSort creates an enumeration sort with the given constants.
|
||||
func (c *Context) MkEnumSort(name string, enumNames []string) (*Sort, []*FuncDecl, []*FuncDecl) {
|
||||
sym := c.MkStringSymbol(name)
|
||||
|
||||
numEnums := uint(len(enumNames))
|
||||
enumSyms := make([]C.Z3_symbol, numEnums)
|
||||
for i, ename := range enumNames {
|
||||
enumSyms[i] = c.MkStringSymbol(ename).ptr
|
||||
}
|
||||
|
||||
enumConsts := make([]C.Z3_func_decl, numEnums)
|
||||
enumTesters := make([]C.Z3_func_decl, numEnums)
|
||||
|
||||
enumSort := C.Z3_mk_enumeration_sort(
|
||||
c.ptr,
|
||||
sym.ptr,
|
||||
C.uint(numEnums),
|
||||
&enumSyms[0],
|
||||
&enumConsts[0],
|
||||
&enumTesters[0],
|
||||
)
|
||||
|
||||
consts := make([]*FuncDecl, numEnums)
|
||||
for i := range enumConsts {
|
||||
consts[i] = newFuncDecl(c, enumConsts[i])
|
||||
}
|
||||
|
||||
testers := make([]*FuncDecl, numEnums)
|
||||
for i := range enumTesters {
|
||||
testers[i] = newFuncDecl(c, enumTesters[i])
|
||||
}
|
||||
|
||||
return newSort(c, enumSort), consts, testers
|
||||
}
|
||||
|
||||
// List sorts (special case of datatypes)
|
||||
|
||||
// MkListSort creates a list sort with the given element sort.
|
||||
func (c *Context) MkListSort(name string, elemSort *Sort) (*Sort, *FuncDecl, *FuncDecl, *FuncDecl, *FuncDecl, *FuncDecl, *FuncDecl) {
|
||||
sym := c.MkStringSymbol(name)
|
||||
|
||||
var nilDecl, consDecl, isNilDecl, isConsDecl, headDecl, tailDecl C.Z3_func_decl
|
||||
|
||||
listSort := C.Z3_mk_list_sort(
|
||||
c.ptr,
|
||||
sym.ptr,
|
||||
elemSort.ptr,
|
||||
&nilDecl,
|
||||
&isNilDecl,
|
||||
&consDecl,
|
||||
&isConsDecl,
|
||||
&headDecl,
|
||||
&tailDecl,
|
||||
)
|
||||
|
||||
return newSort(c, listSort),
|
||||
newFuncDecl(c, nilDecl),
|
||||
newFuncDecl(c, consDecl),
|
||||
newFuncDecl(c, isNilDecl),
|
||||
newFuncDecl(c, isConsDecl),
|
||||
newFuncDecl(c, headDecl),
|
||||
newFuncDecl(c, tailDecl)
|
||||
}
|
||||
272
src/api/go/fixedpoint.go
Normal file
272
src/api/go/fixedpoint.go
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
// Copyright (c) Microsoft Corporation 2025
|
||||
// Z3 Go API: Fixedpoint solver for Datalog and CHC (Constrained Horn Clauses)
|
||||
|
||||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Fixedpoint represents a fixedpoint solver for Datalog/CHC queries
|
||||
type Fixedpoint struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_fixedpoint
|
||||
}
|
||||
|
||||
// newFixedpoint creates a new Fixedpoint solver with proper memory management
|
||||
func newFixedpoint(ctx *Context, ptr C.Z3_fixedpoint) *Fixedpoint {
|
||||
fp := &Fixedpoint{ctx: ctx, ptr: ptr}
|
||||
C.Z3_fixedpoint_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(fp, func(f *Fixedpoint) {
|
||||
C.Z3_fixedpoint_dec_ref(f.ctx.ptr, f.ptr)
|
||||
})
|
||||
return fp
|
||||
}
|
||||
|
||||
// NewFixedpoint creates a new fixedpoint solver
|
||||
func (ctx *Context) NewFixedpoint() *Fixedpoint {
|
||||
ptr := C.Z3_mk_fixedpoint(ctx.ptr)
|
||||
return newFixedpoint(ctx, ptr)
|
||||
}
|
||||
|
||||
// GetHelp returns a string describing all available fixedpoint solver parameters
|
||||
func (f *Fixedpoint) GetHelp() string {
|
||||
cstr := C.Z3_fixedpoint_get_help(f.ctx.ptr, f.ptr)
|
||||
return C.GoString(cstr)
|
||||
}
|
||||
|
||||
// SetParams sets the fixedpoint solver parameters
|
||||
func (f *Fixedpoint) SetParams(params *Params) {
|
||||
C.Z3_fixedpoint_set_params(f.ctx.ptr, f.ptr, params.ptr)
|
||||
}
|
||||
|
||||
// GetParamDescrs retrieves parameter descriptions for the fixedpoint solver
|
||||
func (f *Fixedpoint) GetParamDescrs() *ParamDescrs {
|
||||
ptr := C.Z3_fixedpoint_get_param_descrs(f.ctx.ptr, f.ptr)
|
||||
return newParamDescrs(f.ctx, ptr)
|
||||
}
|
||||
|
||||
// Assert adds a constraint into the fixedpoint solver
|
||||
func (f *Fixedpoint) Assert(constraint *Expr) {
|
||||
C.Z3_fixedpoint_assert(f.ctx.ptr, f.ptr, constraint.ptr)
|
||||
}
|
||||
|
||||
// RegisterRelation registers a predicate as a recursive relation
|
||||
func (f *Fixedpoint) RegisterRelation(funcDecl *FuncDecl) {
|
||||
C.Z3_fixedpoint_register_relation(f.ctx.ptr, f.ptr, funcDecl.ptr)
|
||||
}
|
||||
|
||||
// AddRule adds a rule (Horn clause) to the fixedpoint solver
|
||||
// The rule should be an implication of the form body => head
|
||||
// where head is a relation and body is a conjunction of relations
|
||||
func (f *Fixedpoint) AddRule(rule *Expr, name *Symbol) {
|
||||
var namePtr C.Z3_symbol
|
||||
if name != nil {
|
||||
namePtr = name.ptr
|
||||
} else {
|
||||
namePtr = nil
|
||||
}
|
||||
C.Z3_fixedpoint_add_rule(f.ctx.ptr, f.ptr, rule.ptr, namePtr)
|
||||
}
|
||||
|
||||
// AddFact adds a table fact to the fixedpoint solver
|
||||
func (f *Fixedpoint) AddFact(pred *FuncDecl, args []int) {
|
||||
if len(args) == 0 {
|
||||
C.Z3_fixedpoint_add_fact(f.ctx.ptr, f.ptr, pred.ptr, 0, nil)
|
||||
return
|
||||
}
|
||||
|
||||
cArgs := make([]C.uint, len(args))
|
||||
for i, arg := range args {
|
||||
cArgs[i] = C.uint(arg)
|
||||
}
|
||||
C.Z3_fixedpoint_add_fact(f.ctx.ptr, f.ptr, pred.ptr, C.uint(len(args)), &cArgs[0])
|
||||
}
|
||||
|
||||
// Query queries the fixedpoint solver with a constraint
|
||||
// Returns Satisfiable if there is a derivation, Unsatisfiable if not
|
||||
func (f *Fixedpoint) Query(query *Expr) Status {
|
||||
result := C.Z3_fixedpoint_query(f.ctx.ptr, f.ptr, query.ptr)
|
||||
switch result {
|
||||
case C.Z3_L_TRUE:
|
||||
return Satisfiable
|
||||
case C.Z3_L_FALSE:
|
||||
return Unsatisfiable
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
// QueryRelations queries the fixedpoint solver with an array of relations
|
||||
// Returns Satisfiable if any relation is non-empty, Unsatisfiable otherwise
|
||||
func (f *Fixedpoint) QueryRelations(relations []*FuncDecl) Status {
|
||||
if len(relations) == 0 {
|
||||
return Unknown
|
||||
}
|
||||
|
||||
cRelations := make([]C.Z3_func_decl, len(relations))
|
||||
for i, rel := range relations {
|
||||
cRelations[i] = rel.ptr
|
||||
}
|
||||
|
||||
result := C.Z3_fixedpoint_query_relations(f.ctx.ptr, f.ptr, C.uint(len(relations)), &cRelations[0])
|
||||
switch result {
|
||||
case C.Z3_L_TRUE:
|
||||
return Satisfiable
|
||||
case C.Z3_L_FALSE:
|
||||
return Unsatisfiable
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateRule updates a named rule in the fixedpoint solver
|
||||
func (f *Fixedpoint) UpdateRule(rule *Expr, name *Symbol) {
|
||||
var namePtr C.Z3_symbol
|
||||
if name != nil {
|
||||
namePtr = name.ptr
|
||||
} else {
|
||||
namePtr = nil
|
||||
}
|
||||
C.Z3_fixedpoint_update_rule(f.ctx.ptr, f.ptr, rule.ptr, namePtr)
|
||||
}
|
||||
|
||||
// GetAnswer retrieves the satisfying instance or instances of solver,
|
||||
// or definitions for the recursive predicates that show unsatisfiability
|
||||
func (f *Fixedpoint) GetAnswer() *Expr {
|
||||
ptr := C.Z3_fixedpoint_get_answer(f.ctx.ptr, f.ptr)
|
||||
if ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return newExpr(f.ctx, ptr)
|
||||
}
|
||||
|
||||
// GetReasonUnknown retrieves explanation why fixedpoint engine returned status Unknown
|
||||
func (f *Fixedpoint) GetReasonUnknown() string {
|
||||
cstr := C.Z3_fixedpoint_get_reason_unknown(f.ctx.ptr, f.ptr)
|
||||
return C.GoString(cstr)
|
||||
}
|
||||
|
||||
// GetNumLevels retrieves the number of levels explored for a given predicate
|
||||
func (f *Fixedpoint) GetNumLevels(predicate *FuncDecl) int {
|
||||
return int(C.Z3_fixedpoint_get_num_levels(f.ctx.ptr, f.ptr, predicate.ptr))
|
||||
}
|
||||
|
||||
// GetCoverDelta retrieves the cover delta for a given predicate and level
|
||||
func (f *Fixedpoint) GetCoverDelta(level int, predicate *FuncDecl) *Expr {
|
||||
ptr := C.Z3_fixedpoint_get_cover_delta(f.ctx.ptr, f.ptr, C.int(level), predicate.ptr)
|
||||
if ptr == nil {
|
||||
return nil
|
||||
}
|
||||
return newExpr(f.ctx, ptr)
|
||||
}
|
||||
|
||||
// AddCover adds a cover constraint to a predicate at a given level
|
||||
func (f *Fixedpoint) AddCover(level int, predicate *FuncDecl, property *Expr) {
|
||||
C.Z3_fixedpoint_add_cover(f.ctx.ptr, f.ptr, C.int(level), predicate.ptr, property.ptr)
|
||||
}
|
||||
|
||||
// String returns the string representation of the fixedpoint solver
|
||||
func (f *Fixedpoint) String() string {
|
||||
cstr := C.Z3_fixedpoint_to_string(f.ctx.ptr, f.ptr, 0, nil)
|
||||
return C.GoString(cstr)
|
||||
}
|
||||
|
||||
// GetStatistics retrieves statistics for the fixedpoint solver
|
||||
func (f *Fixedpoint) GetStatistics() *Statistics {
|
||||
ptr := C.Z3_fixedpoint_get_statistics(f.ctx.ptr, f.ptr)
|
||||
return newStatistics(f.ctx, ptr)
|
||||
}
|
||||
|
||||
// GetRules retrieves the current rules as a string
|
||||
func (f *Fixedpoint) GetRules() string {
|
||||
return f.String()
|
||||
}
|
||||
|
||||
// GetAssertions retrieves the fixedpoint assertions as an AST vector
|
||||
func (f *Fixedpoint) GetAssertions() *ASTVector {
|
||||
ptr := C.Z3_fixedpoint_get_assertions(f.ctx.ptr, f.ptr)
|
||||
return newASTVector(f.ctx, ptr)
|
||||
}
|
||||
|
||||
// SetPredicateRepresentation sets the predicate representation for a given relation
|
||||
func (f *Fixedpoint) SetPredicateRepresentation(funcDecl *FuncDecl, kinds []C.Z3_symbol) {
|
||||
if len(kinds) == 0 {
|
||||
C.Z3_fixedpoint_set_predicate_representation(f.ctx.ptr, f.ptr, funcDecl.ptr, 0, nil)
|
||||
return
|
||||
}
|
||||
C.Z3_fixedpoint_set_predicate_representation(f.ctx.ptr, f.ptr, funcDecl.ptr, C.uint(len(kinds)), &kinds[0])
|
||||
}
|
||||
|
||||
// FromString parses a Datalog program from a string
|
||||
func (f *Fixedpoint) FromString(s string) {
|
||||
cstr := C.CString(s)
|
||||
defer C.free(unsafe.Pointer(cstr))
|
||||
C.Z3_fixedpoint_from_string(f.ctx.ptr, f.ptr, cstr)
|
||||
}
|
||||
|
||||
// FromFile parses a Datalog program from a file
|
||||
func (f *Fixedpoint) FromFile(filename string) {
|
||||
cstr := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cstr))
|
||||
C.Z3_fixedpoint_from_file(f.ctx.ptr, f.ptr, cstr)
|
||||
}
|
||||
|
||||
// Statistics represents statistics for Z3 solvers
|
||||
type Statistics struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_stats
|
||||
}
|
||||
|
||||
// newStatistics creates a new Statistics object with proper memory management
|
||||
func newStatistics(ctx *Context, ptr C.Z3_stats) *Statistics {
|
||||
stats := &Statistics{ctx: ctx, ptr: ptr}
|
||||
C.Z3_stats_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(stats, func(s *Statistics) {
|
||||
C.Z3_stats_dec_ref(s.ctx.ptr, s.ptr)
|
||||
})
|
||||
return stats
|
||||
}
|
||||
|
||||
// String returns the string representation of statistics
|
||||
func (s *Statistics) String() string {
|
||||
cstr := C.Z3_stats_to_string(s.ctx.ptr, s.ptr)
|
||||
return C.GoString(cstr)
|
||||
}
|
||||
|
||||
// Size returns the number of statistical data entries
|
||||
func (s *Statistics) Size() int {
|
||||
return int(C.Z3_stats_size(s.ctx.ptr, s.ptr))
|
||||
}
|
||||
|
||||
// GetKey returns the key (name) of a statistical data entry at the given index
|
||||
func (s *Statistics) GetKey(idx int) string {
|
||||
cstr := C.Z3_stats_get_key(s.ctx.ptr, s.ptr, C.uint(idx))
|
||||
return C.GoString(cstr)
|
||||
}
|
||||
|
||||
// IsUint returns true if the statistical data at the given index is unsigned integer
|
||||
func (s *Statistics) IsUint(idx int) bool {
|
||||
return bool(C.Z3_stats_is_uint(s.ctx.ptr, s.ptr, C.uint(idx)))
|
||||
}
|
||||
|
||||
// IsDouble returns true if the statistical data at the given index is double
|
||||
func (s *Statistics) IsDouble(idx int) bool {
|
||||
return bool(C.Z3_stats_is_double(s.ctx.ptr, s.ptr, C.uint(idx)))
|
||||
}
|
||||
|
||||
// GetUintValue returns the unsigned integer value at the given index
|
||||
func (s *Statistics) GetUintValue(idx int) uint64 {
|
||||
return uint64(C.Z3_stats_get_uint_value(s.ctx.ptr, s.ptr, C.uint(idx)))
|
||||
}
|
||||
|
||||
// GetDoubleValue returns the double value at the given index
|
||||
func (s *Statistics) GetDoubleValue(idx int) float64 {
|
||||
return float64(C.Z3_stats_get_double_value(s.ctx.ptr, s.ptr, C.uint(idx)))
|
||||
}
|
||||
139
src/api/go/fp.go
Normal file
139
src/api/go/fp.go
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Floating-point operations
|
||||
|
||||
// MkFPSort creates a floating-point sort.
|
||||
func (c *Context) MkFPSort(ebits, sbits uint) *Sort {
|
||||
return newSort(c, C.Z3_mk_fpa_sort(c.ptr, C.uint(ebits), C.uint(sbits)))
|
||||
}
|
||||
|
||||
// MkFPSort16 creates a 16-bit floating-point sort.
|
||||
func (c *Context) MkFPSort16() *Sort {
|
||||
return newSort(c, C.Z3_mk_fpa_sort_16(c.ptr))
|
||||
}
|
||||
|
||||
// MkFPSort32 creates a 32-bit floating-point sort (single precision).
|
||||
func (c *Context) MkFPSort32() *Sort {
|
||||
return newSort(c, C.Z3_mk_fpa_sort_32(c.ptr))
|
||||
}
|
||||
|
||||
// MkFPSort64 creates a 64-bit floating-point sort (double precision).
|
||||
func (c *Context) MkFPSort64() *Sort {
|
||||
return newSort(c, C.Z3_mk_fpa_sort_64(c.ptr))
|
||||
}
|
||||
|
||||
// MkFPSort128 creates a 128-bit floating-point sort (quadruple precision).
|
||||
func (c *Context) MkFPSort128() *Sort {
|
||||
return newSort(c, C.Z3_mk_fpa_sort_128(c.ptr))
|
||||
}
|
||||
|
||||
// MkFPRoundingModeSort creates the rounding mode sort.
|
||||
func (c *Context) MkFPRoundingModeSort() *Sort {
|
||||
return newSort(c, C.Z3_mk_fpa_rounding_mode_sort(c.ptr))
|
||||
}
|
||||
|
||||
// MkFPNumeral creates a floating-point numeral from a string.
|
||||
func (c *Context) MkFPNumeral(value string, sort *Sort) *Expr {
|
||||
cStr := C.CString(value)
|
||||
defer C.free(unsafe.Pointer(cStr))
|
||||
return newExpr(c, C.Z3_mk_numeral(c.ptr, cStr, sort.ptr))
|
||||
}
|
||||
|
||||
// MkFPInf creates a floating-point infinity.
|
||||
func (c *Context) MkFPInf(sort *Sort, negative bool) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_inf(c.ptr, sort.ptr, C.bool(negative)))
|
||||
}
|
||||
|
||||
// MkFPNaN creates a floating-point NaN.
|
||||
func (c *Context) MkFPNaN(sort *Sort) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_nan(c.ptr, sort.ptr))
|
||||
}
|
||||
|
||||
// MkFPZero creates a floating-point zero.
|
||||
func (c *Context) MkFPZero(sort *Sort, negative bool) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_zero(c.ptr, sort.ptr, C.bool(negative)))
|
||||
}
|
||||
|
||||
// MkFPAdd creates a floating-point addition.
|
||||
func (c *Context) MkFPAdd(rm, lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_add(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkFPSub creates a floating-point subtraction.
|
||||
func (c *Context) MkFPSub(rm, lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_sub(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkFPMul creates a floating-point multiplication.
|
||||
func (c *Context) MkFPMul(rm, lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_mul(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkFPDiv creates a floating-point division.
|
||||
func (c *Context) MkFPDiv(rm, lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_div(c.ptr, rm.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkFPNeg creates a floating-point negation.
|
||||
func (c *Context) MkFPNeg(expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_neg(c.ptr, expr.ptr))
|
||||
}
|
||||
|
||||
// MkFPAbs creates a floating-point absolute value.
|
||||
func (c *Context) MkFPAbs(expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_abs(c.ptr, expr.ptr))
|
||||
}
|
||||
|
||||
// MkFPSqrt creates a floating-point square root.
|
||||
func (c *Context) MkFPSqrt(rm, expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_sqrt(c.ptr, rm.ptr, expr.ptr))
|
||||
}
|
||||
|
||||
// MkFPLT creates a floating-point less-than.
|
||||
func (c *Context) MkFPLT(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_lt(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkFPGT creates a floating-point greater-than.
|
||||
func (c *Context) MkFPGT(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_gt(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkFPLE creates a floating-point less-than-or-equal.
|
||||
func (c *Context) MkFPLE(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_leq(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkFPGE creates a floating-point greater-than-or-equal.
|
||||
func (c *Context) MkFPGE(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_geq(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkFPEq creates a floating-point equality.
|
||||
func (c *Context) MkFPEq(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_eq(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkFPIsNaN creates a predicate checking if a floating-point number is NaN.
|
||||
func (c *Context) MkFPIsNaN(expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_is_nan(c.ptr, expr.ptr))
|
||||
}
|
||||
|
||||
// MkFPIsInf creates a predicate checking if a floating-point number is infinite.
|
||||
func (c *Context) MkFPIsInf(expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_is_infinite(c.ptr, expr.ptr))
|
||||
}
|
||||
|
||||
// MkFPIsZero creates a predicate checking if a floating-point number is zero.
|
||||
func (c *Context) MkFPIsZero(expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_fpa_is_zero(c.ptr, expr.ptr))
|
||||
}
|
||||
6
src/api/go/go.mod
Normal file
6
src/api/go/go.mod
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
module github.com/Z3Prover/z3/src/api/go
|
||||
|
||||
go 1.20
|
||||
|
||||
// This package provides Go bindings for the Z3 theorem prover.
|
||||
// It uses CGO to wrap the Z3 C API.
|
||||
67
src/api/go/log.go
Normal file
67
src/api/go/log.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) Microsoft Corporation 2025
|
||||
// Z3 Go API: Logging functionality
|
||||
|
||||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
logMutex sync.Mutex
|
||||
isLogOpen bool
|
||||
)
|
||||
|
||||
// OpenLog opens an interaction log file
|
||||
// Returns true if successful, false otherwise
|
||||
func OpenLog(filename string) bool {
|
||||
logMutex.Lock()
|
||||
defer logMutex.Unlock()
|
||||
|
||||
cFilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cFilename))
|
||||
|
||||
result := C.Z3_open_log(cFilename)
|
||||
if bool(result) {
|
||||
isLogOpen = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CloseLog closes the interaction log
|
||||
func CloseLog() {
|
||||
logMutex.Lock()
|
||||
defer logMutex.Unlock()
|
||||
|
||||
C.Z3_close_log()
|
||||
isLogOpen = false
|
||||
}
|
||||
|
||||
// AppendLog appends a user-provided string to the interaction log
|
||||
// Panics if the log is not open
|
||||
func AppendLog(s string) {
|
||||
logMutex.Lock()
|
||||
defer logMutex.Unlock()
|
||||
|
||||
if !isLogOpen {
|
||||
panic("Log is not open")
|
||||
}
|
||||
|
||||
cStr := C.CString(s)
|
||||
defer C.free(unsafe.Pointer(cStr))
|
||||
C.Z3_append_log(cStr)
|
||||
}
|
||||
|
||||
// IsLogOpen returns true if the interaction log is open
|
||||
func IsLogOpen() bool {
|
||||
logMutex.Lock()
|
||||
defer logMutex.Unlock()
|
||||
return isLogOpen
|
||||
}
|
||||
195
src/api/go/optimize.go
Normal file
195
src/api/go/optimize.go
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Optimize represents a Z3 optimization context for solving optimization problems.
|
||||
// Unlike Solver which only checks satisfiability, Optimize can find optimal solutions
|
||||
// with respect to objective functions.
|
||||
type Optimize struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_optimize
|
||||
}
|
||||
|
||||
// NewOptimize creates a new optimization context.
|
||||
func (c *Context) NewOptimize() *Optimize {
|
||||
opt := &Optimize{
|
||||
ctx: c,
|
||||
ptr: C.Z3_mk_optimize(c.ptr),
|
||||
}
|
||||
C.Z3_optimize_inc_ref(c.ptr, opt.ptr)
|
||||
runtime.SetFinalizer(opt, func(o *Optimize) {
|
||||
C.Z3_optimize_dec_ref(o.ctx.ptr, o.ptr)
|
||||
})
|
||||
return opt
|
||||
}
|
||||
|
||||
// String returns the string representation of the optimize context.
|
||||
func (o *Optimize) String() string {
|
||||
return C.GoString(C.Z3_optimize_to_string(o.ctx.ptr, o.ptr))
|
||||
}
|
||||
|
||||
// Assert adds a constraint to the optimizer.
|
||||
func (o *Optimize) Assert(constraint *Expr) {
|
||||
C.Z3_optimize_assert(o.ctx.ptr, o.ptr, constraint.ptr)
|
||||
}
|
||||
|
||||
// AssertAndTrack adds a constraint with a tracking literal for unsat core extraction.
|
||||
func (o *Optimize) AssertAndTrack(constraint, track *Expr) {
|
||||
C.Z3_optimize_assert_and_track(o.ctx.ptr, o.ptr, constraint.ptr, track.ptr)
|
||||
}
|
||||
|
||||
// AssertSoft adds a soft constraint with a weight.
|
||||
// Soft constraints are used for MaxSMT problems.
|
||||
// Returns a handle to the objective.
|
||||
func (o *Optimize) AssertSoft(constraint *Expr, weight string, group string) uint {
|
||||
cWeight := C.CString(weight)
|
||||
cGroup := C.CString(group)
|
||||
defer C.free(unsafe.Pointer(cWeight))
|
||||
defer C.free(unsafe.Pointer(cGroup))
|
||||
|
||||
sym := o.ctx.MkStringSymbol(group)
|
||||
return uint(C.Z3_optimize_assert_soft(o.ctx.ptr, o.ptr, constraint.ptr, cWeight, sym.ptr))
|
||||
}
|
||||
|
||||
// Maximize adds a maximization objective.
|
||||
// Returns a handle to the objective that can be used to retrieve bounds.
|
||||
func (o *Optimize) Maximize(expr *Expr) uint {
|
||||
return uint(C.Z3_optimize_maximize(o.ctx.ptr, o.ptr, expr.ptr))
|
||||
}
|
||||
|
||||
// Minimize adds a minimization objective.
|
||||
// Returns a handle to the objective that can be used to retrieve bounds.
|
||||
func (o *Optimize) Minimize(expr *Expr) uint {
|
||||
return uint(C.Z3_optimize_minimize(o.ctx.ptr, o.ptr, expr.ptr))
|
||||
}
|
||||
|
||||
// Check checks the satisfiability of the constraints and optimizes objectives.
|
||||
func (o *Optimize) Check(assumptions ...*Expr) Status {
|
||||
var result C.Z3_lbool
|
||||
if len(assumptions) == 0 {
|
||||
result = C.Z3_optimize_check(o.ctx.ptr, o.ptr, 0, nil)
|
||||
} else {
|
||||
cAssumptions := make([]C.Z3_ast, len(assumptions))
|
||||
for i, a := range assumptions {
|
||||
cAssumptions[i] = a.ptr
|
||||
}
|
||||
result = C.Z3_optimize_check(o.ctx.ptr, o.ptr, C.uint(len(assumptions)), &cAssumptions[0])
|
||||
}
|
||||
return Status(result)
|
||||
}
|
||||
|
||||
// Model returns the model if the constraints are satisfiable.
|
||||
func (o *Optimize) Model() *Model {
|
||||
modelPtr := C.Z3_optimize_get_model(o.ctx.ptr, o.ptr)
|
||||
if modelPtr == nil {
|
||||
return nil
|
||||
}
|
||||
return newModel(o.ctx, modelPtr)
|
||||
}
|
||||
|
||||
// Push creates a backtracking point.
|
||||
func (o *Optimize) Push() {
|
||||
C.Z3_optimize_push(o.ctx.ptr, o.ptr)
|
||||
}
|
||||
|
||||
// Pop removes a backtracking point.
|
||||
func (o *Optimize) Pop() {
|
||||
C.Z3_optimize_pop(o.ctx.ptr, o.ptr)
|
||||
}
|
||||
|
||||
// GetLower retrieves a lower bound for the objective at the given index.
|
||||
func (o *Optimize) GetLower(index uint) *Expr {
|
||||
result := C.Z3_optimize_get_lower(o.ctx.ptr, o.ptr, C.uint(index))
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
return newExpr(o.ctx, result)
|
||||
}
|
||||
|
||||
// GetUpper retrieves an upper bound for the objective at the given index.
|
||||
func (o *Optimize) GetUpper(index uint) *Expr {
|
||||
result := C.Z3_optimize_get_upper(o.ctx.ptr, o.ptr, C.uint(index))
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
return newExpr(o.ctx, result)
|
||||
}
|
||||
|
||||
// GetLowerAsVector retrieves a lower bound as a vector (inf, value, eps).
|
||||
// The objective value is unbounded if inf is non-zero,
|
||||
// otherwise it's represented as value + eps * EPSILON.
|
||||
func (o *Optimize) GetLowerAsVector(index uint) []*Expr {
|
||||
vec := C.Z3_optimize_get_lower_as_vector(o.ctx.ptr, o.ptr, C.uint(index))
|
||||
result := astVectorToExprs(o.ctx, vec)
|
||||
if len(result) != 3 {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetUpperAsVector retrieves an upper bound as a vector (inf, value, eps).
|
||||
// The objective value is unbounded if inf is non-zero,
|
||||
// otherwise it's represented as value + eps * EPSILON.
|
||||
func (o *Optimize) GetUpperAsVector(index uint) []*Expr {
|
||||
vec := C.Z3_optimize_get_upper_as_vector(o.ctx.ptr, o.ptr, C.uint(index))
|
||||
result := astVectorToExprs(o.ctx, vec)
|
||||
if len(result) != 3 {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ReasonUnknown returns the reason why the result is unknown.
|
||||
func (o *Optimize) ReasonUnknown() string {
|
||||
return C.GoString(C.Z3_optimize_get_reason_unknown(o.ctx.ptr, o.ptr))
|
||||
}
|
||||
|
||||
// GetHelp returns help information for the optimizer.
|
||||
func (o *Optimize) GetHelp() string {
|
||||
return C.GoString(C.Z3_optimize_get_help(o.ctx.ptr, o.ptr))
|
||||
}
|
||||
|
||||
// SetParams sets parameters for the optimizer.
|
||||
func (o *Optimize) SetParams(params *Params) {
|
||||
C.Z3_optimize_set_params(o.ctx.ptr, o.ptr, params.ptr)
|
||||
}
|
||||
|
||||
// Assertions returns the assertions in the optimizer.
|
||||
func (o *Optimize) Assertions() []*Expr {
|
||||
vec := C.Z3_optimize_get_assertions(o.ctx.ptr, o.ptr)
|
||||
return astVectorToExprs(o.ctx, vec)
|
||||
}
|
||||
|
||||
// Objectives returns the objectives in the optimizer.
|
||||
func (o *Optimize) Objectives() []*Expr {
|
||||
vec := C.Z3_optimize_get_objectives(o.ctx.ptr, o.ptr)
|
||||
return astVectorToExprs(o.ctx, vec)
|
||||
}
|
||||
|
||||
// UnsatCore returns the unsat core if the constraints are unsatisfiable.
|
||||
func (o *Optimize) UnsatCore() []*Expr {
|
||||
vec := C.Z3_optimize_get_unsat_core(o.ctx.ptr, o.ptr)
|
||||
return astVectorToExprs(o.ctx, vec)
|
||||
}
|
||||
|
||||
// FromFile parses an SMT-LIB2 file with optimization objectives and constraints.
|
||||
func (o *Optimize) FromFile(filename string) {
|
||||
cFilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cFilename))
|
||||
C.Z3_optimize_from_file(o.ctx.ptr, o.ptr, cFilename)
|
||||
}
|
||||
|
||||
// FromString parses an SMT-LIB2 string with optimization objectives and constraints.
|
||||
func (o *Optimize) FromString(s string) {
|
||||
cStr := C.CString(s)
|
||||
defer C.free(unsafe.Pointer(cStr))
|
||||
C.Z3_optimize_from_string(o.ctx.ptr, o.ptr, cStr)
|
||||
}
|
||||
232
src/api/go/seq.go
Normal file
232
src/api/go/seq.go
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Sequence and string operations
|
||||
|
||||
// MkSeqSort creates a sequence sort.
|
||||
func (c *Context) MkSeqSort(elemSort *Sort) *Sort {
|
||||
return newSort(c, C.Z3_mk_seq_sort(c.ptr, elemSort.ptr))
|
||||
}
|
||||
|
||||
// MkStringSort creates a string sort (sequence of characters).
|
||||
func (c *Context) MkStringSort() *Sort {
|
||||
return newSort(c, C.Z3_mk_string_sort(c.ptr))
|
||||
}
|
||||
|
||||
// MkString creates a string constant.
|
||||
func (c *Context) MkString(value string) *Expr {
|
||||
cStr := C.CString(value)
|
||||
defer C.free(unsafe.Pointer(cStr))
|
||||
return newExpr(c, C.Z3_mk_string(c.ptr, cStr))
|
||||
}
|
||||
|
||||
// MkEmptySeq creates an empty sequence.
|
||||
func (c *Context) MkEmptySeq(sort *Sort) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_empty(c.ptr, sort.ptr))
|
||||
}
|
||||
|
||||
// MkSeqUnit creates a unit sequence containing a single element.
|
||||
func (c *Context) MkSeqUnit(elem *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_unit(c.ptr, elem.ptr))
|
||||
}
|
||||
|
||||
// MkSeqConcat creates a sequence concatenation.
|
||||
func (c *Context) MkSeqConcat(exprs ...*Expr) *Expr {
|
||||
if len(exprs) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(exprs) == 1 {
|
||||
return exprs[0]
|
||||
}
|
||||
cExprs := make([]C.Z3_ast, len(exprs))
|
||||
for i, e := range exprs {
|
||||
cExprs[i] = e.ptr
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_seq_concat(c.ptr, C.uint(len(exprs)), &cExprs[0]))
|
||||
}
|
||||
|
||||
// MkSeqLength creates a sequence length operation.
|
||||
func (c *Context) MkSeqLength(seq *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_length(c.ptr, seq.ptr))
|
||||
}
|
||||
|
||||
// MkSeqPrefix creates a sequence prefix predicate.
|
||||
func (c *Context) MkSeqPrefix(prefix, seq *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_prefix(c.ptr, prefix.ptr, seq.ptr))
|
||||
}
|
||||
|
||||
// MkSeqSuffix creates a sequence suffix predicate.
|
||||
func (c *Context) MkSeqSuffix(suffix, seq *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_suffix(c.ptr, suffix.ptr, seq.ptr))
|
||||
}
|
||||
|
||||
// MkSeqContains creates a sequence contains predicate.
|
||||
func (c *Context) MkSeqContains(seq, substr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_contains(c.ptr, seq.ptr, substr.ptr))
|
||||
}
|
||||
|
||||
// MkSeqAt creates a sequence element access operation.
|
||||
func (c *Context) MkSeqAt(seq, index *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_at(c.ptr, seq.ptr, index.ptr))
|
||||
}
|
||||
|
||||
// MkSeqExtract creates a sequence extract (substring) operation.
|
||||
func (c *Context) MkSeqExtract(seq, offset, length *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_extract(c.ptr, seq.ptr, offset.ptr, length.ptr))
|
||||
}
|
||||
|
||||
// MkSeqReplace creates a sequence replace operation.
|
||||
func (c *Context) MkSeqReplace(seq, src, dst *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_replace(c.ptr, seq.ptr, src.ptr, dst.ptr))
|
||||
}
|
||||
|
||||
// MkSeqIndexOf creates a sequence index-of operation.
|
||||
func (c *Context) MkSeqIndexOf(seq, substr, offset *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_index(c.ptr, seq.ptr, substr.ptr, offset.ptr))
|
||||
}
|
||||
|
||||
// MkStrToInt creates a string-to-integer conversion.
|
||||
func (c *Context) MkStrToInt(str *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_str_to_int(c.ptr, str.ptr))
|
||||
}
|
||||
|
||||
// MkIntToStr creates an integer-to-string conversion.
|
||||
func (c *Context) MkIntToStr(num *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_int_to_str(c.ptr, num.ptr))
|
||||
}
|
||||
|
||||
// Regular expression operations
|
||||
|
||||
// MkReSort creates a regular expression sort.
|
||||
func (c *Context) MkReSort(seqSort *Sort) *Sort {
|
||||
return newSort(c, C.Z3_mk_re_sort(c.ptr, seqSort.ptr))
|
||||
}
|
||||
|
||||
// MkToRe converts a sequence to a regular expression that accepts exactly that sequence.
|
||||
func (c *Context) MkToRe(seq *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_to_re(c.ptr, seq.ptr))
|
||||
}
|
||||
|
||||
// MkInRe creates a membership predicate for a sequence in a regular expression.
|
||||
func (c *Context) MkInRe(seq, re *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_in_re(c.ptr, seq.ptr, re.ptr))
|
||||
}
|
||||
|
||||
// MkReStar creates a Kleene star (zero or more repetitions) of a regular expression.
|
||||
func (c *Context) MkReStar(re *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_star(c.ptr, re.ptr))
|
||||
}
|
||||
|
||||
// MkRePlus creates a Kleene plus (one or more repetitions) of a regular expression.
|
||||
func (c *Context) MkRePlus(re *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_plus(c.ptr, re.ptr))
|
||||
}
|
||||
|
||||
// MkReOption creates an optional regular expression (zero or one repetition).
|
||||
func (c *Context) MkReOption(re *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_option(c.ptr, re.ptr))
|
||||
}
|
||||
|
||||
// MkRePower creates a regular expression that matches exactly n repetitions.
|
||||
func (c *Context) MkRePower(re *Expr, n uint) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_power(c.ptr, re.ptr, C.uint(n)))
|
||||
}
|
||||
|
||||
// MkReLoop creates a regular expression with bounded repetition (between lo and hi times).
|
||||
// If hi is 0, it means unbounded (at least lo times).
|
||||
func (c *Context) MkReLoop(re *Expr, lo, hi uint) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_loop(c.ptr, re.ptr, C.uint(lo), C.uint(hi)))
|
||||
}
|
||||
|
||||
// MkReConcat creates a concatenation of regular expressions.
|
||||
func (c *Context) MkReConcat(regexes ...*Expr) *Expr {
|
||||
if len(regexes) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(regexes) == 1 {
|
||||
return regexes[0]
|
||||
}
|
||||
cRegexes := make([]C.Z3_ast, len(regexes))
|
||||
for i, re := range regexes {
|
||||
cRegexes[i] = re.ptr
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_re_concat(c.ptr, C.uint(len(regexes)), &cRegexes[0]))
|
||||
}
|
||||
|
||||
// MkReUnion creates a union (alternation) of regular expressions.
|
||||
func (c *Context) MkReUnion(regexes ...*Expr) *Expr {
|
||||
if len(regexes) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(regexes) == 1 {
|
||||
return regexes[0]
|
||||
}
|
||||
cRegexes := make([]C.Z3_ast, len(regexes))
|
||||
for i, re := range regexes {
|
||||
cRegexes[i] = re.ptr
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_re_union(c.ptr, C.uint(len(regexes)), &cRegexes[0]))
|
||||
}
|
||||
|
||||
// MkReIntersect creates an intersection of regular expressions.
|
||||
func (c *Context) MkReIntersect(regexes ...*Expr) *Expr {
|
||||
if len(regexes) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(regexes) == 1 {
|
||||
return regexes[0]
|
||||
}
|
||||
cRegexes := make([]C.Z3_ast, len(regexes))
|
||||
for i, re := range regexes {
|
||||
cRegexes[i] = re.ptr
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_re_intersect(c.ptr, C.uint(len(regexes)), &cRegexes[0]))
|
||||
}
|
||||
|
||||
// MkReComplement creates the complement of a regular expression.
|
||||
func (c *Context) MkReComplement(re *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_complement(c.ptr, re.ptr))
|
||||
}
|
||||
|
||||
// MkReDiff creates the difference of two regular expressions (a - b).
|
||||
func (c *Context) MkReDiff(a, b *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_diff(c.ptr, a.ptr, b.ptr))
|
||||
}
|
||||
|
||||
// MkReEmpty creates an empty regular expression (accepts no strings).
|
||||
func (c *Context) MkReEmpty(sort *Sort) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_empty(c.ptr, sort.ptr))
|
||||
}
|
||||
|
||||
// MkReFull creates a full regular expression (accepts all strings).
|
||||
func (c *Context) MkReFull(sort *Sort) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_full(c.ptr, sort.ptr))
|
||||
}
|
||||
|
||||
// MkReAllchar creates a regular expression that accepts all single characters.
|
||||
func (c *Context) MkReAllchar(sort *Sort) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_allchar(c.ptr, sort.ptr))
|
||||
}
|
||||
|
||||
// MkReRange creates a regular expression for a character range [lo, hi].
|
||||
func (c *Context) MkReRange(lo, hi *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_re_range(c.ptr, lo.ptr, hi.ptr))
|
||||
}
|
||||
|
||||
// MkSeqReplaceRe replaces the first occurrence matching a regular expression.
|
||||
func (c *Context) MkSeqReplaceRe(seq, re, replacement *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_replace_re(c.ptr, seq.ptr, re.ptr, replacement.ptr))
|
||||
}
|
||||
|
||||
// MkSeqReplaceReAll replaces all occurrences matching a regular expression.
|
||||
func (c *Context) MkSeqReplaceReAll(seq, re, replacement *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_seq_replace_re_all(c.ptr, seq.ptr, re.ptr, replacement.ptr))
|
||||
}
|
||||
403
src/api/go/solver.go
Normal file
403
src/api/go/solver.go
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Status represents the result of a satisfiability check.
|
||||
type Status int
|
||||
|
||||
const (
|
||||
// Unsatisfiable means the constraints are unsatisfiable.
|
||||
Unsatisfiable Status = -1
|
||||
// Unknown means Z3 could not determine satisfiability.
|
||||
Unknown Status = 0
|
||||
// Satisfiable means the constraints are satisfiable.
|
||||
Satisfiable Status = 1
|
||||
)
|
||||
|
||||
// String returns the string representation of the status.
|
||||
func (s Status) String() string {
|
||||
switch s {
|
||||
case Unsatisfiable:
|
||||
return "unsat"
|
||||
case Unknown:
|
||||
return "unknown"
|
||||
case Satisfiable:
|
||||
return "sat"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Solver represents a Z3 solver.
|
||||
type Solver struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_solver
|
||||
}
|
||||
|
||||
// NewSolver creates a new solver for the given context.
|
||||
func (c *Context) NewSolver() *Solver {
|
||||
s := &Solver{
|
||||
ctx: c,
|
||||
ptr: C.Z3_mk_solver(c.ptr),
|
||||
}
|
||||
C.Z3_solver_inc_ref(c.ptr, s.ptr)
|
||||
runtime.SetFinalizer(s, func(solver *Solver) {
|
||||
C.Z3_solver_dec_ref(solver.ctx.ptr, solver.ptr)
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
// NewSolverForLogic creates a solver for a specific logic.
|
||||
func (c *Context) NewSolverForLogic(logic string) *Solver {
|
||||
sym := c.MkStringSymbol(logic)
|
||||
s := &Solver{
|
||||
ctx: c,
|
||||
ptr: C.Z3_mk_solver_for_logic(c.ptr, sym.ptr),
|
||||
}
|
||||
C.Z3_solver_inc_ref(c.ptr, s.ptr)
|
||||
runtime.SetFinalizer(s, func(solver *Solver) {
|
||||
C.Z3_solver_dec_ref(solver.ctx.ptr, solver.ptr)
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
// String returns the string representation of the solver.
|
||||
func (s *Solver) String() string {
|
||||
return C.GoString(C.Z3_solver_to_string(s.ctx.ptr, s.ptr))
|
||||
}
|
||||
|
||||
// Assert adds a constraint to the solver.
|
||||
func (s *Solver) Assert(constraint *Expr) {
|
||||
C.Z3_solver_assert(s.ctx.ptr, s.ptr, constraint.ptr)
|
||||
}
|
||||
|
||||
// AssertAndTrack adds a constraint with a tracking literal.
|
||||
func (s *Solver) AssertAndTrack(constraint, track *Expr) {
|
||||
C.Z3_solver_assert_and_track(s.ctx.ptr, s.ptr, constraint.ptr, track.ptr)
|
||||
}
|
||||
|
||||
// Check checks the satisfiability of the constraints.
|
||||
func (s *Solver) Check() Status {
|
||||
result := C.Z3_solver_check(s.ctx.ptr, s.ptr)
|
||||
return Status(result)
|
||||
}
|
||||
|
||||
// CheckAssumptions checks satisfiability under assumptions.
|
||||
func (s *Solver) CheckAssumptions(assumptions ...*Expr) Status {
|
||||
if len(assumptions) == 0 {
|
||||
return s.Check()
|
||||
}
|
||||
cAssumptions := make([]C.Z3_ast, len(assumptions))
|
||||
for i, a := range assumptions {
|
||||
cAssumptions[i] = a.ptr
|
||||
}
|
||||
result := C.Z3_solver_check_assumptions(s.ctx.ptr, s.ptr, C.uint(len(assumptions)), &cAssumptions[0])
|
||||
return Status(result)
|
||||
}
|
||||
|
||||
// Model returns the model if the constraints are satisfiable.
|
||||
func (s *Solver) Model() *Model {
|
||||
modelPtr := C.Z3_solver_get_model(s.ctx.ptr, s.ptr)
|
||||
if modelPtr == nil {
|
||||
return nil
|
||||
}
|
||||
return newModel(s.ctx, modelPtr)
|
||||
}
|
||||
|
||||
// Push creates a backtracking point.
|
||||
func (s *Solver) Push() {
|
||||
C.Z3_solver_push(s.ctx.ptr, s.ptr)
|
||||
}
|
||||
|
||||
// Pop removes backtracking points.
|
||||
func (s *Solver) Pop(n uint) {
|
||||
C.Z3_solver_pop(s.ctx.ptr, s.ptr, C.uint(n))
|
||||
}
|
||||
|
||||
// Reset removes all assertions from the solver.
|
||||
func (s *Solver) Reset() {
|
||||
C.Z3_solver_reset(s.ctx.ptr, s.ptr)
|
||||
}
|
||||
|
||||
// NumScopes returns the number of backtracking points.
|
||||
func (s *Solver) NumScopes() uint {
|
||||
return uint(C.Z3_solver_get_num_scopes(s.ctx.ptr, s.ptr))
|
||||
}
|
||||
|
||||
// Assertions returns the assertions in the solver.
|
||||
func (s *Solver) Assertions() []*Expr {
|
||||
vec := C.Z3_solver_get_assertions(s.ctx.ptr, s.ptr)
|
||||
return astVectorToExprs(s.ctx, vec)
|
||||
}
|
||||
|
||||
// UnsatCore returns the unsat core if the constraints are unsatisfiable.
|
||||
func (s *Solver) UnsatCore() []*Expr {
|
||||
vec := C.Z3_solver_get_unsat_core(s.ctx.ptr, s.ptr)
|
||||
return astVectorToExprs(s.ctx, vec)
|
||||
}
|
||||
|
||||
// ReasonUnknown returns the reason why the result is unknown.
|
||||
func (s *Solver) ReasonUnknown() string {
|
||||
return C.GoString(C.Z3_solver_get_reason_unknown(s.ctx.ptr, s.ptr))
|
||||
}
|
||||
|
||||
// GetStatistics returns the statistics for the solver.
|
||||
// Statistics include performance metrics, memory usage, and decision statistics.
|
||||
func (s *Solver) GetStatistics() *Statistics {
|
||||
ptr := C.Z3_solver_get_statistics(s.ctx.ptr, s.ptr)
|
||||
return newStatistics(s.ctx, ptr)
|
||||
}
|
||||
|
||||
// FromFile parses and asserts SMT-LIB2 formulas from a file.
|
||||
// The solver will contain the assertions from the file after this call.
|
||||
func (s *Solver) FromFile(filename string) {
|
||||
cFilename := C.CString(filename)
|
||||
defer C.free(unsafe.Pointer(cFilename))
|
||||
C.Z3_solver_from_file(s.ctx.ptr, s.ptr, cFilename)
|
||||
}
|
||||
|
||||
// FromString parses and asserts SMT-LIB2 formulas from a string.
|
||||
// The solver will contain the assertions from the string after this call.
|
||||
func (s *Solver) FromString(str string) {
|
||||
cStr := C.CString(str)
|
||||
defer C.free(unsafe.Pointer(cStr))
|
||||
C.Z3_solver_from_string(s.ctx.ptr, s.ptr, cStr)
|
||||
}
|
||||
|
||||
// GetHelp returns a string describing all available solver parameters.
|
||||
func (s *Solver) GetHelp() string {
|
||||
return C.GoString(C.Z3_solver_get_help(s.ctx.ptr, s.ptr))
|
||||
}
|
||||
|
||||
// SetParams sets solver parameters.
|
||||
// Parameters control solver behavior such as timeout, proof generation, etc.
|
||||
func (s *Solver) SetParams(params *Params) {
|
||||
C.Z3_solver_set_params(s.ctx.ptr, s.ptr, params.ptr)
|
||||
}
|
||||
|
||||
// GetParamDescrs returns parameter descriptions for the solver.
|
||||
func (s *Solver) GetParamDescrs() *ParamDescrs {
|
||||
ptr := C.Z3_solver_get_param_descrs(s.ctx.ptr, s.ptr)
|
||||
return newParamDescrs(s.ctx, ptr)
|
||||
}
|
||||
|
||||
// Interrupt interrupts the solver execution.
|
||||
// This is useful for stopping long-running solver operations gracefully.
|
||||
func (s *Solver) Interrupt() {
|
||||
C.Z3_solver_interrupt(s.ctx.ptr, s.ptr)
|
||||
}
|
||||
|
||||
// Units returns the unit clauses (literals) learned by the solver.
|
||||
// Unit clauses are assertions that have been simplified to single literals.
|
||||
// This is useful for debugging and understanding solver behavior.
|
||||
func (s *Solver) Units() []*Expr {
|
||||
vec := C.Z3_solver_get_units(s.ctx.ptr, s.ptr)
|
||||
return astVectorToExprs(s.ctx, vec)
|
||||
}
|
||||
|
||||
// NonUnits returns the non-unit clauses in the solver's current state.
|
||||
// These are clauses that have not been reduced to unit clauses.
|
||||
// This is useful for debugging and understanding solver behavior.
|
||||
func (s *Solver) NonUnits() []*Expr {
|
||||
vec := C.Z3_solver_get_non_units(s.ctx.ptr, s.ptr)
|
||||
return astVectorToExprs(s.ctx, vec)
|
||||
}
|
||||
|
||||
// Trail returns the decision trail of the solver.
|
||||
// The trail contains the sequence of literals assigned during search.
|
||||
// This is useful for understanding the solver's decision history.
|
||||
// Note: This function works primarily with SimpleSolver. For solvers created
|
||||
// using tactics (e.g., NewSolver()), it may return an error.
|
||||
func (s *Solver) Trail() []*Expr {
|
||||
vec := C.Z3_solver_get_trail(s.ctx.ptr, s.ptr)
|
||||
return astVectorToExprs(s.ctx, vec)
|
||||
}
|
||||
|
||||
// TrailLevels returns the decision levels for each literal in the trail.
|
||||
// The returned slice has the same length as the trail, where each element
|
||||
// indicates the decision level at which the corresponding trail literal was assigned.
|
||||
// This is useful for understanding the structure of the search tree.
|
||||
// Note: This function works primarily with SimpleSolver. For solvers created
|
||||
// using tactics (e.g., NewSolver()), it may return an error.
|
||||
func (s *Solver) TrailLevels() []uint {
|
||||
// Get the trail vector directly from the C API
|
||||
trailVec := C.Z3_solver_get_trail(s.ctx.ptr, s.ptr)
|
||||
C.Z3_ast_vector_inc_ref(s.ctx.ptr, trailVec)
|
||||
defer C.Z3_ast_vector_dec_ref(s.ctx.ptr, trailVec)
|
||||
|
||||
n := uint(C.Z3_ast_vector_size(s.ctx.ptr, trailVec))
|
||||
if n == 0 {
|
||||
return []uint{}
|
||||
}
|
||||
|
||||
// Allocate the levels array
|
||||
levels := make([]C.uint, n)
|
||||
|
||||
// Get the levels using the trail vector directly
|
||||
// Safe to pass &levels[0] because we checked n > 0 above
|
||||
C.Z3_solver_get_levels(s.ctx.ptr, s.ptr, trailVec, C.uint(n), &levels[0])
|
||||
|
||||
// Convert to Go slice
|
||||
result := make([]uint, n)
|
||||
for i := uint(0); i < n; i++ {
|
||||
result[i] = uint(levels[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CongruenceRoot returns the congruence class representative of the given expression.
|
||||
// This returns the root element in the congruence closure for the term.
|
||||
// Note: This function works primarily with SimpleSolver. Terms and variables that
|
||||
// are eliminated during pre-processing are not visible to the congruence closure.
|
||||
func (s *Solver) CongruenceRoot(expr *Expr) *Expr {
|
||||
ast := C.Z3_solver_congruence_root(s.ctx.ptr, s.ptr, expr.ptr)
|
||||
return newExpr(s.ctx, ast)
|
||||
}
|
||||
|
||||
// CongruenceNext returns the next element in the congruence class of the given expression.
|
||||
// This allows iteration through all elements in a congruence class.
|
||||
// Note: This function works primarily with SimpleSolver. Terms and variables that
|
||||
// are eliminated during pre-processing are not visible to the congruence closure.
|
||||
func (s *Solver) CongruenceNext(expr *Expr) *Expr {
|
||||
ast := C.Z3_solver_congruence_next(s.ctx.ptr, s.ptr, expr.ptr)
|
||||
return newExpr(s.ctx, ast)
|
||||
}
|
||||
|
||||
// CongruenceExplain returns an explanation for why two expressions are congruent.
|
||||
// The result is an expression that justifies the congruence between a and b.
|
||||
// Note: This function works primarily with SimpleSolver. Terms and variables that
|
||||
// are eliminated during pre-processing are not visible to the congruence closure.
|
||||
func (s *Solver) CongruenceExplain(a, b *Expr) *Expr {
|
||||
ast := C.Z3_solver_congruence_explain(s.ctx.ptr, s.ptr, a.ptr, b.ptr)
|
||||
return newExpr(s.ctx, ast)
|
||||
}
|
||||
|
||||
// SetInitialValue provides an initial value hint for a variable to the solver.
|
||||
// This can help guide the solver to find solutions more efficiently.
|
||||
// The variable must be a constant or function application, and the value must be
|
||||
// compatible with the variable's sort.
|
||||
func (s *Solver) SetInitialValue(variable, value *Expr) {
|
||||
C.Z3_solver_set_initial_value(s.ctx.ptr, s.ptr, variable.ptr, value.ptr)
|
||||
}
|
||||
|
||||
// Model represents a Z3 model (satisfying assignment).
|
||||
type Model struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_model
|
||||
}
|
||||
|
||||
// newModel creates a new Model and manages its reference count.
|
||||
func newModel(ctx *Context, ptr C.Z3_model) *Model {
|
||||
m := &Model{ctx: ctx, ptr: ptr}
|
||||
C.Z3_model_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(m, func(model *Model) {
|
||||
C.Z3_model_dec_ref(model.ctx.ptr, model.ptr)
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns the string representation of the model.
|
||||
func (m *Model) String() string {
|
||||
return C.GoString(C.Z3_model_to_string(m.ctx.ptr, m.ptr))
|
||||
}
|
||||
|
||||
// NumConsts returns the number of constants in the model.
|
||||
func (m *Model) NumConsts() uint {
|
||||
return uint(C.Z3_model_get_num_consts(m.ctx.ptr, m.ptr))
|
||||
}
|
||||
|
||||
// NumFuncs returns the number of function interpretations in the model.
|
||||
func (m *Model) NumFuncs() uint {
|
||||
return uint(C.Z3_model_get_num_funcs(m.ctx.ptr, m.ptr))
|
||||
}
|
||||
|
||||
// GetConstDecl returns the i-th constant declaration in the model.
|
||||
func (m *Model) GetConstDecl(i uint) *FuncDecl {
|
||||
return newFuncDecl(m.ctx, C.Z3_model_get_const_decl(m.ctx.ptr, m.ptr, C.uint(i)))
|
||||
}
|
||||
|
||||
// GetFuncDecl returns the i-th function declaration in the model.
|
||||
func (m *Model) GetFuncDecl(i uint) *FuncDecl {
|
||||
return newFuncDecl(m.ctx, C.Z3_model_get_func_decl(m.ctx.ptr, m.ptr, C.uint(i)))
|
||||
}
|
||||
|
||||
// Eval evaluates an expression in the model.
|
||||
// If modelCompletion is true, Z3 will assign an interpretation for uninterpreted constants.
|
||||
func (m *Model) Eval(expr *Expr, modelCompletion bool) (*Expr, bool) {
|
||||
var result C.Z3_ast
|
||||
var completion C.bool
|
||||
if modelCompletion {
|
||||
completion = C.bool(true)
|
||||
} else {
|
||||
completion = C.bool(false)
|
||||
}
|
||||
success := C.Z3_model_eval(m.ctx.ptr, m.ptr, expr.ptr, completion, &result)
|
||||
if success == C.bool(false) {
|
||||
return nil, false
|
||||
}
|
||||
return newExpr(m.ctx, result), true
|
||||
}
|
||||
|
||||
// GetConstInterp returns the interpretation of a constant.
|
||||
func (m *Model) GetConstInterp(decl *FuncDecl) *Expr {
|
||||
result := C.Z3_model_get_const_interp(m.ctx.ptr, m.ptr, decl.ptr)
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
return newExpr(m.ctx, result)
|
||||
}
|
||||
|
||||
// FuncInterp represents a function interpretation in a model.
|
||||
type FuncInterp struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_func_interp
|
||||
}
|
||||
|
||||
// GetFuncInterp returns the interpretation of a function.
|
||||
func (m *Model) GetFuncInterp(decl *FuncDecl) *FuncInterp {
|
||||
result := C.Z3_model_get_func_interp(m.ctx.ptr, m.ptr, decl.ptr)
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
fi := &FuncInterp{ctx: m.ctx, ptr: result}
|
||||
C.Z3_func_interp_inc_ref(m.ctx.ptr, result)
|
||||
runtime.SetFinalizer(fi, func(f *FuncInterp) {
|
||||
C.Z3_func_interp_dec_ref(f.ctx.ptr, f.ptr)
|
||||
})
|
||||
return fi
|
||||
}
|
||||
|
||||
// NumEntries returns the number of entries in the function interpretation.
|
||||
func (fi *FuncInterp) NumEntries() uint {
|
||||
return uint(C.Z3_func_interp_get_num_entries(fi.ctx.ptr, fi.ptr))
|
||||
}
|
||||
|
||||
// GetElse returns the else value of the function interpretation.
|
||||
func (fi *FuncInterp) GetElse() *Expr {
|
||||
result := C.Z3_func_interp_get_else(fi.ctx.ptr, fi.ptr)
|
||||
return newExpr(fi.ctx, result)
|
||||
}
|
||||
|
||||
// GetArity returns the arity of the function interpretation.
|
||||
func (fi *FuncInterp) GetArity() uint {
|
||||
return uint(C.Z3_func_interp_get_arity(fi.ctx.ptr, fi.ptr))
|
||||
}
|
||||
|
||||
// SortUniverse returns the universe of values for an uninterpreted sort in the model.
|
||||
// The universe is represented as a list of distinct expressions.
|
||||
// Returns nil if the sort is not an uninterpreted sort in this model.
|
||||
func (m *Model) SortUniverse(sort *Sort) []*Expr {
|
||||
vec := C.Z3_model_get_sort_universe(m.ctx.ptr, m.ptr, sort.ptr)
|
||||
if vec == nil {
|
||||
return nil
|
||||
}
|
||||
return astVectorToExprs(m.ctx, vec)
|
||||
}
|
||||
294
src/api/go/tactic.go
Normal file
294
src/api/go/tactic.go
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
package z3
|
||||
|
||||
/*
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Tactic represents a Z3 tactic for transforming goals.
|
||||
type Tactic struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_tactic
|
||||
}
|
||||
|
||||
// newTactic creates a new Tactic and manages its reference count.
|
||||
func newTactic(ctx *Context, ptr C.Z3_tactic) *Tactic {
|
||||
t := &Tactic{ctx: ctx, ptr: ptr}
|
||||
C.Z3_tactic_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(t, func(tactic *Tactic) {
|
||||
C.Z3_tactic_dec_ref(tactic.ctx.ptr, tactic.ptr)
|
||||
})
|
||||
return t
|
||||
}
|
||||
|
||||
// MkTactic creates a tactic with the given name.
|
||||
func (c *Context) MkTactic(name string) *Tactic {
|
||||
cName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
return newTactic(c, C.Z3_mk_tactic(c.ptr, cName))
|
||||
}
|
||||
|
||||
// Apply applies the tactic to a goal.
|
||||
func (t *Tactic) Apply(g *Goal) *ApplyResult {
|
||||
return newApplyResult(t.ctx, C.Z3_tactic_apply(t.ctx.ptr, t.ptr, g.ptr))
|
||||
}
|
||||
|
||||
// GetHelp returns help information for the tactic.
|
||||
func (t *Tactic) GetHelp() string {
|
||||
return C.GoString(C.Z3_tactic_get_help(t.ctx.ptr, t.ptr))
|
||||
}
|
||||
|
||||
// AndThen creates a tactic that applies t1 and then t2.
|
||||
func (t *Tactic) AndThen(t2 *Tactic) *Tactic {
|
||||
return newTactic(t.ctx, C.Z3_tactic_and_then(t.ctx.ptr, t.ptr, t2.ptr))
|
||||
}
|
||||
|
||||
// OrElse creates a tactic that applies t1, and if it fails, applies t2.
|
||||
func (t *Tactic) OrElse(t2 *Tactic) *Tactic {
|
||||
return newTactic(t.ctx, C.Z3_tactic_or_else(t.ctx.ptr, t.ptr, t2.ptr))
|
||||
}
|
||||
|
||||
// Repeat creates a tactic that applies t repeatedly (at most max times).
|
||||
func (t *Tactic) Repeat(max uint) *Tactic {
|
||||
return newTactic(t.ctx, C.Z3_tactic_repeat(t.ctx.ptr, t.ptr, C.uint(max)))
|
||||
}
|
||||
|
||||
// When creates a conditional tactic that applies t only if probe p evaluates to true.
|
||||
func (c *Context) TacticWhen(p *Probe, t *Tactic) *Tactic {
|
||||
return newTactic(c, C.Z3_tactic_when(c.ptr, p.ptr, t.ptr))
|
||||
}
|
||||
|
||||
// TacticCond creates a conditional tactic: if p then t1 else t2.
|
||||
func (c *Context) TacticCond(p *Probe, t1, t2 *Tactic) *Tactic {
|
||||
return newTactic(c, C.Z3_tactic_cond(c.ptr, p.ptr, t1.ptr, t2.ptr))
|
||||
}
|
||||
|
||||
// TacticFail creates a tactic that always fails.
|
||||
func (c *Context) TacticFail() *Tactic {
|
||||
return newTactic(c, C.Z3_tactic_fail(c.ptr))
|
||||
}
|
||||
|
||||
// TacticSkip creates a tactic that always succeeds.
|
||||
func (c *Context) TacticSkip() *Tactic {
|
||||
return newTactic(c, C.Z3_tactic_skip(c.ptr))
|
||||
}
|
||||
|
||||
// Goal represents a set of formulas that can be solved or transformed.
|
||||
type Goal struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_goal
|
||||
}
|
||||
|
||||
// newGoal creates a new Goal and manages its reference count.
|
||||
func newGoal(ctx *Context, ptr C.Z3_goal) *Goal {
|
||||
g := &Goal{ctx: ctx, ptr: ptr}
|
||||
C.Z3_goal_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(g, func(goal *Goal) {
|
||||
C.Z3_goal_dec_ref(goal.ctx.ptr, goal.ptr)
|
||||
})
|
||||
return g
|
||||
}
|
||||
|
||||
// MkGoal creates a new goal.
|
||||
func (c *Context) MkGoal(models, unsatCores, proofs bool) *Goal {
|
||||
return newGoal(c, C.Z3_mk_goal(c.ptr, C.bool(models), C.bool(unsatCores), C.bool(proofs)))
|
||||
}
|
||||
|
||||
// Assert adds a constraint to the goal.
|
||||
func (g *Goal) Assert(constraint *Expr) {
|
||||
C.Z3_goal_assert(g.ctx.ptr, g.ptr, constraint.ptr)
|
||||
}
|
||||
|
||||
// Size returns the number of formulas in the goal.
|
||||
func (g *Goal) Size() uint {
|
||||
return uint(C.Z3_goal_size(g.ctx.ptr, g.ptr))
|
||||
}
|
||||
|
||||
// Formula returns the i-th formula in the goal.
|
||||
func (g *Goal) Formula(i uint) *Expr {
|
||||
return newExpr(g.ctx, C.Z3_goal_formula(g.ctx.ptr, g.ptr, C.uint(i)))
|
||||
}
|
||||
|
||||
// NumExprs returns the number of expressions in the goal.
|
||||
func (g *Goal) NumExprs() uint {
|
||||
return uint(C.Z3_goal_num_exprs(g.ctx.ptr, g.ptr))
|
||||
}
|
||||
|
||||
// IsDecidedSat returns true if the goal is decided to be satisfiable.
|
||||
func (g *Goal) IsDecidedSat() bool {
|
||||
return bool(C.Z3_goal_is_decided_sat(g.ctx.ptr, g.ptr))
|
||||
}
|
||||
|
||||
// IsDecidedUnsat returns true if the goal is decided to be unsatisfiable.
|
||||
func (g *Goal) IsDecidedUnsat() bool {
|
||||
return bool(C.Z3_goal_is_decided_unsat(g.ctx.ptr, g.ptr))
|
||||
}
|
||||
|
||||
// Reset removes all formulas from the goal.
|
||||
func (g *Goal) Reset() {
|
||||
C.Z3_goal_reset(g.ctx.ptr, g.ptr)
|
||||
}
|
||||
|
||||
// String returns the string representation of the goal.
|
||||
func (g *Goal) String() string {
|
||||
return C.GoString(C.Z3_goal_to_string(g.ctx.ptr, g.ptr))
|
||||
}
|
||||
|
||||
// ApplyResult represents the result of applying a tactic to a goal.
|
||||
type ApplyResult struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_apply_result
|
||||
}
|
||||
|
||||
// newApplyResult creates a new ApplyResult and manages its reference count.
|
||||
func newApplyResult(ctx *Context, ptr C.Z3_apply_result) *ApplyResult {
|
||||
ar := &ApplyResult{ctx: ctx, ptr: ptr}
|
||||
C.Z3_apply_result_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(ar, func(result *ApplyResult) {
|
||||
C.Z3_apply_result_dec_ref(result.ctx.ptr, result.ptr)
|
||||
})
|
||||
return ar
|
||||
}
|
||||
|
||||
// NumSubgoals returns the number of subgoals in the result.
|
||||
func (ar *ApplyResult) NumSubgoals() uint {
|
||||
return uint(C.Z3_apply_result_get_num_subgoals(ar.ctx.ptr, ar.ptr))
|
||||
}
|
||||
|
||||
// Subgoal returns the i-th subgoal.
|
||||
func (ar *ApplyResult) Subgoal(i uint) *Goal {
|
||||
return newGoal(ar.ctx, C.Z3_apply_result_get_subgoal(ar.ctx.ptr, ar.ptr, C.uint(i)))
|
||||
}
|
||||
|
||||
// String returns the string representation of the apply result.
|
||||
func (ar *ApplyResult) String() string {
|
||||
return C.GoString(C.Z3_apply_result_to_string(ar.ctx.ptr, ar.ptr))
|
||||
}
|
||||
|
||||
// Probe represents a probe for checking properties of goals.
|
||||
type Probe struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_probe
|
||||
}
|
||||
|
||||
// newProbe creates a new Probe and manages its reference count.
|
||||
func newProbe(ctx *Context, ptr C.Z3_probe) *Probe {
|
||||
p := &Probe{ctx: ctx, ptr: ptr}
|
||||
C.Z3_probe_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(p, func(probe *Probe) {
|
||||
C.Z3_probe_dec_ref(probe.ctx.ptr, probe.ptr)
|
||||
})
|
||||
return p
|
||||
}
|
||||
|
||||
// MkProbe creates a probe with the given name.
|
||||
func (c *Context) MkProbe(name string) *Probe {
|
||||
cName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
return newProbe(c, C.Z3_mk_probe(c.ptr, cName))
|
||||
}
|
||||
|
||||
// Apply evaluates the probe on a goal.
|
||||
func (p *Probe) Apply(g *Goal) float64 {
|
||||
return float64(C.Z3_probe_apply(p.ctx.ptr, p.ptr, g.ptr))
|
||||
}
|
||||
|
||||
// ProbeConst creates a probe that always evaluates to the given value.
|
||||
func (c *Context) ProbeConst(val float64) *Probe {
|
||||
return newProbe(c, C.Z3_probe_const(c.ptr, C.double(val)))
|
||||
}
|
||||
|
||||
// ProbeLt creates a probe that evaluates to true if p1 < p2.
|
||||
func (p *Probe) Lt(p2 *Probe) *Probe {
|
||||
return newProbe(p.ctx, C.Z3_probe_lt(p.ctx.ptr, p.ptr, p2.ptr))
|
||||
}
|
||||
|
||||
// ProbeGt creates a probe that evaluates to true if p1 > p2.
|
||||
func (p *Probe) Gt(p2 *Probe) *Probe {
|
||||
return newProbe(p.ctx, C.Z3_probe_gt(p.ctx.ptr, p.ptr, p2.ptr))
|
||||
}
|
||||
|
||||
// ProbeLe creates a probe that evaluates to true if p1 <= p2.
|
||||
func (p *Probe) Le(p2 *Probe) *Probe {
|
||||
return newProbe(p.ctx, C.Z3_probe_le(p.ctx.ptr, p.ptr, p2.ptr))
|
||||
}
|
||||
|
||||
// ProbeGe creates a probe that evaluates to true if p1 >= p2.
|
||||
func (p *Probe) Ge(p2 *Probe) *Probe {
|
||||
return newProbe(p.ctx, C.Z3_probe_ge(p.ctx.ptr, p.ptr, p2.ptr))
|
||||
}
|
||||
|
||||
// ProbeEq creates a probe that evaluates to true if p1 == p2.
|
||||
func (p *Probe) Eq(p2 *Probe) *Probe {
|
||||
return newProbe(p.ctx, C.Z3_probe_eq(p.ctx.ptr, p.ptr, p2.ptr))
|
||||
}
|
||||
|
||||
// ProbeAnd creates a probe that is the conjunction of p1 and p2.
|
||||
func (p *Probe) And(p2 *Probe) *Probe {
|
||||
return newProbe(p.ctx, C.Z3_probe_and(p.ctx.ptr, p.ptr, p2.ptr))
|
||||
}
|
||||
|
||||
// ProbeOr creates a probe that is the disjunction of p1 and p2.
|
||||
func (p *Probe) Or(p2 *Probe) *Probe {
|
||||
return newProbe(p.ctx, C.Z3_probe_or(p.ctx.ptr, p.ptr, p2.ptr))
|
||||
}
|
||||
|
||||
// ProbeNot creates a probe that is the negation of p.
|
||||
func (p *Probe) Not() *Probe {
|
||||
return newProbe(p.ctx, C.Z3_probe_not(p.ctx.ptr, p.ptr))
|
||||
}
|
||||
|
||||
// Params represents a parameter set.
|
||||
type Params struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_params
|
||||
}
|
||||
|
||||
// newParams creates a new Params and manages its reference count.
|
||||
func newParams(ctx *Context, ptr C.Z3_params) *Params {
|
||||
params := &Params{ctx: ctx, ptr: ptr}
|
||||
C.Z3_params_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(params, func(p *Params) {
|
||||
C.Z3_params_dec_ref(p.ctx.ptr, p.ptr)
|
||||
})
|
||||
return params
|
||||
}
|
||||
|
||||
// MkParams creates a new parameter set.
|
||||
func (c *Context) MkParams() *Params {
|
||||
return newParams(c, C.Z3_mk_params(c.ptr))
|
||||
}
|
||||
|
||||
// SetBool sets a Boolean parameter.
|
||||
func (p *Params) SetBool(key string, value bool) {
|
||||
sym := p.ctx.MkStringSymbol(key)
|
||||
C.Z3_params_set_bool(p.ctx.ptr, p.ptr, sym.ptr, C.bool(value))
|
||||
}
|
||||
|
||||
// SetUint sets an unsigned integer parameter.
|
||||
func (p *Params) SetUint(key string, value uint) {
|
||||
sym := p.ctx.MkStringSymbol(key)
|
||||
C.Z3_params_set_uint(p.ctx.ptr, p.ptr, sym.ptr, C.uint(value))
|
||||
}
|
||||
|
||||
// SetDouble sets a double parameter.
|
||||
func (p *Params) SetDouble(key string, value float64) {
|
||||
sym := p.ctx.MkStringSymbol(key)
|
||||
C.Z3_params_set_double(p.ctx.ptr, p.ptr, sym.ptr, C.double(value))
|
||||
}
|
||||
|
||||
// SetSymbol sets a symbol parameter.
|
||||
func (p *Params) SetSymbol(key string, value *Symbol) {
|
||||
sym := p.ctx.MkStringSymbol(key)
|
||||
C.Z3_params_set_symbol(p.ctx.ptr, p.ptr, sym.ptr, value.ptr)
|
||||
}
|
||||
|
||||
// String returns the string representation of the parameters.
|
||||
func (p *Params) String() string {
|
||||
return C.GoString(C.Z3_params_to_string(p.ctx.ptr, p.ptr))
|
||||
}
|
||||
806
src/api/go/z3.go
Normal file
806
src/api/go/z3.go
Normal file
|
|
@ -0,0 +1,806 @@
|
|||
// Package z3 provides Go bindings for the Z3 theorem prover.
|
||||
//
|
||||
// Z3 is a high-performance SMT (Satisfiability Modulo Theories) solver
|
||||
// developed at Microsoft Research. These bindings wrap the Z3 C API using
|
||||
// CGO and provide idiomatic Go interfaces with automatic memory management.
|
||||
//
|
||||
// # Basic Usage
|
||||
//
|
||||
// Create a context and solver:
|
||||
//
|
||||
// ctx := z3.NewContext()
|
||||
// solver := ctx.NewSolver()
|
||||
//
|
||||
// Create variables and constraints:
|
||||
//
|
||||
// x := ctx.MkIntConst("x")
|
||||
// y := ctx.MkIntConst("y")
|
||||
// solver.Assert(ctx.MkEq(ctx.MkAdd(x, y), ctx.MkInt(10, ctx.MkIntSort())))
|
||||
// solver.Assert(ctx.MkGt(x, y))
|
||||
//
|
||||
// Check satisfiability and get model:
|
||||
//
|
||||
// if solver.Check() == z3.Satisfiable {
|
||||
// model := solver.Model()
|
||||
// xVal, _ := model.Eval(x, true)
|
||||
// fmt.Println("x =", xVal.String())
|
||||
// }
|
||||
//
|
||||
// # Memory Management
|
||||
//
|
||||
// All Z3 objects are automatically managed using Go finalizers. Reference
|
||||
// counting is handled transparently - you don't need to manually free objects.
|
||||
//
|
||||
// # Supported Features
|
||||
//
|
||||
// - Boolean logic, integer and real arithmetic
|
||||
// - Bit-vectors and floating-point arithmetic
|
||||
// - Arrays, sequences, and strings
|
||||
// - Regular expressions
|
||||
// - Algebraic datatypes
|
||||
// - Quantifiers and lambda expressions
|
||||
// - Tactics and goal-based solving
|
||||
// - Optimization (MaxSMT)
|
||||
// - Fixedpoint solver (Datalog/CHC)
|
||||
//
|
||||
// For more examples, see the examples/go directory in the Z3 repository.
|
||||
package z3
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I${SRCDIR}/..
|
||||
#cgo LDFLAGS: -lz3
|
||||
#include "z3.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Config represents a Z3 configuration object.
|
||||
type Config struct {
|
||||
ptr C.Z3_config
|
||||
}
|
||||
|
||||
// NewConfig creates a new Z3 configuration.
|
||||
func NewConfig() *Config {
|
||||
cfg := &Config{ptr: C.Z3_mk_config()}
|
||||
runtime.SetFinalizer(cfg, func(c *Config) {
|
||||
C.Z3_del_config(c.ptr)
|
||||
})
|
||||
return cfg
|
||||
}
|
||||
|
||||
// SetParamValue sets a configuration parameter.
|
||||
func (c *Config) SetParamValue(paramID, paramValue string) {
|
||||
cParamID := C.CString(paramID)
|
||||
cParamValue := C.CString(paramValue)
|
||||
defer C.free(unsafe.Pointer(cParamID))
|
||||
defer C.free(unsafe.Pointer(cParamValue))
|
||||
C.Z3_set_param_value(c.ptr, cParamID, cParamValue)
|
||||
}
|
||||
|
||||
// Context represents a Z3 logical context.
|
||||
type Context struct {
|
||||
ptr C.Z3_context
|
||||
}
|
||||
|
||||
// NewContext creates a new Z3 context with default configuration.
|
||||
func NewContext() *Context {
|
||||
ctx := &Context{ptr: C.Z3_mk_context_rc(C.Z3_mk_config())}
|
||||
runtime.SetFinalizer(ctx, func(c *Context) {
|
||||
C.Z3_del_context(c.ptr)
|
||||
})
|
||||
return ctx
|
||||
}
|
||||
|
||||
// NewContextWithConfig creates a new Z3 context with the given configuration.
|
||||
func NewContextWithConfig(cfg *Config) *Context {
|
||||
ctx := &Context{ptr: C.Z3_mk_context_rc(cfg.ptr)}
|
||||
runtime.SetFinalizer(ctx, func(c *Context) {
|
||||
C.Z3_del_context(c.ptr)
|
||||
})
|
||||
return ctx
|
||||
}
|
||||
|
||||
// SetParam sets a global or context parameter.
|
||||
func (c *Context) SetParam(key, value string) {
|
||||
cKey := C.CString(key)
|
||||
cValue := C.CString(value)
|
||||
defer C.free(unsafe.Pointer(cKey))
|
||||
defer C.free(unsafe.Pointer(cValue))
|
||||
C.Z3_update_param_value(c.ptr, cKey, cValue)
|
||||
}
|
||||
|
||||
// Symbol represents a Z3 symbol.
|
||||
type Symbol struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_symbol
|
||||
}
|
||||
|
||||
// newSymbol creates a new Symbol.
|
||||
func newSymbol(ctx *Context, ptr C.Z3_symbol) *Symbol {
|
||||
return &Symbol{ctx: ctx, ptr: ptr}
|
||||
}
|
||||
|
||||
// MkIntSymbol creates an integer symbol.
|
||||
func (c *Context) MkIntSymbol(i int) *Symbol {
|
||||
return &Symbol{
|
||||
ctx: c,
|
||||
ptr: C.Z3_mk_int_symbol(c.ptr, C.int(i)),
|
||||
}
|
||||
}
|
||||
|
||||
// MkStringSymbol creates a string symbol.
|
||||
func (c *Context) MkStringSymbol(s string) *Symbol {
|
||||
cStr := C.CString(s)
|
||||
defer C.free(unsafe.Pointer(cStr))
|
||||
return &Symbol{
|
||||
ctx: c,
|
||||
ptr: C.Z3_mk_string_symbol(c.ptr, cStr),
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string representation of the symbol.
|
||||
func (s *Symbol) String() string {
|
||||
kind := C.Z3_get_symbol_kind(s.ctx.ptr, s.ptr)
|
||||
if kind == C.Z3_INT_SYMBOL {
|
||||
return string(rune(C.Z3_get_symbol_int(s.ctx.ptr, s.ptr)))
|
||||
}
|
||||
return C.GoString(C.Z3_get_symbol_string(s.ctx.ptr, s.ptr))
|
||||
}
|
||||
|
||||
// AST represents a Z3 abstract syntax tree node.
|
||||
type AST struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_ast
|
||||
}
|
||||
|
||||
// incRef increments the reference count of the AST.
|
||||
func (a *AST) incRef() {
|
||||
C.Z3_inc_ref(a.ctx.ptr, a.ptr)
|
||||
}
|
||||
|
||||
// decRef decrements the reference count of the AST.
|
||||
func (a *AST) decRef() {
|
||||
C.Z3_dec_ref(a.ctx.ptr, a.ptr)
|
||||
}
|
||||
|
||||
// String returns the string representation of the AST.
|
||||
func (a *AST) String() string {
|
||||
return C.GoString(C.Z3_ast_to_string(a.ctx.ptr, a.ptr))
|
||||
}
|
||||
|
||||
// Hash returns the hash code of the AST.
|
||||
func (a *AST) Hash() uint32 {
|
||||
return uint32(C.Z3_get_ast_hash(a.ctx.ptr, a.ptr))
|
||||
}
|
||||
|
||||
// Equal checks if two ASTs are equal.
|
||||
func (a *AST) Equal(other *AST) bool {
|
||||
if a.ctx != other.ctx {
|
||||
return false
|
||||
}
|
||||
return bool(C.Z3_is_eq_ast(a.ctx.ptr, a.ptr, other.ptr))
|
||||
}
|
||||
|
||||
// Sort represents a Z3 sort (type).
|
||||
type Sort struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_sort
|
||||
}
|
||||
|
||||
// newSort creates a new Sort and manages its reference count.
|
||||
func newSort(ctx *Context, ptr C.Z3_sort) *Sort {
|
||||
sort := &Sort{ctx: ctx, ptr: ptr}
|
||||
C.Z3_inc_ref(ctx.ptr, C.Z3_sort_to_ast(ctx.ptr, ptr))
|
||||
runtime.SetFinalizer(sort, func(s *Sort) {
|
||||
C.Z3_dec_ref(s.ctx.ptr, C.Z3_sort_to_ast(s.ctx.ptr, s.ptr))
|
||||
})
|
||||
return sort
|
||||
}
|
||||
|
||||
// String returns the string representation of the sort.
|
||||
func (s *Sort) String() string {
|
||||
return C.GoString(C.Z3_sort_to_string(s.ctx.ptr, s.ptr))
|
||||
}
|
||||
|
||||
// Equal checks if two sorts are equal.
|
||||
func (s *Sort) Equal(other *Sort) bool {
|
||||
if s.ctx != other.ctx {
|
||||
return false
|
||||
}
|
||||
return bool(C.Z3_is_eq_sort(s.ctx.ptr, s.ptr, other.ptr))
|
||||
}
|
||||
|
||||
// MkBoolSort creates the Boolean sort.
|
||||
func (c *Context) MkBoolSort() *Sort {
|
||||
return newSort(c, C.Z3_mk_bool_sort(c.ptr))
|
||||
}
|
||||
|
||||
// MkBvSort creates a bit-vector sort of the given size.
|
||||
func (c *Context) MkBvSort(sz uint) *Sort {
|
||||
return newSort(c, C.Z3_mk_bv_sort(c.ptr, C.uint(sz)))
|
||||
}
|
||||
|
||||
// Expr represents a Z3 expression.
|
||||
type Expr struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_ast
|
||||
}
|
||||
|
||||
// newExpr creates a new Expr and manages its reference count.
|
||||
func newExpr(ctx *Context, ptr C.Z3_ast) *Expr {
|
||||
expr := &Expr{ctx: ctx, ptr: ptr}
|
||||
C.Z3_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(expr, func(e *Expr) {
|
||||
C.Z3_dec_ref(e.ctx.ptr, e.ptr)
|
||||
})
|
||||
return expr
|
||||
}
|
||||
|
||||
// String returns the string representation of the expression.
|
||||
func (e *Expr) String() string {
|
||||
return C.GoString(C.Z3_ast_to_string(e.ctx.ptr, e.ptr))
|
||||
}
|
||||
|
||||
// Equal checks if two expressions are equal.
|
||||
func (e *Expr) Equal(other *Expr) bool {
|
||||
if e.ctx != other.ctx {
|
||||
return false
|
||||
}
|
||||
return bool(C.Z3_is_eq_ast(e.ctx.ptr, e.ptr, other.ptr))
|
||||
}
|
||||
|
||||
// GetSort returns the sort of the expression.
|
||||
func (e *Expr) GetSort() *Sort {
|
||||
return newSort(e.ctx, C.Z3_get_sort(e.ctx.ptr, e.ptr))
|
||||
}
|
||||
|
||||
// Pattern represents a Z3 pattern for quantifier instantiation.
|
||||
type Pattern struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_pattern
|
||||
}
|
||||
|
||||
// newPattern creates a new Pattern and manages its reference count.
|
||||
func newPattern(ctx *Context, ptr C.Z3_pattern) *Pattern {
|
||||
p := &Pattern{ctx: ctx, ptr: ptr}
|
||||
// Patterns are ASTs in the C API
|
||||
C.Z3_inc_ref(ctx.ptr, (C.Z3_ast)(unsafe.Pointer(ptr)))
|
||||
runtime.SetFinalizer(p, func(pat *Pattern) {
|
||||
C.Z3_dec_ref(pat.ctx.ptr, (C.Z3_ast)(unsafe.Pointer(pat.ptr)))
|
||||
})
|
||||
return p
|
||||
}
|
||||
|
||||
// ASTVector represents a vector of Z3 ASTs.
|
||||
type ASTVector struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_ast_vector
|
||||
}
|
||||
|
||||
// newASTVector creates a new ASTVector and manages its reference count.
|
||||
func newASTVector(ctx *Context, ptr C.Z3_ast_vector) *ASTVector {
|
||||
v := &ASTVector{ctx: ctx, ptr: ptr}
|
||||
C.Z3_ast_vector_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(v, func(vec *ASTVector) {
|
||||
C.Z3_ast_vector_dec_ref(vec.ctx.ptr, vec.ptr)
|
||||
})
|
||||
return v
|
||||
}
|
||||
|
||||
// ParamDescrs represents parameter descriptions for Z3 objects.
|
||||
type ParamDescrs struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_param_descrs
|
||||
}
|
||||
|
||||
// newParamDescrs creates a new ParamDescrs and manages its reference count.
|
||||
func newParamDescrs(ctx *Context, ptr C.Z3_param_descrs) *ParamDescrs {
|
||||
pd := &ParamDescrs{ctx: ctx, ptr: ptr}
|
||||
C.Z3_param_descrs_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(pd, func(descrs *ParamDescrs) {
|
||||
C.Z3_param_descrs_dec_ref(descrs.ctx.ptr, descrs.ptr)
|
||||
})
|
||||
return pd
|
||||
}
|
||||
|
||||
// MkTrue creates the Boolean constant true.
|
||||
func (c *Context) MkTrue() *Expr {
|
||||
return newExpr(c, C.Z3_mk_true(c.ptr))
|
||||
}
|
||||
|
||||
// MkFalse creates the Boolean constant false.
|
||||
func (c *Context) MkFalse() *Expr {
|
||||
return newExpr(c, C.Z3_mk_false(c.ptr))
|
||||
}
|
||||
|
||||
// MkBool creates a Boolean constant.
|
||||
func (c *Context) MkBool(value bool) *Expr {
|
||||
if value {
|
||||
return c.MkTrue()
|
||||
}
|
||||
return c.MkFalse()
|
||||
}
|
||||
|
||||
// MkNumeral creates a numeral from a string.
|
||||
func (c *Context) MkNumeral(numeral string, sort *Sort) *Expr {
|
||||
cStr := C.CString(numeral)
|
||||
defer C.free(unsafe.Pointer(cStr))
|
||||
return newExpr(c, C.Z3_mk_numeral(c.ptr, cStr, sort.ptr))
|
||||
}
|
||||
|
||||
// MkConst creates a constant (variable) with the given name and sort.
|
||||
func (c *Context) MkConst(name *Symbol, sort *Sort) *Expr {
|
||||
return newExpr(c, C.Z3_mk_const(c.ptr, name.ptr, sort.ptr))
|
||||
}
|
||||
|
||||
// MkBoolConst creates a Boolean constant (variable) with the given name.
|
||||
func (c *Context) MkBoolConst(name string) *Expr {
|
||||
sym := c.MkStringSymbol(name)
|
||||
return c.MkConst(sym, c.MkBoolSort())
|
||||
}
|
||||
|
||||
// Boolean operations
|
||||
|
||||
// MkAnd creates a conjunction.
|
||||
func (c *Context) MkAnd(exprs ...*Expr) *Expr {
|
||||
if len(exprs) == 0 {
|
||||
return c.MkTrue()
|
||||
}
|
||||
if len(exprs) == 1 {
|
||||
return exprs[0]
|
||||
}
|
||||
cExprs := make([]C.Z3_ast, len(exprs))
|
||||
for i, e := range exprs {
|
||||
cExprs[i] = e.ptr
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_and(c.ptr, C.uint(len(exprs)), &cExprs[0]))
|
||||
}
|
||||
|
||||
// MkOr creates a disjunction.
|
||||
func (c *Context) MkOr(exprs ...*Expr) *Expr {
|
||||
if len(exprs) == 0 {
|
||||
return c.MkFalse()
|
||||
}
|
||||
if len(exprs) == 1 {
|
||||
return exprs[0]
|
||||
}
|
||||
cExprs := make([]C.Z3_ast, len(exprs))
|
||||
for i, e := range exprs {
|
||||
cExprs[i] = e.ptr
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_or(c.ptr, C.uint(len(exprs)), &cExprs[0]))
|
||||
}
|
||||
|
||||
// MkNot creates a negation.
|
||||
func (c *Context) MkNot(expr *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_not(c.ptr, expr.ptr))
|
||||
}
|
||||
|
||||
// MkImplies creates an implication.
|
||||
func (c *Context) MkImplies(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_implies(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkIff creates a bi-implication (if and only if).
|
||||
func (c *Context) MkIff(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_iff(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkXor creates exclusive or.
|
||||
func (c *Context) MkXor(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_xor(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// Comparison operations
|
||||
|
||||
// MkEq creates an equality.
|
||||
func (c *Context) MkEq(lhs, rhs *Expr) *Expr {
|
||||
return newExpr(c, C.Z3_mk_eq(c.ptr, lhs.ptr, rhs.ptr))
|
||||
}
|
||||
|
||||
// MkDistinct creates a distinct constraint.
|
||||
func (c *Context) MkDistinct(exprs ...*Expr) *Expr {
|
||||
if len(exprs) <= 1 {
|
||||
return c.MkTrue()
|
||||
}
|
||||
cExprs := make([]C.Z3_ast, len(exprs))
|
||||
for i, e := range exprs {
|
||||
cExprs[i] = e.ptr
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_distinct(c.ptr, C.uint(len(exprs)), &cExprs[0]))
|
||||
}
|
||||
|
||||
// FuncDecl represents a function declaration.
|
||||
type FuncDecl struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_func_decl
|
||||
}
|
||||
|
||||
// newFuncDecl creates a new FuncDecl and manages its reference count.
|
||||
func newFuncDecl(ctx *Context, ptr C.Z3_func_decl) *FuncDecl {
|
||||
fd := &FuncDecl{ctx: ctx, ptr: ptr}
|
||||
C.Z3_inc_ref(ctx.ptr, C.Z3_func_decl_to_ast(ctx.ptr, ptr))
|
||||
runtime.SetFinalizer(fd, func(f *FuncDecl) {
|
||||
C.Z3_dec_ref(f.ctx.ptr, C.Z3_func_decl_to_ast(f.ctx.ptr, f.ptr))
|
||||
})
|
||||
return fd
|
||||
}
|
||||
|
||||
// String returns the string representation of the function declaration.
|
||||
func (f *FuncDecl) String() string {
|
||||
return C.GoString(C.Z3_func_decl_to_string(f.ctx.ptr, f.ptr))
|
||||
}
|
||||
|
||||
// GetName returns the name of the function declaration.
|
||||
func (f *FuncDecl) GetName() *Symbol {
|
||||
return &Symbol{
|
||||
ctx: f.ctx,
|
||||
ptr: C.Z3_get_decl_name(f.ctx.ptr, f.ptr),
|
||||
}
|
||||
}
|
||||
|
||||
// GetArity returns the arity (number of parameters) of the function.
|
||||
func (f *FuncDecl) GetArity() int {
|
||||
return int(C.Z3_get_arity(f.ctx.ptr, f.ptr))
|
||||
}
|
||||
|
||||
// GetDomain returns the sort of the i-th parameter.
|
||||
func (f *FuncDecl) GetDomain(i int) *Sort {
|
||||
return newSort(f.ctx, C.Z3_get_domain(f.ctx.ptr, f.ptr, C.uint(i)))
|
||||
}
|
||||
|
||||
// GetRange returns the sort of the return value.
|
||||
func (f *FuncDecl) GetRange() *Sort {
|
||||
return newSort(f.ctx, C.Z3_get_range(f.ctx.ptr, f.ptr))
|
||||
}
|
||||
|
||||
// MkFuncDecl creates a function declaration.
|
||||
func (c *Context) MkFuncDecl(name *Symbol, domain []*Sort, range_ *Sort) *FuncDecl {
|
||||
cDomain := make([]C.Z3_sort, len(domain))
|
||||
for i, s := range domain {
|
||||
cDomain[i] = s.ptr
|
||||
}
|
||||
var domainPtr *C.Z3_sort
|
||||
if len(domain) > 0 {
|
||||
domainPtr = &cDomain[0]
|
||||
}
|
||||
return newFuncDecl(c, C.Z3_mk_func_decl(c.ptr, name.ptr, C.uint(len(domain)), domainPtr, range_.ptr))
|
||||
}
|
||||
|
||||
// MkApp creates a function application.
|
||||
func (c *Context) MkApp(decl *FuncDecl, args ...*Expr) *Expr {
|
||||
cArgs := make([]C.Z3_ast, len(args))
|
||||
for i, a := range args {
|
||||
cArgs[i] = a.ptr
|
||||
}
|
||||
var argsPtr *C.Z3_ast
|
||||
if len(args) > 0 {
|
||||
argsPtr = &cArgs[0]
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_app(c.ptr, decl.ptr, C.uint(len(args)), argsPtr))
|
||||
}
|
||||
|
||||
// Quantifier operations
|
||||
|
||||
// MkForall creates a universal quantifier.
|
||||
func (c *Context) MkForall(bound []*Expr, body *Expr) *Expr {
|
||||
cBound := make([]C.Z3_app, len(bound))
|
||||
for i, b := range bound {
|
||||
// Z3_app is a subtype of Z3_ast; constants are apps
|
||||
cBound[i] = (C.Z3_app)(unsafe.Pointer(b.ptr))
|
||||
}
|
||||
var boundPtr *C.Z3_app
|
||||
if len(bound) > 0 {
|
||||
boundPtr = &cBound[0]
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_forall_const(c.ptr, 0, C.uint(len(bound)), boundPtr, 0, nil, body.ptr))
|
||||
}
|
||||
|
||||
// MkExists creates an existential quantifier.
|
||||
func (c *Context) MkExists(bound []*Expr, body *Expr) *Expr {
|
||||
cBound := make([]C.Z3_app, len(bound))
|
||||
for i, b := range bound {
|
||||
// Z3_app is a subtype of Z3_ast; constants are apps
|
||||
cBound[i] = (C.Z3_app)(unsafe.Pointer(b.ptr))
|
||||
}
|
||||
var boundPtr *C.Z3_app
|
||||
if len(bound) > 0 {
|
||||
boundPtr = &cBound[0]
|
||||
}
|
||||
return newExpr(c, C.Z3_mk_exists_const(c.ptr, 0, C.uint(len(bound)), boundPtr, 0, nil, body.ptr))
|
||||
}
|
||||
|
||||
// Simplify simplifies an expression.
|
||||
func (e *Expr) Simplify() *Expr {
|
||||
return newExpr(e.ctx, C.Z3_simplify(e.ctx.ptr, e.ptr))
|
||||
}
|
||||
|
||||
// MkTypeVariable creates a type variable sort for use in polymorphic functions and datatypes
|
||||
func (c *Context) MkTypeVariable(name *Symbol) *Sort {
|
||||
return newSort(c, C.Z3_mk_type_variable(c.ptr, name.ptr))
|
||||
}
|
||||
|
||||
// Quantifier represents a quantified formula (forall or exists)
|
||||
type Quantifier struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_ast
|
||||
}
|
||||
|
||||
// newQuantifier creates a new Quantifier with proper memory management
|
||||
func newQuantifier(ctx *Context, ptr C.Z3_ast) *Quantifier {
|
||||
q := &Quantifier{ctx: ctx, ptr: ptr}
|
||||
C.Z3_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(q, func(qf *Quantifier) {
|
||||
C.Z3_dec_ref(qf.ctx.ptr, qf.ptr)
|
||||
})
|
||||
return q
|
||||
}
|
||||
|
||||
// AsExpr converts a Quantifier to an Expr
|
||||
func (q *Quantifier) AsExpr() *Expr {
|
||||
return newExpr(q.ctx, q.ptr)
|
||||
}
|
||||
|
||||
// IsUniversal returns true if this is a universal quantifier (forall)
|
||||
func (q *Quantifier) IsUniversal() bool {
|
||||
return bool(C.Z3_is_quantifier_forall(q.ctx.ptr, q.ptr))
|
||||
}
|
||||
|
||||
// IsExistential returns true if this is an existential quantifier (exists)
|
||||
func (q *Quantifier) IsExistential() bool {
|
||||
return bool(C.Z3_is_quantifier_exists(q.ctx.ptr, q.ptr))
|
||||
}
|
||||
|
||||
// GetWeight returns the weight of the quantifier
|
||||
func (q *Quantifier) GetWeight() int {
|
||||
return int(C.Z3_get_quantifier_weight(q.ctx.ptr, q.ptr))
|
||||
}
|
||||
|
||||
// GetNumPatterns returns the number of patterns
|
||||
func (q *Quantifier) GetNumPatterns() int {
|
||||
return int(C.Z3_get_quantifier_num_patterns(q.ctx.ptr, q.ptr))
|
||||
}
|
||||
|
||||
// GetPattern returns the pattern at the given index
|
||||
func (q *Quantifier) GetPattern(idx int) *Pattern {
|
||||
ptr := C.Z3_get_quantifier_pattern_ast(q.ctx.ptr, q.ptr, C.uint(idx))
|
||||
return newPattern(q.ctx, ptr)
|
||||
}
|
||||
|
||||
// GetNumNoPatterns returns the number of no-patterns
|
||||
func (q *Quantifier) GetNumNoPatterns() int {
|
||||
return int(C.Z3_get_quantifier_num_no_patterns(q.ctx.ptr, q.ptr))
|
||||
}
|
||||
|
||||
// GetNoPattern returns the no-pattern at the given index
|
||||
func (q *Quantifier) GetNoPattern(idx int) *Pattern {
|
||||
ptr := C.Z3_get_quantifier_no_pattern_ast(q.ctx.ptr, q.ptr, C.uint(idx))
|
||||
return newPattern(q.ctx, (C.Z3_pattern)(unsafe.Pointer(ptr)))
|
||||
}
|
||||
|
||||
// GetNumBound returns the number of bound variables
|
||||
func (q *Quantifier) GetNumBound() int {
|
||||
return int(C.Z3_get_quantifier_num_bound(q.ctx.ptr, q.ptr))
|
||||
}
|
||||
|
||||
// GetBoundName returns the name of the bound variable at the given index
|
||||
func (q *Quantifier) GetBoundName(idx int) *Symbol {
|
||||
ptr := C.Z3_get_quantifier_bound_name(q.ctx.ptr, q.ptr, C.uint(idx))
|
||||
return newSymbol(q.ctx, ptr)
|
||||
}
|
||||
|
||||
// GetBoundSort returns the sort of the bound variable at the given index
|
||||
func (q *Quantifier) GetBoundSort(idx int) *Sort {
|
||||
ptr := C.Z3_get_quantifier_bound_sort(q.ctx.ptr, q.ptr, C.uint(idx))
|
||||
return newSort(q.ctx, ptr)
|
||||
}
|
||||
|
||||
// GetBody returns the body of the quantifier
|
||||
func (q *Quantifier) GetBody() *Expr {
|
||||
ptr := C.Z3_get_quantifier_body(q.ctx.ptr, q.ptr)
|
||||
return newExpr(q.ctx, ptr)
|
||||
}
|
||||
|
||||
// String returns the string representation of the quantifier
|
||||
func (q *Quantifier) String() string {
|
||||
return q.AsExpr().String()
|
||||
}
|
||||
|
||||
// MkQuantifier creates a quantifier with patterns
|
||||
func (c *Context) MkQuantifier(isForall bool, weight int, sorts []*Sort, names []*Symbol, body *Expr, patterns []*Pattern) *Quantifier {
|
||||
var forallInt C.bool
|
||||
if isForall {
|
||||
forallInt = true
|
||||
} else {
|
||||
forallInt = false
|
||||
}
|
||||
|
||||
numBound := len(sorts)
|
||||
if numBound != len(names) {
|
||||
panic("Number of sorts must match number of names")
|
||||
}
|
||||
|
||||
var cSorts []C.Z3_sort
|
||||
var cNames []C.Z3_symbol
|
||||
if numBound > 0 {
|
||||
cSorts = make([]C.Z3_sort, numBound)
|
||||
cNames = make([]C.Z3_symbol, numBound)
|
||||
for i := 0; i < numBound; i++ {
|
||||
cSorts[i] = sorts[i].ptr
|
||||
cNames[i] = names[i].ptr
|
||||
}
|
||||
}
|
||||
|
||||
var cPatterns []C.Z3_pattern
|
||||
var patternsPtr *C.Z3_pattern
|
||||
numPatterns := len(patterns)
|
||||
if numPatterns > 0 {
|
||||
cPatterns = make([]C.Z3_pattern, numPatterns)
|
||||
for i := 0; i < numPatterns; i++ {
|
||||
cPatterns[i] = patterns[i].ptr
|
||||
}
|
||||
patternsPtr = &cPatterns[0]
|
||||
}
|
||||
|
||||
var sortsPtr *C.Z3_sort
|
||||
var namesPtr *C.Z3_symbol
|
||||
if numBound > 0 {
|
||||
sortsPtr = &cSorts[0]
|
||||
namesPtr = &cNames[0]
|
||||
}
|
||||
|
||||
ptr := C.Z3_mk_quantifier(c.ptr, forallInt, C.uint(weight), C.uint(numPatterns), patternsPtr,
|
||||
C.uint(numBound), sortsPtr, namesPtr, body.ptr)
|
||||
return newQuantifier(c, ptr)
|
||||
}
|
||||
|
||||
// MkQuantifierConst creates a quantifier using constant bound variables
|
||||
func (c *Context) MkQuantifierConst(isForall bool, weight int, bound []*Expr, body *Expr, patterns []*Pattern) *Quantifier {
|
||||
var forallInt C.bool
|
||||
if isForall {
|
||||
forallInt = true
|
||||
} else {
|
||||
forallInt = false
|
||||
}
|
||||
|
||||
numBound := len(bound)
|
||||
var cBound []C.Z3_app
|
||||
var boundPtr *C.Z3_app
|
||||
if numBound > 0 {
|
||||
cBound = make([]C.Z3_app, numBound)
|
||||
for i := 0; i < numBound; i++ {
|
||||
cBound[i] = (C.Z3_app)(unsafe.Pointer(bound[i].ptr))
|
||||
}
|
||||
boundPtr = &cBound[0]
|
||||
}
|
||||
|
||||
var cPatterns []C.Z3_pattern
|
||||
var patternsPtr *C.Z3_pattern
|
||||
numPatterns := len(patterns)
|
||||
if numPatterns > 0 {
|
||||
cPatterns = make([]C.Z3_pattern, numPatterns)
|
||||
for i := 0; i < numPatterns; i++ {
|
||||
cPatterns[i] = patterns[i].ptr
|
||||
}
|
||||
patternsPtr = &cPatterns[0]
|
||||
}
|
||||
|
||||
ptr := C.Z3_mk_quantifier_const(c.ptr, forallInt, C.uint(weight), C.uint(numBound), boundPtr,
|
||||
C.uint(numPatterns), patternsPtr, body.ptr)
|
||||
return newQuantifier(c, ptr)
|
||||
}
|
||||
|
||||
// Lambda represents a lambda expression
|
||||
type Lambda struct {
|
||||
ctx *Context
|
||||
ptr C.Z3_ast
|
||||
}
|
||||
|
||||
// newLambda creates a new Lambda with proper memory management
|
||||
func newLambda(ctx *Context, ptr C.Z3_ast) *Lambda {
|
||||
l := &Lambda{ctx: ctx, ptr: ptr}
|
||||
C.Z3_inc_ref(ctx.ptr, ptr)
|
||||
runtime.SetFinalizer(l, func(lam *Lambda) {
|
||||
C.Z3_dec_ref(lam.ctx.ptr, lam.ptr)
|
||||
})
|
||||
return l
|
||||
}
|
||||
|
||||
// AsExpr converts a Lambda to an Expr
|
||||
func (l *Lambda) AsExpr() *Expr {
|
||||
return newExpr(l.ctx, l.ptr)
|
||||
}
|
||||
|
||||
// GetNumBound returns the number of bound variables
|
||||
func (l *Lambda) GetNumBound() int {
|
||||
return int(C.Z3_get_quantifier_num_bound(l.ctx.ptr, l.ptr))
|
||||
}
|
||||
|
||||
// GetBoundName returns the name of the bound variable at the given index
|
||||
func (l *Lambda) GetBoundName(idx int) *Symbol {
|
||||
ptr := C.Z3_get_quantifier_bound_name(l.ctx.ptr, l.ptr, C.uint(idx))
|
||||
return newSymbol(l.ctx, ptr)
|
||||
}
|
||||
|
||||
// GetBoundSort returns the sort of the bound variable at the given index
|
||||
func (l *Lambda) GetBoundSort(idx int) *Sort {
|
||||
ptr := C.Z3_get_quantifier_bound_sort(l.ctx.ptr, l.ptr, C.uint(idx))
|
||||
return newSort(l.ctx, ptr)
|
||||
}
|
||||
|
||||
// GetBody returns the body of the lambda expression
|
||||
func (l *Lambda) GetBody() *Expr {
|
||||
ptr := C.Z3_get_quantifier_body(l.ctx.ptr, l.ptr)
|
||||
return newExpr(l.ctx, ptr)
|
||||
}
|
||||
|
||||
// String returns the string representation of the lambda
|
||||
func (l *Lambda) String() string {
|
||||
return l.AsExpr().String()
|
||||
}
|
||||
|
||||
// MkLambda creates a lambda expression with sorts and names
|
||||
func (c *Context) MkLambda(sorts []*Sort, names []*Symbol, body *Expr) *Lambda {
|
||||
numBound := len(sorts)
|
||||
if numBound != len(names) {
|
||||
panic("Number of sorts must match number of names")
|
||||
}
|
||||
|
||||
var cSorts []C.Z3_sort
|
||||
var cNames []C.Z3_symbol
|
||||
var sortsPtr *C.Z3_sort
|
||||
var namesPtr *C.Z3_symbol
|
||||
|
||||
if numBound > 0 {
|
||||
cSorts = make([]C.Z3_sort, numBound)
|
||||
cNames = make([]C.Z3_symbol, numBound)
|
||||
for i := 0; i < numBound; i++ {
|
||||
cSorts[i] = sorts[i].ptr
|
||||
cNames[i] = names[i].ptr
|
||||
}
|
||||
sortsPtr = &cSorts[0]
|
||||
namesPtr = &cNames[0]
|
||||
}
|
||||
|
||||
ptr := C.Z3_mk_lambda(c.ptr, C.uint(numBound), sortsPtr, namesPtr, body.ptr)
|
||||
return newLambda(c, ptr)
|
||||
}
|
||||
|
||||
// MkLambdaConst creates a lambda expression using constant bound variables
|
||||
func (c *Context) MkLambdaConst(bound []*Expr, body *Expr) *Lambda {
|
||||
numBound := len(bound)
|
||||
var cBound []C.Z3_app
|
||||
var boundPtr *C.Z3_app
|
||||
|
||||
if numBound > 0 {
|
||||
cBound = make([]C.Z3_app, numBound)
|
||||
for i := 0; i < numBound; i++ {
|
||||
cBound[i] = (C.Z3_app)(unsafe.Pointer(bound[i].ptr))
|
||||
}
|
||||
boundPtr = &cBound[0]
|
||||
}
|
||||
|
||||
ptr := C.Z3_mk_lambda_const(c.ptr, C.uint(numBound), boundPtr, body.ptr)
|
||||
return newLambda(c, ptr)
|
||||
}
|
||||
|
||||
// astVectorToExprs converts a Z3_ast_vector to a slice of Expr.
|
||||
// This function properly manages the reference count of the vector by
|
||||
// incrementing it on entry and decrementing it on exit.
|
||||
// The individual AST elements are already reference counted by newExpr.
|
||||
func astVectorToExprs(ctx *Context, vec C.Z3_ast_vector) []*Expr {
|
||||
// Increment reference count for the vector since we're using it
|
||||
C.Z3_ast_vector_inc_ref(ctx.ptr, vec)
|
||||
defer C.Z3_ast_vector_dec_ref(ctx.ptr, vec)
|
||||
|
||||
size := uint(C.Z3_ast_vector_size(ctx.ptr, vec))
|
||||
result := make([]*Expr, size)
|
||||
for i := uint(0); i < size; i++ {
|
||||
result[i] = newExpr(ctx, C.Z3_ast_vector_get(ctx.ptr, vec, C.uint(i)))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
@ -48,8 +48,17 @@ target_include_directories(z3java PRIVATE
|
|||
"${PROJECT_BINARY_DIR}/src/api"
|
||||
${JNI_INCLUDE_DIRS}
|
||||
)
|
||||
# Add header padding for macOS to allow install_name_tool to modify the dylib
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
target_link_options(z3java PRIVATE "-Wl,-headerpad_max_install_names")
|
||||
endif()
|
||||
# FIXME: Should this library have SONAME and VERSION set?
|
||||
|
||||
# On macOS, add headerpad for install_name_tool compatibility
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
target_link_options(z3java PRIVATE "-Wl,-headerpad_max_install_names")
|
||||
endif()
|
||||
|
||||
# This prevents CMake from automatically defining ``z3java_EXPORTS``
|
||||
set_property(TARGET z3java PROPERTY DEFINE_SYMBOL "")
|
||||
|
||||
|
|
@ -115,6 +124,7 @@ set(Z3_JAVA_JAR_SOURCE_FILES
|
|||
FiniteDomainExpr.java
|
||||
FiniteDomainNum.java
|
||||
FiniteDomainSort.java
|
||||
FiniteSetSort.java
|
||||
Fixedpoint.java
|
||||
FPExpr.java
|
||||
FPNum.java
|
||||
|
|
@ -158,6 +168,7 @@ set(Z3_JAVA_JAR_SOURCE_FILES
|
|||
Symbol.java
|
||||
Tactic.java
|
||||
TupleSort.java
|
||||
TypeVarSort.java
|
||||
UninterpretedSort.java
|
||||
UserPropagatorBase.java
|
||||
Version.java
|
||||
|
|
@ -187,6 +198,8 @@ add_custom_target(build_z3_java_bindings
|
|||
# TODO: Should we set ``CMAKE_JNI_TARGET`` to ``TRUE``?
|
||||
# REMARK: removed VERSION to fix issue with using this to create installations.
|
||||
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS -source 1.8 -target 1.8)
|
||||
|
||||
add_jar(z3JavaJar
|
||||
SOURCES ${Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH}
|
||||
OUTPUT_NAME ${Z3_JAVA_PACKAGE_NAME}
|
||||
|
|
|
|||
|
|
@ -473,6 +473,88 @@ public class Context implements AutoCloseable {
|
|||
return mkDatatypeSorts(mkSymbols(names), c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a type variable for use in polymorphic functions and datatypes.
|
||||
* Type variables can be used as sort parameters in polymorphic datatypes.
|
||||
* @param name name of the type variable
|
||||
* @return a new type variable sort
|
||||
**/
|
||||
public TypeVarSort mkTypeVariable(Symbol name)
|
||||
{
|
||||
checkContextMatch(name);
|
||||
return new TypeVarSort(this, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a type variable for use in polymorphic functions and datatypes.
|
||||
* Type variables can be used as sort parameters in polymorphic datatypes.
|
||||
* @param name name of the type variable
|
||||
* @return a new type variable sort
|
||||
**/
|
||||
public TypeVarSort mkTypeVariable(String name)
|
||||
{
|
||||
return mkTypeVariable(mkSymbol(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a polymorphic (parametric) datatype sort.
|
||||
* A polymorphic datatype is parameterized by type variables, allowing it to
|
||||
* work with different types. This is similar to generic types in programming languages.
|
||||
*
|
||||
* @param name name of the datatype sort
|
||||
* @param parameters array of type variable sorts to parameterize the datatype
|
||||
* @param constructors array of constructor specifications
|
||||
* @return a new polymorphic datatype sort
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
* // Create a polymorphic List datatype: List[T]
|
||||
* TypeVarSort T = ctx.mkTypeVariable("T");
|
||||
* Constructor<Object> nil = ctx.mkConstructor("nil", "is_nil", null, null, null);
|
||||
* Constructor<Object> cons = ctx.mkConstructor("cons", "is_cons",
|
||||
* new String[]{"head", "tail"},
|
||||
* new Sort[]{T, null}, // head has type T, tail is recursive reference
|
||||
* new int[]{0, 0}); // sortRef 0 refers back to List[T]
|
||||
* DatatypeSort<Object> listSort = ctx.mkPolymorphicDatatypeSort("List",
|
||||
* new Sort[]{T}, new Constructor[]{nil, cons});
|
||||
* </pre>
|
||||
**/
|
||||
public <R> DatatypeSort<R> mkPolymorphicDatatypeSort(Symbol name, Sort[] parameters, Constructor<R>[] constructors)
|
||||
{
|
||||
checkContextMatch(name);
|
||||
checkContextMatch(parameters);
|
||||
checkContextMatch(constructors);
|
||||
|
||||
int numParams = parameters.length;
|
||||
long[] paramsNative = AST.arrayToNative(parameters);
|
||||
|
||||
int numConstructors = constructors.length;
|
||||
long[] constructorsNative = new long[numConstructors];
|
||||
for (int i = 0; i < numConstructors; i++) {
|
||||
constructorsNative[i] = constructors[i].getNativeObject();
|
||||
}
|
||||
|
||||
long nativeSort = Native.mkPolymorphicDatatype(nCtx(), name.getNativeObject(),
|
||||
numParams, paramsNative, numConstructors, constructorsNative);
|
||||
|
||||
return new DatatypeSort<>(this, nativeSort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a polymorphic (parametric) datatype sort.
|
||||
* A polymorphic datatype is parameterized by type variables, allowing it to
|
||||
* work with different types. This is similar to generic types in programming languages.
|
||||
*
|
||||
* @param name name of the datatype sort
|
||||
* @param parameters array of type variable sorts to parameterize the datatype
|
||||
* @param constructors array of constructor specifications
|
||||
* @return a new polymorphic datatype sort
|
||||
**/
|
||||
public <R> DatatypeSort<R> mkPolymorphicDatatypeSort(String name, Sort[] parameters, Constructor<R>[] constructors)
|
||||
{
|
||||
return mkPolymorphicDatatypeSort(mkSymbol(name), parameters, constructors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a datatype field at expression t with value v.
|
||||
* The function performs a record update at t. The field
|
||||
|
|
@ -2052,6 +2134,145 @@ public class Context implements AutoCloseable {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finite Sets
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a finite set sort over the given element sort.
|
||||
**/
|
||||
public final FiniteSetSort mkFiniteSetSort(Sort elemSort)
|
||||
{
|
||||
checkContextMatch(elemSort);
|
||||
return new FiniteSetSort(this, elemSort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a sort is a finite set sort.
|
||||
**/
|
||||
public final boolean isFiniteSetSort(Sort s)
|
||||
{
|
||||
checkContextMatch(s);
|
||||
return Native.isFiniteSetSort(nCtx(), s.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element sort (basis) of a finite set sort.
|
||||
**/
|
||||
public final Sort getFiniteSetSortBasis(Sort s)
|
||||
{
|
||||
checkContextMatch(s);
|
||||
return Sort.create(this, Native.getFiniteSetSortBasis(nCtx(), s.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty finite set.
|
||||
**/
|
||||
public final Expr mkFiniteSetEmpty(Sort setSort)
|
||||
{
|
||||
checkContextMatch(setSort);
|
||||
return Expr.create(this, Native.mkFiniteSetEmpty(nCtx(), setSort.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a singleton finite set.
|
||||
**/
|
||||
public final Expr mkFiniteSetSingleton(Expr elem)
|
||||
{
|
||||
checkContextMatch(elem);
|
||||
return Expr.create(this, Native.mkFiniteSetSingleton(nCtx(), elem.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the union of two finite sets.
|
||||
**/
|
||||
public final Expr mkFiniteSetUnion(Expr s1, Expr s2)
|
||||
{
|
||||
checkContextMatch(s1);
|
||||
checkContextMatch(s2);
|
||||
return Expr.create(this, Native.mkFiniteSetUnion(nCtx(), s1.getNativeObject(), s2.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the intersection of two finite sets.
|
||||
**/
|
||||
public final Expr mkFiniteSetIntersect(Expr s1, Expr s2)
|
||||
{
|
||||
checkContextMatch(s1);
|
||||
checkContextMatch(s2);
|
||||
return Expr.create(this, Native.mkFiniteSetIntersect(nCtx(), s1.getNativeObject(), s2.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the difference of two finite sets.
|
||||
**/
|
||||
public final Expr mkFiniteSetDifference(Expr s1, Expr s2)
|
||||
{
|
||||
checkContextMatch(s1);
|
||||
checkContextMatch(s2);
|
||||
return Expr.create(this, Native.mkFiniteSetDifference(nCtx(), s1.getNativeObject(), s2.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for membership in a finite set.
|
||||
**/
|
||||
public final BoolExpr mkFiniteSetMember(Expr elem, Expr set)
|
||||
{
|
||||
checkContextMatch(elem);
|
||||
checkContextMatch(set);
|
||||
return (BoolExpr) Expr.create(this, Native.mkFiniteSetMember(nCtx(), elem.getNativeObject(), set.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cardinality of a finite set.
|
||||
**/
|
||||
public final Expr mkFiniteSetSize(Expr set)
|
||||
{
|
||||
checkContextMatch(set);
|
||||
return Expr.create(this, Native.mkFiniteSetSize(nCtx(), set.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if one finite set is a subset of another.
|
||||
**/
|
||||
public final BoolExpr mkFiniteSetSubset(Expr s1, Expr s2)
|
||||
{
|
||||
checkContextMatch(s1);
|
||||
checkContextMatch(s2);
|
||||
return (BoolExpr) Expr.create(this, Native.mkFiniteSetSubset(nCtx(), s1.getNativeObject(), s2.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a function over all elements in a finite set.
|
||||
**/
|
||||
public final Expr mkFiniteSetMap(Expr f, Expr set)
|
||||
{
|
||||
checkContextMatch(f);
|
||||
checkContextMatch(set);
|
||||
return Expr.create(this, Native.mkFiniteSetMap(nCtx(), f.getNativeObject(), set.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a finite set with a predicate.
|
||||
**/
|
||||
public final Expr mkFiniteSetFilter(Expr f, Expr set)
|
||||
{
|
||||
checkContextMatch(f);
|
||||
checkContextMatch(set);
|
||||
return Expr.create(this, Native.mkFiniteSetFilter(nCtx(), f.getNativeObject(), set.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a finite set containing integers in the range [low, high].
|
||||
**/
|
||||
public final Expr mkFiniteSetRange(Expr low, Expr high)
|
||||
{
|
||||
checkContextMatch(low);
|
||||
checkContextMatch(high);
|
||||
return Expr.create(this, Native.mkFiniteSetRange(nCtx(), low.getNativeObject(), high.getNativeObject()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sequences, Strings and regular expressions.
|
||||
*/
|
||||
|
|
@ -2235,6 +2456,46 @@ public class Context implements AutoCloseable {
|
|||
return (IntExpr)Expr.create(this, Native.mkSeqLastIndex(nCtx(), s.getNativeObject(), substr.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Map function f over sequence s.
|
||||
* Returns a new sequence where f is applied to each element of s.
|
||||
*/
|
||||
public final <R extends Sort> SeqExpr<R> mkSeqMap(Expr<?> f, Expr<SeqSort<R>> s)
|
||||
{
|
||||
checkContextMatch(f, s);
|
||||
return (SeqExpr<R>) Expr.create(this, Native.mkSeqMap(nCtx(), f.getNativeObject(), s.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Map function f over sequence s starting at index i.
|
||||
* Returns a new sequence where f is applied to each element of s along with its index starting from i.
|
||||
*/
|
||||
public final <R extends Sort> SeqExpr<R> mkSeqMapi(Expr<?> f, Expr<IntSort> i, Expr<SeqSort<R>> s)
|
||||
{
|
||||
checkContextMatch(f, i, s);
|
||||
return (SeqExpr<R>) Expr.create(this, Native.mkSeqMapi(nCtx(), f.getNativeObject(), i.getNativeObject(), s.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Left fold of function f over sequence s with accumulator a.
|
||||
* Applies f to accumulate values from left to right over the sequence.
|
||||
*/
|
||||
public final <R extends Sort, A extends Sort> Expr<A> mkSeqFoldl(Expr<?> f, Expr<A> a, Expr<SeqSort<R>> s)
|
||||
{
|
||||
checkContextMatch(f, a, s);
|
||||
return (Expr<A>) Expr.create(this, Native.mkSeqFoldl(nCtx(), f.getNativeObject(), a.getNativeObject(), s.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Left fold of function f over sequence s with accumulator a starting at index i.
|
||||
* Applies f to accumulate values from left to right over the sequence, tracking the index starting from i.
|
||||
*/
|
||||
public final <R extends Sort, A extends Sort> Expr<A> mkSeqFoldli(Expr<?> f, Expr<IntSort> i, Expr<A> a, Expr<SeqSort<R>> s)
|
||||
{
|
||||
checkContextMatch(f, i, a, s);
|
||||
return (Expr<A>) Expr.create(this, Native.mkSeqFoldli(nCtx(), f.getNativeObject(), i.getNativeObject(), a.getNativeObject(), s.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the first occurrence of src by dst in s.
|
||||
*/
|
||||
|
|
@ -4291,7 +4552,7 @@ public class Context implements AutoCloseable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates or a partial order.
|
||||
* Creates a partial order.
|
||||
* @param index The index of the order.
|
||||
* @param sort The sort of the order.
|
||||
*/
|
||||
|
|
@ -4306,6 +4567,40 @@ public class Context implements AutoCloseable {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the transitive closure of a binary relation.
|
||||
* The resulting relation is recursive.
|
||||
* @param f function declaration of a binary relation
|
||||
*/
|
||||
public final <R extends Sort> FuncDecl<BoolSort> mkTransitiveClosure(FuncDecl<BoolSort> f) {
|
||||
return (FuncDecl<BoolSort>) FuncDecl.create(
|
||||
this,
|
||||
Native.mkTransitiveClosure(
|
||||
nCtx(),
|
||||
f.getNativeObject()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the nonzero subresultants of p and q with respect to the "variable" x.
|
||||
* Note that any subterm that cannot be viewed as a polynomial is assumed to be a variable.
|
||||
* @param p arithmetic term
|
||||
* @param q arithmetic term
|
||||
* @param x variable
|
||||
*/
|
||||
public final <R extends Sort> ASTVector polynomialSubresultants(Expr<R> p, Expr<R> q, Expr<R> x) {
|
||||
return new ASTVector(
|
||||
this,
|
||||
Native.polynomialSubresultants(
|
||||
nCtx(),
|
||||
p.getNativeObject(),
|
||||
q.getNativeObject(),
|
||||
x.getNativeObject()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an AST.
|
||||
* Remarks: This function is used for transitions between
|
||||
|
|
@ -4396,6 +4691,14 @@ public class Context implements AutoCloseable {
|
|||
checkContextMatch(other3);
|
||||
}
|
||||
|
||||
void checkContextMatch(Z3Object other1, Z3Object other2, Z3Object other3, Z3Object other4)
|
||||
{
|
||||
checkContextMatch(other1);
|
||||
checkContextMatch(other2);
|
||||
checkContextMatch(other3);
|
||||
checkContextMatch(other4);
|
||||
}
|
||||
|
||||
void checkContextMatch(Z3Object[] arr)
|
||||
{
|
||||
if (arr != null)
|
||||
|
|
|
|||
|
|
@ -188,6 +188,30 @@ public class Expr<R extends Sort> extends AST
|
|||
getNativeObject(), to.length, Expr.arrayToNative(to)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute functions in {@code from} with the expressions in {@code to}.
|
||||
* The expressions in {@code to} can have free variables. The free variable
|
||||
* in {@code to[i]} at de-Bruijn index 0 refers to the first argument of
|
||||
* {@code from[i]}, the free variable at index 1 corresponds to the second
|
||||
* argument, and so on.
|
||||
* Remarks: The arrays {@code from} and {@code to} must have the same size.
|
||||
* @param from Array of function declarations to be substituted
|
||||
* @param to Array of expressions to substitute with
|
||||
* @throws Z3Exception on error
|
||||
* @return an Expr
|
||||
**/
|
||||
public Expr<R> substituteFuns(FuncDecl<?>[] from, Expr<?>[] to)
|
||||
{
|
||||
getContext().checkContextMatch(from);
|
||||
getContext().checkContextMatch(to);
|
||||
if (from.length != to.length) {
|
||||
throw new Z3Exception("Arrays 'from' and 'to' must have the same length");
|
||||
}
|
||||
return (Expr<R>) Expr.create(getContext(), Native.substituteFuns(getContext().nCtx(),
|
||||
getNativeObject(), from.length, AST.arrayToNative(from),
|
||||
Expr.arrayToNative(to)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates (copies) the term to the Context {@code ctx}.
|
||||
*
|
||||
|
|
|
|||
42
src/api/java/FiniteSetSort.java
Normal file
42
src/api/java/FiniteSetSort.java
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
Copyright (c) 2024 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
FiniteSetSort.java
|
||||
|
||||
Abstract:
|
||||
|
||||
Author:
|
||||
|
||||
GitHub Copilot
|
||||
|
||||
Notes:
|
||||
|
||||
**/
|
||||
|
||||
package com.microsoft.z3;
|
||||
|
||||
/**
|
||||
* Finite set sorts.
|
||||
**/
|
||||
public class FiniteSetSort extends Sort
|
||||
{
|
||||
FiniteSetSort(Context ctx, long obj)
|
||||
{
|
||||
super(ctx, obj);
|
||||
}
|
||||
|
||||
FiniteSetSort(Context ctx, Sort elemSort)
|
||||
{
|
||||
super(ctx, Native.mkFiniteSetSort(ctx.nCtx(), elemSort.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element sort (basis) of this finite set sort.
|
||||
**/
|
||||
public Sort getBasis()
|
||||
{
|
||||
return Sort.create(getContext(), Native.getFiniteSetSortBasis(getContext().nCtx(), getNativeObject()));
|
||||
}
|
||||
}
|
||||
374
src/api/java/RCFNum.java
Normal file
374
src/api/java/RCFNum.java
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/**
|
||||
Copyright (c) 2024 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
RCFNum.java
|
||||
|
||||
Abstract:
|
||||
|
||||
Real Closed Field (RCF) numerals
|
||||
|
||||
Author:
|
||||
|
||||
GitHub Copilot 2024-01-12
|
||||
|
||||
Notes:
|
||||
|
||||
**/
|
||||
|
||||
package com.microsoft.z3;
|
||||
|
||||
/**
|
||||
* Real Closed Field (RCF) numerals.
|
||||
*
|
||||
* RCF numerals can represent:
|
||||
* - Rational numbers
|
||||
* - Algebraic numbers (roots of polynomials)
|
||||
* - Transcendental extensions (e.g., pi, e)
|
||||
* - Infinitesimal extensions
|
||||
**/
|
||||
public class RCFNum extends Z3Object {
|
||||
|
||||
/**
|
||||
* Create an RCF numeral from a rational string.
|
||||
* @param ctx Z3 context
|
||||
* @param value String representation of a rational number (e.g., "3/2", "0.5", "42")
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public RCFNum(Context ctx, String value) {
|
||||
super(ctx, Native.rcfMkRational(ctx.nCtx(), value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an RCF numeral from a small integer.
|
||||
* @param ctx Z3 context
|
||||
* @param value Integer value
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public RCFNum(Context ctx, int value) {
|
||||
super(ctx, Native.rcfMkSmallInt(ctx.nCtx(), value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal constructor for wrapping native RCF numeral pointers.
|
||||
**/
|
||||
RCFNum(Context ctx, long obj) {
|
||||
super(ctx, obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an RCF numeral representing pi.
|
||||
* @param ctx Z3 context
|
||||
* @return RCF numeral for pi
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public static RCFNum mkPi(Context ctx) {
|
||||
return new RCFNum(ctx, Native.rcfMkPi(ctx.nCtx()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an RCF numeral representing e (Euler's constant).
|
||||
* @param ctx Z3 context
|
||||
* @return RCF numeral for e
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public static RCFNum mkE(Context ctx) {
|
||||
return new RCFNum(ctx, Native.rcfMkE(ctx.nCtx()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an RCF numeral representing an infinitesimal.
|
||||
* @param ctx Z3 context
|
||||
* @return RCF numeral for an infinitesimal
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public static RCFNum mkInfinitesimal(Context ctx) {
|
||||
return new RCFNum(ctx, Native.rcfMkInfinitesimal(ctx.nCtx()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find roots of a polynomial.
|
||||
*
|
||||
* The polynomial is a[n-1]*x^(n-1) + ... + a[1]*x + a[0].
|
||||
*
|
||||
* @param ctx Z3 context
|
||||
* @param coefficients Polynomial coefficients (constant term first)
|
||||
* @return Array of RCF numerals representing the roots
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public static RCFNum[] mkRoots(Context ctx, RCFNum[] coefficients) {
|
||||
if (coefficients == null || coefficients.length == 0) {
|
||||
throw new Z3Exception("Polynomial coefficients cannot be empty");
|
||||
}
|
||||
|
||||
int n = coefficients.length;
|
||||
long[] a = new long[n];
|
||||
long[] roots = new long[n];
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
a[i] = coefficients[i].getNativeObject();
|
||||
}
|
||||
|
||||
int numRoots = Native.rcfMkRoots(ctx.nCtx(), n, a, roots);
|
||||
|
||||
RCFNum[] result = new RCFNum[numRoots];
|
||||
for (int i = 0; i < numRoots; i++) {
|
||||
result[i] = new RCFNum(ctx, roots[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add two RCF numerals.
|
||||
* @param other The RCF numeral to add
|
||||
* @return this + other
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public RCFNum add(RCFNum other) {
|
||||
checkContext(other);
|
||||
return new RCFNum(getContext(), Native.rcfAdd(getContext().nCtx(),
|
||||
getNativeObject(),
|
||||
other.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract two RCF numerals.
|
||||
* @param other The RCF numeral to subtract
|
||||
* @return this - other
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public RCFNum sub(RCFNum other) {
|
||||
checkContext(other);
|
||||
return new RCFNum(getContext(), Native.rcfSub(getContext().nCtx(),
|
||||
getNativeObject(),
|
||||
other.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply two RCF numerals.
|
||||
* @param other The RCF numeral to multiply
|
||||
* @return this * other
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public RCFNum mul(RCFNum other) {
|
||||
checkContext(other);
|
||||
return new RCFNum(getContext(), Native.rcfMul(getContext().nCtx(),
|
||||
getNativeObject(),
|
||||
other.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide two RCF numerals.
|
||||
* @param other The RCF numeral to divide by
|
||||
* @return this / other
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public RCFNum div(RCFNum other) {
|
||||
checkContext(other);
|
||||
return new RCFNum(getContext(), Native.rcfDiv(getContext().nCtx(),
|
||||
getNativeObject(),
|
||||
other.getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Negate this RCF numeral.
|
||||
* @return -this
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public RCFNum neg() {
|
||||
return new RCFNum(getContext(), Native.rcfNeg(getContext().nCtx(),
|
||||
getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the multiplicative inverse.
|
||||
* @return 1/this
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public RCFNum inv() {
|
||||
return new RCFNum(getContext(), Native.rcfInv(getContext().nCtx(),
|
||||
getNativeObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Raise this RCF numeral to a power.
|
||||
* @param k The exponent
|
||||
* @return this^k
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public RCFNum power(int k) {
|
||||
return new RCFNum(getContext(), Native.rcfPower(getContext().nCtx(),
|
||||
getNativeObject(), k));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this RCF numeral is less than another.
|
||||
* @param other The RCF numeral to compare with
|
||||
* @return true if this < other
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public boolean lt(RCFNum other) {
|
||||
checkContext(other);
|
||||
return Native.rcfLt(getContext().nCtx(), getNativeObject(),
|
||||
other.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this RCF numeral is greater than another.
|
||||
* @param other The RCF numeral to compare with
|
||||
* @return true if this > other
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public boolean gt(RCFNum other) {
|
||||
checkContext(other);
|
||||
return Native.rcfGt(getContext().nCtx(), getNativeObject(),
|
||||
other.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this RCF numeral is less than or equal to another.
|
||||
* @param other The RCF numeral to compare with
|
||||
* @return true if this <= other
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public boolean le(RCFNum other) {
|
||||
checkContext(other);
|
||||
return Native.rcfLe(getContext().nCtx(), getNativeObject(),
|
||||
other.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this RCF numeral is greater than or equal to another.
|
||||
* @param other The RCF numeral to compare with
|
||||
* @return true if this >= other
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public boolean ge(RCFNum other) {
|
||||
checkContext(other);
|
||||
return Native.rcfGe(getContext().nCtx(), getNativeObject(),
|
||||
other.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this RCF numeral is equal to another.
|
||||
* @param other The RCF numeral to compare with
|
||||
* @return true if this == other
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public boolean eq(RCFNum other) {
|
||||
checkContext(other);
|
||||
return Native.rcfEq(getContext().nCtx(), getNativeObject(),
|
||||
other.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this RCF numeral is not equal to another.
|
||||
* @param other The RCF numeral to compare with
|
||||
* @return true if this != other
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public boolean neq(RCFNum other) {
|
||||
checkContext(other);
|
||||
return Native.rcfNeq(getContext().nCtx(), getNativeObject(),
|
||||
other.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this RCF numeral is a rational number.
|
||||
* @return true if this is rational
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public boolean isRational() {
|
||||
return Native.rcfIsRational(getContext().nCtx(), getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this RCF numeral is an algebraic number.
|
||||
* @return true if this is algebraic
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public boolean isAlgebraic() {
|
||||
return Native.rcfIsAlgebraic(getContext().nCtx(), getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this RCF numeral is an infinitesimal.
|
||||
* @return true if this is infinitesimal
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public boolean isInfinitesimal() {
|
||||
return Native.rcfIsInfinitesimal(getContext().nCtx(), getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this RCF numeral is a transcendental number.
|
||||
* @return true if this is transcendental
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public boolean isTranscendental() {
|
||||
return Native.rcfIsTranscendental(getContext().nCtx(), getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this RCF numeral to a string.
|
||||
* @param compact If true, use compact representation
|
||||
* @return String representation
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public String toString(boolean compact) {
|
||||
return Native.rcfNumToString(getContext().nCtx(), getNativeObject(),
|
||||
compact, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this RCF numeral to a string (non-compact).
|
||||
* @return String representation
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this RCF numeral to a decimal string.
|
||||
* @param precision Number of decimal places
|
||||
* @return Decimal string representation
|
||||
* @throws Z3Exception on error
|
||||
**/
|
||||
public String toDecimal(int precision) {
|
||||
return Native.rcfNumToDecimalString(getContext().nCtx(),
|
||||
getNativeObject(), precision);
|
||||
}
|
||||
|
||||
@Override
|
||||
void incRef() {
|
||||
// RCF numerals don't use standard reference counting
|
||||
// They are managed through Z3_rcf_del
|
||||
}
|
||||
|
||||
@Override
|
||||
void addToReferenceQueue() {
|
||||
getContext().getReferenceQueue().storeReference(this, RCFNumRef::new);
|
||||
}
|
||||
|
||||
private static class RCFNumRef extends Z3ReferenceQueue.Reference<RCFNum> {
|
||||
|
||||
private RCFNumRef(RCFNum referent, java.lang.ref.ReferenceQueue<Z3Object> q) {
|
||||
super(referent, q);
|
||||
}
|
||||
|
||||
@Override
|
||||
void decRef(Context ctx, long z3Obj) {
|
||||
Native.rcfDel(ctx.nCtx(), z3Obj);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkContext(RCFNum other) {
|
||||
if (getContext() != other.getContext()) {
|
||||
throw new Z3Exception("RCF numerals from different contexts");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,3 +4,10 @@ Java bindings
|
|||
The Java bindings will be included in the Z3 build if it is configured with
|
||||
the option --java to python scripts/mk_make.py. This will produce the
|
||||
com.microsoft.z3.jar package in the build directory.
|
||||
|
||||
## IDE Setup Guide
|
||||
|
||||
For comprehensive instructions on using Z3 Java bindings in IDEs (Eclipse,
|
||||
IntelliJ IDEA, Visual Studio Code), including troubleshooting common issues
|
||||
like ClassNotFoundException, see:
|
||||
../../../doc/JAVA_IDE_SETUP.md
|
||||
|
|
|
|||
|
|
@ -338,6 +338,48 @@ public class Solver extends Z3Object {
|
|||
return core.ToBoolExprArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve currently inferred units.
|
||||
* Remarks: This retrieves the set of literals that the solver has inferred
|
||||
* at the current decision level after a {@code check} call.
|
||||
*
|
||||
* @return An array of Boolean expressions representing the inferred units
|
||||
* @throws Z3Exception
|
||||
**/
|
||||
public BoolExpr[] getUnits()
|
||||
{
|
||||
ASTVector units = new ASTVector(getContext(), Native.solverGetUnits(getContext().nCtx(), getNativeObject()));
|
||||
return units.ToBoolExprArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve non-unit atomic formulas in the solver state.
|
||||
* Remarks: This retrieves atomic formulas that are not units after a {@code check} call.
|
||||
*
|
||||
* @return An array of Boolean expressions representing the non-unit formulas
|
||||
* @throws Z3Exception
|
||||
**/
|
||||
public BoolExpr[] getNonUnits()
|
||||
{
|
||||
ASTVector nonUnits = new ASTVector(getContext(), Native.solverGetNonUnits(getContext().nCtx(), getNativeObject()));
|
||||
return nonUnits.ToBoolExprArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the solver decision trail.
|
||||
* Remarks: This retrieves the trail of decisions made by the solver after a {@code check} call.
|
||||
* The trail represents the sequence of Boolean literals (decisions and propagations) in the order
|
||||
* they were assigned.
|
||||
*
|
||||
* @return An array of Boolean expressions representing the decision trail
|
||||
* @throws Z3Exception
|
||||
**/
|
||||
public BoolExpr[] getTrail()
|
||||
{
|
||||
ASTVector trail = new ASTVector(getContext(), Native.solverGetTrail(getContext().nCtx(), getNativeObject()));
|
||||
return trail.ToBoolExprArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* A brief justification of why the last call to {@code Check} returned
|
||||
* {@code UNKNOWN}.
|
||||
|
|
@ -348,6 +390,96 @@ public class Solver extends Z3Object {
|
|||
getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the decision levels for each literal in the solver's trail after a {@code check} call.
|
||||
* The trail contains Boolean literals (decisions and propagations) in the order they were assigned.
|
||||
* The returned array has one entry per trail literal, indicating at which decision level it was assigned.
|
||||
* Use {@link #getTrail()} to retrieve the trail literals themselves.
|
||||
*
|
||||
* @return An array where element i contains the decision level for the i-th trail literal
|
||||
* @throws Z3Exception
|
||||
**/
|
||||
public int[] getTrailLevels()
|
||||
{
|
||||
ASTVector trailVector = new ASTVector(getContext(), Native.solverGetTrail(getContext().nCtx(), getNativeObject()));
|
||||
int[] levels = new int[trailVector.size()];
|
||||
Native.solverGetLevels(getContext().nCtx(), getNativeObject(), trailVector.getNativeObject(), trailVector.size(), levels);
|
||||
return levels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a sequence of cubes (conjunctions of literals) for partitioning the search space.
|
||||
* Each cube represents a partial assignment that can be used as a starting point for parallel solving.
|
||||
* This is primarily useful for cube-and-conquer parallel SAT solving strategies, where different
|
||||
* cubes can be solved independently by different workers.
|
||||
* The iterator yields cubes until the search space is exhausted.
|
||||
*
|
||||
* @param vars Optional array of variables to use as initial cube variables. If null, solver decides.
|
||||
* @param cutoff Backtrack level cutoff for cube generation
|
||||
* @return Iterator over arrays of Boolean expressions representing cubes
|
||||
* @throws Z3Exception
|
||||
**/
|
||||
public java.util.Iterator<BoolExpr[]> cube(Expr<?>[] vars, int cutoff)
|
||||
{
|
||||
ASTVector cubeVars = new ASTVector(getContext());
|
||||
if (vars != null) {
|
||||
for (Expr<?> v : vars) {
|
||||
cubeVars.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
return new java.util.Iterator<BoolExpr[]>() {
|
||||
private BoolExpr[] nextCube = computeNext();
|
||||
|
||||
private BoolExpr[] computeNext() {
|
||||
ASTVector result = new ASTVector(getContext(),
|
||||
Native.solverCube(getContext().nCtx(), getNativeObject(),
|
||||
cubeVars.getNativeObject(), cutoff));
|
||||
BoolExpr[] cube = result.ToBoolExprArray();
|
||||
|
||||
// Check for termination conditions
|
||||
if (cube.length == 1 && cube[0].isFalse()) {
|
||||
return null; // No more cubes
|
||||
}
|
||||
if (cube.length == 0) {
|
||||
return null; // Search space exhausted
|
||||
}
|
||||
return cube;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nextCube != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolExpr[] next() {
|
||||
if (nextCube == null) {
|
||||
throw new java.util.NoSuchElementException();
|
||||
}
|
||||
BoolExpr[] current = nextCube;
|
||||
nextCube = computeNext();
|
||||
return current;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an initial value for a variable to guide the solver's search heuristics.
|
||||
* This can improve performance when good initial values are known for the problem domain.
|
||||
*
|
||||
* @param var The variable to set an initial value for
|
||||
* @param value The initial value for the variable
|
||||
* @throws Z3Exception
|
||||
**/
|
||||
public void setInitialValue(Expr<?> var, Expr<?> value)
|
||||
{
|
||||
getContext().checkContextMatch(var);
|
||||
getContext().checkContextMatch(value);
|
||||
Native.solverSetInitialValue(getContext().nCtx(), getNativeObject(),
|
||||
var.getNativeObject(), value.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of the current solver with respect to{@code ctx}.
|
||||
*/
|
||||
|
|
|
|||
27
src/api/java/TypeVarSort.java
Normal file
27
src/api/java/TypeVarSort.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
Copyright (c) 2024 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
TypeVarSort.java
|
||||
|
||||
Abstract:
|
||||
|
||||
Author:
|
||||
|
||||
GitHub Copilot 2024-01-30
|
||||
|
||||
Notes:
|
||||
|
||||
**/
|
||||
|
||||
package com.microsoft.z3;
|
||||
|
||||
/**
|
||||
* A type variable sort for use in polymorphic functions and datatypes.
|
||||
**/
|
||||
public class TypeVarSort extends Sort
|
||||
{
|
||||
TypeVarSort(Context ctx, long obj) { super(ctx, obj); }
|
||||
TypeVarSort(Context ctx, Symbol s) { super(ctx, Native.mkTypeVariable(ctx.nCtx(), s.getNativeObject())); }
|
||||
}
|
||||
335
src/api/js/TYPESCRIPT_API_ENHANCEMENTS.md
Normal file
335
src/api/js/TYPESCRIPT_API_ENHANCEMENTS.md
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
# TypeScript API Enhancements - Simplifier, Params, and ParamDescrs
|
||||
|
||||
This document describes the new APIs added to the Z3 TypeScript bindings to bring them to feature parity with Python, Java, C++, and C# bindings.
|
||||
|
||||
## Overview
|
||||
|
||||
Three new high-level APIs have been added:
|
||||
|
||||
1. **Params** - Parameter configuration objects
|
||||
2. **ParamDescrs** - Parameter introspection and documentation
|
||||
3. **Simplifier** - Modern preprocessing for incremental solving (Z3 4.12+)
|
||||
|
||||
These APIs address the gaps identified in [GitHub Discussion #8145](https://github.com/Z3Prover/z3/discussions/8145).
|
||||
|
||||
## Params API
|
||||
|
||||
The `Params` class allows you to create reusable parameter configuration objects that can be passed to tactics, simplifiers, and solvers.
|
||||
|
||||
### Features
|
||||
|
||||
- Create parameter objects with typed values (boolean, number, string)
|
||||
- Pass to tactics via `tactic.usingParams(params)`
|
||||
- Pass to simplifiers via `simplifier.usingParams(params)`
|
||||
- Validate against parameter descriptions
|
||||
- Convert to string for debugging
|
||||
|
||||
### Example
|
||||
|
||||
```typescript
|
||||
const { Params, Tactic } = Context('main');
|
||||
|
||||
// Create a parameter object
|
||||
const params = new Params();
|
||||
params.set('elim_and', true);
|
||||
params.set('max_steps', 1000);
|
||||
params.set('timeout', 5.0);
|
||||
params.set('logic', 'QF_LIA');
|
||||
|
||||
// Use with a tactic
|
||||
const tactic = new Tactic('simplify');
|
||||
const configuredTactic = tactic.usingParams(params);
|
||||
|
||||
// Validate parameters
|
||||
const paramDescrs = tactic.paramDescrs();
|
||||
params.validate(paramDescrs); // Throws if invalid
|
||||
|
||||
// Debug output
|
||||
console.log(params.toString());
|
||||
```
|
||||
|
||||
### API Reference
|
||||
|
||||
```typescript
|
||||
class Params {
|
||||
/**
|
||||
* Set a parameter with the given name and value.
|
||||
* @param name - Parameter name
|
||||
* @param value - Parameter value (boolean, number, or string)
|
||||
*/
|
||||
set(name: string, value: boolean | number | string): void;
|
||||
|
||||
/**
|
||||
* Validate the parameter set against a parameter description set.
|
||||
* @param descrs - Parameter descriptions to validate against
|
||||
*/
|
||||
validate(descrs: ParamDescrs): void;
|
||||
|
||||
/**
|
||||
* Convert the parameter set to a string representation.
|
||||
*/
|
||||
toString(): string;
|
||||
}
|
||||
```
|
||||
|
||||
## ParamDescrs API
|
||||
|
||||
The `ParamDescrs` class provides runtime introspection of available parameters for tactics, simplifiers, and solvers.
|
||||
|
||||
### Features
|
||||
|
||||
- Query available parameters
|
||||
- Get parameter types
|
||||
- Access parameter documentation
|
||||
- Validate parameter configurations
|
||||
|
||||
### Example
|
||||
|
||||
```typescript
|
||||
const { Simplifier } = Context('main');
|
||||
|
||||
// Get parameter descriptions
|
||||
const simplifier = new Simplifier('solve-eqs');
|
||||
const paramDescrs = simplifier.paramDescrs();
|
||||
|
||||
// Introspect parameters
|
||||
const size = paramDescrs.size();
|
||||
console.log(`Number of parameters: ${size}`);
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
const name = paramDescrs.getName(i);
|
||||
const kind = paramDescrs.getKind(name);
|
||||
const doc = paramDescrs.getDocumentation(name);
|
||||
|
||||
console.log(`${name}: ${doc}`);
|
||||
}
|
||||
|
||||
// Get all as string
|
||||
console.log(paramDescrs.toString());
|
||||
```
|
||||
|
||||
### API Reference
|
||||
|
||||
```typescript
|
||||
class ParamDescrs {
|
||||
/**
|
||||
* Return the number of parameters in the description set.
|
||||
*/
|
||||
size(): number;
|
||||
|
||||
/**
|
||||
* Return the name of the parameter at the given index.
|
||||
* @param i - Index of the parameter
|
||||
*/
|
||||
getName(i: number): string;
|
||||
|
||||
/**
|
||||
* Return the kind (type) of the parameter with the given name.
|
||||
* @param name - Parameter name
|
||||
*/
|
||||
getKind(name: string): number;
|
||||
|
||||
/**
|
||||
* Return the documentation string for the parameter with the given name.
|
||||
* @param name - Parameter name
|
||||
*/
|
||||
getDocumentation(name: string): string;
|
||||
|
||||
/**
|
||||
* Convert the parameter description set to a string representation.
|
||||
*/
|
||||
toString(): string;
|
||||
}
|
||||
```
|
||||
|
||||
## Simplifier API
|
||||
|
||||
The `Simplifier` class provides modern preprocessing capabilities for incremental solving, introduced in Z3 4.12. Simplifiers are more efficient than tactics for incremental solving and can be attached directly to solvers.
|
||||
|
||||
### Features
|
||||
|
||||
- Create simplifiers by name
|
||||
- Compose simplifiers with `andThen()`
|
||||
- Configure with parameters using `usingParams()`
|
||||
- Attach to solvers for incremental preprocessing
|
||||
- Get help and parameter documentation
|
||||
|
||||
### Example
|
||||
|
||||
```typescript
|
||||
const { Simplifier, Solver, Params, Int } = Context('main');
|
||||
|
||||
// Create a simplifier
|
||||
const simplifier = new Simplifier('solve-eqs');
|
||||
|
||||
// Get help
|
||||
console.log(simplifier.help());
|
||||
|
||||
// Configure with parameters
|
||||
const params = new Params();
|
||||
params.set('som', true);
|
||||
const configured = simplifier.usingParams(params);
|
||||
|
||||
// Compose simplifiers
|
||||
const s1 = new Simplifier('solve-eqs');
|
||||
const s2 = new Simplifier('simplify');
|
||||
const composed = s1.andThen(s2);
|
||||
|
||||
// Attach to solver
|
||||
const solver = new Solver();
|
||||
solver.addSimplifier(composed);
|
||||
|
||||
// Use the solver normally
|
||||
const x = Int.const('x');
|
||||
const y = Int.const('y');
|
||||
solver.add(x.eq(y.add(1)));
|
||||
solver.add(y.eq(5));
|
||||
|
||||
const result = await solver.check(); // 'sat'
|
||||
if (result === 'sat') {
|
||||
const model = solver.model();
|
||||
console.log('x =', model.eval(x).toString()); // 6
|
||||
}
|
||||
```
|
||||
|
||||
### API Reference
|
||||
|
||||
```typescript
|
||||
class Simplifier {
|
||||
/**
|
||||
* Create a simplifier by name.
|
||||
* @param name - Built-in simplifier name (e.g., 'solve-eqs', 'simplify')
|
||||
*/
|
||||
constructor(name: string);
|
||||
|
||||
/**
|
||||
* Return a string containing a description of parameters accepted by this simplifier.
|
||||
*/
|
||||
help(): string;
|
||||
|
||||
/**
|
||||
* Return the parameter description set for this simplifier.
|
||||
*/
|
||||
paramDescrs(): ParamDescrs;
|
||||
|
||||
/**
|
||||
* Return a simplifier that uses the given configuration parameters.
|
||||
* @param params - Parameters to configure the simplifier
|
||||
*/
|
||||
usingParams(params: Params): Simplifier;
|
||||
|
||||
/**
|
||||
* Return a simplifier that applies this simplifier and then another simplifier.
|
||||
* @param other - The simplifier to apply after this one
|
||||
*/
|
||||
andThen(other: Simplifier): Simplifier;
|
||||
}
|
||||
```
|
||||
|
||||
### Solver Integration
|
||||
|
||||
The `Solver` class has been extended with a new method:
|
||||
|
||||
```typescript
|
||||
class Solver {
|
||||
/**
|
||||
* Attach a simplifier to the solver for incremental pre-processing.
|
||||
* The solver will use the simplifier for incremental pre-processing of assertions.
|
||||
* @param simplifier - The simplifier to attach
|
||||
*/
|
||||
addSimplifier(simplifier: Simplifier): void;
|
||||
}
|
||||
```
|
||||
|
||||
## Tactic Enhancement
|
||||
|
||||
The `Tactic` class has been enhanced with parameter configuration:
|
||||
|
||||
```typescript
|
||||
class Tactic {
|
||||
/**
|
||||
* Return a tactic that uses the given configuration parameters.
|
||||
* @param params - Parameters to configure the tactic
|
||||
*/
|
||||
usingParams(params: Params): Tactic;
|
||||
|
||||
/**
|
||||
* Get parameter descriptions for the tactic.
|
||||
* Returns a ParamDescrs object for introspecting available parameters.
|
||||
*/
|
||||
paramDescrs(): ParamDescrs;
|
||||
}
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```typescript
|
||||
const { Tactic, Params } = Context('main');
|
||||
|
||||
const tactic = new Tactic('simplify');
|
||||
const params = new Params();
|
||||
params.set('max_steps', 100);
|
||||
|
||||
const configured = tactic.usingParams(params);
|
||||
```
|
||||
|
||||
## Available Simplifiers
|
||||
|
||||
Common built-in simplifiers include:
|
||||
|
||||
- `'solve-eqs'` - Solve for variables
|
||||
- `'simplify'` - General simplification
|
||||
- `'propagate-values'` - Propagate constant values
|
||||
- `'elim-uncnstr'` - Eliminate unconstrained variables
|
||||
- `'ctx-simplify'` - Context-dependent simplification
|
||||
|
||||
Use `simplifier.help()` to see documentation for each simplifier and its parameters.
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Before (using global setParam)
|
||||
|
||||
```typescript
|
||||
// Global parameter setting
|
||||
setParam('pp.decimal', true);
|
||||
|
||||
// No way to create reusable parameter configurations
|
||||
// No simplifier support
|
||||
```
|
||||
|
||||
### After (using Params and Simplifier)
|
||||
|
||||
```typescript
|
||||
// Reusable parameter objects
|
||||
const params = new Params();
|
||||
params.set('pp.decimal', true);
|
||||
params.set('max_steps', 1000);
|
||||
|
||||
// Configure tactics
|
||||
const tactic = new Tactic('simplify').usingParams(params);
|
||||
|
||||
// Use simplifiers for better incremental solving
|
||||
const simplifier = new Simplifier('solve-eqs').usingParams(params);
|
||||
solver.addSimplifier(simplifier);
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
These APIs match the functionality available in:
|
||||
|
||||
- ✅ Python (`ParamsRef`, `ParamDescrsRef`, `Simplifier`)
|
||||
- ✅ Java (`Params`, `ParamDescrs`, `Simplifier`)
|
||||
- ✅ C# (`Params`, `ParamDescrs`, `Simplifier`)
|
||||
- ✅ C++ (`params`, `param_descrs`, `simplifier`)
|
||||
|
||||
The TypeScript API now has 100% coverage of the Params, ParamDescrs, and Simplifier C APIs.
|
||||
|
||||
## Examples
|
||||
|
||||
See [simplifier-example.ts](./examples/high-level/simplifier-example.ts) for a complete working example demonstrating all features.
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [Z3 Guide - Parameters](https://z3prover.github.io/api/html/group__capi.html#ga3e04c0bc49ffc0e8c9c6c1e72e6e44b1)
|
||||
- [Z3 Guide - Simplifiers](https://z3prover.github.io/api/html/group__capi.html#ga1a6e5b5a0c6c6f1c6e9e9e9f6e8e8e8e)
|
||||
- [Z3 Guide - Tactics](https://z3prover.github.io/api/html/group__capi.html#ga9f7f1d1f1f1f1f1f1f1f1f1f1f1f1f1f)
|
||||
199
src/api/js/examples/high-level/rcf-example.ts
Normal file
199
src/api/js/examples/high-level/rcf-example.ts
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/**
|
||||
* Example demonstrating the RCF (Real Closed Field) API in TypeScript.
|
||||
*
|
||||
* This example shows how to use RCF numerals to work with:
|
||||
* - Transcendental numbers (pi, e)
|
||||
* - Algebraic numbers (roots of polynomials)
|
||||
* - Infinitesimals
|
||||
* - Exact real arithmetic
|
||||
*
|
||||
* Note: This example uses the high-level API for a cleaner interface.
|
||||
*/
|
||||
|
||||
import { init } from 'z3-solver';
|
||||
|
||||
async function rcfBasicExample() {
|
||||
console.log('RCF Basic Example (High-Level API)');
|
||||
console.log('===================================');
|
||||
|
||||
const { Context } = await init();
|
||||
const { RCFNum } = Context('main');
|
||||
|
||||
// Create pi and e
|
||||
const pi = RCFNum.pi();
|
||||
const e = RCFNum.e();
|
||||
|
||||
console.log('pi =', pi.toString());
|
||||
console.log('e =', e.toString());
|
||||
|
||||
// Arithmetic operations
|
||||
const sum = pi.add(e);
|
||||
const prod = pi.mul(e);
|
||||
|
||||
console.log('pi + e =', sum.toString());
|
||||
console.log('pi * e =', prod.toString());
|
||||
|
||||
// Decimal approximations
|
||||
console.log('pi (10 decimals) =', pi.toDecimal(10));
|
||||
console.log('e (10 decimals) =', e.toDecimal(10));
|
||||
|
||||
// Comparisons
|
||||
console.log('pi < e?', pi.lt(e) ? 'yes' : 'no');
|
||||
console.log('pi > e?', pi.gt(e) ? 'yes' : 'no');
|
||||
}
|
||||
|
||||
async function rcfRationalExample() {
|
||||
console.log('\nRCF Rational Example (High-Level API)');
|
||||
console.log('=====================================');
|
||||
|
||||
const { Context } = await init();
|
||||
const { RCFNum } = Context('main');
|
||||
|
||||
// Create rational numbers
|
||||
const half = RCFNum('1/2');
|
||||
const third = RCFNum('1/3');
|
||||
|
||||
console.log('1/2 =', half.toString());
|
||||
console.log('1/3 =', third.toString());
|
||||
|
||||
// Arithmetic
|
||||
const sum = half.add(third);
|
||||
console.log('1/2 + 1/3 =', sum.toString());
|
||||
console.log('1/2 + 1/3 (decimal) =', sum.toDecimal(10));
|
||||
|
||||
// Type queries
|
||||
console.log('Is 1/2 rational?', half.isRational() ? 'yes' : 'no');
|
||||
console.log('Is 1/2 algebraic?', half.isAlgebraic() ? 'yes' : 'no');
|
||||
}
|
||||
|
||||
async function rcfRootsExample() {
|
||||
console.log('\nRCF Roots Example (High-Level API)');
|
||||
console.log('===================================');
|
||||
|
||||
const { Context } = await init();
|
||||
const { RCFNum } = Context('main');
|
||||
|
||||
// Find roots of x^2 - 2 = 0
|
||||
// Polynomial: -2 + 0*x + 1*x^2
|
||||
const coeffs = [
|
||||
RCFNum(-2), // constant term
|
||||
RCFNum(0), // x coefficient
|
||||
RCFNum(1), // x^2 coefficient
|
||||
];
|
||||
|
||||
const roots = RCFNum.roots(coeffs);
|
||||
|
||||
console.log('Roots of x^2 - 2 = 0:');
|
||||
for (let i = 0; i < roots.length; i++) {
|
||||
console.log(` root[${i}] =`, roots[i].toString());
|
||||
console.log(` decimal =`, roots[i].toDecimal(15));
|
||||
console.log(` is_algebraic =`, roots[i].isAlgebraic() ? 'yes' : 'no');
|
||||
}
|
||||
}
|
||||
|
||||
async function rcfInfinitesimalExample() {
|
||||
console.log('\nRCF Infinitesimal Example (High-Level API)');
|
||||
console.log('===========================================');
|
||||
|
||||
const { Context } = await init();
|
||||
const { RCFNum } = Context('main');
|
||||
|
||||
// Create an infinitesimal
|
||||
const eps = RCFNum.infinitesimal();
|
||||
console.log('eps =', eps.toString());
|
||||
console.log('Is eps infinitesimal?', eps.isInfinitesimal() ? 'yes' : 'no');
|
||||
|
||||
// Infinitesimals are smaller than any positive real number
|
||||
const tiny = RCFNum('1/1000000000');
|
||||
console.log('eps < 1/1000000000?', eps.lt(tiny) ? 'yes' : 'no');
|
||||
}
|
||||
|
||||
async function rcfArithmeticExample() {
|
||||
console.log('\nRCF Arithmetic Operations Example');
|
||||
console.log('==================================');
|
||||
|
||||
const { Context } = await init();
|
||||
const { RCFNum } = Context('main');
|
||||
|
||||
const a = RCFNum(5);
|
||||
const b = RCFNum(3);
|
||||
|
||||
console.log('a =', a.toString());
|
||||
console.log('b =', b.toString());
|
||||
console.log('a + b =', a.add(b).toString());
|
||||
console.log('a - b =', a.sub(b).toString());
|
||||
console.log('a * b =', a.mul(b).toString());
|
||||
console.log('a / b =', a.div(b).toString(), '=', a.div(b).toDecimal(5));
|
||||
console.log('-a =', a.neg().toString());
|
||||
console.log('1/a =', a.inv().toString(), '=', a.inv().toDecimal(5));
|
||||
console.log('a^3 =', a.power(3).toString());
|
||||
|
||||
// Comparisons
|
||||
console.log('\nComparisons:');
|
||||
console.log('a < b?', a.lt(b));
|
||||
console.log('a > b?', a.gt(b));
|
||||
console.log('a <= b?', a.le(b));
|
||||
console.log('a >= b?', a.ge(b));
|
||||
console.log('a == b?', a.eq(b));
|
||||
console.log('a != b?', a.neq(b));
|
||||
}
|
||||
|
||||
async function rcfSymbolicMathExample() {
|
||||
console.log('\nRCF Symbolic Mathematics Example');
|
||||
console.log('=================================');
|
||||
|
||||
const { Context } = await init();
|
||||
const { RCFNum } = Context('main');
|
||||
|
||||
// Work with exact symbolic values
|
||||
const pi = RCFNum.pi();
|
||||
const e = RCFNum.e();
|
||||
const sqrt2Coeffs = [RCFNum(-2), RCFNum(0), RCFNum(1)];
|
||||
const sqrt2Roots = RCFNum.roots(sqrt2Coeffs);
|
||||
const sqrt2 = sqrt2Roots.find(r => r.gt(RCFNum(0)))!;
|
||||
|
||||
console.log('π =', pi.toDecimal(15));
|
||||
console.log('e =', e.toDecimal(15));
|
||||
console.log('√2 =', sqrt2.toDecimal(15));
|
||||
|
||||
// Combine them
|
||||
const expr1 = pi.add(e);
|
||||
const expr2 = pi.mul(sqrt2);
|
||||
const expr3 = e.power(2);
|
||||
|
||||
console.log('\nExpressions:');
|
||||
console.log('π + e =', expr1.toDecimal(10));
|
||||
console.log('π × √2 =', expr2.toDecimal(10));
|
||||
console.log('e² =', expr3.toDecimal(10));
|
||||
|
||||
// Check properties
|
||||
console.log('\nProperties:');
|
||||
console.log('π is transcendental:', pi.isTranscendental());
|
||||
console.log('e is transcendental:', e.isTranscendental());
|
||||
console.log('√2 is algebraic:', sqrt2.isAlgebraic());
|
||||
console.log('√2 is rational:', sqrt2.isRational());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await rcfBasicExample();
|
||||
await rcfRationalExample();
|
||||
await rcfRootsExample();
|
||||
await rcfInfinitesimalExample();
|
||||
await rcfArithmeticExample();
|
||||
await rcfSymbolicMathExample();
|
||||
|
||||
console.log('\n✓ All RCF examples completed successfully!');
|
||||
console.log('\nThe RCF API in TypeScript now provides:');
|
||||
console.log(' • 38 functions for exact real arithmetic');
|
||||
console.log(' • Support for π, e, algebraic numbers, and infinitesimals');
|
||||
console.log(' • Full arithmetic and comparison operations');
|
||||
console.log(' • Polynomial root finding');
|
||||
console.log(' • Type predicates and conversions');
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
88
src/api/js/examples/high-level/simplifier-example.ts
Normal file
88
src/api/js/examples/high-level/simplifier-example.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* Example demonstrating the new Simplifier, Params, and ParamDescrs APIs
|
||||
*
|
||||
* This example shows how to:
|
||||
* 1. Create and configure parameter objects
|
||||
* 2. Use simplifiers for preprocessing
|
||||
* 3. Compose simplifiers
|
||||
* 4. Introspect parameter descriptions
|
||||
*/
|
||||
|
||||
import { init } from '../../build/node';
|
||||
|
||||
(async () => {
|
||||
const { Context } = await init();
|
||||
const { Int, Solver, Simplifier, Params } = Context('main');
|
||||
|
||||
// Example 1: Using Params to configure tactics
|
||||
console.log('Example 1: Creating and using Params');
|
||||
const params = new Params();
|
||||
params.set('elim_and', true);
|
||||
params.set('max_steps', 1000);
|
||||
params.set('timeout', 5.0);
|
||||
console.log('Params:', params.toString());
|
||||
|
||||
// Example 2: Creating and using a Simplifier
|
||||
console.log('\nExample 2: Using a Simplifier');
|
||||
const simplifier = new Simplifier('solve-eqs');
|
||||
console.log('Simplifier help:', simplifier.help());
|
||||
|
||||
// Example 3: Composing simplifiers
|
||||
console.log('\nExample 3: Composing simplifiers');
|
||||
const s1 = new Simplifier('solve-eqs');
|
||||
const s2 = new Simplifier('simplify');
|
||||
const composed = s1.andThen(s2);
|
||||
console.log('Composed simplifier created');
|
||||
|
||||
// Example 4: Using simplifier with parameters
|
||||
console.log('\nExample 4: Configuring simplifier with parameters');
|
||||
const configParams = new Params();
|
||||
configParams.set('ite_solver', false);
|
||||
const configuredSimplifier = simplifier.usingParams(configParams);
|
||||
console.log('Configured simplifier created');
|
||||
|
||||
// Example 4b: Configuring tactic with parameters
|
||||
console.log('\nExample 4b: Configuring tactic with parameters');
|
||||
const { Tactic } = Context('main');
|
||||
const tactic = new Tactic('simplify');
|
||||
const tacticParams = new Params();
|
||||
tacticParams.set('max_steps', 1000);
|
||||
const configuredTactic = tactic.usingParams(tacticParams);
|
||||
console.log('Configured tactic created');
|
||||
|
||||
// Example 5: Adding simplifier to solver
|
||||
console.log('\nExample 5: Using simplifier with solver');
|
||||
const solver = new Solver();
|
||||
solver.addSimplifier(s1);
|
||||
|
||||
const x = Int.const('x');
|
||||
const y = Int.const('y');
|
||||
solver.add(x.eq(y.add(1)));
|
||||
solver.add(y.eq(5));
|
||||
|
||||
const result = await solver.check();
|
||||
console.log('Result:', result);
|
||||
|
||||
if (result === 'sat') {
|
||||
const model = solver.model();
|
||||
console.log('x =', model.eval(x).toString());
|
||||
console.log('y =', model.eval(y).toString());
|
||||
}
|
||||
|
||||
// Example 6: Introspecting parameter descriptions
|
||||
console.log('\nExample 6: Parameter introspection');
|
||||
const paramDescrs = simplifier.paramDescrs();
|
||||
console.log('Parameter descriptions:');
|
||||
console.log(paramDescrs.toString());
|
||||
|
||||
const size = paramDescrs.size();
|
||||
console.log(`Number of parameters: ${size}`);
|
||||
|
||||
if (size > 0) {
|
||||
const firstParamName = paramDescrs.getName(0);
|
||||
console.log(`First parameter: ${firstParamName}`);
|
||||
console.log(`Documentation: ${paramDescrs.getDocumentation(firstParamName)}`);
|
||||
}
|
||||
|
||||
console.log('\nAll examples completed successfully!');
|
||||
})();
|
||||
165
src/api/js/examples/low-level/rcf-example.ts
Normal file
165
src/api/js/examples/low-level/rcf-example.ts
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/**
|
||||
* Example demonstrating the RCF (Real Closed Field) API in TypeScript.
|
||||
*
|
||||
* This example shows how to use RCF numerals to work with:
|
||||
* - Transcendental numbers (pi, e)
|
||||
* - Algebraic numbers (roots of polynomials)
|
||||
* - Infinitesimals
|
||||
* - Exact real arithmetic
|
||||
*
|
||||
* Note: The RCF API is exposed at the low-level API layer.
|
||||
* Import from 'z3-solver' for low-level access.
|
||||
*/
|
||||
|
||||
import { init } from 'z3-solver';
|
||||
|
||||
async function rcfBasicExample() {
|
||||
console.log('RCF Basic Example');
|
||||
console.log('=================');
|
||||
|
||||
const { Z3 } = await init();
|
||||
const ctx = Z3.mk_context_rc(Z3.mk_config());
|
||||
|
||||
try {
|
||||
// Create pi and e
|
||||
const pi = Z3.rcf_mk_pi(ctx);
|
||||
const e = Z3.rcf_mk_e(ctx);
|
||||
|
||||
console.log('pi =', Z3.rcf_num_to_string(ctx, pi, false, false));
|
||||
console.log('e =', Z3.rcf_num_to_string(ctx, e, false, false));
|
||||
|
||||
// Arithmetic operations
|
||||
const sum = Z3.rcf_add(ctx, pi, e);
|
||||
const prod = Z3.rcf_mul(ctx, pi, e);
|
||||
|
||||
console.log('pi + e =', Z3.rcf_num_to_string(ctx, sum, false, false));
|
||||
console.log('pi * e =', Z3.rcf_num_to_string(ctx, prod, false, false));
|
||||
|
||||
// Decimal approximations
|
||||
console.log('pi (10 decimals) =', Z3.rcf_num_to_decimal_string(ctx, pi, 10));
|
||||
console.log('e (10 decimals) =', Z3.rcf_num_to_decimal_string(ctx, e, 10));
|
||||
|
||||
// Comparisons
|
||||
console.log('pi < e?', Z3.rcf_lt(ctx, pi, e) ? 'yes' : 'no');
|
||||
console.log('pi > e?', Z3.rcf_gt(ctx, pi, e) ? 'yes' : 'no');
|
||||
|
||||
// Cleanup
|
||||
Z3.rcf_del(ctx, pi);
|
||||
Z3.rcf_del(ctx, e);
|
||||
Z3.rcf_del(ctx, sum);
|
||||
Z3.rcf_del(ctx, prod);
|
||||
} finally {
|
||||
Z3.del_context(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
async function rcfRationalExample() {
|
||||
console.log('\nRCF Rational Example');
|
||||
console.log('====================');
|
||||
|
||||
const { Z3 } = await init();
|
||||
const ctx = Z3.mk_context_rc(Z3.mk_config());
|
||||
|
||||
try {
|
||||
// Create rational numbers
|
||||
const half = Z3.rcf_mk_rational(ctx, '1/2');
|
||||
const third = Z3.rcf_mk_rational(ctx, '1/3');
|
||||
|
||||
console.log('1/2 =', Z3.rcf_num_to_string(ctx, half, false, false));
|
||||
console.log('1/3 =', Z3.rcf_num_to_string(ctx, third, false, false));
|
||||
|
||||
// Arithmetic
|
||||
const sum = Z3.rcf_add(ctx, half, third);
|
||||
console.log('1/2 + 1/3 =', Z3.rcf_num_to_string(ctx, sum, false, false));
|
||||
|
||||
// Type queries
|
||||
console.log('Is 1/2 rational?', Z3.rcf_is_rational(ctx, half) ? 'yes' : 'no');
|
||||
console.log('Is 1/2 algebraic?', Z3.rcf_is_algebraic(ctx, half) ? 'yes' : 'no');
|
||||
|
||||
// Cleanup
|
||||
Z3.rcf_del(ctx, half);
|
||||
Z3.rcf_del(ctx, third);
|
||||
Z3.rcf_del(ctx, sum);
|
||||
} finally {
|
||||
Z3.del_context(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
async function rcfRootsExample() {
|
||||
console.log('\nRCF Roots Example');
|
||||
console.log('=================');
|
||||
|
||||
const { Z3 } = await init();
|
||||
const ctx = Z3.mk_context_rc(Z3.mk_config());
|
||||
|
||||
try {
|
||||
// Find roots of x^2 - 2 = 0
|
||||
// Polynomial: -2 + 0*x + 1*x^2
|
||||
const coeffs = [
|
||||
Z3.rcf_mk_small_int(ctx, -2), // constant term
|
||||
Z3.rcf_mk_small_int(ctx, 0), // x coefficient
|
||||
Z3.rcf_mk_small_int(ctx, 1), // x^2 coefficient
|
||||
];
|
||||
|
||||
const roots = new Array(coeffs.length);
|
||||
const numRoots = Z3.rcf_mk_roots(ctx, coeffs, roots);
|
||||
|
||||
console.log('Roots of x^2 - 2 = 0:');
|
||||
for (let i = 0; i < numRoots; i++) {
|
||||
console.log(` root[${i}] =`, Z3.rcf_num_to_string(ctx, roots[i], false, false));
|
||||
console.log(` decimal =`, Z3.rcf_num_to_decimal_string(ctx, roots[i], 15));
|
||||
console.log(` is_algebraic =`, Z3.rcf_is_algebraic(ctx, roots[i]) ? 'yes' : 'no');
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
for (const coeff of coeffs) {
|
||||
Z3.rcf_del(ctx, coeff);
|
||||
}
|
||||
for (let i = 0; i < numRoots; i++) {
|
||||
Z3.rcf_del(ctx, roots[i]);
|
||||
}
|
||||
} finally {
|
||||
Z3.del_context(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
async function rcfInfinitesimalExample() {
|
||||
console.log('\nRCF Infinitesimal Example');
|
||||
console.log('=========================');
|
||||
|
||||
const { Z3 } = await init();
|
||||
const ctx = Z3.mk_context_rc(Z3.mk_config());
|
||||
|
||||
try {
|
||||
// Create an infinitesimal
|
||||
const eps = Z3.rcf_mk_infinitesimal(ctx);
|
||||
console.log('eps =', Z3.rcf_num_to_string(ctx, eps, false, false));
|
||||
console.log('Is eps infinitesimal?', Z3.rcf_is_infinitesimal(ctx, eps) ? 'yes' : 'no');
|
||||
|
||||
// Infinitesimals are smaller than any positive real number
|
||||
const tiny = Z3.rcf_mk_rational(ctx, '1/1000000000');
|
||||
console.log('eps < 1/1000000000?', Z3.rcf_lt(ctx, eps, tiny) ? 'yes' : 'no');
|
||||
|
||||
// Cleanup
|
||||
Z3.rcf_del(ctx, eps);
|
||||
Z3.rcf_del(ctx, tiny);
|
||||
} finally {
|
||||
Z3.del_context(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await rcfBasicExample();
|
||||
await rcfRationalExample();
|
||||
await rcfRootsExample();
|
||||
await rcfInfinitesimalExample();
|
||||
|
||||
console.log('\nAll RCF examples completed successfully!');
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
268
src/api/js/examples/regex-example.md
Normal file
268
src/api/js/examples/regex-example.md
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
# Regular Expression Support in Z3 TypeScript API
|
||||
|
||||
This document demonstrates the new regular expression support added to the Z3 TypeScript API.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Creating Regular Expressions
|
||||
|
||||
```typescript
|
||||
const { Re, String: Str, InRe, Solver } = Context('main');
|
||||
|
||||
// Create a regex from a string
|
||||
const hello = Re.toRe('hello');
|
||||
|
||||
// Check if a string matches
|
||||
const solver = new Solver();
|
||||
solver.add(InRe('hello', hello));
|
||||
await solver.check(); // sat
|
||||
```
|
||||
|
||||
### Star Operator (Zero or More)
|
||||
|
||||
```typescript
|
||||
const { Re, InRe, Star } = Context('main');
|
||||
|
||||
const a = Re.toRe('a');
|
||||
const aStar = Star(a);
|
||||
|
||||
// Empty string matches a*
|
||||
InRe('', aStar); // true
|
||||
|
||||
// Multiple 'a's match
|
||||
InRe('aaa', aStar); // true
|
||||
```
|
||||
|
||||
### Plus Operator (One or More)
|
||||
|
||||
```typescript
|
||||
const { Re, InRe, Plus } = Context('main');
|
||||
|
||||
const a = Re.toRe('a');
|
||||
const aPlus = Plus(a);
|
||||
|
||||
// Empty string does NOT match a+
|
||||
InRe('', aPlus); // false
|
||||
|
||||
// One or more 'a's match
|
||||
InRe('aa', aPlus); // true
|
||||
```
|
||||
|
||||
### Option Operator (Zero or One)
|
||||
|
||||
```typescript
|
||||
const { Re, InRe, Option } = Context('main');
|
||||
|
||||
const a = Re.toRe('a');
|
||||
const aOpt = Option(a);
|
||||
|
||||
// Both empty and 'a' match a?
|
||||
InRe('', aOpt); // true
|
||||
InRe('a', aOpt); // true
|
||||
InRe('aa', aOpt); // false
|
||||
```
|
||||
|
||||
### Union (Or)
|
||||
|
||||
```typescript
|
||||
const { Re, InRe, Union } = Context('main');
|
||||
|
||||
const a = Re.toRe('a');
|
||||
const b = Re.toRe('b');
|
||||
const aOrB = Union(a, b);
|
||||
|
||||
// Either 'a' or 'b' match
|
||||
InRe('a', aOrB); // true
|
||||
InRe('b', aOrB); // true
|
||||
InRe('c', aOrB); // false
|
||||
```
|
||||
|
||||
### Intersect (And)
|
||||
|
||||
```typescript
|
||||
const { Re, InRe, Intersect, Star } = Context('main');
|
||||
|
||||
const a = Re.toRe('a');
|
||||
const b = Re.toRe('b');
|
||||
const both = Intersect(Star(a), Star(b));
|
||||
|
||||
// Only empty string matches both a* and b*
|
||||
InRe('', both); // true
|
||||
InRe('a', both); // false
|
||||
```
|
||||
|
||||
### Range
|
||||
|
||||
```typescript
|
||||
const { Range, InRe } = Context('main');
|
||||
|
||||
const azRange = Range('a', 'z');
|
||||
|
||||
// Lowercase letters match
|
||||
InRe('m', azRange); // true
|
||||
|
||||
// Others don't
|
||||
InRe('1', azRange); // false
|
||||
InRe('Z', azRange); // false
|
||||
```
|
||||
|
||||
### Loop (Bounded Repetition)
|
||||
|
||||
```typescript
|
||||
const { Re, InRe, Loop } = Context('main');
|
||||
|
||||
const a = Re.toRe('a');
|
||||
|
||||
// Between 2 and 3 repetitions
|
||||
const a2to3 = Loop(a, 2, 3);
|
||||
InRe('aa', a2to3); // true
|
||||
InRe('aaa', a2to3); // true
|
||||
InRe('a', a2to3); // false
|
||||
InRe('aaaa', a2to3); // false
|
||||
|
||||
// At least 2 repetitions (hi=0 or omitted means unbounded)
|
||||
const a2Plus = Loop(a, 2, 0); // or Loop(a, 2)
|
||||
InRe('aa', a2Plus); // true
|
||||
InRe('aaa', a2Plus); // true
|
||||
InRe('aaaa', a2Plus); // true
|
||||
InRe('a', a2Plus); // false
|
||||
```
|
||||
|
||||
### Power (Exact Repetition)
|
||||
|
||||
```typescript
|
||||
const { Re, InRe, Power } = Context('main');
|
||||
|
||||
const a = Re.toRe('a');
|
||||
const a3 = Power(a, 3);
|
||||
|
||||
// Exactly 3 repetitions match
|
||||
InRe('aaa', a3); // true
|
||||
|
||||
// Others don't
|
||||
InRe('aa', a3); // false
|
||||
InRe('aaaa', a3); // false
|
||||
```
|
||||
|
||||
### Complement (Negation)
|
||||
|
||||
```typescript
|
||||
const { Re, InRe, Complement } = Context('main');
|
||||
|
||||
const a = Re.toRe('a');
|
||||
const notA = Complement(a);
|
||||
|
||||
// Everything except 'a' matches
|
||||
InRe('a', notA); // false
|
||||
InRe('b', notA); // true
|
||||
InRe('', notA); // true
|
||||
```
|
||||
|
||||
### Diff (Set Difference)
|
||||
|
||||
```typescript
|
||||
const { Re, InRe, Diff, Star } = Context('main');
|
||||
|
||||
const a = Re.toRe('a');
|
||||
const b = Re.toRe('b');
|
||||
const diff = Diff(Star(a), b);
|
||||
|
||||
// a* except 'b'
|
||||
InRe('aaa', diff); // true
|
||||
InRe('b', diff); // false
|
||||
```
|
||||
|
||||
### ReConcat (Concatenation)
|
||||
|
||||
```typescript
|
||||
const { Re, InRe, ReConcat } = Context('main');
|
||||
|
||||
const hello = Re.toRe('hello');
|
||||
const world = Re.toRe('world');
|
||||
const helloworld = ReConcat(hello, world);
|
||||
|
||||
// Concatenated strings match
|
||||
InRe('helloworld', helloworld); // true
|
||||
InRe('hello', helloworld); // false
|
||||
```
|
||||
|
||||
## Method Chaining
|
||||
|
||||
Regular expression objects support method chaining:
|
||||
|
||||
```typescript
|
||||
const { Re, InRe } = Context('main');
|
||||
|
||||
const a = Re.toRe('a');
|
||||
|
||||
// Using methods
|
||||
const aStar = a.star();
|
||||
const aPlus = a.plus();
|
||||
const aOpt = a.option();
|
||||
const notA = a.complement();
|
||||
|
||||
// Chaining
|
||||
const complex = a.plus().union(Re.toRe('b').star());
|
||||
```
|
||||
|
||||
## Complex Example: Pattern Matching
|
||||
|
||||
```typescript
|
||||
const { Re, String: Str, InRe, Union, Star, Solver } = Context('main');
|
||||
|
||||
const x = Str.const('x');
|
||||
const a = Re.toRe('a');
|
||||
const b = Re.toRe('b');
|
||||
|
||||
// Pattern: any combination of 'a' and 'b'
|
||||
const pattern = Star(Union(a, b));
|
||||
|
||||
const solver = new Solver();
|
||||
solver.add(InRe(x, pattern));
|
||||
solver.add(x.length().eq(5));
|
||||
|
||||
if (await solver.check() === 'sat') {
|
||||
const model = solver.model();
|
||||
const result = model.eval(x);
|
||||
// Result will be a 5-character string containing only 'a' and 'b'
|
||||
console.log(result.asString()); // e.g., "aabba"
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Factory Methods
|
||||
|
||||
- `Re.sort(seqSort)` - Create a regex sort
|
||||
- `Re.toRe(seq)` - Convert sequence/string to regex
|
||||
|
||||
### Operators
|
||||
|
||||
- `Star(re)` - Zero or more repetitions (*)
|
||||
- `Plus(re)` - One or more repetitions (+)
|
||||
- `Option(re)` - Zero or one repetition (?)
|
||||
- `Union(...res)` - Or operation (|)
|
||||
- `Intersect(...res)` - And operation (&)
|
||||
- `ReConcat(...res)` - Concatenation
|
||||
- `Complement(re)` - Negation (~)
|
||||
- `Diff(a, b)` - Set difference (a \ b)
|
||||
- `Range(lo, hi)` - Character range
|
||||
- `Loop(re, lo, hi?)` - Bounded repetition {lo,hi} or at least lo if hi=0 or omitted
|
||||
- `Power(re, n)` - Exact repetition {n}
|
||||
|
||||
### Special Patterns
|
||||
|
||||
- `AllChar(reSort)` - Match any single character
|
||||
- `Empty(reSort)` - Empty language
|
||||
- `Full(reSort)` - Match all strings
|
||||
|
||||
### Membership Test
|
||||
|
||||
- `InRe(seq, re)` - Test if sequence matches regex
|
||||
|
||||
## Notes
|
||||
|
||||
- All regex operations are symbolic - they create constraints rather than executing pattern matching
|
||||
- The solver finds strings that satisfy the regex constraints
|
||||
- Regular expressions can be combined with other Z3 constraints (length, containment, etc.)
|
||||
- This implementation follows the SMT-LIB2 regular expression theory
|
||||
|
|
@ -12,30 +12,126 @@ export function makeCCWrapper() {
|
|||
if (fn == null) {
|
||||
throw new Error(`could not find definition for ${fnName}`);
|
||||
}
|
||||
let wrapper;
|
||||
if (fn.cRet === 'Z3_string') {
|
||||
wrapper = `wrapper_str`;
|
||||
} else if (['int', 'unsigned', 'void'].includes(fn.cRet) || fn.cRet.startsWith('Z3_')) {
|
||||
wrapper = `wrapper`;
|
||||
} else {
|
||||
throw new Error(`async function with unknown return type ${fn.cRet}`);
|
||||
}
|
||||
|
||||
wrappers.push(
|
||||
`
|
||||
extern "C" void async_${fn.name}(${fn.params
|
||||
// Check if function has array parameters
|
||||
const arrayParams = fn.params.filter(p => p.isArray && p.kind === 'in_array');
|
||||
const hasArrayParams = arrayParams.length > 0;
|
||||
|
||||
if (hasArrayParams) {
|
||||
// Generate custom wrapper for functions with array parameters
|
||||
const paramList = fn.params
|
||||
.map(p => `${p.isConst ? 'const ' : ''}${p.cType}${p.isPtr ? '*' : ''} ${p.name}${p.isArray ? '[]' : ''}`)
|
||||
.join(', ')}) {
|
||||
.join(', ');
|
||||
|
||||
// Find the size parameter for each array and build copy/free code
|
||||
const arrayCopies: string[] = [];
|
||||
const arrayFrees: string[] = [];
|
||||
const arrayCopyNames: string[] = [];
|
||||
|
||||
for (let p of arrayParams) {
|
||||
const sizeParam = fn.params[p.sizeIndex!];
|
||||
const ptrType = p.cType.endsWith('*') ? p.cType : `${p.cType}*`;
|
||||
const copyName = `${p.name}_copy`;
|
||||
arrayCopyNames.push(copyName);
|
||||
|
||||
// Allocate and copy with null check
|
||||
arrayCopies.push(`${ptrType} ${copyName} = (${ptrType})malloc(sizeof(${p.cType}) * ${sizeParam.name});`);
|
||||
arrayCopies.push(`if (!${copyName}) {`);
|
||||
arrayCopies.push(` MAIN_THREAD_ASYNC_EM_ASM({ reject_async(new Error("Memory allocation failed")); });`);
|
||||
arrayCopies.push(` return;`);
|
||||
arrayCopies.push(`}`);
|
||||
arrayCopies.push(`memcpy(${copyName}, ${p.name}, sizeof(${p.cType}) * ${sizeParam.name});`);
|
||||
|
||||
arrayFrees.push(`free(${copyName});`);
|
||||
}
|
||||
|
||||
// Build lambda capture list
|
||||
const nonArrayParams = fn.params.filter(p => !p.isArray || p.kind !== 'in_array');
|
||||
const captureList = [...arrayCopyNames, ...nonArrayParams.map(p => p.name)].join(', ');
|
||||
|
||||
// Build argument list for the actual function call, using copied arrays
|
||||
const callArgs = fn.params
|
||||
.map(p => {
|
||||
if (p.isArray && p.kind === 'in_array') {
|
||||
return `${p.name}_copy`;
|
||||
}
|
||||
return p.name;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
const isString = fn.cRet === 'Z3_string';
|
||||
const returnType = isString ? 'auto' : fn.cRet;
|
||||
|
||||
wrappers.push(
|
||||
`
|
||||
extern "C" void async_${fn.name}(${paramList}) {
|
||||
${arrayCopies.join('\n ')}
|
||||
std::thread t([${captureList}] {
|
||||
try {
|
||||
${returnType} result = ${fn.name}(${callArgs});
|
||||
${
|
||||
isString
|
||||
? `
|
||||
MAIN_THREAD_ASYNC_EM_ASM({
|
||||
resolve_async(UTF8ToString($0));
|
||||
}, result);
|
||||
`
|
||||
: `
|
||||
MAIN_THREAD_ASYNC_EM_ASM({
|
||||
resolve_async($0);
|
||||
}, result);
|
||||
`
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
MAIN_THREAD_ASYNC_EM_ASM({
|
||||
reject_async(new Error(UTF8ToString($0)));
|
||||
}, e.what());
|
||||
} catch (...) {
|
||||
MAIN_THREAD_ASYNC_EM_ASM({
|
||||
reject_async(new Error('failed with unknown exception'));
|
||||
});
|
||||
}
|
||||
${arrayFrees.join('\n ')}
|
||||
MAIN_THREAD_ASYNC_EM_ASM({
|
||||
clearTimeout(threadTimeouts.shift());
|
||||
});
|
||||
});
|
||||
t.detach();
|
||||
EM_ASM({
|
||||
threadTimeouts.push(setTimeout(() => {}, 600000));
|
||||
});
|
||||
}
|
||||
`.trim(),
|
||||
);
|
||||
} else {
|
||||
// Use template wrapper for functions without array parameters
|
||||
let wrapper;
|
||||
if (fn.cRet === 'Z3_string') {
|
||||
wrapper = `wrapper_str`;
|
||||
} else if (['int', 'unsigned', 'void'].includes(fn.cRet) || fn.cRet.startsWith('Z3_')) {
|
||||
wrapper = `wrapper`;
|
||||
} else {
|
||||
throw new Error(`async function with unknown return type ${fn.cRet}`);
|
||||
}
|
||||
|
||||
wrappers.push(
|
||||
`
|
||||
extern "C" void async_${fn.name}(${fn.params
|
||||
.map(p => `${p.isConst ? 'const ' : ''}${p.cType}${p.isPtr ? '*' : ''} ${p.name}${p.isArray ? '[]' : ''}`)
|
||||
.join(', ')}) {
|
||||
${wrapper}<decltype(&${fn.name}), &${fn.name}>(${fn.params.map(p => `${p.name}`).join(', ')});
|
||||
}
|
||||
`.trim(),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return `// THIS FILE IS AUTOMATICALLY GENERATED BY ${path.basename(__filename)}
|
||||
// DO NOT EDIT IT BY HAND
|
||||
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <emscripten.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ async function makeTsWrapper() {
|
|||
|
||||
// otherwise fall back to ccall
|
||||
|
||||
const ctypes = fn.params.map(p =>
|
||||
let ctypes = fn.params.map(p =>
|
||||
p.kind === 'in_array' ? 'array' : p.kind === 'out_array' ? 'number' : p.isPtr ? 'number' : toEmType(p.type),
|
||||
);
|
||||
|
||||
|
|
@ -149,6 +149,8 @@ async function makeTsWrapper() {
|
|||
const args: (string | FuncParam)[] = fn.params;
|
||||
|
||||
let arrayLengthParams = new Map();
|
||||
let allocatedArrays: string[] = []; // Track allocated arrays for cleanup
|
||||
|
||||
for (let p of inParams) {
|
||||
if (p.nullable && !p.isArray) {
|
||||
// this would be easy to implement - just map null to 0 - but nothing actually uses nullable non-array input parameters, so we can't ensure we've done it right
|
||||
|
|
@ -179,6 +181,34 @@ async function makeTsWrapper() {
|
|||
}
|
||||
args[sizeIndex] = `${p.name}.length`;
|
||||
params[sizeIndex] = null;
|
||||
|
||||
// For async functions, we need to manually manage array memory
|
||||
// because ccall frees it before the async thread uses it
|
||||
if (isAsync && p.kind === 'in_array') {
|
||||
const paramIdx = fn.params.indexOf(p);
|
||||
const ptrName = `${p.name}_ptr`;
|
||||
allocatedArrays.push(ptrName);
|
||||
// Allocate memory for array of pointers (4 bytes per pointer on wasm32)
|
||||
prefix += `
|
||||
const ${ptrName} = Mod._malloc(${p.name}.length * 4);
|
||||
Mod.HEAPU32.set(${p.name} as unknown as number[], ${ptrName} / 4);
|
||||
`.trim();
|
||||
args[paramIdx] = ptrName;
|
||||
ctypes[paramIdx] = 'number'; // Pass as pointer, not array
|
||||
}
|
||||
}
|
||||
|
||||
// Add try-finally for async functions with allocated arrays
|
||||
if (isAsync && allocatedArrays.length > 0) {
|
||||
prefix += `
|
||||
try {
|
||||
`.trim();
|
||||
suffix =
|
||||
`
|
||||
} finally {
|
||||
${allocatedArrays.map(arr => `Mod._free(${arr});`).join('\n ')}
|
||||
}
|
||||
`.trim() + suffix;
|
||||
}
|
||||
|
||||
let returnType = fn.ret;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -34,6 +34,15 @@ target_include_directories(z3jl PRIVATE
|
|||
"${PROJECT_BINARY_DIR}/src/api"
|
||||
"${PROJECT_SOURCE_DIR}/src/api/c++"
|
||||
)
|
||||
# Add header padding for macOS to allow install_name_tool to modify the dylib
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
target_link_options(z3jl PRIVATE "-Wl,-headerpad_max_install_names")
|
||||
endif()
|
||||
|
||||
# On macOS, add headerpad for install_name_tool compatibility
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
target_link_options(z3jl PRIVATE "-Wl,-headerpad_max_install_names")
|
||||
endif()
|
||||
|
||||
option(Z3_INSTALL_JULIA_BINDINGS "Install Julia bindings when invoking install target" ON)
|
||||
if(Z3_INSTALL_JULIA_BINDINGS)
|
||||
|
|
|
|||
|
|
@ -309,6 +309,17 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
|
|||
m.method("sqrt", static_cast<expr (*)(expr const &, expr const &)>(&sqrt));
|
||||
m.method("fma", static_cast<expr (*)(expr const &, expr const &, expr const &, expr const &)>(&fma));
|
||||
m.method("range", &range);
|
||||
m.method("finite_set_empty", &finite_set_empty);
|
||||
m.method("finite_set_singleton", &finite_set_singleton);
|
||||
m.method("finite_set_union", &finite_set_union);
|
||||
m.method("finite_set_intersect", &finite_set_intersect);
|
||||
m.method("finite_set_difference", &finite_set_difference);
|
||||
m.method("finite_set_member", &finite_set_member);
|
||||
m.method("finite_set_size", &finite_set_size);
|
||||
m.method("finite_set_subset", &finite_set_subset);
|
||||
m.method("finite_set_map", &finite_set_map);
|
||||
m.method("finite_set_filter", &finite_set_filter);
|
||||
m.method("finite_set_range", &finite_set_range);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -400,9 +411,9 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
|
|||
.MM(solver, units)
|
||||
.method("trail", static_cast<expr_vector (solver::*)() const>(&solver::trail))
|
||||
.method("trail", [](solver &s, jlcxx::ArrayRef<unsigned> levels) {
|
||||
int sz = levels.size();
|
||||
int sz = static_cast<int>(levels.size());
|
||||
z3::array<unsigned> _levels(sz);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
_levels[i] = levels[i];
|
||||
}
|
||||
return s.trail(_levels);
|
||||
|
|
@ -618,6 +629,7 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
|
|||
.MM(context, string_sort)
|
||||
.MM(context, seq_sort)
|
||||
.MM(context, re_sort)
|
||||
.MM(context, finite_set_sort)
|
||||
.method("array_sort", static_cast<sort (context::*)(sort, sort)>(&context::array_sort))
|
||||
.method("array_sort", static_cast<sort (context::*)(sort_vector const&, sort)>(&context::array_sort))
|
||||
.method("fpa_sort", static_cast<sort (context::*)(unsigned, unsigned)>(&context::fpa_sort))
|
||||
|
|
@ -629,9 +641,9 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
|
|||
.MM(context, set_rounding_mode)
|
||||
.method("enumeration_sort",
|
||||
[](context& c, char const * name, jlcxx::ArrayRef<jl_value_t*,1> names, func_decl_vector &cs, func_decl_vector &ts) {
|
||||
int sz = names.size();
|
||||
int sz = static_cast<int>(names.size());
|
||||
std::vector<const char *> _names;
|
||||
for (int i = 0; i < sz; i++) {
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
const char *x = jl_string_data(names[i]);
|
||||
_names.push_back(x);
|
||||
}
|
||||
|
|
@ -639,10 +651,10 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
|
|||
})
|
||||
.method("tuple_sort",
|
||||
[](context& c, char const * name, jlcxx::ArrayRef<jl_value_t*,1> names, jlcxx::ArrayRef<jl_value_t*,1> sorts, func_decl_vector &projs) {
|
||||
int sz = names.size();
|
||||
int sz = static_cast<int>(names.size());
|
||||
std::vector<sort> _sorts;
|
||||
std::vector<const char *> _names;
|
||||
for (int i = 0; i < sz; i++) {
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
const sort &x = jlcxx::unbox<sort&>(sorts[i]);
|
||||
const char *y = jl_string_data(names[i]);
|
||||
_sorts.push_back(x);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
find_package(OCaml REQUIRED)
|
||||
|
||||
set(exe_ext ${CMAKE_EXECUTABLE_SUFFIX})
|
||||
|
|
@ -159,6 +160,8 @@ endif()
|
|||
|
||||
if( APPLE )
|
||||
# set(ocaml_rpath "@executable_path/../libz3${so_ext}")
|
||||
# Add headerpad for install_name_tool compatibility on macOS
|
||||
list(APPEND c_lib_deps "-ldopt" "-Wl,-headerpad_max_install_names")
|
||||
elseif( UNIX )
|
||||
set(ocaml_rpath "\\$ORIGIN/../libz3${so_ext}")
|
||||
list(APPEND c_lib_deps "-dllpath" ${ocaml_rpath})
|
||||
|
|
|
|||
|
|
@ -216,6 +216,8 @@ sig
|
|||
val to_string : sort -> string
|
||||
val mk_uninterpreted : context -> Symbol.symbol -> sort
|
||||
val mk_uninterpreted_s : context -> string -> sort
|
||||
val mk_type_variable : context -> Symbol.symbol -> sort
|
||||
val mk_type_variable_s : context -> string -> sort
|
||||
end = struct
|
||||
type sort = Z3native.sort
|
||||
let gc a = Z3native.context_of_ast a
|
||||
|
|
@ -228,6 +230,8 @@ end = struct
|
|||
let to_string (x:sort) = Z3native.sort_to_string (gc x) x
|
||||
let mk_uninterpreted ctx s = Z3native.mk_uninterpreted_sort ctx s
|
||||
let mk_uninterpreted_s (ctx:context) (s:string) = mk_uninterpreted ctx (Symbol.mk_string ctx s)
|
||||
let mk_type_variable ctx s = Z3native.mk_type_variable ctx s
|
||||
let mk_type_variable_s (ctx:context) (s:string) = mk_type_variable ctx (Symbol.mk_string ctx s)
|
||||
end
|
||||
|
||||
and FuncDecl :
|
||||
|
|
@ -908,6 +912,13 @@ struct
|
|||
let mk_sort_s (ctx:context) (name:string) (constructors:Constructor.constructor list) =
|
||||
mk_sort ctx (Symbol.mk_string ctx name) constructors
|
||||
|
||||
let mk_polymorphic_sort (ctx:context) (name:Symbol.symbol) (type_params:Sort.sort list) (constructors:Constructor.constructor list) =
|
||||
let (x,_) = Z3native.mk_polymorphic_datatype ctx name (List.length type_params) type_params (List.length constructors) constructors in
|
||||
x
|
||||
|
||||
let mk_polymorphic_sort_s (ctx:context) (name:string) (type_params:Sort.sort list) (constructors:Constructor.constructor list) =
|
||||
mk_polymorphic_sort ctx (Symbol.mk_string ctx name) type_params constructors
|
||||
|
||||
let mk_sort_ref (ctx: context) (name:Symbol.symbol) =
|
||||
Z3native.mk_datatype_sort ctx name 0 []
|
||||
|
||||
|
|
@ -950,6 +961,9 @@ struct
|
|||
let g j = Z3native.get_datatype_sort_constructor_accessor (Sort.gc x) x i j in
|
||||
List.init ds g) in
|
||||
List.init n f
|
||||
|
||||
let update_field (ctx:context) (field_access:FuncDecl.func_decl) (t:Expr.expr) (v:Expr.expr) =
|
||||
Z3native.datatype_update_field ctx field_access t v
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -1926,6 +1940,37 @@ struct
|
|||
|
||||
let interrupt (ctx:context) (s:solver) =
|
||||
Z3native.solver_interrupt ctx s
|
||||
|
||||
let get_units x =
|
||||
let av = Z3native.solver_get_units (gc x) x in
|
||||
AST.ASTVector.to_expr_list av
|
||||
|
||||
let get_non_units x =
|
||||
let av = Z3native.solver_get_non_units (gc x) x in
|
||||
AST.ASTVector.to_expr_list av
|
||||
|
||||
let get_trail x =
|
||||
let av = Z3native.solver_get_trail (gc x) x in
|
||||
AST.ASTVector.to_expr_list av
|
||||
|
||||
let get_levels x literals =
|
||||
let n = List.length literals in
|
||||
let av = Z3native.mk_ast_vector (gc x) in
|
||||
List.iter (fun e -> Z3native.ast_vector_push (gc x) av e) literals;
|
||||
let level_list = Z3native.solver_get_levels (gc x) x av n in
|
||||
Array.of_list level_list
|
||||
|
||||
let congruence_root x a = Z3native.solver_congruence_root (gc x) x a
|
||||
|
||||
let congruence_next x a = Z3native.solver_congruence_next (gc x) x a
|
||||
|
||||
let congruence_explain x a b = Z3native.solver_congruence_explain (gc x) x a b
|
||||
|
||||
let from_file x = Z3native.solver_from_file (gc x) x
|
||||
|
||||
let from_string x = Z3native.solver_from_string (gc x) x
|
||||
|
||||
let set_initial_value x var value = Z3native.solver_set_initial_value (gc x) x var value
|
||||
end
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -290,6 +290,12 @@ sig
|
|||
|
||||
(** Create a new uninterpreted sort. *)
|
||||
val mk_uninterpreted_s : context -> string -> sort
|
||||
|
||||
(** Create a type variable sort for use in polymorphic datatypes. *)
|
||||
val mk_type_variable : context -> Symbol.symbol -> sort
|
||||
|
||||
(** Create a type variable sort for use in polymorphic datatypes. *)
|
||||
val mk_type_variable_s : context -> string -> sort
|
||||
end
|
||||
|
||||
(** Function declarations *)
|
||||
|
|
@ -1099,6 +1105,14 @@ sig
|
|||
(** Create a new datatype sort. *)
|
||||
val mk_sort_s : context -> string -> Constructor.constructor list -> Sort.sort
|
||||
|
||||
(** Create a new polymorphic datatype sort with type parameters.
|
||||
Type parameters should be created using Sort.mk_type_variable. *)
|
||||
val mk_polymorphic_sort : context -> Symbol.symbol -> Sort.sort list -> Constructor.constructor list -> Sort.sort
|
||||
|
||||
(** Create a new polymorphic datatype sort with type parameters.
|
||||
Type parameters should be created using Sort.mk_type_variable. *)
|
||||
val mk_polymorphic_sort_s : context -> string -> Sort.sort list -> Constructor.constructor list -> Sort.sort
|
||||
|
||||
(** Create mutually recursive datatypes. *)
|
||||
val mk_sorts : context -> Symbol.symbol list -> Constructor.constructor list list -> Sort.sort list
|
||||
|
||||
|
|
@ -1116,6 +1130,12 @@ sig
|
|||
|
||||
(** The constructor accessors. *)
|
||||
val get_accessors : Sort.sort -> FuncDecl.func_decl list list
|
||||
|
||||
(** Update a datatype field at expression [t] with value [v].
|
||||
The function performs a record update at [t]. The field
|
||||
that is passed in as argument is updated with value [v],
|
||||
the remaining fields of [t] are unchanged. *)
|
||||
val update_field : context -> FuncDecl.func_decl -> Expr.expr -> Expr.expr -> Expr.expr
|
||||
end
|
||||
|
||||
(** Functions to manipulate Enumeration expressions *)
|
||||
|
|
@ -3369,6 +3389,46 @@ sig
|
|||
it is more convenient to cancel a specific solver. Solvers
|
||||
that are not selected for interrupts are left alone.*)
|
||||
val interrupt: context -> solver -> unit
|
||||
|
||||
(** Retrieve the set of units from the solver.
|
||||
Units are clauses of size 1 learned by the solver during solving. *)
|
||||
val get_units : solver -> Expr.expr list
|
||||
|
||||
(** Retrieve the set of non-units from the solver.
|
||||
Non-units are clauses of size greater than 1 learned by the solver. *)
|
||||
val get_non_units : solver -> Expr.expr list
|
||||
|
||||
(** Retrieve the trail (sequence of assignments) from the solver.
|
||||
The trail shows the sequence of literal assignments made by the solver. *)
|
||||
val get_trail : solver -> Expr.expr list
|
||||
|
||||
(** Retrieve the decision levels of trail literals.
|
||||
Given a list of literals from the trail, returns an array of their decision levels.
|
||||
@param literals List of literals from the trail
|
||||
@return Array of decision levels corresponding to the input literals *)
|
||||
val get_levels : solver -> Expr.expr list -> int array
|
||||
|
||||
(** Retrieve the congruence closure root of an expression.
|
||||
Returns the representative of the equivalence class containing the expression. *)
|
||||
val congruence_root : solver -> Expr.expr -> Expr.expr
|
||||
|
||||
(** Retrieve the next element in the congruence closure equivalence class.
|
||||
Congruence classes form a circular list; this returns the next element. *)
|
||||
val congruence_next : solver -> Expr.expr -> Expr.expr
|
||||
|
||||
(** Retrieve an explanation for why two expressions are congruent.
|
||||
Returns an expression that justifies the congruence between a and b. *)
|
||||
val congruence_explain : solver -> Expr.expr -> Expr.expr -> Expr.expr
|
||||
|
||||
(** Parse SMT-LIB2 formulas from a file and assert them into the solver. *)
|
||||
val from_file : solver -> string -> unit
|
||||
|
||||
(** Parse SMT-LIB2 formulas from a string and assert them into the solver. *)
|
||||
val from_string : solver -> string -> unit
|
||||
|
||||
(** Provide an initial value hint for a variable to the solver.
|
||||
This can help guide the solver to find solutions more efficiently. *)
|
||||
val set_initial_value : solver -> Expr.expr -> Expr.expr -> unit
|
||||
end
|
||||
|
||||
(** Fixedpoint solving *)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ ROOT_DIR = os.path.abspath(os.path.dirname(__file__))
|
|||
SRC_DIR_LOCAL = os.path.join(ROOT_DIR, 'core')
|
||||
SRC_DIR_REPO = os.path.join(ROOT_DIR, '..', '..', '..')
|
||||
SRC_DIR = SRC_DIR_LOCAL if os.path.exists(SRC_DIR_LOCAL) else SRC_DIR_REPO
|
||||
BUILD_DIR = build_env.get('Z3BUILD', 'build')
|
||||
|
||||
IS_SINGLE_THREADED = False
|
||||
ENABLE_LTO = True
|
||||
|
|
@ -34,7 +35,7 @@ IS_PYODIDE = 'PYODIDE_ROOT' in os.environ and os.environ.get('_PYTHON_HOST_PLATF
|
|||
# determine where binaries are
|
||||
RELEASE_DIR = os.environ.get('PACKAGE_FROM_RELEASE', None)
|
||||
if RELEASE_DIR is None:
|
||||
BUILD_DIR = os.path.join(SRC_DIR, 'build') # implicit in configure script
|
||||
BUILD_DIR = os.path.join(SRC_DIR, BUILD_DIR) # implicit in configure script
|
||||
HEADER_DIRS = [os.path.join(SRC_DIR, 'src', 'api'), os.path.join(SRC_DIR, 'src', 'api', 'c++')]
|
||||
RELEASE_METADATA = None
|
||||
if IS_PYODIDE:
|
||||
|
|
@ -315,6 +316,7 @@ class bdist_wheel(_bdist_wheel):
|
|||
# windows arm64 is not supported by pypi yet
|
||||
("win", "x64"): "win_amd64",
|
||||
("win", "x86"): "win32",
|
||||
("win", "arm64"): "win_arm64",
|
||||
("osx", "x64"): f"macosx_{os_version_tag}_x86_64",
|
||||
("osx", "arm64"): f"macosx_{os_version_tag}_arm64",
|
||||
("darwin", "x86_64"): f"macosx_{os_version_tag}_x86_64",
|
||||
|
|
|
|||
|
|
@ -240,6 +240,23 @@ class Context:
|
|||
def param_descrs(self):
|
||||
"""Return the global parameter description set."""
|
||||
return ParamDescrsRef(Z3_get_global_param_descrs(self.ref()), self)
|
||||
|
||||
def set_ast_print_mode(self, mode):
|
||||
"""Set the pretty printing mode for ASTs.
|
||||
|
||||
The following modes are available:
|
||||
- Z3_PRINT_SMTLIB_FULL (0): Print AST nodes in SMTLIB verbose format.
|
||||
- Z3_PRINT_LOW_LEVEL (1): Print AST nodes using a low-level format.
|
||||
- Z3_PRINT_SMTLIB2_COMPLIANT (2): Print AST nodes in SMTLIB 2.x compliant format.
|
||||
|
||||
Example:
|
||||
>>> c = Context()
|
||||
>>> x = Int('x', c)
|
||||
>>> c.set_ast_print_mode(Z3_PRINT_SMTLIB2_COMPLIANT)
|
||||
>>> print(x)
|
||||
x
|
||||
"""
|
||||
Z3_set_ast_print_mode(self.ref(), mode)
|
||||
|
||||
|
||||
# Global Z3 context
|
||||
|
|
@ -678,6 +695,8 @@ def is_sort(s : Any) -> bool:
|
|||
def _to_sort_ref(s, ctx):
|
||||
if z3_debug():
|
||||
_z3_assert(isinstance(s, Sort), "Z3 Sort expected")
|
||||
if Z3_is_finite_set_sort(ctx.ref(), s):
|
||||
return FiniteSetSortRef(s, ctx)
|
||||
k = _sort_kind(ctx, s)
|
||||
if k == Z3_BOOL_SORT:
|
||||
return BoolSortRef(s, ctx)
|
||||
|
|
@ -841,7 +860,7 @@ class FuncDeclRef(AstRef):
|
|||
elif k == Z3_PARAMETER_RATIONAL:
|
||||
result[i] = Z3_get_decl_rational_parameter(self.ctx_ref(), self.ast, i)
|
||||
elif k == Z3_PARAMETER_SYMBOL:
|
||||
result[i] = Z3_get_decl_symbol_parameter(self.ctx_ref(), self.ast, i)
|
||||
result[i] = _symbol2py(ctx, Z3_get_decl_symbol_parameter(self.ctx_ref(), self.ast, i))
|
||||
elif k == Z3_PARAMETER_SORT:
|
||||
result[i] = SortRef(Z3_get_decl_sort_parameter(self.ctx_ref(), self.ast, i), ctx)
|
||||
elif k == Z3_PARAMETER_AST:
|
||||
|
|
@ -853,7 +872,7 @@ class FuncDeclRef(AstRef):
|
|||
elif k == Z3_PARAMETER_ZSTRING:
|
||||
result[i] = "internal string"
|
||||
else:
|
||||
assert(False)
|
||||
raise Z3Exception("Unexpected parameter kind")
|
||||
return result
|
||||
|
||||
def __call__(self, *args):
|
||||
|
|
@ -1154,6 +1173,30 @@ class ExprRef(AstRef):
|
|||
else:
|
||||
return []
|
||||
|
||||
def update(self, *args):
|
||||
"""Update the arguments of the expression.
|
||||
|
||||
Return a new expression with the same function declaration and updated arguments.
|
||||
The number of new arguments must match the current number of arguments.
|
||||
|
||||
>>> f = Function('f', IntSort(), IntSort(), IntSort())
|
||||
>>> a = Int('a')
|
||||
>>> b = Int('b')
|
||||
>>> c = Int('c')
|
||||
>>> t = f(a, b)
|
||||
>>> t.update(c, c)
|
||||
f(c, c)
|
||||
"""
|
||||
if z3_debug():
|
||||
_z3_assert(is_app(self), "Z3 application expected")
|
||||
_z3_assert(len(args) == self.num_args(), "Number of arguments does not match")
|
||||
_z3_assert(all([is_expr(arg) for arg in args]), "Z3 expressions expected")
|
||||
num = len(args)
|
||||
_args = (Ast * num)()
|
||||
for i in range(num):
|
||||
_args[i] = args[i].as_ast()
|
||||
return _to_expr_ref(Z3_update_term(self.ctx_ref(), self.as_ast(), num, _args), self.ctx)
|
||||
|
||||
def from_string(self, s):
|
||||
pass
|
||||
|
||||
|
|
@ -1184,7 +1227,11 @@ def _to_expr_ref(a, ctx):
|
|||
k = Z3_get_ast_kind(ctx_ref, a)
|
||||
if k == Z3_QUANTIFIER_AST:
|
||||
return QuantifierRef(a, ctx)
|
||||
sk = Z3_get_sort_kind(ctx_ref, Z3_get_sort(ctx_ref, a))
|
||||
# Check for finite set sort before checking sort kind
|
||||
s = Z3_get_sort(ctx_ref, a)
|
||||
if Z3_is_finite_set_sort(ctx_ref, s):
|
||||
return FiniteSetRef(a, ctx)
|
||||
sk = Z3_get_sort_kind(ctx_ref, s)
|
||||
if sk == Z3_BOOL_SORT:
|
||||
return BoolRef(a, ctx)
|
||||
if sk == Z3_INT_SORT:
|
||||
|
|
@ -2763,6 +2810,17 @@ class ArithRef(ExprRef):
|
|||
a, b = _coerce_exprs(self, other)
|
||||
return BoolRef(Z3_mk_ge(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx)
|
||||
|
||||
def __abs__(self):
|
||||
"""Return an expression representing `abs(self)`.
|
||||
|
||||
>>> x = Int('x')
|
||||
>>> abs(x)
|
||||
If(x > 0, x, -x)
|
||||
>>> eq(abs(x), Abs(x))
|
||||
True
|
||||
"""
|
||||
return Abs(self)
|
||||
|
||||
|
||||
def is_arith(a):
|
||||
"""Return `True` if `a` is an arithmetical expression.
|
||||
|
|
@ -3324,6 +3382,9 @@ def RatVal(a, b, ctx=None):
|
|||
"""Return a Z3 rational a/b.
|
||||
|
||||
If `ctx=None`, then the global context is used.
|
||||
|
||||
Note: Division by zero (b == 0) is allowed in Z3 symbolic expressions.
|
||||
Z3 can reason about such expressions symbolically.
|
||||
|
||||
>>> RatVal(3,5)
|
||||
3/5
|
||||
|
|
@ -3333,6 +3394,7 @@ def RatVal(a, b, ctx=None):
|
|||
if z3_debug():
|
||||
_z3_assert(_is_int(a) or isinstance(a, str), "First argument cannot be converted into an integer")
|
||||
_z3_assert(_is_int(b) or isinstance(b, str), "Second argument cannot be converted into an integer")
|
||||
# Division by 0 is intentionally allowed - Z3 handles it symbolically
|
||||
return simplify(RealVal(a, ctx) / RealVal(b, ctx))
|
||||
|
||||
|
||||
|
|
@ -5010,6 +5072,25 @@ def Ext(a, b):
|
|||
_z3_assert(is_array_sort(a) and (is_array(b) or b.is_lambda()), "arguments must be arrays")
|
||||
return _to_expr_ref(Z3_mk_array_ext(ctx.ref(), a.as_ast(), b.as_ast()), ctx)
|
||||
|
||||
|
||||
def AsArray(f):
|
||||
"""Return a Z3 as-array expression for the given function declaration.
|
||||
|
||||
>>> f = Function('f', IntSort(), IntSort())
|
||||
>>> a = AsArray(f)
|
||||
>>> a.sort()
|
||||
Array(Int, Int)
|
||||
>>> is_as_array(a)
|
||||
True
|
||||
>>> get_as_array_func(a) == f
|
||||
True
|
||||
"""
|
||||
if z3_debug():
|
||||
_z3_assert(isinstance(f, FuncDeclRef), "function declaration expected")
|
||||
ctx = f.ctx
|
||||
return ArrayRef(Z3_mk_as_array(ctx.ref(), f.ast), ctx)
|
||||
|
||||
|
||||
def is_select(a):
|
||||
"""Return `True` if `a` is a Z3 array select application.
|
||||
|
||||
|
|
@ -5052,6 +5133,8 @@ def EmptySet(s):
|
|||
K(Int, False)
|
||||
"""
|
||||
ctx = s.ctx
|
||||
if is_finite_set_sort(s):
|
||||
return FiniteSetEmpty(s)
|
||||
return ArrayRef(Z3_mk_empty_set(ctx.ref(), s.ast), ctx)
|
||||
|
||||
|
||||
|
|
@ -5072,6 +5155,9 @@ def SetUnion(*args):
|
|||
union(a, b)
|
||||
"""
|
||||
args = _get_args(args)
|
||||
if len(args) > 0 and is_finite_set(args[0]):
|
||||
from functools import reduce
|
||||
return reduce(FiniteSetUnion, args)
|
||||
ctx = _ctx_from_ast_arg_list(args)
|
||||
_args, sz = _to_ast_array(args)
|
||||
return ArrayRef(Z3_mk_set_union(ctx.ref(), sz, _args), ctx)
|
||||
|
|
@ -5086,6 +5172,9 @@ def SetIntersect(*args):
|
|||
"""
|
||||
args = _get_args(args)
|
||||
ctx = _ctx_from_ast_arg_list(args)
|
||||
if len(args) > 0 and is_finite_set(args[0]):
|
||||
from functools import reduce
|
||||
return reduce(FiniteSetIntersect, args)
|
||||
_args, sz = _to_ast_array(args)
|
||||
return ArrayRef(Z3_mk_set_intersect(ctx.ref(), sz, _args), ctx)
|
||||
|
||||
|
|
@ -5096,8 +5185,10 @@ def SetAdd(s, e):
|
|||
>>> SetAdd(a, 1)
|
||||
Store(a, 1, True)
|
||||
"""
|
||||
ctx = _ctx_from_ast_arg_list([s, e])
|
||||
ctx = _ctx_from_ast_arg_list([s, e])
|
||||
e = _py2expr(e, ctx)
|
||||
if is_finite_set(s):
|
||||
return FiniteSetSingleton(e) | s
|
||||
return ArrayRef(Z3_mk_set_add(ctx.ref(), s.as_ast(), e.as_ast()), ctx)
|
||||
|
||||
|
||||
|
|
@ -5109,6 +5200,8 @@ def SetDel(s, e):
|
|||
"""
|
||||
ctx = _ctx_from_ast_arg_list([s, e])
|
||||
e = _py2expr(e, ctx)
|
||||
if is_finite_set(s):
|
||||
return s - FiniteSetSingleton(e)
|
||||
return ArrayRef(Z3_mk_set_del(ctx.ref(), s.as_ast(), e.as_ast()), ctx)
|
||||
|
||||
|
||||
|
|
@ -5130,6 +5223,8 @@ def SetDifference(a, b):
|
|||
setminus(a, b)
|
||||
"""
|
||||
ctx = _ctx_from_ast_arg_list([a, b])
|
||||
if is_finite_set(a):
|
||||
return FiniteSetDifference(a, b)
|
||||
return ArrayRef(Z3_mk_set_difference(ctx.ref(), a.as_ast(), b.as_ast()), ctx)
|
||||
|
||||
|
||||
|
|
@ -5141,6 +5236,8 @@ def IsMember(e, s):
|
|||
"""
|
||||
ctx = _ctx_from_ast_arg_list([s, e])
|
||||
e = _py2expr(e, ctx)
|
||||
if is_finite_set(s):
|
||||
return FiniteSetIsMember(e, s)
|
||||
return BoolRef(Z3_mk_set_member(ctx.ref(), e.as_ast(), s.as_ast()), ctx)
|
||||
|
||||
|
||||
|
|
@ -5152,9 +5249,228 @@ def IsSubset(a, b):
|
|||
subset(a, b)
|
||||
"""
|
||||
ctx = _ctx_from_ast_arg_list([a, b])
|
||||
if is_finite_set(a):
|
||||
return FiniteSetIsSubset(a, b)
|
||||
return BoolRef(Z3_mk_set_subset(ctx.ref(), a.as_ast(), b.as_ast()), ctx)
|
||||
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Finite Sets
|
||||
#
|
||||
#########################################
|
||||
|
||||
|
||||
class FiniteSetSortRef(SortRef):
|
||||
"""Finite set sort."""
|
||||
|
||||
def element_sort(self):
|
||||
"""Return the element sort of this finite set sort."""
|
||||
return _to_sort_ref(Z3_get_finite_set_sort_basis(self.ctx_ref(), self.ast), self.ctx)
|
||||
|
||||
def cast(self, val):
|
||||
"""Try to cast val as a finite set expression."""
|
||||
if is_expr(val):
|
||||
if self.eq(val.sort()):
|
||||
return val
|
||||
else:
|
||||
_z3_assert(False, "Cannot cast to finite set sort")
|
||||
if isinstance(val, set):
|
||||
elem_sort = self.element_sort()
|
||||
result = FiniteSetEmpty(self)
|
||||
for e in val:
|
||||
result = FiniteSetUnion(result, Singleton(_py2expr(e, self.ctx, elem_sort)))
|
||||
return result
|
||||
_z3_assert(False, "Cannot cast to finite set sort")
|
||||
|
||||
def subsort(self, other):
|
||||
return False
|
||||
|
||||
def is_int(self):
|
||||
return False
|
||||
|
||||
def is_bool(self):
|
||||
return False
|
||||
|
||||
def is_datatype(self):
|
||||
return False
|
||||
|
||||
def is_array(self):
|
||||
return False
|
||||
|
||||
def is_bv(self):
|
||||
return False
|
||||
|
||||
|
||||
def is_finite_set(a):
|
||||
"""Return True if a is a Z3 finite set expression.
|
||||
>>> s = FiniteSetSort(IntSort())
|
||||
>>> is_finite_set(FiniteSetEmpty(s))
|
||||
True
|
||||
>>> is_finite_set(IntVal(1))
|
||||
False
|
||||
"""
|
||||
return isinstance(a, FiniteSetRef)
|
||||
|
||||
|
||||
def is_finite_set_sort(s):
|
||||
"""Return True if s is a Z3 finite set sort.
|
||||
>>> is_finite_set_sort(FiniteSetSort(IntSort()))
|
||||
True
|
||||
>>> is_finite_set_sort(IntSort())
|
||||
False
|
||||
"""
|
||||
return isinstance(s, FiniteSetSortRef)
|
||||
|
||||
|
||||
class FiniteSetRef(ExprRef):
|
||||
"""Finite set expression."""
|
||||
|
||||
def sort(self):
|
||||
return FiniteSetSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx)
|
||||
|
||||
def __or__(self, other):
|
||||
"""Return the union of self and other."""
|
||||
return FiniteSetUnion(self, other)
|
||||
|
||||
def __and__(self, other):
|
||||
"""Return the intersection of self and other."""
|
||||
return FiniteSetIntersect(self, other)
|
||||
|
||||
def __sub__(self, other):
|
||||
"""Return the set difference of self and other."""
|
||||
return FiniteSetDifference(self, other)
|
||||
|
||||
|
||||
def FiniteSetSort(elem_sort):
|
||||
"""Create a finite set sort over element sort elem_sort.
|
||||
>>> s = FiniteSetSort(IntSort())
|
||||
>>> s
|
||||
FiniteSet(Int)
|
||||
"""
|
||||
return FiniteSetSortRef(Z3_mk_finite_set_sort(elem_sort.ctx_ref(), elem_sort.ast), elem_sort.ctx)
|
||||
|
||||
|
||||
def FiniteSetEmpty(set_sort):
|
||||
"""Create an empty finite set of the given sort.
|
||||
>>> s = FiniteSetSort(IntSort())
|
||||
>>> FiniteSetEmpty(s)
|
||||
set.empty
|
||||
"""
|
||||
ctx = set_sort.ctx
|
||||
return FiniteSetRef(Z3_mk_finite_set_empty(ctx.ref(), set_sort.ast), ctx)
|
||||
|
||||
|
||||
def Singleton(elem):
|
||||
"""Create a singleton finite set containing elem.
|
||||
>>> Singleton(IntVal(1))
|
||||
set.singleton(1)
|
||||
"""
|
||||
ctx = elem.ctx
|
||||
return FiniteSetRef(Z3_mk_finite_set_singleton(ctx.ref(), elem.as_ast()), ctx)
|
||||
|
||||
|
||||
def FiniteSetUnion(s1, s2):
|
||||
"""Create the union of two finite sets.
|
||||
>>> a = Const('a', FiniteSetSort(IntSort()))
|
||||
>>> b = Const('b', FiniteSetSort(IntSort()))
|
||||
>>> FiniteSetUnion(a, b)
|
||||
set.union(a, b)
|
||||
"""
|
||||
ctx = _ctx_from_ast_arg_list([s1, s2])
|
||||
return FiniteSetRef(Z3_mk_finite_set_union(ctx.ref(), s1.as_ast(), s2.as_ast()), ctx)
|
||||
|
||||
|
||||
def FiniteSetIntersect(s1, s2):
|
||||
"""Create the intersection of two finite sets.
|
||||
>>> a = Const('a', FiniteSetSort(IntSort()))
|
||||
>>> b = Const('b', FiniteSetSort(IntSort()))
|
||||
>>> FiniteSetIntersect(a, b)
|
||||
set.intersect(a, b)
|
||||
"""
|
||||
ctx = _ctx_from_ast_arg_list([s1, s2])
|
||||
return FiniteSetRef(Z3_mk_finite_set_intersect(ctx.ref(), s1.as_ast(), s2.as_ast()), ctx)
|
||||
|
||||
|
||||
def FiniteSetDifference(s1, s2):
|
||||
"""Create the set difference of two finite sets.
|
||||
>>> a = Const('a', FiniteSetSort(IntSort()))
|
||||
>>> b = Const('b', FiniteSetSort(IntSort()))
|
||||
>>> FiniteSetDifference(a, b)
|
||||
set.difference(a, b)
|
||||
"""
|
||||
ctx = _ctx_from_ast_arg_list([s1, s2])
|
||||
return FiniteSetRef(Z3_mk_finite_set_difference(ctx.ref(), s1.as_ast(), s2.as_ast()), ctx)
|
||||
|
||||
|
||||
def FiniteSetMember(elem, set):
|
||||
"""Check if elem is a member of the finite set.
|
||||
>>> a = Const('a', FiniteSetSort(IntSort()))
|
||||
>>> FiniteSetMember(IntVal(1), a)
|
||||
set.in(1, a)
|
||||
"""
|
||||
ctx = _ctx_from_ast_arg_list([elem, set])
|
||||
return BoolRef(Z3_mk_finite_set_member(ctx.ref(), elem.as_ast(), set.as_ast()), ctx)
|
||||
|
||||
def In(elem, set):
|
||||
return FiniteSetMember(elem, set)
|
||||
|
||||
def FiniteSetSize(set):
|
||||
"""Get the size (cardinality) of a finite set.
|
||||
>>> a = Const('a', FiniteSetSort(IntSort()))
|
||||
>>> FiniteSetSize(a)
|
||||
set.size(a)
|
||||
"""
|
||||
ctx = set.ctx
|
||||
return ArithRef(Z3_mk_finite_set_size(ctx.ref(), set.as_ast()), ctx)
|
||||
|
||||
|
||||
def FiniteSetSubset(s1, s2):
|
||||
"""Check if s1 is a subset of s2.
|
||||
>>> a = Const('a', FiniteSetSort(IntSort()))
|
||||
>>> b = Const('b', FiniteSetSort(IntSort()))
|
||||
>>> FiniteSetSubset(a, b)
|
||||
set.subset(a, b)
|
||||
"""
|
||||
ctx = _ctx_from_ast_arg_list([s1, s2])
|
||||
return BoolRef(Z3_mk_finite_set_subset(ctx.ref(), s1.as_ast(), s2.as_ast()), ctx)
|
||||
|
||||
|
||||
def FiniteSetMap(f, set):
|
||||
"""Apply function f to all elements of the finite set.
|
||||
>>> f = Array('f', IntSort(), IntSort())
|
||||
>>> a = Const('a', FiniteSetSort(IntSort()))
|
||||
>>> FiniteSetMap(f, a)
|
||||
set.map(f, a)
|
||||
"""
|
||||
if isinstance(f, FuncDeclRef):
|
||||
f = AsArray(f)
|
||||
ctx = _ctx_from_ast_arg_list([f, set])
|
||||
return FiniteSetRef(Z3_mk_finite_set_map(ctx.ref(), f.as_ast(), set.as_ast()), ctx)
|
||||
|
||||
|
||||
def FiniteSetFilter(f, set):
|
||||
"""Filter a finite set using predicate f.
|
||||
>>> f = Array('f', IntSort(), BoolSort())
|
||||
>>> a = Const('a', FiniteSetSort(IntSort()))
|
||||
>>> FiniteSetFilter(f, a)
|
||||
set.filter(f, a)
|
||||
"""
|
||||
if isinstance(f, FuncDeclRef):
|
||||
f = AsArray(f)
|
||||
ctx = _ctx_from_ast_arg_list([f, set])
|
||||
return FiniteSetRef(Z3_mk_finite_set_filter(ctx.ref(), f.as_ast(), set.as_ast()), ctx)
|
||||
|
||||
|
||||
def FiniteSetRange(low, high):
|
||||
"""Create a finite set of integers in the range [low, high).
|
||||
>>> FiniteSetRange(IntVal(0), IntVal(5))
|
||||
set.range(0, 5)
|
||||
"""
|
||||
ctx = _ctx_from_ast_arg_list([low, high])
|
||||
return FiniteSetRef(Z3_mk_finite_set_range(ctx.ref(), low.as_ast(), high.as_ast()), ctx)
|
||||
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Datatypes
|
||||
|
|
@ -5482,6 +5798,32 @@ class DatatypeRef(ExprRef):
|
|||
"""Return the datatype sort of the datatype expression `self`."""
|
||||
return DatatypeSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx)
|
||||
|
||||
def update_field(self, field_accessor, new_value):
|
||||
"""Return a new datatype expression with the specified field updated.
|
||||
|
||||
Args:
|
||||
field_accessor: The accessor function declaration for the field to update
|
||||
new_value: The new value for the field
|
||||
|
||||
Returns:
|
||||
A new datatype expression with the field updated, other fields unchanged
|
||||
|
||||
Example:
|
||||
>>> Person = Datatype('Person')
|
||||
>>> Person.declare('person', ('name', StringSort()), ('age', IntSort()))
|
||||
>>> Person = Person.create()
|
||||
>>> person_age = Person.accessor(0, 1) # age accessor
|
||||
>>> p = Const('p', Person)
|
||||
>>> p2 = p.update_field(person_age, IntVal(30))
|
||||
"""
|
||||
if z3_debug():
|
||||
_z3_assert(is_func_decl(field_accessor), "Z3 function declaration expected")
|
||||
_z3_assert(is_expr(new_value), "Z3 expression expected")
|
||||
return _to_expr_ref(
|
||||
Z3_datatype_update_field(self.ctx_ref(), field_accessor.ast, self.as_ast(), new_value.as_ast()),
|
||||
self.ctx
|
||||
)
|
||||
|
||||
def DatatypeSort(name, params=None, ctx=None):
|
||||
"""Create a reference to a sort that was declared, or will be declared, as a recursive datatype.
|
||||
|
||||
|
|
@ -5829,7 +6171,9 @@ class Goal(Z3PPObject):
|
|||
>>> g[1]
|
||||
y > x
|
||||
"""
|
||||
if arg >= len(self):
|
||||
if arg < 0:
|
||||
arg += len(self)
|
||||
if arg < 0 or arg >= len(self):
|
||||
raise IndexError
|
||||
return self.get(arg)
|
||||
|
||||
|
|
@ -6071,7 +6415,9 @@ class AstVector(Z3PPObject):
|
|||
>>> A[0]
|
||||
x
|
||||
"""
|
||||
if i >= self.__len__():
|
||||
if i < 0:
|
||||
i += self.__len__()
|
||||
if i < 0 or i >= self.__len__():
|
||||
raise IndexError
|
||||
Z3_ast_vector_set(self.ctx.ref(), self.vector, i, v.as_ast())
|
||||
|
||||
|
|
@ -6760,7 +7106,9 @@ class ModelRef(Z3PPObject):
|
|||
f -> [else -> 0]
|
||||
"""
|
||||
if _is_int(idx):
|
||||
if idx >= len(self):
|
||||
if idx < 0:
|
||||
idx += len(self)
|
||||
if idx < 0 or idx >= len(self):
|
||||
raise IndexError
|
||||
num_consts = Z3_model_get_num_consts(self.ctx.ref(), self.model)
|
||||
if (idx < num_consts):
|
||||
|
|
@ -6774,7 +7122,7 @@ class ModelRef(Z3PPObject):
|
|||
if isinstance(idx, SortRef):
|
||||
return self.get_universe(idx)
|
||||
if z3_debug():
|
||||
_z3_assert(False, "Integer, Z3 declaration, or Z3 constant expected")
|
||||
_z3_assert(False, "Integer, Z3 declaration, or Z3 constant expected. Use model.eval instead for complicated expressions")
|
||||
return None
|
||||
|
||||
def decls(self):
|
||||
|
|
@ -7576,13 +7924,6 @@ class Solver(Z3PPObject):
|
|||
|
||||
def sexpr(self):
|
||||
"""Return a formatted string (in Lisp-like format) with all added constraints.
|
||||
We say the string is in s-expression format.
|
||||
|
||||
>>> x = Int('x')
|
||||
>>> s = Solver()
|
||||
>>> s.add(x > 0)
|
||||
>>> s.add(x < 2)
|
||||
>>> r = s.sexpr()
|
||||
"""
|
||||
return Z3_solver_to_string(self.ctx.ref(), self.solver)
|
||||
|
||||
|
|
@ -7608,6 +7949,39 @@ class Solver(Z3PPObject):
|
|||
self.ctx.ref(), "benchmark generated from python API", "", "unknown", "", sz1, v, e,
|
||||
)
|
||||
|
||||
def solutions(self, t):
|
||||
"""Returns an iterator over solutions that satisfy the constraints.
|
||||
|
||||
The parameter `t` is an expression whose values should be returned.
|
||||
|
||||
>>> s = Solver()
|
||||
>>> x, y, z = Ints("x y z")
|
||||
>>> s.add(x * x == 4)
|
||||
>>> print(list(s.solutions(x)))
|
||||
[-2, 2]
|
||||
>>> s.reset()
|
||||
>>> s.add(x >= 0, x < 10)
|
||||
>>> print(list(s.solutions(x)))
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
>>> s.reset()
|
||||
>>> s.add(x >= 0, y < 10, y == 2*x)
|
||||
>>> print(list(s.solutions([x, y])))
|
||||
[[0, 0], [1, 2], [2, 4], [3, 6], [4, 8]]
|
||||
"""
|
||||
s = Solver()
|
||||
s.add(self.assertions())
|
||||
t = _get_args(t)
|
||||
if isinstance(t, (list, tuple)):
|
||||
while s.check() == sat:
|
||||
result = [s.model().eval(t_, model_completion=True) for t_ in t]
|
||||
yield result
|
||||
s.add(*(t_ != result_ for t_, result_ in zip(t, result)))
|
||||
else:
|
||||
while s.check() == sat:
|
||||
result = s.model().eval(t, model_completion=True)
|
||||
yield result
|
||||
s.add(t != result)
|
||||
|
||||
|
||||
def SolverFor(logic, ctx=None, logFile=None):
|
||||
"""Create a solver customized for the given logic.
|
||||
|
|
@ -8367,7 +8741,9 @@ class ApplyResult(Z3PPObject):
|
|||
>>> r[1]
|
||||
[a == 1, Or(b == 0, b == 1), a > b]
|
||||
"""
|
||||
if idx >= len(self):
|
||||
if idx < 0:
|
||||
idx += len(self)
|
||||
if idx < 0 or idx >= len(self):
|
||||
raise IndexError
|
||||
return Goal(goal=Z3_apply_result_get_subgoal(self.ctx.ref(), self.result, idx), ctx=self.ctx)
|
||||
|
||||
|
|
@ -11537,6 +11913,14 @@ def Union(*args):
|
|||
sz = len(args)
|
||||
if z3_debug():
|
||||
_z3_assert(sz > 0, "At least one argument expected.")
|
||||
arg0 = args[0]
|
||||
if is_finite_set(arg0):
|
||||
for a in args[1:]:
|
||||
if not is_finite_set(a):
|
||||
raise Z3Exception("All arguments must be regular expressions or finite sets.")
|
||||
arg0 = arg0 | a
|
||||
return arg0
|
||||
if z3_debug():
|
||||
_z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.")
|
||||
if sz == 1:
|
||||
return args[0]
|
||||
|
|
@ -11555,6 +11939,14 @@ def Intersect(*args):
|
|||
sz = len(args)
|
||||
if z3_debug():
|
||||
_z3_assert(sz > 0, "At least one argument expected.")
|
||||
arg0 = args[0]
|
||||
if is_finite_set(arg0):
|
||||
for a in args[1:]:
|
||||
if not is_finite_set(a):
|
||||
raise Z3Exception("All arguments must be regular expressions or finite sets.")
|
||||
arg0 = arg0 & a
|
||||
return arg0
|
||||
if z3_debug():
|
||||
_z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.")
|
||||
if sz == 1:
|
||||
return args[0]
|
||||
|
|
@ -11941,50 +12333,64 @@ class UserPropagateBase:
|
|||
return self.ctx().ref()
|
||||
|
||||
def add_fixed(self, fixed):
|
||||
assert not self.fixed
|
||||
assert not self._ctx
|
||||
if self.fixed:
|
||||
raise Z3Exception("fixed callback already registered")
|
||||
if self._ctx:
|
||||
raise Z3Exception("context already initialized")
|
||||
if self.solver:
|
||||
Z3_solver_propagate_fixed(self.ctx_ref(), self.solver.solver, _user_prop_fixed)
|
||||
self.fixed = fixed
|
||||
|
||||
def add_created(self, created):
|
||||
assert not self.created
|
||||
assert not self._ctx
|
||||
if self.created:
|
||||
raise Z3Exception("created callback already registered")
|
||||
if self._ctx:
|
||||
raise Z3Exception("context already initialized")
|
||||
if self.solver:
|
||||
Z3_solver_propagate_created(self.ctx_ref(), self.solver.solver, _user_prop_created)
|
||||
self.created = created
|
||||
|
||||
def add_final(self, final):
|
||||
assert not self.final
|
||||
assert not self._ctx
|
||||
if self.final:
|
||||
raise Z3Exception("final callback already registered")
|
||||
if self._ctx:
|
||||
raise Z3Exception("context already initialized")
|
||||
if self.solver:
|
||||
Z3_solver_propagate_final(self.ctx_ref(), self.solver.solver, _user_prop_final)
|
||||
self.final = final
|
||||
|
||||
def add_eq(self, eq):
|
||||
assert not self.eq
|
||||
assert not self._ctx
|
||||
if self.eq:
|
||||
raise Z3Exception("eq callback already registered")
|
||||
if self._ctx:
|
||||
raise Z3Exception("context already initialized")
|
||||
if self.solver:
|
||||
Z3_solver_propagate_eq(self.ctx_ref(), self.solver.solver, _user_prop_eq)
|
||||
self.eq = eq
|
||||
|
||||
def add_diseq(self, diseq):
|
||||
assert not self.diseq
|
||||
assert not self._ctx
|
||||
if self.diseq:
|
||||
raise Z3Exception("diseq callback already registered")
|
||||
if self._ctx:
|
||||
raise Z3Exception("context already initialized")
|
||||
if self.solver:
|
||||
Z3_solver_propagate_diseq(self.ctx_ref(), self.solver.solver, _user_prop_diseq)
|
||||
self.diseq = diseq
|
||||
|
||||
def add_decide(self, decide):
|
||||
assert not self.decide
|
||||
assert not self._ctx
|
||||
if self.decide:
|
||||
raise Z3Exception("decide callback already registered")
|
||||
if self._ctx:
|
||||
raise Z3Exception("context already initialized")
|
||||
if self.solver:
|
||||
Z3_solver_propagate_decide(self.ctx_ref(), self.solver.solver, _user_prop_decide)
|
||||
self.decide = decide
|
||||
|
||||
def add_on_binding(self, binding):
|
||||
assert not self.binding
|
||||
assert not self._ctx
|
||||
if self.binding:
|
||||
raise Z3Exception("binding callback already registered")
|
||||
if self._ctx:
|
||||
raise Z3Exception("context already initialized")
|
||||
if self.solver:
|
||||
Z3_solver_propagate_on_binding(self.ctx_ref(), self.solver.solver, _user_prop_binding)
|
||||
self.binding = binding
|
||||
|
|
@ -11999,7 +12405,8 @@ class UserPropagateBase:
|
|||
raise Z3Exception("fresh needs to be overwritten")
|
||||
|
||||
def add(self, e):
|
||||
assert not self._ctx
|
||||
if self._ctx:
|
||||
raise Z3Exception("context already initialized")
|
||||
if self.solver:
|
||||
Z3_solver_propagate_register(self.ctx_ref(), self.solver.solver, e.ast)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -142,7 +142,8 @@ class Numeral:
|
|||
>>> Numeral("2/3").denominator()
|
||||
3
|
||||
"""
|
||||
assert(self.is_rational())
|
||||
if not self.is_rational():
|
||||
raise Z3Exception("denominator() requires a rational numeral")
|
||||
return Numeral(Z3_get_denominator(self.ctx_ref(), self.as_ast()), self.ctx)
|
||||
|
||||
def numerator(self):
|
||||
|
|
@ -151,7 +152,8 @@ class Numeral:
|
|||
>>> Numeral("2/3").numerator()
|
||||
2
|
||||
"""
|
||||
assert(self.is_rational())
|
||||
if not self.is_rational():
|
||||
raise Z3Exception("numerator() requires a rational numeral")
|
||||
return Numeral(Z3_get_numerator(self.ctx_ref(), self.as_ast()), self.ctx)
|
||||
|
||||
def is_irrational(self):
|
||||
|
|
@ -170,7 +172,8 @@ class Numeral:
|
|||
""" Return a numeral (that is an integer) as a Python long.
|
||||
|
||||
"""
|
||||
assert(self.is_integer())
|
||||
if not self.is_integer():
|
||||
raise Z3Exception("as_long() requires an integer numeral")
|
||||
if sys.version_info.major >= 3:
|
||||
return int(Z3_get_numeral_string(self.ctx_ref(), self.as_ast()))
|
||||
else:
|
||||
|
|
@ -181,7 +184,8 @@ class Numeral:
|
|||
>>> Numeral("1/5").as_fraction()
|
||||
Fraction(1, 5)
|
||||
"""
|
||||
assert(self.is_rational())
|
||||
if not self.is_rational():
|
||||
raise Z3Exception("as_fraction() requires a rational numeral")
|
||||
return Fraction(self.numerator().as_long(), self.denominator().as_long())
|
||||
|
||||
def approx(self, precision=10):
|
||||
|
|
|
|||
|
|
@ -760,6 +760,8 @@ class Formatter:
|
|||
return seq1("Seq", (self.pp_sort(s.basis()), ))
|
||||
elif isinstance(s, z3.CharSortRef):
|
||||
return to_format("Char")
|
||||
elif isinstance(s, z3.FiniteSetSortRef):
|
||||
return seq1("FiniteSet", (self.pp_sort(s.element_sort()), ))
|
||||
else:
|
||||
return to_format(s.name())
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,8 @@ def ehash(v):
|
|||
|
||||
"""
|
||||
if z3_debug():
|
||||
assert is_expr(v)
|
||||
if not is_expr(v):
|
||||
raise ValueError(f"ehash expects a Z3 expression, got {type(v)}")
|
||||
|
||||
return "{}_{}_{}".format(str(v), v.hash(), v.sort_kind())
|
||||
|
||||
|
|
|
|||
142
src/api/z3_api.h
142
src/api/z3_api.h
|
|
@ -980,6 +980,32 @@ typedef enum
|
|||
3 = 011 = Z3_OP_FPA_RM_TOWARD_NEGATIVE,
|
||||
4 = 100 = Z3_OP_FPA_RM_TOWARD_ZERO.
|
||||
|
||||
- Z3_OP_FINITE_SET_EMPTY: Empty finite set.
|
||||
|
||||
- Z3_OP_FINITE_SET_SINGLETON: Finite set containing a single element.
|
||||
|
||||
- Z3_OP_FINITE_SET_UNION: Union of two finite sets.
|
||||
|
||||
- Z3_OP_FINITE_SET_INTERSECT: Intersection of two finite sets.
|
||||
|
||||
- Z3_OP_FINITE_SET_DIFFERENCE: Difference of two finite sets.
|
||||
|
||||
- Z3_OP_FINITE_SET_IN: Membership predicate for finite sets.
|
||||
|
||||
- Z3_OP_FINITE_SET_SIZE: Cardinality of a finite set.
|
||||
|
||||
- Z3_OP_FINITE_SET_SUBSET: Subset predicate for finite sets.
|
||||
|
||||
- Z3_OP_FINITE_SET_MAP: Map operation on finite sets.
|
||||
|
||||
- Z3_OP_FINITE_SET_FILTER: Filter operation on finite sets.
|
||||
|
||||
- Z3_OP_FINITE_SET_RANGE: Range operation for finite sets of integers.
|
||||
|
||||
- Z3_OP_FINITE_SET_EXT: Finite set extensionality. Returns a witness element that is in one set but not the other, demonstrating that two sets are different.
|
||||
|
||||
- Z3_OP_FINITE_SET_MAP_INVERSE: Inverse image under a finite set map operation. Related to reasoning about the pre-image of elements under set mappings.
|
||||
|
||||
- Z3_OP_INTERNAL: internal (often interpreted) symbol, but no additional
|
||||
information is exposed. Tools may use the string representation of the
|
||||
function declaration to obtain more information.
|
||||
|
|
@ -1313,6 +1339,21 @@ typedef enum {
|
|||
Z3_OP_FPA_BVWRAP,
|
||||
Z3_OP_FPA_BV2RM,
|
||||
|
||||
// Finite Sets
|
||||
Z3_OP_FINITE_SET_EMPTY = 0xc000,
|
||||
Z3_OP_FINITE_SET_SINGLETON,
|
||||
Z3_OP_FINITE_SET_UNION,
|
||||
Z3_OP_FINITE_SET_INTERSECT,
|
||||
Z3_OP_FINITE_SET_DIFFERENCE,
|
||||
Z3_OP_FINITE_SET_IN,
|
||||
Z3_OP_FINITE_SET_SIZE,
|
||||
Z3_OP_FINITE_SET_SUBSET,
|
||||
Z3_OP_FINITE_SET_MAP,
|
||||
Z3_OP_FINITE_SET_FILTER,
|
||||
Z3_OP_FINITE_SET_RANGE,
|
||||
Z3_OP_FINITE_SET_EXT,
|
||||
Z3_OP_FINITE_SET_MAP_INVERSE,
|
||||
|
||||
Z3_OP_INTERNAL,
|
||||
Z3_OP_RECURSIVE,
|
||||
|
||||
|
|
@ -3413,6 +3454,107 @@ extern "C" {
|
|||
Z3_ast Z3_API Z3_mk_array_ext(Z3_context c, Z3_ast arg1, Z3_ast arg2);
|
||||
/**@}*/
|
||||
|
||||
/** @name Finite Sets */
|
||||
/**@{*/
|
||||
/**
|
||||
\brief Create a finite set sort.
|
||||
|
||||
def_API('Z3_mk_finite_set_sort', SORT, (_in(CONTEXT), _in(SORT)))
|
||||
*/
|
||||
Z3_sort Z3_API Z3_mk_finite_set_sort(Z3_context c, Z3_sort elem_sort);
|
||||
|
||||
/**
|
||||
\brief Check if a sort is a finite set sort.
|
||||
|
||||
def_API('Z3_is_finite_set_sort', BOOL, (_in(CONTEXT), _in(SORT)))
|
||||
*/
|
||||
bool Z3_API Z3_is_finite_set_sort(Z3_context c, Z3_sort s);
|
||||
|
||||
/**
|
||||
\brief Get the element sort of a finite set sort.
|
||||
|
||||
def_API('Z3_get_finite_set_sort_basis', SORT, (_in(CONTEXT), _in(SORT)))
|
||||
*/
|
||||
Z3_sort Z3_API Z3_get_finite_set_sort_basis(Z3_context c, Z3_sort s);
|
||||
|
||||
/**
|
||||
\brief Create an empty finite set of the given sort.
|
||||
|
||||
def_API('Z3_mk_finite_set_empty', AST, (_in(CONTEXT), _in(SORT)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_empty(Z3_context c, Z3_sort set_sort);
|
||||
|
||||
/**
|
||||
\brief Create a singleton finite set.
|
||||
|
||||
def_API('Z3_mk_finite_set_singleton', AST, (_in(CONTEXT), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_singleton(Z3_context c, Z3_ast elem);
|
||||
|
||||
/**
|
||||
\brief Create the union of two finite sets.
|
||||
|
||||
def_API('Z3_mk_finite_set_union', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_union(Z3_context c, Z3_ast s1, Z3_ast s2);
|
||||
|
||||
/**
|
||||
\brief Create the intersection of two finite sets.
|
||||
|
||||
def_API('Z3_mk_finite_set_intersect', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_intersect(Z3_context c, Z3_ast s1, Z3_ast s2);
|
||||
|
||||
/**
|
||||
\brief Create the set difference of two finite sets.
|
||||
|
||||
def_API('Z3_mk_finite_set_difference', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_difference(Z3_context c, Z3_ast s1, Z3_ast s2);
|
||||
|
||||
/**
|
||||
\brief Check if an element is a member of a finite set.
|
||||
|
||||
def_API('Z3_mk_finite_set_member', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_member(Z3_context c, Z3_ast elem, Z3_ast set);
|
||||
|
||||
/**
|
||||
\brief Get the size (cardinality) of a finite set.
|
||||
|
||||
def_API('Z3_mk_finite_set_size', AST, (_in(CONTEXT), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_size(Z3_context c, Z3_ast set);
|
||||
|
||||
/**
|
||||
\brief Check if one finite set is a subset of another.
|
||||
|
||||
def_API('Z3_mk_finite_set_subset', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_subset(Z3_context c, Z3_ast s1, Z3_ast s2);
|
||||
|
||||
/**
|
||||
\brief Apply a function to all elements of a finite set.
|
||||
|
||||
def_API('Z3_mk_finite_set_map', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_map(Z3_context c, Z3_ast f, Z3_ast set);
|
||||
|
||||
/**
|
||||
\brief Filter a finite set using a predicate.
|
||||
|
||||
def_API('Z3_mk_finite_set_filter', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_filter(Z3_context c, Z3_ast f, Z3_ast set);
|
||||
|
||||
/**
|
||||
\brief Create a finite set of integers in the range [low, high].
|
||||
|
||||
def_API('Z3_mk_finite_set_range', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_mk_finite_set_range(Z3_context c, Z3_ast low, Z3_ast high);
|
||||
/**@}*/
|
||||
|
||||
/** @name Numerals */
|
||||
/**@{*/
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Notes:
|
|||
void register_z3_replayer_cmds(z3_replayer & in);
|
||||
|
||||
|
||||
void throw_invalid_reference() {
|
||||
static void throw_invalid_reference() {
|
||||
TRACE(z3_replayer, tout << "invalid argument reference\n";);
|
||||
throw z3_replayer_exception("invalid argument reference");
|
||||
}
|
||||
|
|
@ -155,7 +155,7 @@ struct z3_replayer::imp {
|
|||
}
|
||||
|
||||
void display_args(std::ostream & out) const {
|
||||
for (unsigned i = 0; i < m_args.size(); i++) {
|
||||
for (unsigned i = 0; i < m_args.size(); ++i) {
|
||||
if (i > 0) out << " ";
|
||||
display_arg(out, m_args[i]);
|
||||
}
|
||||
|
|
@ -348,7 +348,7 @@ struct z3_replayer::imp {
|
|||
throw z3_replayer_exception("invalid array size");
|
||||
uint64_t aidx;
|
||||
value_kind nk;
|
||||
for (unsigned i = asz - sz; i < asz; i++) {
|
||||
for (unsigned i = asz - sz; i < asz; ++i) {
|
||||
if (m_args[i].m_kind != k)
|
||||
throw z3_replayer_exception("invalid array: mixed value types");
|
||||
}
|
||||
|
|
@ -357,7 +357,7 @@ struct z3_replayer::imp {
|
|||
nk = UINT_ARRAY;
|
||||
m_unsigned_arrays.push_back(unsigned_vector());
|
||||
unsigned_vector & v = m_unsigned_arrays.back();
|
||||
for (unsigned i = asz - sz; i < asz; i++) {
|
||||
for (unsigned i = asz - sz; i < asz; ++i) {
|
||||
v.push_back(static_cast<unsigned>(m_args[i].m_uint));
|
||||
}
|
||||
}
|
||||
|
|
@ -366,7 +366,7 @@ struct z3_replayer::imp {
|
|||
nk = INT_ARRAY;
|
||||
m_int_arrays.push_back(svector<int>());
|
||||
svector<int> & v = m_int_arrays.back();
|
||||
for (unsigned i = asz - sz; i < asz; i++) {
|
||||
for (unsigned i = asz - sz; i < asz; ++i) {
|
||||
v.push_back(static_cast<int>(m_args[i].m_int));
|
||||
}
|
||||
}
|
||||
|
|
@ -375,7 +375,7 @@ struct z3_replayer::imp {
|
|||
nk = SYMBOL_ARRAY;
|
||||
m_sym_arrays.push_back(svector<Z3_symbol>());
|
||||
svector<Z3_symbol> & v = m_sym_arrays.back();
|
||||
for (unsigned i = asz - sz; i < asz; i++) {
|
||||
for (unsigned i = asz - sz; i < asz; ++i) {
|
||||
v.push_back(reinterpret_cast<Z3_symbol>(const_cast<char*>(m_args[i].m_str)));
|
||||
}
|
||||
}
|
||||
|
|
@ -383,14 +383,14 @@ struct z3_replayer::imp {
|
|||
TRACE(z3_replayer_bug,
|
||||
tout << "args: "; display_args(tout); tout << "\n";
|
||||
tout << "push_back, sz: " << sz << ", m_obj_arrays.size(): " << m_obj_arrays.size() << "\n";
|
||||
for (unsigned i = asz - sz; i < asz; i++) {
|
||||
for (unsigned i = asz - sz; i < asz; ++i) {
|
||||
tout << "pushing: " << m_args[i].m_obj << "\n";
|
||||
});
|
||||
aidx = m_obj_arrays.size();
|
||||
nk = OBJECT_ARRAY;
|
||||
m_obj_arrays.push_back(ptr_vector<void>());
|
||||
ptr_vector<void> & v = m_obj_arrays.back();
|
||||
for (unsigned i = asz - sz; i < asz; i++) {
|
||||
for (unsigned i = asz - sz; i < asz; ++i) {
|
||||
v.push_back(m_args[i].m_obj);
|
||||
}
|
||||
}
|
||||
|
|
@ -427,13 +427,13 @@ struct z3_replayer::imp {
|
|||
case 'R':
|
||||
// reset
|
||||
next();
|
||||
TRACE(z3_replayer, tout << "[" << m_line << "] " << "R\n";);
|
||||
TRACE(z3_replayer, tout << "[" << m_line << "] R\n";);
|
||||
reset();
|
||||
break;
|
||||
case 'P': {
|
||||
// push pointer
|
||||
next(); skip_blank(); read_ptr();
|
||||
TRACE(z3_replayer, tout << "[" << m_line << "] " << "P " << m_ptr << "\n";);
|
||||
TRACE(z3_replayer, tout << "[" << m_line << "] P " << m_ptr << "\n";);
|
||||
if (m_ptr == 0) {
|
||||
m_args.push_back(nullptr);
|
||||
}
|
||||
|
|
@ -449,7 +449,7 @@ struct z3_replayer::imp {
|
|||
case 'S': {
|
||||
// push string
|
||||
next(); skip_blank(); read_string();
|
||||
TRACE(z3_replayer, tout << "[" << m_line << "] " << "S " << m_string.begin() << "\n";);
|
||||
TRACE(z3_replayer, tout << "[" << m_line << "] S " << m_string.begin() << "\n";);
|
||||
symbol sym(m_string.begin()); // save string
|
||||
m_args.push_back(value(STRING, sym.bare_str()));
|
||||
break;
|
||||
|
|
@ -457,7 +457,7 @@ struct z3_replayer::imp {
|
|||
case 'N':
|
||||
// push null symbol
|
||||
next();
|
||||
TRACE(z3_replayer, tout << "[" << m_line << "] " << "N\n";);
|
||||
TRACE(z3_replayer, tout << "[" << m_line << "] N\n";);
|
||||
m_args.push_back(value(SYMBOL, symbol::null));
|
||||
break;
|
||||
case '$': {
|
||||
|
|
@ -658,7 +658,7 @@ struct z3_replayer::imp {
|
|||
unsigned idx = static_cast<unsigned>(m_args[pos].m_uint);
|
||||
ptr_vector<void> const & v = m_obj_arrays[idx];
|
||||
TRACE(z3_replayer_bug, tout << "pos: " << pos << ", idx: " << idx << " size(): " << v.size() << "\n";
|
||||
for (unsigned i = 0; i < v.size(); i++) tout << v[i] << " "; tout << "\n";);
|
||||
for (unsigned i = 0; i < v.size(); ++i) tout << v[i] << " "; tout << "\n";);
|
||||
return v.data();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ z3_add_component(ast
|
|||
expr_map.cpp
|
||||
expr_stat.cpp
|
||||
expr_substitution.cpp
|
||||
finite_set_decl_plugin.cpp
|
||||
for_each_ast.cpp
|
||||
for_each_expr.cpp
|
||||
format.cpp
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ void act_cache::compress_queue() {
|
|||
SASSERT(m_qhead > 0);
|
||||
unsigned sz = m_queue.size();
|
||||
unsigned j = 0;
|
||||
for (unsigned i = m_qhead; i < sz; i++, j++) {
|
||||
for (unsigned i = m_qhead; i < sz; ++i, ++j) {
|
||||
m_queue[j] = m_queue[i];
|
||||
}
|
||||
m_queue.shrink(j);
|
||||
|
|
|
|||
|
|
@ -490,14 +490,14 @@ static bool use_coercion(decl_kind k) {
|
|||
}
|
||||
|
||||
static bool has_real_arg(unsigned arity, sort * const * domain, sort * real_sort) {
|
||||
for (unsigned i = 0; i < arity; i++)
|
||||
for (unsigned i = 0; i < arity; ++i)
|
||||
if (domain[i] == real_sort)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool has_real_arg(ast_manager * m, unsigned num_args, expr * const * args, sort * real_sort) {
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
if (args[i]->get_sort() == real_sort)
|
||||
return true;
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -503,9 +503,13 @@ public:
|
|||
app * mk_power0(expr* arg1, expr* arg2) { return m_manager.mk_app(arith_family_id, OP_POWER0, arg1, arg2); }
|
||||
|
||||
app* mk_band(unsigned n, expr* arg1, expr* arg2) { parameter p(n); expr* args[2] = { arg1, arg2 }; return m_manager.mk_app(arith_family_id, OP_ARITH_BAND, 1, &p, 2, args); }
|
||||
app* mk_band(unsigned n, std::initializer_list<expr*> args) { parameter p(n); return m_manager.mk_app(arith_family_id, OP_ARITH_BAND, 1, &p, static_cast<unsigned>(args.size()), args.begin()); }
|
||||
app* mk_shl(unsigned n, expr* arg1, expr* arg2) { parameter p(n); expr* args[2] = { arg1, arg2 }; return m_manager.mk_app(arith_family_id, OP_ARITH_SHL, 1, &p, 2, args); }
|
||||
app* mk_shl(unsigned n, std::initializer_list<expr*> args) { parameter p(n); return m_manager.mk_app(arith_family_id, OP_ARITH_SHL, 1, &p, static_cast<unsigned>(args.size()), args.begin()); }
|
||||
app* mk_ashr(unsigned n, expr* arg1, expr* arg2) { parameter p(n); expr* args[2] = { arg1, arg2 }; return m_manager.mk_app(arith_family_id, OP_ARITH_ASHR, 1, &p, 2, args); }
|
||||
app* mk_ashr(unsigned n, std::initializer_list<expr*> args) { parameter p(n); return m_manager.mk_app(arith_family_id, OP_ARITH_ASHR, 1, &p, static_cast<unsigned>(args.size()), args.begin()); }
|
||||
app* mk_lshr(unsigned n, expr* arg1, expr* arg2) { parameter p(n); expr* args[2] = { arg1, arg2 }; return m_manager.mk_app(arith_family_id, OP_ARITH_LSHR, 1, &p, 2, args); }
|
||||
app* mk_lshr(unsigned n, std::initializer_list<expr*> args) { parameter p(n); return m_manager.mk_app(arith_family_id, OP_ARITH_LSHR, 1, &p, static_cast<unsigned>(args.size()), args.begin()); }
|
||||
|
||||
app * mk_sin(expr * arg) { return m_manager.mk_app(arith_family_id, OP_SIN, arg); }
|
||||
app * mk_cos(expr * arg) { return m_manager.mk_app(arith_family_id, OP_COS, arg); }
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ Revision History:
|
|||
|
||||
--*/
|
||||
#include<sstream>
|
||||
#include<format>
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "util/warning.h"
|
||||
#include "ast/ast_pp.h"
|
||||
|
|
@ -56,7 +57,7 @@ sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < num_parameters; i++) {
|
||||
for (unsigned i = 0; i < num_parameters; ++i) {
|
||||
if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) {
|
||||
m_manager->raise_exception("invalid array sort definition, parameter is not a sort");
|
||||
return nullptr;
|
||||
|
|
@ -70,7 +71,7 @@ sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete
|
|||
}
|
||||
bool is_infinite = false;
|
||||
bool is_very_big = false;
|
||||
for (unsigned i = 0; i < num_parameters; i++) {
|
||||
for (unsigned i = 0; i < num_parameters; ++i) {
|
||||
sort * s = to_sort(parameters[i].get_ast());
|
||||
if (s->is_infinite()) {
|
||||
is_infinite = true;
|
||||
|
|
@ -89,7 +90,7 @@ sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete
|
|||
else {
|
||||
rational domain_sz(1);
|
||||
rational num_elements;
|
||||
for (unsigned i = 0; i < num_parameters - 1; i++) {
|
||||
for (unsigned i = 0; i < num_parameters - 1; ++i) {
|
||||
domain_sz *= rational(to_sort(parameters[i].get_ast())->get_num_elements().size(),rational::ui64());
|
||||
}
|
||||
if (domain_sz <= rational(128)) {
|
||||
|
|
@ -139,10 +140,8 @@ func_decl * array_decl_plugin::mk_const(sort * s, unsigned arity, sort * const *
|
|||
|
||||
func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const* domain) {
|
||||
if (arity != f->get_arity()) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "map expects to take as many arguments as the function being mapped, "
|
||||
<< "it was given " << arity << " but expects " << f->get_arity();
|
||||
m_manager->raise_exception(buffer.str());
|
||||
m_manager->raise_exception(std::format("map expects to take as many arguments as the function being mapped, it was given {} but expects {}",
|
||||
arity, f->get_arity()));
|
||||
return nullptr;
|
||||
}
|
||||
if (arity == 0) {
|
||||
|
|
@ -157,32 +156,21 @@ func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const*
|
|||
unsigned dom_arity = get_array_arity(domain[0]);
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
if (!is_array_sort(domain[i])) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "map expects an array sort as argument at position " << i;
|
||||
m_manager->raise_exception(buffer.str());
|
||||
m_manager->raise_exception(std::format("map expects an array sort as argument at position {}", i));
|
||||
return nullptr;
|
||||
}
|
||||
if (get_array_arity(domain[i]) != dom_arity) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "map expects all arguments to have the same array domain, "
|
||||
<< "this is not the case for argument " << i;
|
||||
m_manager->raise_exception(buffer.str());
|
||||
m_manager->raise_exception(std::format("map expects all arguments to have the same array domain, this is not the case for argument {}", i));
|
||||
return nullptr;
|
||||
}
|
||||
for (unsigned j = 0; j < dom_arity; ++j) {
|
||||
if (get_array_domain(domain[i],j) != get_array_domain(domain[0],j)) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "map expects all arguments to have the same array domain, "
|
||||
<< "this is not the case for argument " << i;
|
||||
m_manager->raise_exception(buffer.str());
|
||||
m_manager->raise_exception(std::format("map expects all arguments to have the same array domain, this is not the case for argument {}", i));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (get_array_range(domain[i]) != f->get_domain(i)) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "map expects the argument at position " << i
|
||||
<< " to have the array range the same as the function";
|
||||
m_manager->raise_exception(buffer.str());
|
||||
m_manager->raise_exception(std::format("map expects the argument at position {} to have the array range the same as the function", i));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -243,9 +231,8 @@ func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) {
|
|||
parameter const* parameters = s->get_parameters();
|
||||
|
||||
if (num_parameters != arity) {
|
||||
std::stringstream strm;
|
||||
strm << "select requires " << num_parameters << " arguments, but was provided with " << arity << " arguments";
|
||||
m_manager->raise_exception(strm.str());
|
||||
m_manager->raise_exception(std::format("select requires {} arguments, but was provided with {} arguments",
|
||||
num_parameters, arity));
|
||||
return nullptr;
|
||||
}
|
||||
ptr_buffer<sort> new_domain; // we need this because of coercions.
|
||||
|
|
@ -254,10 +241,9 @@ func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) {
|
|||
if (!parameters[i].is_ast() ||
|
||||
!is_sort(parameters[i].get_ast()) ||
|
||||
!m_manager->compatible_sorts(domain[i+1], to_sort(parameters[i].get_ast()))) {
|
||||
std::stringstream strm;
|
||||
strm << "domain sort " << sort_ref(domain[i+1], *m_manager) << " and parameter ";
|
||||
strm << parameter_pp(parameters[i], *m_manager) << " do not match";
|
||||
m_manager->raise_exception(strm.str());
|
||||
m_manager->raise_exception(std::format("domain sort {} and parameter {} do not match",
|
||||
to_string(sort_ref(domain[i+1], *m_manager)),
|
||||
to_string(parameter_pp(parameters[i], *m_manager))));
|
||||
return nullptr;
|
||||
}
|
||||
new_domain.push_back(to_sort(parameters[i].get_ast()));
|
||||
|
|
@ -281,10 +267,8 @@ func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) {
|
|||
return nullptr;
|
||||
}
|
||||
if (arity != num_parameters+1) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "store expects the first argument to be an array taking " << num_parameters+1
|
||||
<< ", instead it was passed " << (arity - 1) << "arguments";
|
||||
m_manager->raise_exception(buffer.str());
|
||||
m_manager->raise_exception(std::format("store expects the first argument to be an array taking {}, instead it was passed {} arguments",
|
||||
num_parameters+1, arity - 1));
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -298,9 +282,9 @@ func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) {
|
|||
sort* srt1 = to_sort(parameters[i].get_ast());
|
||||
sort* srt2 = domain[i+1];
|
||||
if (!m_manager->compatible_sorts(srt1, srt2)) {
|
||||
std::stringstream strm;
|
||||
strm << "domain sort " << sort_ref(srt2, *m_manager) << " and parameter sort " << sort_ref(srt1, *m_manager) << " do not match";
|
||||
m_manager->raise_exception(strm.str());
|
||||
m_manager->raise_exception(std::format("domain sort {} and parameter sort {} do not match",
|
||||
to_string(sort_ref(srt2, *m_manager)),
|
||||
to_string(sort_ref(srt1, *m_manager))));
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -333,15 +317,11 @@ func_decl * array_decl_plugin::mk_array_ext(unsigned arity, sort * const * domai
|
|||
bool array_decl_plugin::check_set_arguments(unsigned arity, sort * const * domain) {
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
if (domain[i] != domain[0]) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "arguments " << 1 << " and " << (i+1) << " have different sorts";
|
||||
m_manager->raise_exception(buffer.str());
|
||||
m_manager->raise_exception(std::format("arguments {} and {} have different sorts", 1, i+1));
|
||||
return false;
|
||||
}
|
||||
if (domain[i]->get_family_id() != m_family_id) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "argument " << (i+1) << " is not of array sort";
|
||||
m_manager->raise_exception(buffer.str());
|
||||
m_manager->raise_exception(std::format("argument {} is not of array sort", i+1));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -443,7 +423,7 @@ func_decl * array_decl_plugin::mk_set_subset(unsigned arity, sort * const * doma
|
|||
|
||||
func_decl * array_decl_plugin::mk_as_array(func_decl * f) {
|
||||
vector<parameter> parameters;
|
||||
for (unsigned i = 0; i < f->get_arity(); i++) {
|
||||
for (unsigned i = 0; i < f->get_arity(); ++i) {
|
||||
parameters.push_back(parameter(f->get_domain(i)));
|
||||
}
|
||||
parameters.push_back(parameter(f->get_range()));
|
||||
|
|
@ -570,7 +550,7 @@ expr * array_decl_plugin::get_some_value(sort * s) {
|
|||
bool array_decl_plugin::is_fully_interp(sort * s) const {
|
||||
SASSERT(s->is_sort_of(m_family_id, ARRAY_SORT));
|
||||
unsigned sz = get_array_arity(s);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (!m_manager->is_fully_interp(get_array_domain(s, i)))
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
184
src/ast/ast.cpp
184
src/ast/ast.cpp
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ Revision History:
|
|||
|
||||
--*/
|
||||
#include <sstream>
|
||||
#include <format>
|
||||
#include <cstring>
|
||||
#include "ast/ast.h"
|
||||
#include "ast/ast_pp.h"
|
||||
|
|
@ -40,18 +42,12 @@ Revision History:
|
|||
// -----------------------------------
|
||||
|
||||
parameter::~parameter() {
|
||||
if (auto p = std::get_if<rational*>(&m_val)) {
|
||||
dealloc(*p);
|
||||
}
|
||||
if (auto p = std::get_if<zstring*>(&m_val)) {
|
||||
dealloc(*p);
|
||||
}
|
||||
}
|
||||
|
||||
parameter::parameter(parameter const& other) : m_val(other.m_val) {
|
||||
if (auto p = std::get_if<rational*>(&m_val)) {
|
||||
m_val = alloc(rational, **p);
|
||||
}
|
||||
if (auto p = std::get_if<zstring*>(&m_val)) {
|
||||
m_val = alloc(zstring, **p);
|
||||
}
|
||||
|
|
@ -199,7 +195,7 @@ bool decl_info::operator==(decl_info const & info) const {
|
|||
|
||||
std::ostream & operator<<(std::ostream & out, decl_info const & info) {
|
||||
out << ":fid " << info.get_family_id() << " :decl-kind " << info.get_decl_kind() << " :parameters (";
|
||||
for (unsigned i = 0; i < info.get_num_parameters(); i++) {
|
||||
for (unsigned i = 0; i < info.get_num_parameters(); ++i) {
|
||||
if (i > 0) out << " ";
|
||||
out << info.get_parameter(i);
|
||||
}
|
||||
|
|
@ -315,7 +311,7 @@ app::app(func_decl * decl, unsigned num_args, expr * const * args):
|
|||
expr(AST_APP),
|
||||
m_decl(decl),
|
||||
m_num_args(num_args) {
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
m_args[i] = args[i];
|
||||
}
|
||||
|
||||
|
|
@ -634,7 +630,7 @@ bool decl_plugin::log_constant_meaning_prelude(app * a) {
|
|||
func_decl * decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned num_args, expr * const * args, sort * range) {
|
||||
ptr_buffer<sort> sorts;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
sorts.push_back(args[i]->get_sort());
|
||||
}
|
||||
return mk_func_decl(k, num_parameters, parameters, num_args, sorts.data(), range);
|
||||
|
|
@ -652,7 +648,7 @@ bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort
|
|||
if (arity == 0)
|
||||
return false;
|
||||
else {
|
||||
for (unsigned i = 0; i < arity - 1; i++)
|
||||
for (unsigned i = 0; i < arity - 1; ++i)
|
||||
if (domain[i] != m_proof_sort)
|
||||
return false;
|
||||
#define is_array(_x_) true
|
||||
|
|
@ -666,7 +662,7 @@ bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, exp
|
|||
if (num_args == 0)
|
||||
return false;
|
||||
else {
|
||||
for (unsigned i = 0; i < num_args - 1; i++)
|
||||
for (unsigned i = 0; i < num_args - 1; ++i)
|
||||
if (args[i]->get_sort() != m_proof_sort)
|
||||
return false;
|
||||
return
|
||||
|
|
@ -679,7 +675,7 @@ bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, exp
|
|||
func_decl * basic_decl_plugin::mk_bool_op_decl(char const * name, basic_op_kind k, unsigned num_args, bool assoc, bool comm, bool idempotent,
|
||||
bool flat_associative, bool chainable) {
|
||||
ptr_buffer<sort> domain;
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
domain.push_back(m_bool_sort);
|
||||
func_decl_info info(m_family_id, k);
|
||||
info.set_associative(assoc);
|
||||
|
|
@ -705,7 +701,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(
|
|||
char const * name, basic_op_kind k,
|
||||
unsigned num_parameters, parameter const* params, unsigned num_parents) {
|
||||
ptr_buffer<sort> domain;
|
||||
for (unsigned i = 0; i < num_parents; i++)
|
||||
for (unsigned i = 0; i < num_parents; ++i)
|
||||
domain.push_back(m_proof_sort);
|
||||
domain.push_back(m_bool_sort);
|
||||
func_decl_info info(m_family_id, k, num_parameters, params);
|
||||
|
|
@ -714,7 +710,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(
|
|||
|
||||
func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, bool inc_ref) {
|
||||
ptr_buffer<sort> domain;
|
||||
for (unsigned i = 0; i < num_parents; i++)
|
||||
for (unsigned i = 0; i < num_parents; ++i)
|
||||
domain.push_back(m_proof_sort);
|
||||
domain.push_back(m_bool_sort);
|
||||
func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents+1, domain.data(), m_proof_sort, func_decl_info(m_family_id, k));
|
||||
|
|
@ -724,7 +720,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k,
|
|||
|
||||
func_decl * basic_decl_plugin::mk_compressed_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) {
|
||||
ptr_buffer<sort> domain;
|
||||
for (unsigned i = 0; i < num_parents; i++)
|
||||
for (unsigned i = 0; i < num_parents; ++i)
|
||||
domain.push_back(m_proof_sort);
|
||||
func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents, domain.data(), m_proof_sort, func_decl_info(m_family_id, k));
|
||||
m_manager->inc_ref(d);
|
||||
|
|
@ -1021,9 +1017,9 @@ sort* basic_decl_plugin::join(sort* s1, sort* s2) {
|
|||
return s2;
|
||||
if (s2 == m_bool_sort && s1->get_family_id() == arith_family_id)
|
||||
return s1;
|
||||
std::ostringstream buffer;
|
||||
buffer << "Sorts " << mk_pp(s1, *m_manager) << " and " << mk_pp(s2, *m_manager) << " are incompatible";
|
||||
throw ast_exception(buffer.str());
|
||||
throw ast_exception(std::format("Sorts {} and {} are incompatible",
|
||||
to_string(mk_pp(s1, *m_manager)),
|
||||
to_string(mk_pp(s2, *m_manager))));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1049,7 +1045,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
|
|||
func_decl_info info(m_family_id, OP_DISTINCT);
|
||||
info.set_pairwise();
|
||||
ptr_buffer<sort> sorts;
|
||||
for (unsigned i = 1; i < arity; i++) {
|
||||
for (unsigned i = 1; i < arity; ++i) {
|
||||
if (domain[i] != domain[0]) {
|
||||
sort* srt = join(arity, domain);
|
||||
for (unsigned j = 0; j < arity; ++j)
|
||||
|
|
@ -1144,7 +1140,7 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
|
|||
m_manager->raise_exception("invalid label declaration");
|
||||
return nullptr;
|
||||
}
|
||||
for (unsigned i = 2; i < num_parameters; i++) {
|
||||
for (unsigned i = 2; i < num_parameters; ++i) {
|
||||
if (!parameters[i].is_symbol()) {
|
||||
m_manager->raise_exception("invalid label declaration");
|
||||
return nullptr;
|
||||
|
|
@ -1159,7 +1155,7 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
|
|||
m_manager->raise_exception("invalid label literal declaration");
|
||||
return nullptr;
|
||||
}
|
||||
for (unsigned i = 0; i < num_parameters; i++) {
|
||||
for (unsigned i = 0; i < num_parameters; ++i) {
|
||||
if (!parameters[i].is_symbol()) {
|
||||
m_manager->raise_exception("invalid label literal declaration");
|
||||
return nullptr;
|
||||
|
|
@ -1355,13 +1351,13 @@ void ast_manager::init() {
|
|||
|
||||
template<typename T>
|
||||
static void mark_array_ref(ast_mark& mark, unsigned sz, T * const * a) {
|
||||
for(unsigned i = 0; i < sz; i++) {
|
||||
for(unsigned i = 0; i < sz; ++i) {
|
||||
mark.mark(a[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
static void mark_array_ref(ast_mark& mark, unsigned sz, parameter const * a) {
|
||||
for(unsigned i = 0; i < sz; i++) {
|
||||
for(unsigned i = 0; i < sz; ++i) {
|
||||
if (a[i].is_ast()) {
|
||||
mark.mark(a[i].get_ast(), true);
|
||||
}
|
||||
|
|
@ -1508,14 +1504,14 @@ std::ostream& ast_manager::display(std::ostream& out, parameter const& p) {
|
|||
void ast_manager::copy_families_plugins(ast_manager const & from) {
|
||||
TRACE(copy_families_plugins,
|
||||
tout << "target:\n";
|
||||
for (family_id fid = 0; m_family_manager.has_family(fid); fid++) {
|
||||
for (family_id fid = 0; m_family_manager.has_family(fid); ++fid) {
|
||||
tout << "fid: " << fid << " fidname: " << get_family_name(fid) << "\n";
|
||||
});
|
||||
ast_translation trans(const_cast<ast_manager&>(from), *this, false);
|
||||
// Inheriting plugins can create new family ids. Since new family ids are
|
||||
// assigned in the order that they are created, this can result in differing
|
||||
// family ids. To avoid this, we first assign all family ids and only then inherit plugins.
|
||||
for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) {
|
||||
for (family_id fid = 0; from.m_family_manager.has_family(fid); ++fid) {
|
||||
symbol fid_name = from.get_family_name(fid);
|
||||
if (!m_family_manager.has_family(fid)) {
|
||||
family_id new_fid = mk_family_id(fid_name);
|
||||
|
|
@ -1523,7 +1519,7 @@ void ast_manager::copy_families_plugins(ast_manager const & from) {
|
|||
TRACE(copy_families_plugins, tout << "new target fid created: " << new_fid << " fid_name: " << fid_name << "\n";);
|
||||
}
|
||||
}
|
||||
for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) {
|
||||
for (family_id fid = 0; from.m_family_manager.has_family(fid); ++fid) {
|
||||
SASSERT(from.is_builtin_family_id(fid) == is_builtin_family_id(fid));
|
||||
SASSERT(!from.is_builtin_family_id(fid) || m_family_manager.has_family(fid));
|
||||
symbol fid_name = from.get_family_name(fid);
|
||||
|
|
@ -1674,7 +1670,7 @@ bool ast_manager::slow_not_contains(ast const * n) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#if 1
|
||||
static unsigned s_count = 0;
|
||||
|
||||
static void track_id(ast_manager& m, ast* n, unsigned id) {
|
||||
|
|
@ -1700,10 +1696,8 @@ ast * ast_manager::register_node_core(ast * n) {
|
|||
SASSERT(contains);
|
||||
SASSERT(m_ast_table.contains(n));
|
||||
if (is_func_decl(r) && to_func_decl(r)->get_range() != to_func_decl(n)->get_range()) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "Recycling of declaration for the same name '" << to_func_decl(r)->get_name().str()
|
||||
<< "' and domain, but different range type is not permitted";
|
||||
throw ast_exception(buffer.str());
|
||||
throw ast_exception(std::format("Recycling of declaration for the same name '{}' and domain, but different range type is not permitted",
|
||||
to_func_decl(r)->get_name().str()));
|
||||
}
|
||||
deallocate_node(n, ::get_node_size(n));
|
||||
return r;
|
||||
|
|
@ -1715,9 +1709,8 @@ ast * ast_manager::register_node_core(ast * n) {
|
|||
|
||||
n->m_id = is_decl(n) ? m_decl_id_gen.mk() : m_expr_id_gen.mk();
|
||||
|
||||
// track_id(*this, n, 9213);
|
||||
|
||||
// TRACE(ast, tout << (s_count++) << " Object " << n->m_id << " was created.\n";);
|
||||
|
||||
// TRACE(ast, tout << (s_count++) << " Object " << n->m_id << " was created.\n";);
|
||||
TRACE(mk_var_bug, tout << "mk_ast: " << n->m_id << "\n";);
|
||||
// increment reference counters
|
||||
switch (n->get_kind()) {
|
||||
|
|
@ -1747,7 +1740,7 @@ ast * ast_manager::register_node_core(ast * n) {
|
|||
if (is_label(t))
|
||||
f->m_has_labels = true;
|
||||
unsigned depth = 0;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
expr * arg = t->get_arg(i);
|
||||
inc_ref(arg);
|
||||
unsigned arg_depth = 0;
|
||||
|
|
@ -1914,6 +1907,10 @@ app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_args, expr *
|
|||
return mk_app(fid, k, 0, nullptr, num_args, args);
|
||||
}
|
||||
|
||||
app * ast_manager::mk_app(family_id fid, decl_kind k, std::span<expr* const> args) {
|
||||
return mk_app(fid, k, 0, nullptr, static_cast<unsigned>(args.size()), args.data());
|
||||
}
|
||||
|
||||
app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg) {
|
||||
return mk_app(fid, k, 0, nullptr, 1, &arg);
|
||||
}
|
||||
|
|
@ -2019,14 +2016,14 @@ void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * c
|
|||
|
||||
if (decl->is_associative()) {
|
||||
sort * expected = decl->get_domain(0);
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
sort * given = args[i]->get_sort();
|
||||
if (!compatible_sorts(expected, given)) {
|
||||
std::ostringstream buff;
|
||||
buff << "invalid function application for " << decl->get_name() << ", ";
|
||||
buff << "sort mismatch on argument at position " << (i+1) << ", ";
|
||||
buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m);
|
||||
throw ast_exception(buff.str());
|
||||
throw ast_exception(std::format("invalid function application for {}, sort mismatch on argument at position {}, expected {} but given {}",
|
||||
to_string(decl->get_name()),
|
||||
i + 1,
|
||||
to_string(mk_pp(expected, m)),
|
||||
to_string(mk_pp(given, m))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2034,15 +2031,15 @@ void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * c
|
|||
if (decl->get_arity() != num_args) {
|
||||
throw ast_exception("invalid function application, wrong number of arguments");
|
||||
}
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
sort * expected = decl->get_domain(i);
|
||||
sort * given = args[i]->get_sort();
|
||||
if (!compatible_sorts(expected, given)) {
|
||||
std::ostringstream buff;
|
||||
buff << "invalid function application for " << decl->get_name() << ", ";
|
||||
buff << "sort mismatch on argument at position " << (i+1) << ", ";
|
||||
buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m);
|
||||
throw ast_exception(buff.str());
|
||||
throw ast_exception(std::format("invalid function application for {}, sort mismatch on argument at position {}, expected {} but given {}",
|
||||
to_string(decl->get_name()),
|
||||
i + 1,
|
||||
to_string(mk_pp(expected, m)),
|
||||
to_string(mk_pp(given, m))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2099,7 +2096,7 @@ bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * co
|
|||
if (decl->is_associative()) {
|
||||
sort * d = decl->get_domain(0);
|
||||
if (d->get_family_id() == arith_family_id) {
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
if (d != args[i]->get_sort())
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2111,7 +2108,7 @@ bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * co
|
|||
// So, there is no point in coercing the input arguments.
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
sort * d = decl->get_domain(i);
|
||||
if (d->get_family_id() == arith_family_id && d != args[i]->get_sort())
|
||||
return true;
|
||||
|
|
@ -2148,7 +2145,7 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const
|
|||
try {
|
||||
if (m_int_real_coercions && coercion_needed(decl, num_args, args)) {
|
||||
expr_ref_buffer new_args(*this);
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
sort * d = decl->is_associative() ? decl->get_domain(0) : decl->get_domain(i);
|
||||
new_args.push_back(coerce_to(args[i], d));
|
||||
}
|
||||
|
|
@ -2178,7 +2175,7 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const
|
|||
ast_ll_pp(*m_trace_stream, *this, r);
|
||||
else {
|
||||
*m_trace_stream << r->get_decl()->get_name();
|
||||
for (unsigned i = 0; i < r->get_num_args(); i++)
|
||||
for (unsigned i = 0; i < r->get_num_args(); ++i)
|
||||
*m_trace_stream << " #" << r->get_arg(i)->get_id();
|
||||
*m_trace_stream << "\n";
|
||||
}
|
||||
|
|
@ -2193,16 +2190,14 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const
|
|||
}
|
||||
|
||||
void ast_manager::check_args(func_decl* f, unsigned n, expr* const* es) {
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
sort * actual_sort = es[i]->get_sort();
|
||||
sort * expected_sort = f->is_associative() ? f->get_domain(0) : f->get_domain(i);
|
||||
if (expected_sort != actual_sort) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "Sort mismatch at argument #" << (i+1)
|
||||
<< " for function " << mk_pp(f,*this)
|
||||
<< " supplied sort is "
|
||||
<< mk_pp(actual_sort, *this);
|
||||
throw ast_exception(buffer.str());
|
||||
throw ast_exception(std::format("Sort mismatch at argument #{} for function {} supplied sort is {}",
|
||||
i + 1,
|
||||
to_string(mk_pp(f, *this)),
|
||||
to_string(mk_pp(actual_sort, *this))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2223,12 +2218,13 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar
|
|||
decl->get_family_id() == basic_family_id && !decl->is_associative());
|
||||
|
||||
if (type_error) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "Wrong number of arguments (" << num_args
|
||||
<< ") passed to function " << mk_pp(decl, *this) << " ";
|
||||
std::string arg_list;
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
buffer << "\narg: " << mk_pp(args[i], *this) << "\n";
|
||||
throw ast_exception(std::move(buffer).str());
|
||||
arg_list += std::format("\narg: {}\n", to_string(mk_pp(args[i], *this)));
|
||||
throw ast_exception(std::format("Wrong number of arguments ({}) passed to function {} {}",
|
||||
num_args,
|
||||
to_string(mk_pp(decl, *this)),
|
||||
arg_list));
|
||||
}
|
||||
app * r = nullptr;
|
||||
if (num_args == 1 && decl->is_chainable() && decl->get_arity() == 2) {
|
||||
|
|
@ -2246,14 +2242,14 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar
|
|||
}
|
||||
else if (decl->is_left_associative()) {
|
||||
r = mk_app_core(decl, args[0], args[1]);
|
||||
for (unsigned i = 2; i < num_args; i++) {
|
||||
for (unsigned i = 2; i < num_args; ++i) {
|
||||
r = mk_app_core(decl, r, args[i]);
|
||||
}
|
||||
}
|
||||
else if (decl->is_chainable()) {
|
||||
TRACE(chainable, tout << "chainable...\n";);
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned i = 1; i < num_args; i++) {
|
||||
for (unsigned i = 1; i < num_args; ++i) {
|
||||
new_args.push_back(mk_app_core(decl, args[i-1], args[i]));
|
||||
}
|
||||
r = mk_and(new_args.size(), new_args.data());
|
||||
|
|
@ -2336,7 +2332,7 @@ app * ast_manager::mk_label(bool pos, unsigned num_names, symbol const * names,
|
|||
SASSERT(n->get_sort() == m_bool_sort);
|
||||
buffer<parameter> p;
|
||||
p.push_back(parameter(static_cast<int>(pos)));
|
||||
for (unsigned i = 0; i < num_names; i++)
|
||||
for (unsigned i = 0; i < num_names; ++i)
|
||||
p.push_back(parameter(names[i]));
|
||||
return mk_app(label_family_id, OP_LABEL, p.size(), p.data(), 1, &n);
|
||||
}
|
||||
|
|
@ -2351,7 +2347,7 @@ bool ast_manager::is_label(expr const * n, bool & pos, buffer<symbol> & names) c
|
|||
}
|
||||
func_decl const * decl = to_app(n)->get_decl();
|
||||
pos = decl->get_parameter(0).get_int() != 0;
|
||||
for (unsigned i = 1; i < decl->get_num_parameters(); i++)
|
||||
for (unsigned i = 1; i < decl->get_num_parameters(); ++i)
|
||||
names.push_back(decl->get_parameter(i).get_symbol());
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2359,7 +2355,7 @@ bool ast_manager::is_label(expr const * n, bool & pos, buffer<symbol> & names) c
|
|||
app * ast_manager::mk_label_lit(unsigned num_names, symbol const * names) {
|
||||
SASSERT(num_names > 0);
|
||||
buffer<parameter> p;
|
||||
for (unsigned i = 0; i < num_names; i++)
|
||||
for (unsigned i = 0; i < num_names; ++i)
|
||||
p.push_back(parameter(names[i]));
|
||||
return mk_app(label_family_id, OP_LABEL_LIT, p.size(), p.data(), 0, nullptr);
|
||||
}
|
||||
|
|
@ -2473,7 +2469,7 @@ quantifier * ast_manager::mk_lambda(unsigned num_decls, sort * const * decl_sort
|
|||
static bool same_patterns(quantifier * q, unsigned num_patterns, expr * const * patterns) {
|
||||
if (num_patterns != q->get_num_patterns())
|
||||
return false;
|
||||
for (unsigned i = 0; i < num_patterns; i++)
|
||||
for (unsigned i = 0; i < num_patterns; ++i)
|
||||
if (q->get_pattern(i) != patterns[i])
|
||||
return false;
|
||||
return true;
|
||||
|
|
@ -2483,7 +2479,7 @@ static bool same_patterns(quantifier * q, unsigned num_patterns, expr * const *
|
|||
static bool same_no_patterns(quantifier * q, unsigned num_no_patterns, expr * const * no_patterns) {
|
||||
if (num_no_patterns != q->get_num_no_patterns())
|
||||
return false;
|
||||
for (unsigned i = 0; i < num_no_patterns; i++)
|
||||
for (unsigned i = 0; i < num_no_patterns; ++i)
|
||||
if (q->get_no_pattern(i) != no_patterns[i])
|
||||
return false;
|
||||
return true;
|
||||
|
|
@ -2592,6 +2588,15 @@ quantifier * ast_manager::update_quantifier(quantifier * q, quantifier_kind k, u
|
|||
num_patterns == 0 ? q->get_no_patterns() : nullptr);
|
||||
}
|
||||
|
||||
quantifier * ast_manager::update_quantifier(quantifier * q, std::initializer_list<expr*> patterns, expr * body) {
|
||||
return update_quantifier(q, static_cast<unsigned>(patterns.size()), patterns.begin(), body);
|
||||
}
|
||||
|
||||
quantifier * ast_manager::update_quantifier(quantifier * q, std::initializer_list<expr*> patterns, std::initializer_list<expr*> no_patterns, expr * body) {
|
||||
return update_quantifier(q, static_cast<unsigned>(patterns.size()), patterns.begin(),
|
||||
static_cast<unsigned>(no_patterns.size()), no_patterns.begin(), body);
|
||||
}
|
||||
|
||||
app * ast_manager::mk_distinct(unsigned num_args, expr * const * args) {
|
||||
return mk_app(basic_family_id, OP_DISTINCT, num_args, args);
|
||||
}
|
||||
|
|
@ -2602,9 +2607,9 @@ app * ast_manager::mk_distinct_expanded(unsigned num_args, expr * const * args)
|
|||
if (num_args == 2)
|
||||
return mk_not(mk_eq(args[0], args[1]));
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned i = 0; i < num_args - 1; i++) {
|
||||
for (unsigned i = 0; i < num_args - 1; ++i) {
|
||||
expr * a1 = args[i];
|
||||
for (unsigned j = i + 1; j < num_args; j++) {
|
||||
for (unsigned j = i + 1; j < num_args; ++j) {
|
||||
expr * a2 = args[j];
|
||||
new_args.push_back(mk_not(mk_eq(a1, a2)));
|
||||
}
|
||||
|
|
@ -2629,7 +2634,7 @@ expr_dependency * ast_manager::mk_leaf(expr * t) {
|
|||
|
||||
expr_dependency * ast_manager::mk_join(unsigned n, expr * const * ts) {
|
||||
expr_dependency * d = nullptr;
|
||||
for (unsigned i = 0; i < n; i++)
|
||||
for (unsigned i = 0; i < n; ++i)
|
||||
d = mk_join(d, mk_leaf(ts[i]));
|
||||
return d;
|
||||
}
|
||||
|
|
@ -2897,7 +2902,7 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3, proof *
|
|||
proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs) {
|
||||
SASSERT(num_proofs > 0);
|
||||
proof * r = proofs[0];
|
||||
for (unsigned i = 1; i < num_proofs; i++)
|
||||
for (unsigned i = 1; i < num_proofs; ++i)
|
||||
r = mk_transitivity(r, proofs[i]);
|
||||
return r;
|
||||
}
|
||||
|
|
@ -2908,7 +2913,7 @@ proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs
|
|||
if (num_proofs == 1)
|
||||
return proofs[0];
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 0; i < num_proofs; i++) {
|
||||
for (unsigned i = 0; i < num_proofs; ++i) {
|
||||
SASSERT(proofs[i]);
|
||||
SASSERT(!is_reflexivity(proofs[i]));
|
||||
}
|
||||
|
|
@ -3050,7 +3055,7 @@ proof * ast_manager::mk_def_axiom(expr * ax) {
|
|||
|
||||
proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs) {
|
||||
SASSERT(num_proofs >= 2);
|
||||
DEBUG_CODE(for (unsigned i = 0; i < num_proofs; i++) SASSERT(has_fact(proofs[i])););
|
||||
DEBUG_CODE(for (unsigned i = 0; i < num_proofs; ++i) SASSERT(has_fact(proofs[i])););
|
||||
ptr_buffer<expr> args;
|
||||
expr * fact;
|
||||
expr * f1 = get_fact(proofs[0]);
|
||||
|
|
@ -3091,10 +3096,10 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
|
|||
bool_vector found;
|
||||
#endif
|
||||
ast_mark mark;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
bool found_complement = false;
|
||||
expr * lit = cls->get_arg(i);
|
||||
for (unsigned j = 1; j < num_proofs; j++) {
|
||||
for (unsigned j = 1; j < num_proofs; ++j) {
|
||||
expr const * _fact = get_fact(proofs[j]);
|
||||
if (is_complement(lit, _fact)) {
|
||||
found_complement = true;
|
||||
|
|
@ -3110,9 +3115,9 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
|
|||
}
|
||||
}
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 1; proofs_enabled() && i < num_proofs; i++) {
|
||||
for (unsigned i = 1; proofs_enabled() && i < num_proofs; ++i) {
|
||||
CTRACE(mk_unit_resolution_bug, !found.get(i, false),
|
||||
for (unsigned j = 0; j < num_proofs; j++) {
|
||||
for (unsigned j = 0; j < num_proofs; ++j) {
|
||||
if (j == i) tout << "Index " << i << " was not found:\n";
|
||||
tout << mk_ll_pp(get_fact(proofs[j]), *this);
|
||||
});
|
||||
|
|
@ -3140,7 +3145,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
|
|||
|
||||
proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact) {
|
||||
TRACE(unit_bug,
|
||||
for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n";
|
||||
for (unsigned i = 0; i < num_proofs; ++i) tout << mk_pp(get_fact(proofs[i]), *this) << "\n";
|
||||
tout << "===>\n";
|
||||
tout << mk_pp(new_fact, *this) << "\n";);
|
||||
|
||||
|
|
@ -3158,7 +3163,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
|
|||
app * cls = to_app(f1);
|
||||
unsigned cls_sz = cls->get_num_args();
|
||||
CTRACE(unit_bug, !(num_proofs == cls_sz || (num_proofs == cls_sz + 1 && is_false(new_fact))),
|
||||
for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n";
|
||||
for (unsigned i = 0; i < num_proofs; ++i) tout << mk_pp(get_fact(proofs[i]), *this) << "\n";
|
||||
tout << "===>\n";
|
||||
tout << mk_pp(new_fact, *this) << "\n";);
|
||||
//
|
||||
|
|
@ -3166,10 +3171,10 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
|
|||
// but formula could have repeated literals that are merged in the clausal representation.
|
||||
//
|
||||
unsigned num_matches = 0, num_occ = 0;
|
||||
for (unsigned i = 0; i < cls_sz; i++) {
|
||||
for (unsigned i = 0; i < cls_sz; ++i) {
|
||||
expr * lit = cls->get_arg(i);
|
||||
unsigned j = 1;
|
||||
for (; j < num_proofs; j++) {
|
||||
for (; j < num_proofs; ++j) {
|
||||
if (is_complement(lit, get_fact(proofs[j]))) {
|
||||
num_matches++;
|
||||
break;
|
||||
|
|
@ -3231,7 +3236,7 @@ proof * ast_manager::mk_iff_oeq(proof * p) {
|
|||
}
|
||||
|
||||
bool ast_manager::check_nnf_proof_parents(unsigned num_proofs, proof * const * proofs) const {
|
||||
for (unsigned i = 0; i < num_proofs; i++) {
|
||||
for (unsigned i = 0; i < num_proofs; ++i) {
|
||||
if (!has_fact(proofs[i]))
|
||||
return false;
|
||||
if (!is_oeq(get_fact(proofs[i])))
|
||||
|
|
@ -3338,15 +3343,14 @@ proof * ast_manager::mk_th_lemma(
|
|||
if (proofs_disabled())
|
||||
return nullptr;
|
||||
|
||||
ptr_buffer<expr> args;
|
||||
vector<parameter> parameters;
|
||||
parameters.push_back(parameter(get_family_name(tid)));
|
||||
for (unsigned i = 0; i < num_params; ++i) {
|
||||
parameters.push_back(params[i]);
|
||||
}
|
||||
for (unsigned i = 0; i < num_params; ++i)
|
||||
parameters.push_back(params[i]);
|
||||
ptr_buffer<expr> args;
|
||||
args.append(num_proofs, (expr**) proofs);
|
||||
args.push_back(fact);
|
||||
return mk_app(basic_family_id, PR_TH_LEMMA, num_params+1, parameters.data(), args.size(), args.data());
|
||||
return mk_app(basic_family_id, PR_TH_LEMMA, parameters.size(), parameters.data(), args.size(), args.data());
|
||||
}
|
||||
|
||||
proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premises, expr* concl,
|
||||
|
|
|
|||
241
src/ast/ast.h
241
src/ast/ast.h
|
|
@ -26,7 +26,7 @@ Revision History:
|
|||
#include "util/symbol.h"
|
||||
#include "util/rational.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/optional.h"
|
||||
#include <optional>
|
||||
#include "util/trace.h"
|
||||
#include "util/bit_vector.h"
|
||||
#include "util/symbol_table.h"
|
||||
|
|
@ -48,6 +48,8 @@ Revision History:
|
|||
#include "util/dependency.h"
|
||||
#include "util/rlimit.h"
|
||||
#include <variant>
|
||||
#include <span>
|
||||
#include <initializer_list>
|
||||
|
||||
#define RECYCLE_FREE_AST_INDICES
|
||||
|
||||
|
|
@ -122,20 +124,20 @@ private:
|
|||
ast*, // for PARAM_AST
|
||||
symbol, // for PARAM_SYMBOL
|
||||
zstring*, // for PARAM_ZSTRING
|
||||
rational*, // for PARAM_RATIONAL
|
||||
rational, // for PARAM_RATIONAL
|
||||
double, // for PARAM_DOUBLE (remark: this is not used in float_decl_plugin)
|
||||
unsigned // for PARAM_EXTERNAL
|
||||
> m_val;
|
||||
> m_val = 0;
|
||||
|
||||
public:
|
||||
|
||||
parameter() : m_val(0) {}
|
||||
parameter() noexcept = default;
|
||||
explicit parameter(int val): m_val(val) {}
|
||||
explicit parameter(unsigned val): m_val((int)val) {}
|
||||
explicit parameter(ast * p): m_val(p) {}
|
||||
explicit parameter(symbol const & s): m_val(s) {}
|
||||
explicit parameter(rational const & r): m_val(alloc(rational, r)) {}
|
||||
explicit parameter(rational && r) : m_val(alloc(rational, std::move(r))) {}
|
||||
explicit parameter(rational const & r): m_val(r) {}
|
||||
explicit parameter(rational && r) : m_val(std::move(r)) {}
|
||||
explicit parameter(zstring const& s): m_val(alloc(zstring, s)) {}
|
||||
explicit parameter(zstring && s): m_val(alloc(zstring, std::move(s))) {}
|
||||
explicit parameter(double d): m_val(d) {}
|
||||
|
|
@ -187,7 +189,7 @@ public:
|
|||
int get_int() const { SASSERT(is_int()); return std::get<int>(m_val); }
|
||||
ast * get_ast() const { SASSERT(is_ast()); return std::get<ast*>(m_val); }
|
||||
symbol get_symbol() const { SASSERT(is_symbol()); return std::get<symbol>(m_val); }
|
||||
rational const & get_rational() const { SASSERT(is_rational()); return *std::get<rational*>(m_val); }
|
||||
rational const & get_rational() const { SASSERT(is_rational()); return std::get<rational>(m_val); }
|
||||
zstring const& get_zstring() const { SASSERT(is_zstring()); return *std::get<zstring*>(m_val); }
|
||||
double get_double() const { SASSERT(is_double()); return std::get<double>(m_val); }
|
||||
unsigned get_ext_id() const { SASSERT(is_external()); return std::get<unsigned>(m_val); }
|
||||
|
|
@ -314,12 +316,12 @@ class sort_size {
|
|||
// of elements is at least bigger than 2^64.
|
||||
SS_FINITE_VERY_BIG,
|
||||
SS_INFINITE
|
||||
} m_kind;
|
||||
uint64_t m_size; // It is only meaningful if m_kind == SS_FINITE
|
||||
} m_kind = SS_INFINITE;
|
||||
uint64_t m_size = 0; // It is only meaningful if m_kind == SS_FINITE
|
||||
sort_size(kind_t k, uint64_t r):m_kind(k), m_size(r) {}
|
||||
public:
|
||||
sort_size():m_kind(SS_INFINITE), m_size(0) {}
|
||||
sort_size(uint64_t const & sz):m_kind(SS_FINITE), m_size(sz) {}
|
||||
sort_size() = default;
|
||||
sort_size(uint64_t sz):m_kind(SS_FINITE), m_size(sz) {}
|
||||
explicit sort_size(rational const& r) {
|
||||
if (r.is_uint64()) {
|
||||
m_kind = SS_FINITE;
|
||||
|
|
@ -1667,17 +1669,29 @@ public:
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void inc_array_ref(unsigned sz, T * const * a) {
|
||||
for(unsigned i = 0; i < sz; i++) {
|
||||
inc_ref(a[i]);
|
||||
void inc_array_ref(std::span<T * const> a) {
|
||||
for(auto elem : a) {
|
||||
inc_ref(elem);
|
||||
}
|
||||
}
|
||||
|
||||
// Backward compatibility overload
|
||||
template<typename T>
|
||||
void inc_array_ref(unsigned sz, T * const * a) {
|
||||
inc_array_ref(std::span<T * const>(a, sz));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void dec_array_ref(std::span<T * const> a) {
|
||||
for(auto elem : a) {
|
||||
dec_ref(elem);
|
||||
}
|
||||
}
|
||||
|
||||
// Backward compatibility overload
|
||||
template<typename T>
|
||||
void dec_array_ref(unsigned sz, T * const * a) {
|
||||
for(unsigned i = 0; i < sz; i++) {
|
||||
dec_ref(a[i]);
|
||||
}
|
||||
dec_array_ref(std::span<T * const>(a, sz));
|
||||
}
|
||||
|
||||
static unsigned get_node_size(ast const * n);
|
||||
|
|
@ -1719,13 +1733,13 @@ private:
|
|||
sort * mk_sort(symbol const & name, sort_info * info);
|
||||
|
||||
public:
|
||||
sort * mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters);
|
||||
[[nodiscard]] sort * mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters);
|
||||
|
||||
sort * mk_uninterpreted_sort(symbol const & name) { return mk_uninterpreted_sort(name, 0, nullptr); }
|
||||
[[nodiscard]] sort * mk_uninterpreted_sort(symbol const & name) { return mk_uninterpreted_sort(name, 0, nullptr); }
|
||||
|
||||
sort * mk_type_var(symbol const& name);
|
||||
[[nodiscard]] sort * mk_type_var(symbol const& name);
|
||||
|
||||
sort * mk_sort(symbol const & name, sort_info const & info) {
|
||||
[[nodiscard]] sort * mk_sort(symbol const & name, sort_info const & info) {
|
||||
if (info.get_family_id() == null_family_id) {
|
||||
return mk_uninterpreted_sort(name);
|
||||
}
|
||||
|
|
@ -1734,15 +1748,15 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
sort * mk_sort(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr);
|
||||
[[nodiscard]] sort * mk_sort(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr);
|
||||
|
||||
sort * substitute(sort* s, unsigned n, sort * const * src, sort * const * dst);
|
||||
[[nodiscard]] sort * substitute(sort* s, unsigned n, sort * const * src, sort * const * dst);
|
||||
|
||||
sort * mk_bool_sort() const { return m_bool_sort; }
|
||||
[[nodiscard]] sort * mk_bool_sort() const { return m_bool_sort; }
|
||||
|
||||
sort * mk_proof_sort() const { return m_proof_sort; }
|
||||
[[nodiscard]] sort * mk_proof_sort() const { return m_proof_sort; }
|
||||
|
||||
sort * mk_fresh_sort(char const * prefix = "");
|
||||
[[nodiscard]] sort * mk_fresh_sort(char const * prefix = "");
|
||||
|
||||
bool is_uninterp(sort const * s) const { return s->get_family_id() == null_family_id || s->get_family_id() == user_sort_family_id; }
|
||||
|
||||
|
|
@ -1767,24 +1781,26 @@ public:
|
|||
|
||||
bool has_type_var(unsigned n, sort* const* domain, sort* range) const;
|
||||
|
||||
func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
[[nodiscard]] func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range = nullptr);
|
||||
|
||||
func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
[[nodiscard]] func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned num_args, expr * const * args, sort * range = nullptr);
|
||||
|
||||
app * mk_app(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr,
|
||||
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr,
|
||||
unsigned num_args = 0, expr * const * args = nullptr, sort * range = nullptr);
|
||||
|
||||
app * mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args);
|
||||
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args);
|
||||
|
||||
app * mk_app(family_id fid, decl_kind k, expr * arg);
|
||||
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, std::span<expr* const> args);
|
||||
|
||||
app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2);
|
||||
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, expr * arg);
|
||||
|
||||
app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3);
|
||||
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2);
|
||||
|
||||
app * mk_const(family_id fid, decl_kind k) { return mk_app(fid, k, 0, static_cast<expr * const *>(nullptr)); }
|
||||
[[nodiscard]] app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3);
|
||||
|
||||
[[nodiscard]] app * mk_const(family_id fid, decl_kind k) { return mk_app(fid, k, 0, static_cast<expr * const *>(nullptr)); }
|
||||
private:
|
||||
func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
|
||||
func_decl_info * info);
|
||||
|
|
@ -1793,12 +1809,14 @@ private:
|
|||
|
||||
app * mk_app_core(func_decl * decl, unsigned num_args, expr * const * args);
|
||||
|
||||
app * mk_and(unsigned num_args, expr * const * args) { return mk_app(basic_family_id, OP_AND, num_args, args); }
|
||||
|
||||
public:
|
||||
func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range) {
|
||||
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range) {
|
||||
return mk_func_decl(name, arity, domain, range, static_cast<func_decl_info *>(nullptr));
|
||||
}
|
||||
|
||||
func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
|
||||
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
|
||||
func_decl_info const & info) {
|
||||
if (info.is_null()) {
|
||||
return mk_func_decl(name, arity, domain, range, static_cast<func_decl_info *>(nullptr));
|
||||
|
|
@ -1808,55 +1826,55 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
func_decl * mk_func_decl(unsigned arity, sort * const * domain, func_decl_info const & info) {
|
||||
[[nodiscard]] func_decl * mk_func_decl(unsigned arity, sort * const * domain, func_decl_info const & info) {
|
||||
return mk_func_decl(info.get_family_id(), info.get_decl_kind(), info.get_num_parameters(), info.get_parameters(),
|
||||
arity, domain);
|
||||
}
|
||||
|
||||
func_decl * mk_skolem_const_decl(symbol const& name, sort* s) {
|
||||
[[nodiscard]] func_decl * mk_skolem_const_decl(symbol const& name, sort* s) {
|
||||
func_decl_info info;
|
||||
info.set_skolem(true);
|
||||
return mk_func_decl(name, static_cast<unsigned>(0), nullptr, s, info);
|
||||
}
|
||||
|
||||
func_decl * mk_const_decl(const char* name, sort * s) {
|
||||
[[nodiscard]] func_decl * mk_const_decl(const char* name, sort * s) {
|
||||
return mk_func_decl(symbol(name), static_cast<unsigned>(0), nullptr, s);
|
||||
}
|
||||
|
||||
func_decl * mk_const_decl(std::string const& name, sort * s) {
|
||||
[[nodiscard]] func_decl * mk_const_decl(std::string const& name, sort * s) {
|
||||
return mk_func_decl(symbol(name.c_str()), static_cast<unsigned>(0), nullptr, s);
|
||||
}
|
||||
|
||||
func_decl * mk_const_decl(symbol const & name, sort * s) {
|
||||
[[nodiscard]] func_decl * mk_const_decl(symbol const & name, sort * s) {
|
||||
return mk_func_decl(name, static_cast<unsigned>(0), nullptr, s);
|
||||
}
|
||||
|
||||
func_decl * mk_const_decl(symbol const & name, sort * s, func_decl_info const & info) {
|
||||
[[nodiscard]] func_decl * mk_const_decl(symbol const & name, sort * s, func_decl_info const & info) {
|
||||
return mk_func_decl(name, static_cast<unsigned>(0), nullptr, s, info);
|
||||
}
|
||||
|
||||
func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range, func_decl_info const & info) {
|
||||
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range, func_decl_info const & info) {
|
||||
return mk_func_decl(name, 1, &domain, range, info);
|
||||
}
|
||||
|
||||
func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range) {
|
||||
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range) {
|
||||
return mk_func_decl(name, 1, &domain, range);
|
||||
}
|
||||
|
||||
func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, func_decl_info const & info) {
|
||||
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, func_decl_info const & info) {
|
||||
sort * d[2] = { domain1, domain2 };
|
||||
return mk_func_decl(name, 2, d, range, info);
|
||||
}
|
||||
|
||||
func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range) {
|
||||
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range) {
|
||||
sort * d[2] = { domain1, domain2 };
|
||||
return mk_func_decl(name, 2, d, range);
|
||||
}
|
||||
|
||||
func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
|
||||
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
|
||||
bool assoc, bool comm = false, bool inj = false);
|
||||
|
||||
func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, bool assoc, bool comm = false) {
|
||||
[[nodiscard]] func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, bool assoc, bool comm = false) {
|
||||
sort * d[2] = { domain1, domain2 };
|
||||
return mk_func_decl(name, 2, d, range, assoc, comm, false);
|
||||
}
|
||||
|
|
@ -1869,113 +1887,113 @@ public:
|
|||
return !p || p->is_considered_uninterpreted(f);
|
||||
}
|
||||
|
||||
app * mk_app(func_decl * decl, unsigned num_args, expr * const * args);
|
||||
[[nodiscard]] app * mk_app(func_decl * decl, unsigned num_args, expr * const * args);
|
||||
|
||||
app* mk_app(func_decl* decl, ref_vector<expr, ast_manager> const& args) {
|
||||
[[nodiscard]] app* mk_app(func_decl* decl, ref_vector<expr, ast_manager> const& args) {
|
||||
return mk_app(decl, args.size(), args.data());
|
||||
}
|
||||
|
||||
app* mk_app(func_decl* decl, ref_buffer<expr, ast_manager> const& args) {
|
||||
[[nodiscard]] app* mk_app(func_decl* decl, ref_buffer<expr, ast_manager> const& args) {
|
||||
return mk_app(decl, args.size(), args.data());
|
||||
}
|
||||
|
||||
app* mk_app(func_decl* decl, ref_vector<app, ast_manager> const& args) {
|
||||
[[nodiscard]] app* mk_app(func_decl* decl, ref_vector<app, ast_manager> const& args) {
|
||||
return mk_app(decl, args.size(), (expr*const*)args.data());
|
||||
}
|
||||
|
||||
app * mk_app(func_decl * decl, ptr_vector<expr> const& args) {
|
||||
[[nodiscard]] app * mk_app(func_decl * decl, ptr_vector<expr> const& args) {
|
||||
return mk_app(decl, args.size(), args.data());
|
||||
}
|
||||
|
||||
app * mk_app(func_decl * decl, ptr_buffer<expr> const& args) {
|
||||
[[nodiscard]] app * mk_app(func_decl * decl, ptr_buffer<expr> const& args) {
|
||||
return mk_app(decl, args.size(), args.data());
|
||||
}
|
||||
|
||||
app * mk_app(func_decl * decl, ptr_vector<app> const& args) {
|
||||
[[nodiscard]] app * mk_app(func_decl * decl, ptr_vector<app> const& args) {
|
||||
return mk_app(decl, args.size(), (expr*const*)args.data());
|
||||
}
|
||||
|
||||
app * mk_app(func_decl * decl, expr * const * args) {
|
||||
[[nodiscard]] app * mk_app(func_decl * decl, expr * const * args) {
|
||||
return mk_app(decl, decl->get_arity(), args);
|
||||
}
|
||||
|
||||
app * mk_app(func_decl * decl, expr * arg) {
|
||||
[[nodiscard]] app * mk_app(func_decl * decl, expr * arg) {
|
||||
SASSERT(decl->get_arity() == 1);
|
||||
return mk_app(decl, 1, &arg);
|
||||
}
|
||||
|
||||
app * mk_app(func_decl * decl, expr * arg1, expr * arg2) {
|
||||
[[nodiscard]] app * mk_app(func_decl * decl, expr * arg1, expr * arg2) {
|
||||
SASSERT(decl->get_arity() == 2);
|
||||
expr * args[2] = { arg1, arg2 };
|
||||
return mk_app(decl, 2, args);
|
||||
}
|
||||
|
||||
app * mk_app(func_decl * decl, expr * arg1, expr * arg2, expr * arg3) {
|
||||
[[nodiscard]] app * mk_app(func_decl * decl, expr * arg1, expr * arg2, expr * arg3) {
|
||||
SASSERT(decl->get_arity() == 3);
|
||||
expr * args[3] = { arg1, arg2, arg3 };
|
||||
return mk_app(decl, 3, args);
|
||||
}
|
||||
|
||||
app * mk_app(symbol const& name, unsigned n, expr* const* args, sort* range);
|
||||
[[nodiscard]] app * mk_app(symbol const& name, unsigned n, expr* const* args, sort* range);
|
||||
|
||||
app * mk_const(func_decl * decl) {
|
||||
[[nodiscard]] app * mk_const(func_decl * decl) {
|
||||
SASSERT(decl->get_arity() == 0);
|
||||
return mk_app(decl, static_cast<unsigned>(0), static_cast<expr**>(nullptr));
|
||||
}
|
||||
|
||||
app * mk_skolem_const(symbol const & name, sort * s) {
|
||||
[[nodiscard]] app * mk_skolem_const(symbol const & name, sort * s) {
|
||||
return mk_const(mk_skolem_const_decl(name, s));
|
||||
}
|
||||
|
||||
app * mk_const(symbol const & name, sort * s) {
|
||||
[[nodiscard]] app * mk_const(symbol const & name, sort * s) {
|
||||
return mk_const(mk_const_decl(name, s));
|
||||
}
|
||||
|
||||
app * mk_const(std::string const & name, sort * s) {
|
||||
[[nodiscard]] app * mk_const(std::string const & name, sort * s) {
|
||||
return mk_const(mk_const_decl(name, s));
|
||||
}
|
||||
|
||||
app * mk_const(char const* name, sort * s) {
|
||||
[[nodiscard]] app * mk_const(char const* name, sort * s) {
|
||||
return mk_const(mk_const_decl(name, s));
|
||||
}
|
||||
|
||||
func_decl * mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity,
|
||||
[[nodiscard]] func_decl * mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity,
|
||||
sort * const * domain, sort * range, bool skolem = true);
|
||||
|
||||
func_decl * mk_fresh_func_decl(unsigned arity, sort * const * domain, sort * range, bool skolem = true) {
|
||||
[[nodiscard]] func_decl * mk_fresh_func_decl(unsigned arity, sort * const * domain, sort * range, bool skolem = true) {
|
||||
return mk_fresh_func_decl(symbol::null, symbol::null, arity, domain, range, skolem);
|
||||
}
|
||||
|
||||
func_decl * mk_fresh_func_decl(char const * prefix, char const * suffix, unsigned arity,
|
||||
[[nodiscard]] func_decl * mk_fresh_func_decl(char const * prefix, char const * suffix, unsigned arity,
|
||||
sort * const * domain, sort * range, bool skolem = true) {
|
||||
return mk_fresh_func_decl(symbol(prefix), symbol(suffix), arity, domain, range, skolem);
|
||||
}
|
||||
|
||||
func_decl * mk_fresh_func_decl(char const * prefix, unsigned arity, sort * const * domain, sort * range, bool skolem = true) {
|
||||
[[nodiscard]] func_decl * mk_fresh_func_decl(char const * prefix, unsigned arity, sort * const * domain, sort * range, bool skolem = true) {
|
||||
return mk_fresh_func_decl(symbol(prefix), symbol::null, arity, domain, range, skolem);
|
||||
}
|
||||
|
||||
bool is_parametric_function(func_decl* f, func_decl *& g) const;
|
||||
|
||||
app * mk_fresh_const(char const * prefix, sort * s, bool skolem = true) {
|
||||
[[nodiscard]] app * mk_fresh_const(char const * prefix, sort * s, bool skolem = true) {
|
||||
return mk_const(mk_fresh_func_decl(prefix, 0, nullptr, s, skolem));
|
||||
}
|
||||
|
||||
app * mk_fresh_const(std::string const& prefix, sort * s, bool skolem = true) {
|
||||
[[nodiscard]] app * mk_fresh_const(std::string const& prefix, sort * s, bool skolem = true) {
|
||||
return mk_fresh_const(prefix.c_str(), s, skolem);
|
||||
}
|
||||
|
||||
app * mk_fresh_const(symbol const& prefix, sort * s, bool skolem = true) {
|
||||
[[nodiscard]] app * mk_fresh_const(symbol const& prefix, sort * s, bool skolem = true) {
|
||||
return mk_const(mk_fresh_func_decl(prefix, symbol::null, 0, nullptr, s, skolem));
|
||||
}
|
||||
|
||||
symbol mk_fresh_var_name(char const * prefix = nullptr);
|
||||
[[nodiscard]] symbol mk_fresh_var_name(char const * prefix = nullptr);
|
||||
|
||||
var * mk_var(unsigned idx, sort * ty);
|
||||
[[nodiscard]] var * mk_var(unsigned idx, sort * ty);
|
||||
|
||||
app * mk_label(bool pos, unsigned num_names, symbol const * names, expr * n);
|
||||
[[nodiscard]] app * mk_label(bool pos, unsigned num_names, symbol const * names, expr * n);
|
||||
|
||||
app * mk_label(bool pos, symbol const & name, expr * n);
|
||||
[[nodiscard]] app * mk_label(bool pos, symbol const & name, expr * n);
|
||||
|
||||
bool is_label(expr const * n, bool & pos, buffer<symbol> & names) const;
|
||||
|
||||
|
|
@ -1999,9 +2017,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
app * mk_label_lit(unsigned num_names, symbol const * names);
|
||||
[[nodiscard]] app * mk_label_lit(unsigned num_names, symbol const * names);
|
||||
|
||||
app * mk_label_lit(symbol const & name);
|
||||
[[nodiscard]] app * mk_label_lit(symbol const & name);
|
||||
|
||||
bool is_label_lit(expr const * n, buffer<symbol> & names) const;
|
||||
|
||||
|
|
@ -2009,9 +2027,9 @@ public:
|
|||
|
||||
family_id get_label_family_id() const { return label_family_id; }
|
||||
|
||||
app * mk_pattern(unsigned num_exprs, app * const * exprs);
|
||||
[[nodiscard]] app * mk_pattern(unsigned num_exprs, app * const * exprs);
|
||||
|
||||
app * mk_pattern(app * expr) { return mk_pattern(1, &expr); }
|
||||
[[nodiscard]] app * mk_pattern(app * expr) { return mk_pattern(1, &expr); }
|
||||
|
||||
bool is_pattern(expr const * n) const;
|
||||
|
||||
|
|
@ -2019,12 +2037,12 @@ public:
|
|||
|
||||
public:
|
||||
|
||||
quantifier * mk_quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
|
||||
[[nodiscard]] quantifier * mk_quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
|
||||
int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null,
|
||||
unsigned num_patterns = 0, expr * const * patterns = nullptr,
|
||||
unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr);
|
||||
|
||||
quantifier * mk_forall(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
|
||||
[[nodiscard]] quantifier * mk_forall(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
|
||||
int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null,
|
||||
unsigned num_patterns = 0, expr * const * patterns = nullptr,
|
||||
unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr) {
|
||||
|
|
@ -2032,7 +2050,7 @@ public:
|
|||
num_no_patterns, no_patterns);
|
||||
}
|
||||
|
||||
quantifier * mk_exists(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
|
||||
[[nodiscard]] quantifier * mk_exists(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
|
||||
int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null,
|
||||
unsigned num_patterns = 0, expr * const * patterns = nullptr,
|
||||
unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr) {
|
||||
|
|
@ -2040,13 +2058,13 @@ public:
|
|||
num_no_patterns, no_patterns);
|
||||
}
|
||||
|
||||
quantifier * mk_lambda(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body);
|
||||
[[nodiscard]] quantifier * mk_lambda(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body);
|
||||
|
||||
quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body);
|
||||
[[nodiscard]] quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body);
|
||||
|
||||
quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, unsigned new_num_no_patterns, expr * const * new_no_patterns, expr * new_body);
|
||||
[[nodiscard]] quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, unsigned new_num_no_patterns, expr * const * new_no_patterns, expr * new_body);
|
||||
|
||||
quantifier * update_quantifier(quantifier * q, expr * new_body);
|
||||
[[nodiscard]] quantifier * update_quantifier(quantifier * q, expr * new_body);
|
||||
|
||||
quantifier * update_quantifier_weight(quantifier * q, int new_weight);
|
||||
|
||||
|
|
@ -2054,6 +2072,12 @@ public:
|
|||
|
||||
quantifier * update_quantifier(quantifier * q, quantifier_kind new_kind, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body);
|
||||
|
||||
// Convenience overloads with std::initializer_list
|
||||
[[nodiscard]] quantifier * update_quantifier(quantifier * q, std::initializer_list<expr*> new_patterns, expr * new_body);
|
||||
|
||||
[[nodiscard]] quantifier * update_quantifier(quantifier * q, std::initializer_list<expr*> new_patterns, std::initializer_list<expr*> new_no_patterns, expr * new_body);
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// expr_array
|
||||
|
|
@ -2188,21 +2212,22 @@ public:
|
|||
app * mk_xor(ptr_vector<expr> const& args) { return mk_xor(args.size(), args.data()); }
|
||||
app * mk_xor(ref_buffer<expr, ast_manager> const& args) { return mk_xor(args.size(), args.data()); }
|
||||
app * mk_or(unsigned num_args, expr * const * args) { return mk_app(basic_family_id, OP_OR, num_args, args); }
|
||||
app * mk_and(unsigned num_args, expr * const * args) { return mk_app(basic_family_id, OP_AND, num_args, args); }
|
||||
app * mk_and(std::span<expr* const> args) { return mk_app(basic_family_id, OP_AND, args); }
|
||||
app * mk_or(std::span<expr* const> args) { return mk_app(basic_family_id, OP_OR, args); }
|
||||
app * mk_or(expr * arg1, expr * arg2) { return mk_app(basic_family_id, OP_OR, arg1, arg2); }
|
||||
app * mk_and(expr * arg1, expr * arg2) { return mk_app(basic_family_id, OP_AND, arg1, arg2); }
|
||||
app * mk_or(expr * arg1, expr * arg2, expr * arg3) { return mk_app(basic_family_id, OP_OR, arg1, arg2, arg3); }
|
||||
app * mk_or(expr* a, expr* b, expr* c, expr* d) { expr* args[4] = { a, b, c, d }; return mk_app(basic_family_id, OP_OR, 4, args); }
|
||||
app * mk_and(expr * arg1, expr * arg2, expr * arg3) { return mk_app(basic_family_id, OP_AND, arg1, arg2, arg3); }
|
||||
|
||||
app * mk_and(ref_vector<expr, ast_manager> const& args) { return mk_and(args.size(), args.data()); }
|
||||
app * mk_and(ptr_vector<expr> const& args) { return mk_and(args.size(), args.data()); }
|
||||
app * mk_and(ref_buffer<expr, ast_manager> const& args) { return mk_and(args.size(), args.data()); }
|
||||
app * mk_and(ptr_buffer<expr> const& args) { return mk_and(args.size(), args.data()); }
|
||||
app * mk_or(ref_vector<expr, ast_manager> const& args) { return mk_or(args.size(), args.data()); }
|
||||
app * mk_or(ptr_vector<expr> const& args) { return mk_or(args.size(), args.data()); }
|
||||
app * mk_or(ref_buffer<expr, ast_manager> const& args) { return mk_or(args.size(), args.data()); }
|
||||
app * mk_or(ptr_buffer<expr> const& args) { return mk_or(args.size(), args.data()); }
|
||||
app * mk_and(ref_vector<expr, ast_manager> const& args) { return mk_and(std::span<expr* const>(args.data(), args.size())); }
|
||||
app * mk_and(ptr_vector<expr> const& args) { return mk_and(std::span<expr* const>(args.data(), args.size())); }
|
||||
app * mk_and(ref_buffer<expr, ast_manager> const& args) { return mk_and(std::span<expr* const>(args.data(), args.size())); }
|
||||
app * mk_and(ptr_buffer<expr> const& args) { return mk_and(std::span<expr* const>(args.data(), args.size())); }
|
||||
app * mk_or(ref_vector<expr, ast_manager> const& args) { return mk_or(std::span<expr* const>(args.data(), args.size())); }
|
||||
app * mk_or(ptr_vector<expr> const& args) { return mk_or(std::span<expr* const>(args.data(), args.size())); }
|
||||
app * mk_or(ref_buffer<expr, ast_manager> const& args) { return mk_or(std::span<expr* const>(args.data(), args.size())); }
|
||||
app * mk_or(ptr_buffer<expr> const& args) { return mk_or(std::span<expr* const>(args.data(), args.size())); }
|
||||
app * mk_implies(expr * arg1, expr * arg2) { return mk_app(basic_family_id, OP_IMPLIES, arg1, arg2); }
|
||||
app * mk_not(expr * n) { return mk_app(basic_family_id, OP_NOT, n); }
|
||||
app * mk_distinct(unsigned num_args, expr * const * args);
|
||||
|
|
@ -2343,6 +2368,12 @@ public:
|
|||
proof * mk_transitivity(proof * p1, proof * p2, proof * p3, proof * p4);
|
||||
proof * mk_transitivity(unsigned num_proofs, proof * const * proofs);
|
||||
proof * mk_transitivity(unsigned num_proofs, proof * const * proofs, expr * n1, expr * n2);
|
||||
proof * mk_transitivity(std::initializer_list<proof*> const& proofs) {
|
||||
return mk_transitivity(static_cast<unsigned>(proofs.size()), proofs.begin());
|
||||
}
|
||||
proof * mk_transitivity(std::initializer_list<proof*> const& proofs, expr * n1, expr * n2) {
|
||||
return mk_transitivity(static_cast<unsigned>(proofs.size()), proofs.begin(), n1, n2);
|
||||
}
|
||||
proof * mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned num_proofs, proof * const * proofs);
|
||||
proof * mk_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs);
|
||||
proof * mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs);
|
||||
|
|
@ -2373,6 +2404,12 @@ public:
|
|||
proof * mk_def_axiom(expr * ax);
|
||||
proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs);
|
||||
proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact);
|
||||
proof * mk_unit_resolution(std::initializer_list<proof*> const& proofs) {
|
||||
return mk_unit_resolution(static_cast<unsigned>(proofs.size()), proofs.begin());
|
||||
}
|
||||
proof * mk_unit_resolution(std::initializer_list<proof*> const& proofs, expr * new_fact) {
|
||||
return mk_unit_resolution(static_cast<unsigned>(proofs.size()), proofs.begin(), new_fact);
|
||||
}
|
||||
proof * mk_hypothesis(expr * h);
|
||||
proof * mk_lemma(proof * p, expr * lemma);
|
||||
|
||||
|
|
@ -2405,11 +2442,17 @@ private:
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void push_dec_array_ref(unsigned sz, T * const * a) {
|
||||
for(unsigned i = 0; i < sz; i++) {
|
||||
push_dec_ref(a[i]);
|
||||
void push_dec_array_ref(std::span<T * const> a) {
|
||||
for(auto elem : a) {
|
||||
push_dec_ref(elem);
|
||||
}
|
||||
}
|
||||
|
||||
// Backward compatibility overload
|
||||
template<typename T>
|
||||
void push_dec_array_ref(unsigned sz, T * const * a) {
|
||||
push_dec_array_ref(std::span<T * const>(a, sz));
|
||||
}
|
||||
};
|
||||
|
||||
typedef ast_manager::expr_array expr_array;
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class ll_printer {
|
|||
|
||||
template<typename T>
|
||||
void display_children(unsigned num_children, T * const * children) {
|
||||
for (unsigned i = 0; i < num_children; i++) {
|
||||
for (unsigned i = 0; i < num_children; ++i) {
|
||||
if (i > 0) {
|
||||
m_out << " ";
|
||||
}
|
||||
|
|
@ -213,7 +213,7 @@ public:
|
|||
m_out << n->get_decl()->get_parameter(i);
|
||||
}
|
||||
unsigned num_parents = m_manager.get_num_parents(n);
|
||||
for (unsigned i = 0; i < num_parents; i++) {
|
||||
for (unsigned i = 0; i < num_parents; ++i) {
|
||||
m_out << " ";
|
||||
display_child(m_manager.get_parent(n, i));
|
||||
}
|
||||
|
|
@ -256,7 +256,7 @@ public:
|
|||
m_out << "(" << (n->get_kind() == forall_k ? "forall" : (n->get_kind() == exists_k ? "exists" : "lambda")) << " ";
|
||||
unsigned num_decls = n->get_num_decls();
|
||||
m_out << "(vars ";
|
||||
for (unsigned i = 0; i < num_decls; i++) {
|
||||
for (unsigned i = 0; i < num_decls; ++i) {
|
||||
if (i > 0) {
|
||||
m_out << " ";
|
||||
}
|
||||
|
|
@ -307,7 +307,7 @@ public:
|
|||
m_out << "(";
|
||||
display_name(to_app(n)->get_decl());
|
||||
display_params(to_app(n)->get_decl());
|
||||
for (unsigned i = 0; i < num_args && i < 16; i++) {
|
||||
for (unsigned i = 0; i < num_args && i < 16; ++i) {
|
||||
m_out << " ";
|
||||
display(to_app(n)->get_arg(i), depth-1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ bool lt(ast * n1, ast * n2) {
|
|||
check_value(to_sort(n1)->get_num_parameters(), to_sort(n2)->get_num_parameters());
|
||||
num = to_sort(n1)->get_num_parameters();
|
||||
SASSERT(num > 0);
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
const parameter &p1 = to_sort(n1)->get_parameter(i);
|
||||
const parameter &p2 = to_sort(n2)->get_parameter(i);
|
||||
check_parameter(p1, p2);
|
||||
|
|
@ -79,13 +79,13 @@ bool lt(ast * n1, ast * n2) {
|
|||
check_value(to_func_decl(n1)->get_arity(), to_func_decl(n2)->get_arity());
|
||||
check_value(to_func_decl(n1)->get_num_parameters(), to_func_decl(n2)->get_num_parameters());
|
||||
num = to_func_decl(n1)->get_num_parameters();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
const parameter &p1 = to_func_decl(n1)->get_parameter(i);
|
||||
const parameter &p2 = to_func_decl(n2)->get_parameter(i);
|
||||
check_parameter(p1, p2);
|
||||
}
|
||||
num = to_func_decl(n1)->get_arity();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
ast * d1 = to_func_decl(n1)->get_domain(i);
|
||||
ast * d2 = to_func_decl(n2)->get_domain(i);
|
||||
check_ast(d1, d2);
|
||||
|
|
@ -98,7 +98,7 @@ bool lt(ast * n1, ast * n2) {
|
|||
check_value(to_app(n1)->get_depth(), to_app(n2)->get_depth());
|
||||
check_ast(to_app(n1)->get_decl(), to_app(n2)->get_decl());
|
||||
num = to_app(n1)->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
expr * arg1 = to_app(n1)->get_arg(i);
|
||||
expr * arg2 = to_app(n2)->get_arg(i);
|
||||
check_ast(arg1, arg2);
|
||||
|
|
@ -112,16 +112,16 @@ bool lt(ast * n1, ast * n2) {
|
|||
check_value(to_quantifier(n1)->get_num_no_patterns(), to_quantifier(n2)->get_num_no_patterns());
|
||||
check_value(to_quantifier(n1)->get_weight(), to_quantifier(n2)->get_weight());
|
||||
num = to_quantifier(n1)->get_num_decls();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
check_symbol(to_quantifier(n1)->get_decl_name(i), to_quantifier(n2)->get_decl_name(i));
|
||||
check_ast(to_quantifier(n1)->get_decl_sort(i), to_quantifier(n2)->get_decl_sort(i));
|
||||
}
|
||||
num = to_quantifier(n1)->get_num_patterns();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
check_ast(to_quantifier(n1)->get_pattern(i), to_quantifier(n2)->get_pattern(i));
|
||||
}
|
||||
num = to_quantifier(n1)->get_num_no_patterns();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
check_ast(to_quantifier(n1)->get_no_pattern(i), to_quantifier(n2)->get_no_pattern(i));
|
||||
}
|
||||
n1 = to_quantifier(n1)->get_expr();
|
||||
|
|
@ -139,7 +139,7 @@ bool lt(ast * n1, ast * n2) {
|
|||
}
|
||||
|
||||
bool is_sorted(unsigned num, expr * const * ns) {
|
||||
for (unsigned i = 1; i < num; i++) {
|
||||
for (unsigned i = 1; i < num; ++i) {
|
||||
ast * prev = ns[i-1];
|
||||
ast * curr = ns[i];
|
||||
if (lt(curr, prev))
|
||||
|
|
|
|||
|
|
@ -71,3 +71,11 @@ inline std::string& operator+=(std::string& s, mk_pp const& pp) {
|
|||
return s = s + pp;
|
||||
}
|
||||
|
||||
// Helper function to convert streamable objects (like mk_pp) to strings for use with std::format
|
||||
template<typename T>
|
||||
inline std::string to_string(T const& obj) {
|
||||
std::ostringstream strm;
|
||||
strm << obj;
|
||||
return std::move(strm).str();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ bool smt2_pp_environment::is_indexed_fdecl(func_decl * f) const {
|
|||
return false;
|
||||
unsigned num = f->get_num_parameters();
|
||||
unsigned i;
|
||||
for (i = 0; i < num; i++) {
|
||||
for (i = 0; i < num; ++i) {
|
||||
if (f->get_parameter(i).is_int())
|
||||
continue;
|
||||
if (f->get_parameter(i).is_rational())
|
||||
|
|
@ -111,7 +111,7 @@ format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) {
|
|||
unsigned num = f->get_num_parameters();
|
||||
ptr_buffer<format> fs;
|
||||
fs.push_back(fname);
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
SASSERT(f->get_parameter(i).is_int() ||
|
||||
f->get_parameter(i).is_rational() ||
|
||||
(f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast())));
|
||||
|
|
@ -149,7 +149,7 @@ format * smt2_pp_environment::pp_signature(format * f_name, func_decl * f) {
|
|||
f_name = pp_fdecl_params(f_name, f);
|
||||
}
|
||||
ptr_buffer<format> f_domain;
|
||||
for (unsigned i = 0; i < f->get_arity(); i++)
|
||||
for (unsigned i = 0; i < f->get_arity(); ++i)
|
||||
f_domain.push_back(pp_sort(f->get_domain(i)));
|
||||
ptr_buffer<format> args;
|
||||
args.push_back(f_name);
|
||||
|
|
@ -417,7 +417,7 @@ format_ns::format * smt2_pp_environment::pp_sort(sort * s) {
|
|||
if (get_arutil().is_array(s)) {
|
||||
ptr_buffer<format> fs;
|
||||
unsigned sz = get_array_arity(s);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
fs.push_back(pp_sort(get_array_domain(s, i)));
|
||||
}
|
||||
fs.push_back(pp_sort(get_array_range(s)));
|
||||
|
|
@ -437,13 +437,18 @@ format_ns::format * smt2_pp_environment::pp_sort(sort * s) {
|
|||
fs.push_back(pp_sort(to_sort(s->get_parameter(0).get_ast())));
|
||||
return mk_seq1(m, fs.begin(), fs.end(), f2f(), get_sutil().is_seq(s)?"Seq":"RegEx");
|
||||
}
|
||||
if ((get_fsutil().is_finite_set(s))) {
|
||||
ptr_buffer<format> fs;
|
||||
fs.push_back(pp_sort(to_sort(s->get_parameter(0).get_ast())));
|
||||
return mk_seq1(m, fs.begin(), fs.end(), f2f(), "FiniteSet");
|
||||
}
|
||||
std::string name = ensure_quote(s->get_name());
|
||||
|
||||
if (get_dtutil().is_datatype(s)) {
|
||||
unsigned sz = get_dtutil().get_datatype_num_parameter_sorts(s);
|
||||
if (sz > 0) {
|
||||
ptr_buffer<format> fs;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
fs.push_back(pp_sort(get_dtutil().get_datatype_parameter_sort(s, i)));
|
||||
}
|
||||
return mk_seq1(m, fs.begin(), fs.end(), f2f(), name);
|
||||
|
|
@ -804,7 +809,7 @@ class smt2_printer {
|
|||
if (old_sz == sz)
|
||||
return f;
|
||||
vector<ptr_vector<format> > decls;
|
||||
for (unsigned i = old_sz; i < sz; i++) {
|
||||
for (unsigned i = old_sz; i < sz; ++i) {
|
||||
unsigned lvl = m_aliased_lvls_names[i].first;
|
||||
symbol f_name = m_aliased_lvls_names[i].second;
|
||||
format * f_def[1] = { m_aliased_pps.get(i) };
|
||||
|
|
@ -828,7 +833,7 @@ class smt2_printer {
|
|||
if (num_op == 0)
|
||||
return f;
|
||||
buf.push_back(mk_indent(m(), SMALL_INDENT, mk_compose(m(), mk_line_break(m()), f)));
|
||||
for (unsigned i = 0; i < num_op; i++)
|
||||
for (unsigned i = 0; i < num_op; ++i)
|
||||
buf.push_back(mk_string(m(), ")"));
|
||||
return mk_compose(m(), buf.size(), buf.data());
|
||||
}
|
||||
|
|
@ -869,7 +874,7 @@ class smt2_printer {
|
|||
|
||||
void register_var_names(quantifier * q) {
|
||||
unsigned num_decls = q->get_num_decls();
|
||||
for (unsigned i = 0; i < num_decls; i++) {
|
||||
for (unsigned i = 0; i < num_decls; ++i) {
|
||||
symbol name = ensure_quote_sym(q->get_decl_name(i));
|
||||
if (name.is_numerical()) {
|
||||
unsigned idx = 1;
|
||||
|
|
@ -887,7 +892,7 @@ class smt2_printer {
|
|||
|
||||
void register_var_names(unsigned n) {
|
||||
unsigned idx = 1;
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
symbol name = next_name("x", idx);
|
||||
SASSERT(!m_var_names_set.contains(name));
|
||||
m_var_names.push_back(name);
|
||||
|
|
@ -900,7 +905,7 @@ class smt2_printer {
|
|||
}
|
||||
|
||||
void unregister_var_names(unsigned num_decls) {
|
||||
for (unsigned i = 0; i < num_decls; i++) {
|
||||
for (unsigned i = 0; i < num_decls; ++i) {
|
||||
symbol s = m_var_names.back();
|
||||
m_var_names.pop_back();
|
||||
m_var_names_set.erase(s);
|
||||
|
|
@ -911,7 +916,7 @@ class smt2_printer {
|
|||
ptr_buffer<format> buf;
|
||||
SASSERT(num_decls <= m_var_names.size());
|
||||
symbol * it = m_var_names.end() - num_decls;
|
||||
for (unsigned i = 0; i < num_decls; i++, it++) {
|
||||
for (unsigned i = 0; i < num_decls; ++i, ++it) {
|
||||
format * fs[1] = { m_env.pp_sort(srts[i]) };
|
||||
std::string var_name;
|
||||
if (is_smt2_quoted_symbol (*it)) {
|
||||
|
|
@ -1110,7 +1115,7 @@ public:
|
|||
var_prefix = "_a";
|
||||
}
|
||||
unsigned idx = 0;
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
symbol name = next_name(var_prefix, idx);
|
||||
name = ensure_quote_sym(name);
|
||||
var_names.push_back(name);
|
||||
|
|
@ -1136,7 +1141,7 @@ public:
|
|||
format * args[3];
|
||||
args[0] = fname;
|
||||
ptr_buffer<format> buf;
|
||||
for (unsigned i = 0; i < arity; i++) {
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
buf.push_back(m_env.pp_sort(f->get_domain(i)));
|
||||
}
|
||||
args[1] = mk_seq5<format**, f2f>(m(), buf.begin(), buf.end(), f2f());
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ Revision History:
|
|||
#include "ast/dl_decl_plugin.h"
|
||||
#include "ast/seq_decl_plugin.h"
|
||||
#include "ast/datatype_decl_plugin.h"
|
||||
#include "ast/finite_set_decl_plugin.h"
|
||||
#include "ast/ast_smt_pp.h"
|
||||
#include "util/smt2_util.h"
|
||||
|
||||
|
|
@ -53,6 +54,7 @@ public:
|
|||
virtual array_util & get_arutil() = 0;
|
||||
virtual fpa_util & get_futil() = 0;
|
||||
virtual seq_util & get_sutil() = 0;
|
||||
virtual finite_set_util &get_fsutil() = 0;
|
||||
virtual datalog::dl_decl_util& get_dlutil() = 0;
|
||||
virtual datatype_util& get_dtutil() = 0;
|
||||
virtual bool uses(symbol const & s) const = 0;
|
||||
|
|
@ -80,9 +82,12 @@ class smt2_pp_environment_dbg : public smt2_pp_environment {
|
|||
fpa_util m_futil;
|
||||
seq_util m_sutil;
|
||||
datatype_util m_dtutil;
|
||||
finite_set_util m_fsutil;
|
||||
datalog::dl_decl_util m_dlutil;
|
||||
public:
|
||||
smt2_pp_environment_dbg(ast_manager & m):m_manager(m), m_autil(m), m_bvutil(m), m_arutil(m), m_futil(m), m_sutil(m), m_dtutil(m), m_dlutil(m) {}
|
||||
smt2_pp_environment_dbg(ast_manager &m)
|
||||
: m_manager(m), m_autil(m), m_bvutil(m), m_arutil(m), m_futil(m), m_sutil(m), m_dtutil(m), m_fsutil(m),
|
||||
m_dlutil(m) {}
|
||||
ast_manager & get_manager() const override { return m_manager; }
|
||||
arith_util & get_autil() override { return m_autil; }
|
||||
bv_util & get_bvutil() override { return m_bvutil; }
|
||||
|
|
@ -91,6 +96,7 @@ public:
|
|||
fpa_util & get_futil() override { return m_futil; }
|
||||
datalog::dl_decl_util& get_dlutil() override { return m_dlutil; }
|
||||
datatype_util& get_dtutil() override { return m_dtutil; }
|
||||
finite_set_util &get_fsutil() override { return m_fsutil; }
|
||||
bool uses(symbol const & s) const override { return false; }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -977,7 +977,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
|
|||
strm << "(set-logic " << m_logic << ")\n";
|
||||
}
|
||||
if (!m_attributes.empty()) {
|
||||
strm << "; " << m_attributes;
|
||||
strm << "; " << m_attributes << "\n";
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ void ast_translation::cache(ast * s, ast * t) {
|
|||
|
||||
void ast_translation::collect_decl_extra_children(decl * d) {
|
||||
unsigned num_params = d->get_num_parameters();
|
||||
for (unsigned i = 0; i < num_params; i++) {
|
||||
for (unsigned i = 0; i < num_params; ++i) {
|
||||
parameter const & p = d->get_parameter(i);
|
||||
if (p.is_ast())
|
||||
m_extra_children_stack.push_back(p.get_ast());
|
||||
|
|
@ -102,7 +102,7 @@ bool ast_translation::visit(ast * n) {
|
|||
void ast_translation::copy_params(decl * d, unsigned rpos, buffer<parameter> & ps) {
|
||||
unsigned num = d->get_num_parameters();
|
||||
unsigned j = rpos;
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
parameter const & p = d->get_parameter(i);
|
||||
if (p.is_ast()) {
|
||||
ps.push_back(parameter(m_result_stack[j]));
|
||||
|
|
@ -365,7 +365,7 @@ expr_dependency * expr_dependency_translation::operator()(expr_dependency * d) {
|
|||
m_translation.from().linearize(d, m_buffer);
|
||||
unsigned sz = m_buffer.size();
|
||||
SASSERT(sz >= 1);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
m_buffer[i] = m_translation(m_buffer[i]);
|
||||
}
|
||||
return m_translation.to().mk_join(sz, m_buffer.data());
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args) {
|
|||
else if (num_args == 1)
|
||||
return args[0];
|
||||
else
|
||||
return m.mk_and(num_args, args);
|
||||
return m.mk_and(std::span<expr* const>(args, num_args));
|
||||
}
|
||||
|
||||
app* mk_and(ast_manager & m, unsigned num_args, app * const * args) {
|
||||
|
|
@ -232,8 +232,8 @@ expr_ref push_not(const expr_ref& e, unsigned limit) {
|
|||
|
||||
expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) {
|
||||
expr_ref_buffer new_diseqs(m);
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
for (unsigned j = i + 1; j < num_args; j++)
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
for (unsigned j = i + 1; j < num_args; ++j)
|
||||
new_diseqs.push_back(m.mk_not(m.mk_eq(args[i], args[j])));
|
||||
}
|
||||
return mk_and(m, new_diseqs.size(), new_diseqs.data());
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ void remove_duplicates(C & v) {
|
|||
if (!v.empty()) {
|
||||
unsigned sz = v.size();
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
auto curr = v.get(i);
|
||||
if (!visited.is_marked(curr)) {
|
||||
visited.mark(curr);
|
||||
|
|
@ -47,7 +47,7 @@ bool is_well_formed_vars(ptr_vector<sort>& bound, expr* n);
|
|||
|
||||
inline bool args_are_vars(app const * n) {
|
||||
unsigned sz = n->get_num_args();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (!is_var(n->get_arg(i)))
|
||||
return false;
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ inline bool args_are_vars(app const * n) {
|
|||
|
||||
inline bool depth_leq_one(app * n) {
|
||||
unsigned sz = n->get_num_args();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr * arg = n->get_arg(i);
|
||||
if (is_app(arg) && to_app(arg)->get_num_args() > 0)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ Revision History:
|
|||
|
||||
--*/
|
||||
#include<sstream>
|
||||
#include<format>
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "util/warning.h"
|
||||
|
|
@ -44,7 +45,7 @@ bv_decl_plugin::bv_decl_plugin():
|
|||
void bv_decl_plugin::set_manager(ast_manager * m, family_id id) {
|
||||
decl_plugin::set_manager(m, id);
|
||||
|
||||
for (unsigned i = 1; i <= 64; i++)
|
||||
for (unsigned i = 1; i <= 64; ++i)
|
||||
mk_bv_sort(i);
|
||||
|
||||
m_bit0 = m->mk_const_decl(symbol("bit0"), get_bv_sort(1), func_decl_info(m_family_id, OP_BIT0));
|
||||
|
|
@ -161,7 +162,7 @@ void bv_decl_plugin::mk_bv_sort(unsigned bv_size) {
|
|||
sz = sort_size::mk_very_big();
|
||||
}
|
||||
else {
|
||||
sz = sort_size(rational::power_of_two(bv_size));
|
||||
sz = sort_size(1ULL << bv_size);
|
||||
}
|
||||
m_bv_sorts[bv_size] = m_manager->mk_sort(m_bv_sym, sort_info(m_family_id, BV_SORT, sz, 1, &p));
|
||||
m_manager->inc_ref(m_bv_sorts[bv_size]);
|
||||
|
|
@ -407,7 +408,7 @@ inline bool bv_decl_plugin::get_bv_size(expr * t, int & result) {
|
|||
|
||||
bool bv_decl_plugin::get_concat_size(unsigned arity, sort * const * domain, int & result) {
|
||||
result = 0;
|
||||
for (unsigned i = 0; i < arity; i++) {
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
int sz;
|
||||
if (!get_bv_size(domain[i], sz)) {
|
||||
return false;
|
||||
|
|
@ -500,7 +501,7 @@ func_decl * bv_decl_plugin::mk_bit2bool(unsigned bv_size, unsigned num_parameter
|
|||
}
|
||||
|
||||
func_decl * bv_decl_plugin::mk_mkbv(unsigned arity, sort * const * domain) {
|
||||
for (unsigned i = 0; i < arity; i++) {
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
if (!m_manager->is_bool(domain[i])) {
|
||||
m_manager->raise_exception("invalid mkbv operator");
|
||||
return nullptr;
|
||||
|
|
@ -672,9 +673,11 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p
|
|||
}
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
if (args[i]->get_sort() != r->get_domain(i)) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "Argument " << mk_pp(args[i], m) << " at position " << i << " has sort " << mk_pp(args[i]->get_sort(), m) << " it does not match declaration " << mk_pp(r, m);
|
||||
m.raise_exception(buffer.str());
|
||||
m.raise_exception(std::format("Argument {} at position {} has sort {} it does not match declaration {}",
|
||||
to_string(mk_pp(args[i], m)),
|
||||
i,
|
||||
to_string(mk_pp(args[i]->get_sort(), m)),
|
||||
to_string(mk_pp(r, m))));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -833,9 +836,7 @@ rational bv_recognizers::norm(rational const & val, unsigned bv_size, bool is_si
|
|||
|
||||
bool bv_recognizers::has_sign_bit(rational const & n, unsigned bv_size) const {
|
||||
SASSERT(bv_size > 0);
|
||||
rational m = norm(n, bv_size, false);
|
||||
rational p = rational::power_of_two(bv_size - 1);
|
||||
return m >= p;
|
||||
return numerator(n).get_bit(bv_size - 1) == 1;
|
||||
}
|
||||
|
||||
bool bv_recognizers::is_bv_sort(sort const * s) const {
|
||||
|
|
|
|||
|
|
@ -118,26 +118,6 @@ enum bv_op_kind {
|
|||
LAST_BV_OP
|
||||
};
|
||||
|
||||
// Assume k is a "div" operator. It returns the div0 uninterpreted function that
|
||||
// models the value of "div" it is underspecified (i.e., when the denominator is zero).
|
||||
inline bv_op_kind get_div0_op(bv_op_kind k) {
|
||||
switch (k) {
|
||||
case OP_BSDIV: return OP_BSDIV0;
|
||||
case OP_BUDIV: return OP_BUDIV0;
|
||||
case OP_BSREM: return OP_BSREM0;
|
||||
case OP_BUREM: return OP_BUREM0;
|
||||
case OP_BSMOD: return OP_BSMOD0;
|
||||
default: UNREACHABLE(); return LAST_BV_OP;
|
||||
}
|
||||
}
|
||||
|
||||
// Assume decl is the declaration of a "div" operator. It returns the div0 declaration that
|
||||
// models the value of "div" it is underspecified (i.e., when the denominator is zero).
|
||||
inline func_decl * get_div0_decl(ast_manager & m, func_decl * decl) {
|
||||
return m.mk_func_decl(decl->get_family_id(), get_div0_op(static_cast<bv_op_kind>(decl->get_decl_kind())),
|
||||
0, nullptr, 1, decl->get_domain());
|
||||
}
|
||||
|
||||
class bv_decl_plugin : public decl_plugin {
|
||||
friend class bv_util;
|
||||
protected:
|
||||
|
|
@ -461,8 +441,7 @@ public:
|
|||
MATCH_UNARY(is_int2bv);
|
||||
bool is_bit2bool(expr* e, expr*& bv, unsigned& idx) const;
|
||||
|
||||
rational norm(rational const & val, unsigned bv_size, bool is_signed) const ;
|
||||
rational norm(rational const & val, unsigned bv_size) const { return norm(val, bv_size, false); }
|
||||
rational norm(rational const & val, unsigned bv_size, bool is_signed = false) const ;
|
||||
bool has_sign_bit(rational const & n, unsigned bv_size) const;
|
||||
};
|
||||
|
||||
|
|
@ -501,13 +480,17 @@ public:
|
|||
return m_manager.mk_app(get_fid(), OP_EXTRACT, 2, params, 1, &n);
|
||||
}
|
||||
app * mk_concat(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_CONCAT, num, args); }
|
||||
app * mk_concat(std::initializer_list<expr*> args) { return m_manager.mk_app(get_fid(), OP_CONCAT, static_cast<unsigned>(args.size()), args.begin()); }
|
||||
app * mk_concat(expr_ref_vector const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
|
||||
app * mk_concat(expr_ref_buffer const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
|
||||
app * mk_concat(ptr_buffer<expr> const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
|
||||
app * mk_concat(ptr_vector<expr> const& es) { return m_manager.mk_app(get_fid(), OP_CONCAT, es.size(), es.data()); }
|
||||
app * mk_bv_or(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BOR, num, args); }
|
||||
app * mk_bv_or(std::initializer_list<expr*> args) { return m_manager.mk_app(get_fid(), OP_BOR, static_cast<unsigned>(args.size()), args.begin()); }
|
||||
app * mk_bv_and(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BAND, num, args); }
|
||||
app * mk_bv_and(std::initializer_list<expr*> args) { return m_manager.mk_app(get_fid(), OP_BAND, static_cast<unsigned>(args.size()), args.begin()); }
|
||||
app * mk_bv_xor(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BXOR, num, args); }
|
||||
app * mk_bv_xor(std::initializer_list<expr*> args) { return m_manager.mk_app(get_fid(), OP_BXOR, static_cast<unsigned>(args.size()), args.begin()); }
|
||||
|
||||
app * mk_concat(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_concat(2, args); }
|
||||
app * mk_bv_and(expr* x, expr* y) { expr* args[2] = { x, y }; return mk_bv_and(2, args); }
|
||||
|
|
|
|||
|
|
@ -23,9 +23,8 @@ Notes:
|
|||
#include "ast/ast_translation.h"
|
||||
|
||||
class converter {
|
||||
unsigned m_ref_count;
|
||||
unsigned m_ref_count = 0;
|
||||
public:
|
||||
converter():m_ref_count(0) {}
|
||||
virtual ~converter() = default;
|
||||
|
||||
void inc_ref() { ++m_ref_count; }
|
||||
|
|
@ -90,7 +89,7 @@ protected:
|
|||
public:
|
||||
concat_star_converter(T * c1, unsigned num, T * const * c2s, unsigned * szs):
|
||||
m_c1(c1) {
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
T * c2 = c2s[i];
|
||||
if (c2)
|
||||
c2->inc_ref();
|
||||
|
|
|
|||
|
|
@ -44,6 +44,6 @@ public:
|
|||
|
||||
ast_manager& get_manager() { return m; }
|
||||
|
||||
void display(std::ostream & out) override {}
|
||||
void display(std::ostream &) override { }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ public:
|
|||
return false;
|
||||
unsigned i;
|
||||
expr* v = nullptr;
|
||||
for (i = 0; i < num; i++) {
|
||||
for (i = 0; i < num; ++i) {
|
||||
expr* arg = args[i];
|
||||
if (uncnstr(arg)) {
|
||||
v = arg;
|
||||
|
|
@ -196,7 +196,7 @@ public:
|
|||
if (!m_mc)
|
||||
return true;
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned j = 0; j < num; j++)
|
||||
for (unsigned j = 0; j < num; ++j)
|
||||
if (j != i)
|
||||
new_args.push_back(args[j]);
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ class bv_expr_inverter : public iexpr_inverter {
|
|||
return false;
|
||||
unsigned i;
|
||||
expr* v = nullptr;
|
||||
for (i = 0; i < num; i++) {
|
||||
for (i = 0; i < num; ++i) {
|
||||
expr* arg = args[i];
|
||||
if (uncnstr(arg)) {
|
||||
v = arg;
|
||||
|
|
@ -283,7 +283,7 @@ class bv_expr_inverter : public iexpr_inverter {
|
|||
if (!m_mc)
|
||||
return true;
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned j = 0; j < num; j++)
|
||||
for (unsigned j = 0; j < num; ++j)
|
||||
if (j != i)
|
||||
new_args.push_back(args[j]);
|
||||
|
||||
|
|
@ -648,7 +648,7 @@ public:
|
|||
if (m.is_uninterp(get_array_range(s)))
|
||||
return false;
|
||||
unsigned arity = get_array_arity(s);
|
||||
for (unsigned i = 0; i < arity; i++)
|
||||
for (unsigned i = 0; i < arity; ++i)
|
||||
if (m.is_uninterp(get_array_domain(s, i)))
|
||||
return false;
|
||||
// building
|
||||
|
|
@ -657,7 +657,7 @@ public:
|
|||
// and d is a term different from (select t i1 ... in)
|
||||
expr_ref_vector new_args(m);
|
||||
new_args.push_back(t);
|
||||
for (unsigned i = 0; i < arity; i++)
|
||||
for (unsigned i = 0; i < arity; ++i)
|
||||
new_args.push_back(m.get_some_value(get_array_domain(s, i)));
|
||||
expr_ref sel(m);
|
||||
sel = a.mk_select(new_args);
|
||||
|
|
@ -692,13 +692,13 @@ public:
|
|||
return true;
|
||||
}
|
||||
func_decl* c = dt.get_accessor_constructor(f);
|
||||
for (unsigned i = 0; i < c->get_arity(); i++)
|
||||
for (unsigned i = 0; i < c->get_arity(); ++i)
|
||||
if (!m.is_fully_interp(c->get_domain(i)))
|
||||
return false;
|
||||
mk_fresh_uncnstr_var_for(f, r);
|
||||
ptr_vector<func_decl> const& accs = *dt.get_constructor_accessors(c);
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned i = 0; i < accs.size(); i++) {
|
||||
for (unsigned i = 0; i < accs.size(); ++i) {
|
||||
if (accs[i] == f)
|
||||
new_args.push_back(r);
|
||||
else
|
||||
|
|
@ -719,7 +719,7 @@ public:
|
|||
for (func_decl* constructor : constructors) {
|
||||
unsigned num = constructor->get_arity();
|
||||
unsigned target = UINT_MAX;
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
sort* s_arg = constructor->get_domain(i);
|
||||
if (s == s_arg) {
|
||||
target = i;
|
||||
|
|
@ -732,7 +732,7 @@ public:
|
|||
continue;
|
||||
// use the constructor the distinct term constructor(...,t,...)
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
if (i == target)
|
||||
new_args.push_back(t);
|
||||
else
|
||||
|
|
@ -823,6 +823,47 @@ public:
|
|||
};
|
||||
#endif
|
||||
|
||||
class finite_set_inverter : public iexpr_inverter {
|
||||
finite_set_util fs;
|
||||
public:
|
||||
finite_set_inverter(ast_manager& m): iexpr_inverter(m), fs(m) {}
|
||||
|
||||
family_id get_fid() const override { return fs.get_family_id(); }
|
||||
|
||||
bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_FINITE_SET_UNION:
|
||||
// x union y -> x
|
||||
// y := x
|
||||
if (num == 2 && uncnstr(args[0]) && uncnstr(args[1])) {
|
||||
r = args[0];
|
||||
if (m_mc) {
|
||||
add_def(args[1], r);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case OP_FINITE_SET_INTERSECT:
|
||||
// x intersect y -> x
|
||||
// y := x
|
||||
if (num == 2 && uncnstr(args[0]) && uncnstr(args[1])) {
|
||||
r = args[0];
|
||||
if (m_mc) {
|
||||
add_def(args[1], r);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mk_diff(expr* t, expr_ref& r) override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class seq_expr_inverter : public iexpr_inverter {
|
||||
seq_util seq;
|
||||
|
|
@ -924,7 +965,7 @@ expr_inverter::~expr_inverter() {
|
|||
|
||||
|
||||
bool iexpr_inverter::uncnstr(unsigned num, expr * const * args) const {
|
||||
for (unsigned i = 0; i < num; i++)
|
||||
for (unsigned i = 0; i < num; ++i)
|
||||
if (!m_is_var(args[i]))
|
||||
return false;
|
||||
return true;
|
||||
|
|
@ -956,7 +997,7 @@ void iexpr_inverter::add_defs(unsigned num, expr* const* args, expr* u, expr* id
|
|||
if (!m_mc)
|
||||
return;
|
||||
add_def(args[0], u);
|
||||
for (unsigned i = 1; i < num; i++)
|
||||
for (unsigned i = 1; i < num; ++i)
|
||||
add_def(args[i], identity);
|
||||
}
|
||||
|
||||
|
|
@ -972,6 +1013,7 @@ expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) {
|
|||
add(alloc(basic_expr_inverter, m, *this));
|
||||
add(alloc(seq_expr_inverter, m));
|
||||
//add(alloc(pb_expr_inverter, m));
|
||||
add(alloc(finite_set_inverter, m));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -979,7 +1021,7 @@ bool expr_inverter::operator()(func_decl* f, unsigned num, expr* const* args, ex
|
|||
if (num == 0)
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < num; i++)
|
||||
for (unsigned i = 0; i < num; ++i)
|
||||
if (!is_ground(args[i]))
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ protected:
|
|||
|
||||
public:
|
||||
iexpr_inverter(ast_manager& m): m(m) {}
|
||||
virtual ~iexpr_inverter() {}
|
||||
virtual ~iexpr_inverter() = default;
|
||||
virtual void set_is_var(std::function<bool(expr*)>& is_var) { m_is_var = is_var; }
|
||||
virtual void set_model_converter(generic_model_converter* mc) { m_mc = mc; }
|
||||
virtual void set_produce_proofs(bool p) { m_produce_proofs = true; }
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ Notes:
|
|||
void generic_model_converter::add(func_decl * d, expr* e) {
|
||||
VERIFY(e);
|
||||
VERIFY(d->get_range() == e->get_sort());
|
||||
m_entries.push_back(entry(d, e, m, ADD));
|
||||
m_entries.push_back(entry(d, e, m, instruction::ADD));
|
||||
}
|
||||
|
||||
void generic_model_converter::operator()(model_ref & md) {
|
||||
|
|
@ -138,9 +138,9 @@ void generic_model_converter::convert_initialize_value(vector<std::pair<expr_ref
|
|||
auto& [var, value] = var2value[i];
|
||||
for (auto const& e : m_entries) {
|
||||
switch (e.m_instruction) {
|
||||
case HIDE:
|
||||
case instruction::HIDE:
|
||||
break;
|
||||
case ADD:
|
||||
case instruction::ADD:
|
||||
if (is_uninterp_const(var) && e.m_f == to_app(var)->get_decl())
|
||||
convert_initialize_value(e.m_def, i, var2value);
|
||||
break;
|
||||
|
|
@ -203,14 +203,14 @@ void generic_model_converter::get_units(obj_map<expr, bool>& units) {
|
|||
for (unsigned i = m_entries.size(); i-- > 0;) {
|
||||
entry const& e = m_entries[i];
|
||||
switch (e.m_instruction) {
|
||||
case HIDE:
|
||||
case instruction::HIDE:
|
||||
tmp = m.mk_const(e.m_f);
|
||||
if (units.contains(tmp)) {
|
||||
m.dec_ref(tmp);
|
||||
units.remove(tmp);
|
||||
}
|
||||
break;
|
||||
case ADD:
|
||||
case instruction::ADD:
|
||||
if (e.m_f->get_arity() == 0 && m.is_bool(e.m_f->get_range())) {
|
||||
tmp = m.mk_const(e.m_f);
|
||||
if (units.contains(tmp)) {
|
||||
|
|
|
|||
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