mirror of
https://github.com/Z3Prover/z3
synced 2025-06-23 06:13:40 +00:00
merged
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
commit
265bdbe757
76 changed files with 3991 additions and 1422 deletions
422
src/api/api_algebraic.cpp
Normal file
422
src/api/api_algebraic.cpp
Normal file
|
@ -0,0 +1,422 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
api_algebraic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Additional APIs for handling Z3 algebraic numbers encoded as
|
||||
Z3_ASTs
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-12-07
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include<iostream>
|
||||
#include"z3.h"
|
||||
#include"api_log_macros.h"
|
||||
#include"api_context.h"
|
||||
#include"api_ast_vector.h"
|
||||
#include"algebraic_numbers.h"
|
||||
#include"expr2polynomial.h"
|
||||
#include"cancel_eh.h"
|
||||
#include"scoped_timer.h"
|
||||
|
||||
|
||||
#define CHECK_IS_ALGEBRAIC(ARG, RET) { \
|
||||
if (!Z3_algebraic_is_value_core(c, ARG)) { \
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG); \
|
||||
return RET; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CHECK_IS_ALGEBRAIC_X(ARG, RET) { \
|
||||
if (!Z3_algebraic_is_value_core(c, ARG)) { \
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG); \
|
||||
RETURN_Z3(RET); \
|
||||
} \
|
||||
}
|
||||
|
||||
static arith_util & au(Z3_context c) {
|
||||
return mk_c(c)->autil();
|
||||
}
|
||||
|
||||
static algebraic_numbers::manager & am(Z3_context c) {
|
||||
return au(c).am();
|
||||
}
|
||||
|
||||
static bool is_rational(Z3_context c, Z3_ast a) {
|
||||
return au(c).is_numeral(to_expr(a));
|
||||
}
|
||||
|
||||
static bool is_irrational(Z3_context c, Z3_ast a) {
|
||||
return au(c).is_irrational_algebraic_numeral(to_expr(a));
|
||||
}
|
||||
|
||||
static rational get_rational(Z3_context c, Z3_ast a) {
|
||||
SASSERT(is_rational(c, a));
|
||||
rational r;
|
||||
VERIFY(au(c).is_numeral(to_expr(a), r));
|
||||
return r;
|
||||
}
|
||||
|
||||
static algebraic_numbers::anum const & get_irrational(Z3_context c, Z3_ast a) {
|
||||
SASSERT(is_irrational(c, a));
|
||||
return au(c).to_irrational_algebraic_numeral(to_expr(a));
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool Z3_algebraic_is_value_core(Z3_context c, Z3_ast a) {
|
||||
api::context * _c = mk_c(c);
|
||||
return
|
||||
is_expr(a) &&
|
||||
(_c->autil().is_numeral(to_expr(a)) ||
|
||||
_c->autil().is_irrational_algebraic_numeral(to_expr(a)));
|
||||
}
|
||||
|
||||
Z3_bool Z3_API Z3_algebraic_is_value(Z3_context c, Z3_ast a) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_is_value(c, a);
|
||||
RESET_ERROR_CODE();
|
||||
return Z3_algebraic_is_value_core(c, a) ? Z3_TRUE : Z3_FALSE;
|
||||
Z3_CATCH_RETURN(Z3_FALSE);
|
||||
}
|
||||
|
||||
Z3_bool Z3_API Z3_algebraic_is_pos(Z3_context c, Z3_ast a) {
|
||||
return Z3_algebraic_sign(c, a) > 0;
|
||||
}
|
||||
|
||||
Z3_bool Z3_API Z3_algebraic_is_neg(Z3_context c, Z3_ast a) {
|
||||
return Z3_algebraic_sign(c, a) < 0;
|
||||
}
|
||||
|
||||
Z3_bool Z3_API Z3_algebraic_is_zero(Z3_context c, Z3_ast a) {
|
||||
return Z3_algebraic_sign(c, a) == 0;
|
||||
}
|
||||
|
||||
int Z3_API Z3_algebraic_sign(Z3_context c, Z3_ast a) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_sign(c, a);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_ALGEBRAIC(a, 0);
|
||||
if (is_rational(c, a)) {
|
||||
rational v = get_rational(c, a);
|
||||
if (v.is_pos()) return 1;
|
||||
else if (v.is_neg()) return -1;
|
||||
else return 0;
|
||||
}
|
||||
else {
|
||||
algebraic_numbers::anum const & v = get_irrational(c, a);
|
||||
if (am(c).is_pos(v)) return 1;
|
||||
else if (am(c).is_neg(v)) return -1;
|
||||
else return 0;
|
||||
}
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
#define BIN_OP(RAT_OP, IRAT_OP) \
|
||||
algebraic_numbers::manager & _am = am(c); \
|
||||
ast * r = 0; \
|
||||
if (is_rational(c, a)) { \
|
||||
rational av = get_rational(c, a); \
|
||||
if (is_rational(c, b)) { \
|
||||
rational bv = get_rational(c, b); \
|
||||
r = au(c).mk_numeral(av RAT_OP bv, false); \
|
||||
} \
|
||||
else { \
|
||||
algebraic_numbers::anum const & bv = get_irrational(c, b); \
|
||||
scoped_anum _av(_am); \
|
||||
_am.set(_av, av.to_mpq()); \
|
||||
scoped_anum _r(_am); \
|
||||
_am.IRAT_OP(_av, bv, _r); \
|
||||
r = au(c).mk_numeral(_r, false); \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
algebraic_numbers::anum const & av = get_irrational(c, a); \
|
||||
if (is_rational(c, b)) { \
|
||||
rational bv = get_rational(c, b); \
|
||||
scoped_anum _bv(_am); \
|
||||
_am.set(_bv, bv.to_mpq()); \
|
||||
scoped_anum _r(_am); \
|
||||
_am.IRAT_OP(av, _bv, _r); \
|
||||
r = au(c).mk_numeral(_r, false); \
|
||||
} \
|
||||
else { \
|
||||
algebraic_numbers::anum const & bv = get_irrational(c, b); \
|
||||
scoped_anum _r(_am); \
|
||||
_am.IRAT_OP(av, bv, _r); \
|
||||
r = au(c).mk_numeral(_r, false); \
|
||||
} \
|
||||
} \
|
||||
mk_c(c)->save_ast_trail(r); \
|
||||
RETURN_Z3(of_ast(r));
|
||||
|
||||
|
||||
Z3_ast Z3_API Z3_algebraic_add(Z3_context c, Z3_ast a, Z3_ast b) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_add(c, a, b);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_ALGEBRAIC_X(a, 0);
|
||||
CHECK_IS_ALGEBRAIC_X(b, 0);
|
||||
BIN_OP(+,add);
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_algebraic_sub(Z3_context c, Z3_ast a, Z3_ast b) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_sub(c, a, b);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_ALGEBRAIC_X(a, 0);
|
||||
CHECK_IS_ALGEBRAIC_X(b, 0);
|
||||
BIN_OP(-,sub);
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_algebraic_mul(Z3_context c, Z3_ast a, Z3_ast b) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_mul(c, a, b);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_ALGEBRAIC_X(a, 0);
|
||||
CHECK_IS_ALGEBRAIC_X(b, 0);
|
||||
BIN_OP(*,mul);
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_algebraic_div(Z3_context c, Z3_ast a, Z3_ast b) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_div(c, a, b);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_ALGEBRAIC_X(a, 0);
|
||||
CHECK_IS_ALGEBRAIC_X(b, 0);
|
||||
if ((is_rational(c, b) && get_rational(c, b).is_zero()) ||
|
||||
(!is_rational(c, b) && am(c).is_zero(get_irrational(c, b)))) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG);
|
||||
RETURN_Z3(0);
|
||||
}
|
||||
BIN_OP(/,div);
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_algebraic_root(Z3_context c, Z3_ast a, unsigned k) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_root(c, a, k);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_ALGEBRAIC_X(a, 0);
|
||||
if (k % 2 == 0) {
|
||||
if ((is_rational(c, a) && get_rational(c, a).is_neg()) ||
|
||||
(!is_rational(c, a) && am(c).is_neg(get_irrational(c, a)))) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG);
|
||||
RETURN_Z3(0);
|
||||
}
|
||||
}
|
||||
algebraic_numbers::manager & _am = am(c);
|
||||
scoped_anum _r(_am);
|
||||
if (is_rational(c, a)) {
|
||||
scoped_anum av(_am);
|
||||
_am.set(av, get_rational(c, a).to_mpq());
|
||||
_am.root(av, k, _r);
|
||||
}
|
||||
else {
|
||||
algebraic_numbers::anum const & av = get_irrational(c, a);
|
||||
_am.root(av, k, _r);
|
||||
}
|
||||
expr * r = au(c).mk_numeral(_r, false);
|
||||
mk_c(c)->save_ast_trail(r);
|
||||
RETURN_Z3(of_ast(r));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
Z3_ast Z3_API Z3_algebraic_power(Z3_context c, Z3_ast a, unsigned k) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_power(c, a, k);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_ALGEBRAIC_X(a, 0);
|
||||
algebraic_numbers::manager & _am = am(c);
|
||||
scoped_anum _r(_am);
|
||||
if (is_rational(c, a)) {
|
||||
scoped_anum av(_am);
|
||||
_am.set(av, get_rational(c, a).to_mpq());
|
||||
_am.power(av, k, _r);
|
||||
}
|
||||
else {
|
||||
algebraic_numbers::anum const & av = get_irrational(c, a);
|
||||
_am.power(av, k, _r);
|
||||
}
|
||||
expr * r = au(c).mk_numeral(_r, false);
|
||||
mk_c(c)->save_ast_trail(r);
|
||||
RETURN_Z3(of_ast(r));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
#define BIN_PRED(RAT_PRED, IRAT_PRED) \
|
||||
algebraic_numbers::manager & _am = am(c); \
|
||||
bool r; \
|
||||
if (is_rational(c, a)) { \
|
||||
rational av = get_rational(c, a); \
|
||||
if (is_rational(c, b)) { \
|
||||
rational bv = get_rational(c, b); \
|
||||
r = av RAT_PRED bv; \
|
||||
} \
|
||||
else { \
|
||||
algebraic_numbers::anum const & bv = get_irrational(c, b); \
|
||||
scoped_anum _av(_am); \
|
||||
_am.set(_av, av.to_mpq()); \
|
||||
r = _am.IRAT_PRED(_av, bv); \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
algebraic_numbers::anum const & av = get_irrational(c, a); \
|
||||
if (is_rational(c, b)) { \
|
||||
rational bv = get_rational(c, b); \
|
||||
scoped_anum _bv(_am); \
|
||||
_am.set(_bv, bv.to_mpq()); \
|
||||
r = _am.IRAT_PRED(av, _bv); \
|
||||
} \
|
||||
else { \
|
||||
algebraic_numbers::anum const & bv = get_irrational(c, b); \
|
||||
r = _am.IRAT_PRED(av, bv); \
|
||||
} \
|
||||
} \
|
||||
return r ? Z3_TRUE : Z3_FALSE;
|
||||
|
||||
|
||||
Z3_bool Z3_API Z3_algebraic_lt(Z3_context c, Z3_ast a, Z3_ast b) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_lt(c, a, b);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_ALGEBRAIC(a, 0);
|
||||
CHECK_IS_ALGEBRAIC(b, 0);
|
||||
BIN_PRED(<,lt);
|
||||
Z3_CATCH_RETURN(Z3_FALSE);
|
||||
}
|
||||
|
||||
Z3_bool Z3_API Z3_algebraic_gt(Z3_context c, Z3_ast a, Z3_ast b) {
|
||||
return Z3_algebraic_lt(c, b, a);
|
||||
}
|
||||
|
||||
Z3_bool Z3_API Z3_algebraic_le(Z3_context c, Z3_ast a, Z3_ast b) {
|
||||
return !Z3_algebraic_lt(c, b, a);
|
||||
}
|
||||
|
||||
Z3_bool Z3_API Z3_algebraic_ge(Z3_context c, Z3_ast a, Z3_ast b) {
|
||||
return !Z3_algebraic_lt(c, a, b);
|
||||
}
|
||||
|
||||
Z3_bool Z3_API Z3_algebraic_eq(Z3_context c, Z3_ast a, Z3_ast b) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_eq(c, a, b);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_IS_ALGEBRAIC(a, 0);
|
||||
CHECK_IS_ALGEBRAIC(b, 0);
|
||||
BIN_PRED(==,eq);
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
Z3_bool Z3_API Z3_algebraic_neq(Z3_context c, Z3_ast a, Z3_ast b) {
|
||||
return !Z3_algebraic_eq(c, a, b);
|
||||
}
|
||||
|
||||
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++) {
|
||||
if (is_rational(c, a[i])) {
|
||||
_am.set(tmp, get_rational(c, a[i]).to_mpq());
|
||||
as.push_back(tmp);
|
||||
}
|
||||
else if (is_irrational(c, a[i])) {
|
||||
as.push_back(get_irrational(c, a[i]));
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class vector_var2anum : public polynomial::var2anum {
|
||||
scoped_anum_vector const & m_as;
|
||||
public:
|
||||
vector_var2anum(scoped_anum_vector & as):m_as(as) {}
|
||||
virtual ~vector_var2anum() {}
|
||||
virtual algebraic_numbers::manager & m() const { return m_as.m(); }
|
||||
virtual bool contains(polynomial::var x) const { return static_cast<unsigned>(x) < m_as.size(); }
|
||||
virtual algebraic_numbers::anum const & operator()(polynomial::var x) const { return m_as.get(x); }
|
||||
};
|
||||
|
||||
Z3_ast_vector Z3_API Z3_algebraic_roots(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_roots(c, p, n, a);
|
||||
RESET_ERROR_CODE();
|
||||
polynomial::manager & pm = mk_c(c)->pm();
|
||||
polynomial_ref _p(pm);
|
||||
polynomial::scoped_numeral d(pm.m());
|
||||
expr2polynomial converter(mk_c(c)->m(), pm, 0, true);
|
||||
if (!converter.to_polynomial(to_expr(p), _p, d) ||
|
||||
static_cast<unsigned>(max_var(_p)) >= n + 1) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG);
|
||||
return 0;
|
||||
}
|
||||
algebraic_numbers::manager & _am = am(c);
|
||||
scoped_anum_vector as(_am);
|
||||
if (!to_anum_vector(c, n, a, as)) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG);
|
||||
return 0;
|
||||
}
|
||||
scoped_anum_vector roots(_am);
|
||||
{
|
||||
cancel_eh<algebraic_numbers::manager> eh(_am);
|
||||
api::context::set_interruptable(*(mk_c(c)), eh);
|
||||
scoped_timer timer(mk_c(c)->params().m_timeout, &eh);
|
||||
vector_var2anum v2a(as);
|
||||
_am.isolate_roots(_p, v2a, roots);
|
||||
}
|
||||
Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, mk_c(c)->m());
|
||||
mk_c(c)->save_object(result);
|
||||
for (unsigned i = 0; i < roots.size(); i++) {
|
||||
result->m_ast_vector.push_back(au(c).mk_numeral(roots.get(i), false));
|
||||
}
|
||||
RETURN_Z3(of_ast_vector(result));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
int Z3_API Z3_algebraic_eval(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_algebraic_eval(c, p, n, a);
|
||||
RESET_ERROR_CODE();
|
||||
polynomial::manager & pm = mk_c(c)->pm();
|
||||
polynomial_ref _p(pm);
|
||||
polynomial::scoped_numeral d(pm.m());
|
||||
expr2polynomial converter(mk_c(c)->m(), pm, 0, true);
|
||||
if (!converter.to_polynomial(to_expr(p), _p, d) ||
|
||||
static_cast<unsigned>(max_var(_p)) >= n) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG);
|
||||
return 0;
|
||||
}
|
||||
algebraic_numbers::manager & _am = am(c);
|
||||
scoped_anum_vector as(_am);
|
||||
if (!to_anum_vector(c, n, a, as)) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG);
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
cancel_eh<algebraic_numbers::manager> eh(_am);
|
||||
api::context::set_interruptable(*(mk_c(c)), eh);
|
||||
scoped_timer timer(mk_c(c)->params().m_timeout, &eh);
|
||||
vector_var2anum v2a(as);
|
||||
int r = _am.eval_sign_at(_p, v2a);
|
||||
if (r > 0) return 1;
|
||||
else if (r < 0) return -1;
|
||||
else return 0;
|
||||
}
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
};
|
|
@ -324,7 +324,8 @@ extern "C" {
|
|||
switch (_a->get_kind()) {
|
||||
case AST_APP: {
|
||||
expr * e = to_expr(_a);
|
||||
if (is_numeral_sort(c, of_sort(mk_c(c)->m().get_sort(e))) && mk_c(c)->m().is_value(e))
|
||||
// Real algebraic numbers are not considered Z3_NUMERAL_AST
|
||||
if (is_numeral_sort(c, of_sort(mk_c(c)->m().get_sort(e))) && mk_c(c)->m().is_unique_value(e))
|
||||
return Z3_NUMERAL_AST;
|
||||
return Z3_APP_AST;
|
||||
}
|
||||
|
|
|
@ -82,13 +82,13 @@ namespace api {
|
|||
context::context(context_params * p, bool user_ref_count):
|
||||
m_params(p != 0 ? *p : context_params()),
|
||||
m_user_ref_count(user_ref_count),
|
||||
m_manager(m_params.m_proof ? PGM_FINE : PGM_DISABLED, m_params.m_trace ? m_params.m_trace_file_name.c_str() : 0),
|
||||
m_plugins(m_manager),
|
||||
m_arith_util(m_manager),
|
||||
m_bv_util(m_manager),
|
||||
m_datalog_util(m_manager),
|
||||
m_last_result(m_manager),
|
||||
m_ast_trail(m_manager),
|
||||
m_manager(m_params.mk_ast_manager()),
|
||||
m_plugins(m()),
|
||||
m_arith_util(m()),
|
||||
m_bv_util(m()),
|
||||
m_datalog_util(m()),
|
||||
m_last_result(m()),
|
||||
m_ast_trail(m()),
|
||||
m_replay_stack() {
|
||||
|
||||
m_solver = 0;
|
||||
|
@ -102,22 +102,16 @@ namespace api {
|
|||
m_smtlib_parser_has_decls = false;
|
||||
|
||||
z3_bound_num_procs();
|
||||
//
|
||||
// Configuration parameter settings.
|
||||
//
|
||||
if (m_params.m_debug_ref_count) {
|
||||
m_manager.debug_ref_count();
|
||||
}
|
||||
|
||||
m_error_handler = &default_error_handler;
|
||||
|
||||
m_basic_fid = m_manager.get_basic_family_id();
|
||||
m_arith_fid = m_manager.get_family_id("arith");
|
||||
m_bv_fid = m_manager.get_family_id("bv");
|
||||
m_array_fid = m_manager.get_family_id("array");
|
||||
m_dt_fid = m_manager.get_family_id("datatype");
|
||||
m_datalog_fid = m_manager.get_family_id("datalog_relation");
|
||||
m_dt_plugin = static_cast<datatype_decl_plugin*>(m_manager.get_plugin(m_dt_fid));
|
||||
m_basic_fid = m().get_basic_family_id();
|
||||
m_arith_fid = m().get_family_id("arith");
|
||||
m_bv_fid = m().get_family_id("bv");
|
||||
m_array_fid = m().get_family_id("array");
|
||||
m_dt_fid = m().get_family_id("datatype");
|
||||
m_datalog_fid = m().get_family_id("datalog_relation");
|
||||
m_dt_plugin = static_cast<datatype_decl_plugin*>(m().get_plugin(m_dt_fid));
|
||||
|
||||
if (!m_user_ref_count) {
|
||||
m_replay_stack.push_back(0);
|
||||
|
@ -143,6 +137,7 @@ namespace api {
|
|||
{
|
||||
if (m_interruptable)
|
||||
(*m_interruptable)();
|
||||
m().set_cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,12 +190,12 @@ namespace api {
|
|||
expr * context::mk_and(unsigned num_exprs, expr * const * exprs) {
|
||||
switch(num_exprs) {
|
||||
case 0:
|
||||
return m_manager.mk_true();
|
||||
return m().mk_true();
|
||||
case 1:
|
||||
save_ast_trail(exprs[0]);
|
||||
return exprs[0];
|
||||
default: {
|
||||
expr * a = m_manager.mk_and(num_exprs, exprs);
|
||||
expr * a = m().mk_and(num_exprs, exprs);
|
||||
save_ast_trail(a);
|
||||
return a;
|
||||
} }
|
||||
|
@ -216,7 +211,7 @@ namespace api {
|
|||
SASSERT(m_replay_stack.size() > num_scopes);
|
||||
unsigned j = m_replay_stack.size() - num_scopes - 1;
|
||||
if (!m_replay_stack[j]) {
|
||||
m_replay_stack[j] = alloc(ast_ref_vector, m_manager);
|
||||
m_replay_stack[j] = alloc(ast_ref_vector, m());
|
||||
}
|
||||
m_replay_stack[j]->push_back(n);
|
||||
}
|
||||
|
@ -324,7 +319,7 @@ namespace api {
|
|||
smt::kernel & context::get_smt_kernel() {
|
||||
if (!m_solver) {
|
||||
m_fparams.updt_params(m_params);
|
||||
m_solver = alloc(smt::kernel, m_manager, m_fparams);
|
||||
m_solver = alloc(smt::kernel, m(), m_fparams);
|
||||
}
|
||||
return *m_solver;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ Revision History:
|
|||
#include"event_handler.h"
|
||||
#include"tactic_manager.h"
|
||||
#include"context_params.h"
|
||||
#include"api_polynomial.h"
|
||||
|
||||
namespace smtlib {
|
||||
class parser;
|
||||
|
@ -45,7 +46,7 @@ namespace api {
|
|||
struct add_plugins { add_plugins(ast_manager & m); };
|
||||
context_params m_params;
|
||||
bool m_user_ref_count; //!< if true, the user is responsible for managing referenc counters.
|
||||
ast_manager m_manager;
|
||||
scoped_ptr<ast_manager> m_manager;
|
||||
add_plugins m_plugins;
|
||||
|
||||
arith_util m_arith_util;
|
||||
|
@ -81,6 +82,8 @@ namespace api {
|
|||
Z3_ast_print_mode m_print_mode;
|
||||
|
||||
event_handler * m_interruptable; // Reference to an object that can be interrupted by Z3_interrupt
|
||||
|
||||
pmanager m_pmanager;
|
||||
public:
|
||||
// Scoped obj for setting m_interruptable
|
||||
class set_interruptable {
|
||||
|
@ -98,10 +101,10 @@ namespace api {
|
|||
|
||||
context(context_params * p, bool user_ref_count = false);
|
||||
~context();
|
||||
ast_manager & m() { return m_manager; }
|
||||
ast_manager & m() const { return *(m_manager.get()); }
|
||||
|
||||
context_params & params() { return m_params; }
|
||||
bool produce_proofs() const { return m_manager.proofs_enabled(); }
|
||||
bool produce_proofs() const { return m().proofs_enabled(); }
|
||||
bool produce_models() const { return m_params.m_model; }
|
||||
bool produce_unsat_cores() const { return m_params.m_unsat_core; }
|
||||
bool use_auto_config() const { return m_params.m_auto_config; }
|
||||
|
@ -167,6 +170,13 @@ namespace api {
|
|||
|
||||
void check_sorts(ast * n);
|
||||
|
||||
// ------------------------
|
||||
//
|
||||
// Polynomial manager & caches
|
||||
//
|
||||
// -----------------------
|
||||
polynomial::manager & pm() { return m_pmanager.pm(); }
|
||||
|
||||
// ------------------------
|
||||
//
|
||||
// Solver interface for backward compatibility
|
||||
|
|
84
src/api/api_polynomial.cpp
Normal file
84
src/api/api_polynomial.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
api_polynomial.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Polynomial manager and caches for the external API.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-12-08
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include<iostream>
|
||||
#include"z3.h"
|
||||
#include"api_log_macros.h"
|
||||
#include"api_context.h"
|
||||
#include"api_polynomial.h"
|
||||
#include"api_ast_vector.h"
|
||||
#include"expr2polynomial.h"
|
||||
#include"cancel_eh.h"
|
||||
#include"scoped_timer.h"
|
||||
#include"expr2var.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
pmanager::pmanager():
|
||||
m_pm(m_nm) {
|
||||
}
|
||||
|
||||
pmanager::~pmanager() {
|
||||
}
|
||||
|
||||
void pmanager::set_cancel(bool f) {
|
||||
m_pm.set_cancel(f);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x) {
|
||||
Z3_TRY;
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, 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<polynomial::manager> eh(pm);
|
||||
api::context::set_interruptable(*(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);
|
||||
}
|
||||
}
|
||||
RETURN_Z3(of_ast_vector(result));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
};
|
39
src/api/api_polynomial.h
Normal file
39
src/api/api_polynomial.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
api_polynomial.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Polynomial manager and caches for the external API.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-12-08
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _API_POLYNOMIAL_H_
|
||||
#define _API_POLYNOMIAL_H_
|
||||
|
||||
#include"polynomial.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
class pmanager {
|
||||
unsynch_mpz_manager m_nm;
|
||||
polynomial::manager m_pm;
|
||||
// TODO: add support for caching expressions -> polynomial and back
|
||||
public:
|
||||
pmanager();
|
||||
virtual ~pmanager();
|
||||
polynomial::manager & pm() { return m_pm; }
|
||||
void set_cancel(bool f);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -36,17 +36,7 @@ extern "C" {
|
|||
static void init_solver_core(Z3_context c, Z3_solver _s) {
|
||||
ast_manager & m = mk_c(c)->m();
|
||||
Z3_solver_ref * s = to_solver(_s);
|
||||
s->m_solver->set_produce_proofs(mk_c(c)->produce_proofs());
|
||||
s->m_solver->set_produce_unsat_cores(s->m_params.get_bool("unsat_core", mk_c(c)->produce_unsat_cores()));
|
||||
s->m_solver->set_produce_models(s->m_params.get_bool("model", mk_c(c)->produce_models()));
|
||||
if (!mk_c(c)->use_auto_config()) {
|
||||
params_ref p = s->m_params;
|
||||
p.set_bool("auto_config", false);
|
||||
s->m_solver->updt_params(p);
|
||||
}
|
||||
else {
|
||||
s->m_solver->updt_params(s->m_params);
|
||||
}
|
||||
mk_c(c)->params().init_solver_params(mk_c(c)->m(), *(s->m_solver), s->m_params);
|
||||
s->m_solver->init(m, s->m_logic);
|
||||
s->m_initialized = true;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
Java bindings
|
||||
-------------
|
||||
|
||||
This is currently "working in progress".
|
|
@ -1126,6 +1126,27 @@ def Var(idx, s):
|
|||
_z3_assert(is_sort(s), "Z3 sort expected")
|
||||
return _to_expr_ref(Z3_mk_bound(s.ctx_ref(), idx, s.ast), s.ctx)
|
||||
|
||||
def RealVar(idx, ctx=None):
|
||||
"""
|
||||
Create a real free variable. Free variables are used to create quantified formulas.
|
||||
They are also used to create polynomials.
|
||||
|
||||
>>> RealVar(0)
|
||||
Var(0)
|
||||
"""
|
||||
return Var(idx, RealSort(ctx))
|
||||
|
||||
def RealVarVector(n, ctx=None):
|
||||
"""
|
||||
Create a list of Real free variables.
|
||||
The variables have ids: 0, 1, ..., n-1
|
||||
|
||||
>>> x0, x1, x2, x3 = RealVarVector(4)
|
||||
>>> x2
|
||||
Var(2)
|
||||
"""
|
||||
return [ RealVar(i, ctx) for i in range(n) ]
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Booleans
|
||||
|
|
564
src/api/python/z3num.py
Normal file
564
src/api/python/z3num.py
Normal file
|
@ -0,0 +1,564 @@
|
|||
############################################
|
||||
# Copyright (c) 2012 Microsoft Corporation
|
||||
#
|
||||
# Z3 Python interface for Z3 numerals
|
||||
#
|
||||
# Author: Leonardo de Moura (leonardo)
|
||||
############################################
|
||||
from z3 import *
|
||||
from z3core import *
|
||||
from z3printer import *
|
||||
|
||||
def _to_numeral(num, ctx=None):
|
||||
if isinstance(num, Numeral):
|
||||
return num
|
||||
else:
|
||||
return Numeral(num, ctx)
|
||||
|
||||
class Numeral:
|
||||
"""
|
||||
A Z3 numeral can be used to perform computations over arbitrary
|
||||
precision integers, rationals and real algebraic numbers.
|
||||
It also automatically converts python numeric values.
|
||||
|
||||
>>> Numeral(2)
|
||||
2
|
||||
>>> Numeral("3/2") + 1
|
||||
5/2
|
||||
>>> Numeral(Sqrt(2))
|
||||
1.4142135623?
|
||||
>>> Numeral(Sqrt(2)) + 2
|
||||
3.4142135623?
|
||||
>>> Numeral(Sqrt(2)) + Numeral(Sqrt(3))
|
||||
3.1462643699?
|
||||
|
||||
Z3 numerals can be used to perform computations with
|
||||
values in a Z3 model.
|
||||
|
||||
>>> s = Solver()
|
||||
>>> x = Real('x')
|
||||
>>> s.add(x*x == 2)
|
||||
>>> s.add(x > 0)
|
||||
>>> s.check()
|
||||
sat
|
||||
>>> m = s.model()
|
||||
>>> m[x]
|
||||
1.4142135623?
|
||||
>>> m[x] + 1
|
||||
1.4142135623? + 1
|
||||
|
||||
The previous result is a Z3 expression.
|
||||
|
||||
>>> (m[x] + 1).sexpr()
|
||||
'(+ (root-obj (+ (^ x 2) (- 2)) 2) 1.0)'
|
||||
|
||||
>>> Numeral(m[x]) + 1
|
||||
2.4142135623?
|
||||
>>> Numeral(m[x]).is_pos()
|
||||
True
|
||||
>>> Numeral(m[x])**2
|
||||
2
|
||||
|
||||
We can also isolate the roots of polynomials.
|
||||
|
||||
>>> x0, x1, x2 = RealVarVector(3)
|
||||
>>> r0 = isolate_roots(x0**5 - x0 - 1)
|
||||
>>> r0
|
||||
[1.1673039782?]
|
||||
|
||||
In the following example, we are isolating the roots
|
||||
of a univariate polynomial (on x1) obtained after substituting
|
||||
x0 -> r0[0]
|
||||
|
||||
>>> r1 = isolate_roots(x1**2 - x0 + 1, [ r0[0] ])
|
||||
>>> r1
|
||||
[-0.4090280898?, 0.4090280898?]
|
||||
|
||||
Similarly, in the next example we isolate the roots of
|
||||
a univariate polynomial (on x2) obtained after substituting
|
||||
x0 -> r0[0] and x1 -> r1[0]
|
||||
|
||||
>>> isolate_roots(x1*x2 + x0, [ r0[0], r1[0] ])
|
||||
[2.8538479564?]
|
||||
|
||||
"""
|
||||
def __init__(self, num, ctx=None):
|
||||
if isinstance(num, Ast):
|
||||
self.ast = num
|
||||
self.ctx = z3._get_ctx(ctx)
|
||||
elif isinstance(num, RatNumRef) or isinstance(num, AlgebraicNumRef):
|
||||
self.ast = num.ast
|
||||
self.ctx = num.ctx
|
||||
elif isinstance(num, ArithRef):
|
||||
r = simplify(num)
|
||||
self.ast = r.ast
|
||||
self.ctx = r.ctx
|
||||
else:
|
||||
v = RealVal(num, ctx)
|
||||
self.ast = v.ast
|
||||
self.ctx = v.ctx
|
||||
Z3_inc_ref(self.ctx_ref(), self.as_ast())
|
||||
assert Z3_algebraic_is_value(self.ctx_ref(), self.ast)
|
||||
|
||||
def __del__(self):
|
||||
Z3_dec_ref(self.ctx_ref(), self.as_ast())
|
||||
|
||||
def is_integer(self):
|
||||
""" Return True if the numeral is integer.
|
||||
|
||||
>>> Numeral(2).is_integer()
|
||||
True
|
||||
>>> (Numeral(Sqrt(2)) * Numeral(Sqrt(2))).is_integer()
|
||||
True
|
||||
>>> Numeral(Sqrt(2)).is_integer()
|
||||
False
|
||||
>>> Numeral("2/3").is_integer()
|
||||
False
|
||||
"""
|
||||
return self.is_rational() and self.denominator() == 1
|
||||
|
||||
def is_rational(self):
|
||||
""" Return True if the numeral is rational.
|
||||
|
||||
>>> Numeral(2).is_rational()
|
||||
True
|
||||
>>> Numeral("2/3").is_rational()
|
||||
True
|
||||
>>> Numeral(Sqrt(2)).is_rational()
|
||||
False
|
||||
|
||||
"""
|
||||
return Z3_get_ast_kind(self.ctx_ref(), self.as_ast()) == Z3_NUMERAL_AST
|
||||
|
||||
def denominator(self):
|
||||
""" Return the denominator if `self` is rational.
|
||||
|
||||
>>> Numeral("2/3").denominator()
|
||||
3
|
||||
"""
|
||||
assert(self.is_rational())
|
||||
return Numeral(Z3_get_denominator(self.ctx_ref(), self.as_ast()), self.ctx)
|
||||
|
||||
def numerator(self):
|
||||
""" Return the numerator if `self` is rational.
|
||||
|
||||
>>> Numeral("2/3").numerator()
|
||||
2
|
||||
"""
|
||||
assert(self.is_rational())
|
||||
return Numeral(Z3_get_numerator(self.ctx_ref(), self.as_ast()), self.ctx)
|
||||
|
||||
|
||||
def is_irrational(self):
|
||||
""" Return True if the numeral is irrational.
|
||||
|
||||
>>> Numeral(2).is_irrational()
|
||||
False
|
||||
>>> Numeral("2/3").is_irrational()
|
||||
False
|
||||
>>> Numeral(Sqrt(2)).is_irrational()
|
||||
True
|
||||
"""
|
||||
return not self.is_rational()
|
||||
|
||||
def as_long(self):
|
||||
""" Return a numeral (that is an integer) as a Python long.
|
||||
|
||||
>>> (Numeral(10)**20).as_long()
|
||||
100000000000000000000L
|
||||
"""
|
||||
assert(self.is_integer())
|
||||
return long(Z3_get_numeral_string(self.ctx_ref(), self.as_ast()))
|
||||
|
||||
def approx(self, precision=10):
|
||||
"""Return a numeral that approximates the numeral `self`.
|
||||
The result `r` is such that |r - self| <= 1/10^precision
|
||||
|
||||
If `self` is rational, then the result is `self`.
|
||||
|
||||
>>> x = Numeral(2).root(2)
|
||||
>>> x.approx(20)
|
||||
6838717160008073720548335/4835703278458516698824704
|
||||
>>> x.approx(5)
|
||||
2965821/2097152
|
||||
>>> Numeral(2).approx(10)
|
||||
2
|
||||
"""
|
||||
return self.upper(precision)
|
||||
|
||||
def upper(self, precision=10):
|
||||
"""Return a upper bound that approximates the numeral `self`.
|
||||
The result `r` is such that r - self <= 1/10^precision
|
||||
|
||||
If `self` is rational, then the result is `self`.
|
||||
|
||||
>>> x = Numeral(2).root(2)
|
||||
>>> x.upper(20)
|
||||
6838717160008073720548335/4835703278458516698824704
|
||||
>>> x.upper(5)
|
||||
2965821/2097152
|
||||
>>> Numeral(2).upper(10)
|
||||
2
|
||||
"""
|
||||
if self.is_rational():
|
||||
return self
|
||||
else:
|
||||
return Numeral(Z3_get_algebraic_number_upper(self.ctx_ref(), self.as_ast(), precision), self.ctx)
|
||||
|
||||
def lower(self, precision=10):
|
||||
"""Return a lower bound that approximates the numeral `self`.
|
||||
The result `r` is such that self - r <= 1/10^precision
|
||||
|
||||
If `self` is rational, then the result is `self`.
|
||||
|
||||
>>> x = Numeral(2).root(2)
|
||||
>>> x.lower(20)
|
||||
1709679290002018430137083/1208925819614629174706176
|
||||
>>> Numeral("2/3").lower(10)
|
||||
2/3
|
||||
"""
|
||||
if self.is_rational():
|
||||
return self
|
||||
else:
|
||||
return Numeral(Z3_get_algebraic_number_lower(self.ctx_ref(), self.as_ast(), precision), self.ctx)
|
||||
|
||||
def sign(self):
|
||||
""" Return the sign of the numeral.
|
||||
|
||||
>>> Numeral(2).sign()
|
||||
1
|
||||
>>> Numeral(-3).sign()
|
||||
-1
|
||||
>>> Numeral(0).sign()
|
||||
0
|
||||
"""
|
||||
return Z3_algebraic_sign(self.ctx_ref(), self.ast)
|
||||
|
||||
def is_pos(self):
|
||||
""" Return True if the numeral is positive.
|
||||
|
||||
>>> Numeral(2).is_pos()
|
||||
True
|
||||
>>> Numeral(-3).is_pos()
|
||||
False
|
||||
>>> Numeral(0).is_pos()
|
||||
False
|
||||
"""
|
||||
return Z3_algebraic_is_pos(self.ctx_ref(), self.ast)
|
||||
|
||||
def is_neg(self):
|
||||
""" Return True if the numeral is negative.
|
||||
|
||||
>>> Numeral(2).is_neg()
|
||||
False
|
||||
>>> Numeral(-3).is_neg()
|
||||
True
|
||||
>>> Numeral(0).is_neg()
|
||||
False
|
||||
"""
|
||||
return Z3_algebraic_is_neg(self.ctx_ref(), self.ast)
|
||||
|
||||
def is_zero(self):
|
||||
""" Return True if the numeral is zero.
|
||||
|
||||
>>> Numeral(2).is_zero()
|
||||
False
|
||||
>>> Numeral(-3).is_zero()
|
||||
False
|
||||
>>> Numeral(0).is_zero()
|
||||
True
|
||||
>>> sqrt2 = Numeral(2).root(2)
|
||||
>>> sqrt2.is_zero()
|
||||
False
|
||||
>>> (sqrt2 - sqrt2).is_zero()
|
||||
True
|
||||
"""
|
||||
return Z3_algebraic_is_zero(self.ctx_ref(), self.ast)
|
||||
|
||||
def __add__(self, other):
|
||||
""" Return the numeral `self + other`.
|
||||
|
||||
>>> Numeral(2) + 3
|
||||
5
|
||||
>>> Numeral(2) + Numeral(4)
|
||||
6
|
||||
>>> Numeral("2/3") + 1
|
||||
5/3
|
||||
"""
|
||||
return Numeral(Z3_algebraic_add(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx)
|
||||
|
||||
def __radd__(self, other):
|
||||
""" Return the numeral `other + self`.
|
||||
|
||||
>>> 3 + Numeral(2)
|
||||
5
|
||||
"""
|
||||
return Numeral(Z3_algebraic_add(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx)
|
||||
|
||||
def __sub__(self, other):
|
||||
""" Return the numeral `self - other`.
|
||||
|
||||
>>> Numeral(2) - 3
|
||||
-1
|
||||
"""
|
||||
return Numeral(Z3_algebraic_sub(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx)
|
||||
|
||||
def __rsub__(self, other):
|
||||
""" Return the numeral `other - self`.
|
||||
|
||||
>>> 3 - Numeral(2)
|
||||
1
|
||||
"""
|
||||
return Numeral(Z3_algebraic_sub(self.ctx_ref(), _to_numeral(other, self.ctx).ast, self.ast), self.ctx)
|
||||
|
||||
def __mul__(self, other):
|
||||
""" Return the numeral `self * other`.
|
||||
>>> Numeral(2) * 3
|
||||
6
|
||||
"""
|
||||
return Numeral(Z3_algebraic_mul(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx)
|
||||
|
||||
def __rmul__(self, other):
|
||||
""" Return the numeral `other * mul`.
|
||||
>>> 3 * Numeral(2)
|
||||
6
|
||||
"""
|
||||
return Numeral(Z3_algebraic_mul(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx)
|
||||
|
||||
def __div__(self, other):
|
||||
""" Return the numeral `self / other`.
|
||||
>>> Numeral(2) / 3
|
||||
2/3
|
||||
>>> Numeral(2).root(2) / 3
|
||||
0.4714045207?
|
||||
>>> Numeral(Sqrt(2)) / Numeral(Sqrt(3))
|
||||
0.8164965809?
|
||||
"""
|
||||
return Numeral(Z3_algebraic_div(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx)
|
||||
|
||||
def __rdiv__(self, other):
|
||||
""" Return the numeral `other / self`.
|
||||
>>> 3 / Numeral(2)
|
||||
3/2
|
||||
>>> 3 / Numeral(2).root(2)
|
||||
2.1213203435?
|
||||
"""
|
||||
return Numeral(Z3_algebraic_div(self.ctx_ref(), _to_numeral(other, self.ctx).ast, self.ast), self.ctx)
|
||||
|
||||
def root(self, k):
|
||||
""" Return the numeral `self^(1/k)`.
|
||||
|
||||
>>> sqrt2 = Numeral(2).root(2)
|
||||
>>> sqrt2
|
||||
1.4142135623?
|
||||
>>> sqrt2 * sqrt2
|
||||
2
|
||||
>>> sqrt2 * 2 + 1
|
||||
3.8284271247?
|
||||
>>> (sqrt2 * 2 + 1).sexpr()
|
||||
'(root-obj (+ (^ x 2) (* (- 2) x) (- 7)) 2)'
|
||||
"""
|
||||
return Numeral(Z3_algebraic_root(self.ctx_ref(), self.ast, k), self.ctx)
|
||||
|
||||
def power(self, k):
|
||||
""" Return the numeral `self^k`.
|
||||
|
||||
>>> sqrt3 = Numeral(3).root(2)
|
||||
>>> sqrt3
|
||||
1.7320508075?
|
||||
>>> sqrt3.power(2)
|
||||
3
|
||||
"""
|
||||
return Numeral(Z3_algebraic_power(self.ctx_ref(), self.ast, k), self.ctx)
|
||||
|
||||
def __pow__(self, k):
|
||||
""" Return the numeral `self^k`.
|
||||
|
||||
>>> sqrt3 = Numeral(3).root(2)
|
||||
>>> sqrt3
|
||||
1.7320508075?
|
||||
>>> sqrt3**2
|
||||
3
|
||||
"""
|
||||
return self.power(k)
|
||||
|
||||
def __lt__(self, other):
|
||||
""" Return True if `self < other`.
|
||||
|
||||
>>> Numeral(Sqrt(2)) < 2
|
||||
True
|
||||
>>> Numeral(Sqrt(3)) < Numeral(Sqrt(2))
|
||||
False
|
||||
>>> Numeral(Sqrt(2)) < Numeral(Sqrt(2))
|
||||
False
|
||||
"""
|
||||
return Z3_algebraic_lt(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast)
|
||||
|
||||
def __rlt__(self, other):
|
||||
""" Return True if `other < self`.
|
||||
|
||||
>>> 2 < Numeral(Sqrt(2))
|
||||
False
|
||||
"""
|
||||
return self > other
|
||||
|
||||
def __gt__(self, other):
|
||||
""" Return True if `self > other`.
|
||||
|
||||
>>> Numeral(Sqrt(2)) > 2
|
||||
False
|
||||
>>> Numeral(Sqrt(3)) > Numeral(Sqrt(2))
|
||||
True
|
||||
>>> Numeral(Sqrt(2)) > Numeral(Sqrt(2))
|
||||
False
|
||||
"""
|
||||
return Z3_algebraic_gt(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast)
|
||||
|
||||
def __rgt__(self, other):
|
||||
""" Return True if `other > self`.
|
||||
|
||||
>>> 2 > Numeral(Sqrt(2))
|
||||
True
|
||||
"""
|
||||
return self < other
|
||||
|
||||
|
||||
def __le__(self, other):
|
||||
""" Return True if `self <= other`.
|
||||
|
||||
>>> Numeral(Sqrt(2)) <= 2
|
||||
True
|
||||
>>> Numeral(Sqrt(3)) <= Numeral(Sqrt(2))
|
||||
False
|
||||
>>> Numeral(Sqrt(2)) <= Numeral(Sqrt(2))
|
||||
True
|
||||
"""
|
||||
return Z3_algebraic_le(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast)
|
||||
|
||||
def __rle__(self, other):
|
||||
""" Return True if `other <= self`.
|
||||
|
||||
>>> 2 <= Numeral(Sqrt(2))
|
||||
False
|
||||
"""
|
||||
return self >= other
|
||||
|
||||
def __ge__(self, other):
|
||||
""" Return True if `self >= other`.
|
||||
|
||||
>>> Numeral(Sqrt(2)) >= 2
|
||||
False
|
||||
>>> Numeral(Sqrt(3)) >= Numeral(Sqrt(2))
|
||||
True
|
||||
>>> Numeral(Sqrt(2)) >= Numeral(Sqrt(2))
|
||||
True
|
||||
"""
|
||||
return Z3_algebraic_ge(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast)
|
||||
|
||||
def __rge__(self, other):
|
||||
""" Return True if `other >= self`.
|
||||
|
||||
>>> 2 >= Numeral(Sqrt(2))
|
||||
True
|
||||
"""
|
||||
return self <= other
|
||||
|
||||
def __eq__(self, other):
|
||||
""" Return True if `self == other`.
|
||||
|
||||
>>> Numeral(Sqrt(2)) == 2
|
||||
False
|
||||
>>> Numeral(Sqrt(3)) == Numeral(Sqrt(2))
|
||||
False
|
||||
>>> Numeral(Sqrt(2)) == Numeral(Sqrt(2))
|
||||
True
|
||||
"""
|
||||
return Z3_algebraic_eq(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast)
|
||||
|
||||
def __ne__(self, other):
|
||||
""" Return True if `self != other`.
|
||||
|
||||
>>> Numeral(Sqrt(2)) != 2
|
||||
True
|
||||
>>> Numeral(Sqrt(3)) != Numeral(Sqrt(2))
|
||||
True
|
||||
>>> Numeral(Sqrt(2)) != Numeral(Sqrt(2))
|
||||
False
|
||||
"""
|
||||
return Z3_algebraic_neq(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast)
|
||||
|
||||
def __str__(self):
|
||||
if Z3_is_numeral_ast(self.ctx_ref(), self.ast):
|
||||
return str(RatNumRef(self.ast, self.ctx))
|
||||
else:
|
||||
return str(AlgebraicNumRef(self.ast, self.ctx))
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
def sexpr(self):
|
||||
return Z3_ast_to_string(self.ctx_ref(), self.as_ast())
|
||||
|
||||
def as_ast(self):
|
||||
return self.ast
|
||||
|
||||
def ctx_ref(self):
|
||||
return self.ctx.ref()
|
||||
|
||||
def eval_sign_at(p, vs):
|
||||
"""
|
||||
Evaluate the sign of the polynomial `p` at `vs`. `p` is a Z3
|
||||
Expression containing arithmetic operators: +, -, *, ^k where k is
|
||||
an integer; and free variables x that is_var(x) is True. Moreover,
|
||||
all variables must be real.
|
||||
|
||||
The result is 1 if the polynomial is positive at the given point,
|
||||
-1 if negative, and 0 if zero.
|
||||
|
||||
>>> x0, x1, x2 = RealVarVector(3)
|
||||
>>> eval_sign_at(x0**2 + x1*x2 + 1, (Numeral(0), Numeral(1), Numeral(2)))
|
||||
1
|
||||
>>> eval_sign_at(x0**2 - 2, [ Numeral(Sqrt(2)) ])
|
||||
0
|
||||
>>> eval_sign_at((x0 + x1)*(x0 + x2), (Numeral(0), Numeral(Sqrt(2)), Numeral(Sqrt(3))))
|
||||
1
|
||||
"""
|
||||
num = len(vs)
|
||||
_vs = (Ast * num)()
|
||||
for i in range(num):
|
||||
_vs[i] = vs[i].ast
|
||||
return Z3_algebraic_eval(p.ctx_ref(), p.as_ast(), num, _vs)
|
||||
|
||||
def isolate_roots(p, vs=[]):
|
||||
"""
|
||||
Given a multivariate polynomial p(x_0, ..., x_{n-1}, x_n), returns the
|
||||
roots of the univariate polynomial p(vs[0], ..., vs[len(vs)-1], x_n).
|
||||
|
||||
Remarks:
|
||||
* p is a Z3 expression that contains only arithmetic terms and free variables.
|
||||
* forall i in [0, n) vs is a numeral.
|
||||
|
||||
The result is a list of numerals
|
||||
|
||||
>>> x0 = RealVar(0)
|
||||
>>> isolate_roots(x0**5 - x0 - 1)
|
||||
[1.1673039782?]
|
||||
>>> x1 = RealVar(1)
|
||||
>>> isolate_roots(x0**2 - x1**4 - 1, [ Numeral(Sqrt(3)) ])
|
||||
[-1.1892071150?, 1.1892071150?]
|
||||
>>> x2 = RealVar(2)
|
||||
>>> isolate_roots(x2**2 + x0 - x1, [ Numeral(Sqrt(3)), Numeral(Sqrt(2)) ])
|
||||
[]
|
||||
"""
|
||||
num = len(vs)
|
||||
_vs = (Ast * num)()
|
||||
for i in range(num):
|
||||
_vs[i] = vs[i].ast
|
||||
_roots = AstVector(Z3_algebraic_roots(p.ctx_ref(), p.as_ast(), num, _vs), p.ctx)
|
||||
return [ Numeral(r) for r in _roots ]
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
if doctest.testmod().failed:
|
||||
exit(1)
|
||||
|
37
src/api/python/z3poly.py
Normal file
37
src/api/python/z3poly.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
############################################
|
||||
# Copyright (c) 2012 Microsoft Corporation
|
||||
#
|
||||
# Z3 Python interface for Z3 polynomials
|
||||
#
|
||||
# Author: Leonardo de Moura (leonardo)
|
||||
############################################
|
||||
from z3 import *
|
||||
|
||||
def subresultants(p, q, x):
|
||||
"""
|
||||
Return the non-constant subresultants of 'p' and 'q' with respect to the "variable" 'x'.
|
||||
|
||||
'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.
|
||||
Example: f(a) is a considered to be a variable b in the polynomial
|
||||
|
||||
f(a)*f(a) + 2*f(a) + 1
|
||||
|
||||
>>> x, y = Reals('x y')
|
||||
>>> subresultants(2*x + y, 3*x - 2*y + 2, x)
|
||||
[-7*y + 4]
|
||||
>>> r = subresultants(3*y*x**2 + y**3 + 1, 2*x**3 + y + 3, x)
|
||||
>>> r[0]
|
||||
4*y**9 + 12*y**6 + 27*y**5 + 162*y**4 + 255*y**3 + 4
|
||||
>>> r[1]
|
||||
-6*y**4 + -6*y
|
||||
"""
|
||||
return AstVector(Z3_polynomial_subresultants(p.ctx_ref(), p.as_ast(), q.as_ast(), x.as_ast()), p.ctx)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
if doctest.testmod().failed:
|
||||
exit(1)
|
||||
|
||||
|
||||
|
|
@ -24,6 +24,8 @@ Notes:
|
|||
#include<stdio.h>
|
||||
#include"z3_macros.h"
|
||||
#include"z3_api.h"
|
||||
#include"z3_algebraic.h"
|
||||
#include"z3_polynomial.h"
|
||||
|
||||
#undef __in
|
||||
#undef __out
|
||||
|
|
226
src/api/z3_algebraic.h
Normal file
226
src/api/z3_algebraic.h
Normal file
|
@ -0,0 +1,226 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
z3_algebraic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Additional APIs for handling Z3 algebraic numbers encoded as
|
||||
Z3_ASTs
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-12-07
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _Z3_ALGEBRAIC_H_
|
||||
#define _Z3_ALGEBRAIC_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/**
|
||||
\brief Return Z3_TRUE if \c can be used as value in the Z3 real algebraic
|
||||
number package.
|
||||
|
||||
def_API('Z3_algebraic_is_value', BOOL, (_in(CONTEXT), _in(AST)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_algebraic_is_value(__in Z3_context c, __in Z3_ast a);
|
||||
|
||||
/**
|
||||
\brief Return the Z3_TRUE if \c a is positive, and Z3_FALSE otherwise.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
|
||||
def_API('Z3_algebraic_is_pos', BOOL, (_in(CONTEXT), _in(AST)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_algebraic_is_pos(__in Z3_context c, __in Z3_ast a);
|
||||
|
||||
/**
|
||||
\brief Return the Z3_TRUE if \c a is negative, and Z3_FALSE otherwise.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
|
||||
def_API('Z3_algebraic_is_neg', BOOL, (_in(CONTEXT), _in(AST)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_algebraic_is_neg(__in Z3_context c, __in Z3_ast a);
|
||||
|
||||
/**
|
||||
\brief Return the Z3_TRUE if \c a is zero, and Z3_FALSE otherwise.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
|
||||
def_API('Z3_algebraic_is_zero', BOOL, (_in(CONTEXT), _in(AST)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_algebraic_is_zero(__in Z3_context c, __in Z3_ast a);
|
||||
|
||||
/**
|
||||
\brief Return 1 if \c a is positive, 0 if \c a is zero, and -1 if \c a is negative.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
|
||||
def_API('Z3_algebraic_sign', INT, (_in(CONTEXT), _in(AST)))
|
||||
*/
|
||||
int Z3_API Z3_algebraic_sign(__in Z3_context c, __in Z3_ast a);
|
||||
|
||||
/**
|
||||
\brief Return the value a + b.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre Z3_algebraic_is_value(c, b)
|
||||
\post Z3_algebraic_is_value(c, result)
|
||||
|
||||
def_API('Z3_algebraic_add', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_algebraic_add(__in Z3_context c, __in Z3_ast a, __in Z3_ast b);
|
||||
|
||||
/**
|
||||
\brief Return the value a - b.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre Z3_algebraic_is_value(c, b)
|
||||
\post Z3_algebraic_is_value(c, result)
|
||||
|
||||
def_API('Z3_algebraic_sub', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_algebraic_sub(__in Z3_context c, __in Z3_ast a, __in Z3_ast b);
|
||||
|
||||
/**
|
||||
\brief Return the value a * b.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre Z3_algebraic_is_value(c, b)
|
||||
\post Z3_algebraic_is_value(c, result)
|
||||
|
||||
def_API('Z3_algebraic_mul', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_algebraic_mul(__in Z3_context c, __in Z3_ast a, __in Z3_ast b);
|
||||
|
||||
/**
|
||||
\brief Return the value a / b.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre Z3_algebraic_is_value(c, b)
|
||||
\pre !Z3_algebraic_is_zero(c, b)
|
||||
\post Z3_algebraic_is_value(c, result)
|
||||
|
||||
def_API('Z3_algebraic_div', AST, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_algebraic_div(__in Z3_context c, __in Z3_ast a, __in Z3_ast b);
|
||||
|
||||
/**
|
||||
\brief Return the a^(1/k)
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre k is even => !Z3_algebraic_is_neg(c, a)
|
||||
\post Z3_algebraic_is_value(c, result)
|
||||
|
||||
def_API('Z3_algebraic_root', AST, (_in(CONTEXT), _in(AST), _in(UINT)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_algebraic_root(__in Z3_context c, __in Z3_ast a, __in unsigned k);
|
||||
|
||||
/**
|
||||
\brief Return the a^k
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\post Z3_algebraic_is_value(c, result)
|
||||
|
||||
def_API('Z3_algebraic_power', AST, (_in(CONTEXT), _in(AST), _in(UINT)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_algebraic_power(__in Z3_context c, __in Z3_ast a, __in unsigned k);
|
||||
|
||||
/**
|
||||
\brief Return Z3_TRUE if a < b, and Z3_FALSE otherwise.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre Z3_algebraic_is_value(c, b)
|
||||
|
||||
def_API('Z3_algebraic_lt', BOOL, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_algebraic_lt(__in Z3_context c, __in Z3_ast a, __in Z3_ast b);
|
||||
|
||||
/**
|
||||
\brief Return Z3_TRUE if a > b, and Z3_FALSE otherwise.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre Z3_algebraic_is_value(c, b)
|
||||
|
||||
def_API('Z3_algebraic_gt', BOOL, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_algebraic_gt(__in Z3_context c, __in Z3_ast a, __in Z3_ast b);
|
||||
|
||||
/**
|
||||
\brief Return Z3_TRUE if a <= b, and Z3_FALSE otherwise.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre Z3_algebraic_is_value(c, b)
|
||||
|
||||
def_API('Z3_algebraic_le', BOOL, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_algebraic_le(__in Z3_context c, __in Z3_ast a, __in Z3_ast b);
|
||||
|
||||
/**
|
||||
\brief Return Z3_TRUE if a >= b, and Z3_FALSE otherwise.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre Z3_algebraic_is_value(c, b)
|
||||
|
||||
def_API('Z3_algebraic_ge', BOOL, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_algebraic_ge(__in Z3_context c, __in Z3_ast a, __in Z3_ast b);
|
||||
|
||||
/**
|
||||
\brief Return Z3_TRUE if a == b, and Z3_FALSE otherwise.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre Z3_algebraic_is_value(c, b)
|
||||
|
||||
def_API('Z3_algebraic_eq', BOOL, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_algebraic_eq(__in Z3_context c, __in Z3_ast a, __in Z3_ast b);
|
||||
|
||||
/**
|
||||
\brief Return Z3_TRUE if a != b, and Z3_FALSE otherwise.
|
||||
|
||||
\pre Z3_algebraic_is_value(c, a)
|
||||
\pre Z3_algebraic_is_value(c, b)
|
||||
|
||||
def_API('Z3_algebraic_neq', BOOL, (_in(CONTEXT), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_algebraic_neq(__in Z3_context c, __in Z3_ast a, __in Z3_ast b);
|
||||
|
||||
/**
|
||||
\brief Given a multivariate polynomial p(x_0, ..., x_{n-1}, x_n), returns the
|
||||
roots of the univariate polynomial p(a[0], ..., a[n-1], x_n).
|
||||
|
||||
\pre p is a Z3 expression that contains only arithmetic terms and free variables.
|
||||
\pre forall i in [0, n) Z3_algebraic_is_value(c, a[i])
|
||||
\post forall r in result Z3_algebraic_is_value(c, result)
|
||||
|
||||
def_API('Z3_algebraic_roots', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST)))
|
||||
*/
|
||||
Z3_ast_vector Z3_API Z3_algebraic_roots(__in Z3_context c, __in Z3_ast p, __in unsigned n, __in Z3_ast a[]);
|
||||
|
||||
/**
|
||||
\brief Given a multivariate polynomial p(x_0, ..., x_{n-1}), return the
|
||||
sign of p(a[0], ..., a[n-1]).
|
||||
|
||||
\pre p is a Z3 expression that contains only arithmetic terms and free variables.
|
||||
\pre forall i in [0, n) Z3_algebraic_is_value(c, a[i])
|
||||
|
||||
def_API('Z3_algebraic_eval', INT, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST)))
|
||||
*/
|
||||
int Z3_API Z3_algebraic_eval(__in Z3_context c, __in Z3_ast p, __in unsigned n, __in Z3_ast a[]);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
45
src/api/z3_polynomial.h
Normal file
45
src/api/z3_polynomial.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
z3_polynomial.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Additional APIs for polynomials.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-12-09
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _Z3_POLYNOMIAL_H_
|
||||
#define _Z3_POLYNOMIAL_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/**
|
||||
\brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x.
|
||||
|
||||
\pre \c p, \c q and \c x are Z3 expressions where \c p and \c q are arithmetic terms.
|
||||
Note that, any subterm that cannot be viewed as a polynomial is assumed to be a variable.
|
||||
Example: f(a) is a considered to be a variable in the polynomial
|
||||
|
||||
f(a)*f(a) + 2*f(a) + 1
|
||||
|
||||
def_API('Z3_polynomial_subresultants', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast_vector Z3_API Z3_polynomial_subresultants(__in Z3_context c, __in Z3_ast p, __in Z3_ast q, __in Z3_ast x);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue