z3-solver
Version:
This project provides high-level and low-level TypeScript bindings for the [Z3 theorem prover](https://github.com/Z3Prover/z3). It is available on npm as [z3-solver](https://www.npmjs.com/package/z3-solver).
2,303 lines • 90 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createApi = createApi;
// TODO(ritave): Add typing for Context Options
// https://github.com/Z3Prover/z3/pull/6048#discussion_r883391669
// TODO(ritave): Add an error handler
// TODO(ritave): Add support for building faster floats without support for Safari
// TODO(ritave): Use Z3_DECLARE_CLOSURE macro to generate code https://github.com/Z3Prover/z3/pull/6048#discussion_r884155462
// TODO(ritave): Add pretty printing
// TODO(ritave): Make Z3 multi-threaded
// TODO(ritave): If a test times out, jest kills it, and the global state of Z3 is left in an unexpected state.
// This occurs specifically during longer check(). Afterwards, all next tests will fail to run
// thinking the previous call was not finished. Find a way to stop execution and clean up the global state
const async_mutex_1 = require("async-mutex");
const low_level_1 = require("../low-level");
const types_1 = require("./types");
const utils_1 = require("./utils");
const FALLBACK_PRECISION = 17;
const asyncMutex = new async_mutex_1.Mutex();
function isCoercibleRational(obj) {
// prettier-ignore
const r = ((obj !== null &&
(typeof obj === 'object' || typeof obj === 'function')) &&
(obj.numerator !== null &&
(typeof obj.numerator === 'number' || typeof obj.numerator === 'bigint')) &&
(obj.denominator !== null &&
(typeof obj.denominator === 'number' || typeof obj.denominator === 'bigint')));
r &&
(0, utils_1.assert)((typeof obj.numerator !== 'number' || Number.isSafeInteger(obj.numerator)) &&
(typeof obj.denominator !== 'number' || Number.isSafeInteger(obj.denominator)), 'Fraction numerator and denominator must be integers');
return r;
}
function createApi(Z3) {
// TODO(ritave): Create a custom linting rule that checks if the provided callbacks to cleanup
// Don't capture `this`
const cleanup = new FinalizationRegistry(callback => callback());
function enableTrace(tag) {
Z3.enable_trace(tag);
}
function disableTrace(tag) {
Z3.disable_trace(tag);
}
function getVersion() {
return Z3.get_version();
}
function getVersionString() {
const { major, minor, build_number } = Z3.get_version();
return `${major}.${minor}.${build_number}`;
}
function getFullVersion() {
return Z3.get_full_version();
}
function openLog(filename) {
return Z3.open_log(filename);
}
function appendLog(s) {
Z3.append_log(s);
}
function setParam(key, value) {
if (typeof key === 'string') {
Z3.global_param_set(key, value.toString());
}
else {
(0, utils_1.assert)(value === undefined, "Can't provide a Record and second parameter to set_param at the same time");
Object.entries(key).forEach(([key, value]) => setParam(key, value));
}
}
function resetParams() {
Z3.global_param_reset_all();
}
function getParam(name) {
return Z3.global_param_get(name);
}
function createContext(name, options) {
const cfg = Z3.mk_config();
if (options != null) {
Object.entries(options).forEach(([key, value]) => check(Z3.set_param_value(cfg, key, value.toString())));
}
const contextPtr = Z3.mk_context_rc(cfg);
Z3.set_ast_print_mode(contextPtr, low_level_1.Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT);
Z3.del_config(cfg);
function _assertContext(...ctxs) {
ctxs.forEach(other => (0, utils_1.assert)('ctx' in other ? ctx === other.ctx : ctx === other, 'Context mismatch'));
}
function _assertPtr(ptr) {
if (ptr == null)
throw new TypeError('Expected non-null pointer');
}
// call this after every nontrivial call to the underlying API
function throwIfError() {
if (Z3.get_error_code(contextPtr) !== low_level_1.Z3_error_code.Z3_OK) {
throw new Error(Z3.get_error_msg(ctx.ptr, Z3.get_error_code(ctx.ptr)));
}
}
function check(val) {
throwIfError();
return val;
}
/////////////
// Private //
/////////////
function _toSymbol(s) {
if (typeof s === 'number') {
return check(Z3.mk_int_symbol(contextPtr, s));
}
else {
return check(Z3.mk_string_symbol(contextPtr, s));
}
}
function _fromSymbol(sym) {
const kind = check(Z3.get_symbol_kind(contextPtr, sym));
switch (kind) {
case low_level_1.Z3_symbol_kind.Z3_INT_SYMBOL:
return Z3.get_symbol_int(contextPtr, sym);
case low_level_1.Z3_symbol_kind.Z3_STRING_SYMBOL:
return Z3.get_symbol_string(contextPtr, sym);
default:
(0, utils_1.assertExhaustive)(kind);
}
}
function _toParams(key, value) {
const params = Z3.mk_params(contextPtr);
Z3.params_inc_ref(contextPtr, params);
// If value is a boolean
if (typeof value === 'boolean') {
Z3.params_set_bool(contextPtr, params, _toSymbol(key), value);
}
else if (typeof value === 'number') {
// If value is a uint
if (Number.isInteger(value)) {
check(Z3.params_set_uint(contextPtr, params, _toSymbol(key), value));
}
else {
// If value is a double
check(Z3.params_set_double(contextPtr, params, _toSymbol(key), value));
}
}
else if (typeof value === 'string') {
check(Z3.params_set_symbol(contextPtr, params, _toSymbol(key), _toSymbol(value)));
}
return params;
}
function _toAst(ast) {
switch (check(Z3.get_ast_kind(contextPtr, ast))) {
case low_level_1.Z3_ast_kind.Z3_SORT_AST:
return _toSort(ast);
case low_level_1.Z3_ast_kind.Z3_FUNC_DECL_AST:
return new FuncDeclImpl(ast);
default:
return _toExpr(ast);
}
}
function _toSort(ast) {
switch (check(Z3.get_sort_kind(contextPtr, ast))) {
case low_level_1.Z3_sort_kind.Z3_BOOL_SORT:
return new BoolSortImpl(ast);
case low_level_1.Z3_sort_kind.Z3_INT_SORT:
case low_level_1.Z3_sort_kind.Z3_REAL_SORT:
return new ArithSortImpl(ast);
case low_level_1.Z3_sort_kind.Z3_BV_SORT:
return new BitVecSortImpl(ast);
case low_level_1.Z3_sort_kind.Z3_ARRAY_SORT:
return new ArraySortImpl(ast);
default:
return new SortImpl(ast);
}
}
function _toExpr(ast) {
const kind = check(Z3.get_ast_kind(contextPtr, ast));
if (kind === low_level_1.Z3_ast_kind.Z3_QUANTIFIER_AST) {
if (Z3.is_lambda(contextPtr, ast)) {
return new LambdaImpl(ast);
}
return new NonLambdaQuantifierImpl(ast);
}
const sortKind = check(Z3.get_sort_kind(contextPtr, Z3.get_sort(contextPtr, ast)));
switch (sortKind) {
case low_level_1.Z3_sort_kind.Z3_BOOL_SORT:
return new BoolImpl(ast);
case low_level_1.Z3_sort_kind.Z3_INT_SORT:
if (kind === low_level_1.Z3_ast_kind.Z3_NUMERAL_AST) {
return new IntNumImpl(ast);
}
return new ArithImpl(ast);
case low_level_1.Z3_sort_kind.Z3_REAL_SORT:
if (kind === low_level_1.Z3_ast_kind.Z3_NUMERAL_AST) {
return new RatNumImpl(ast);
}
return new ArithImpl(ast);
case low_level_1.Z3_sort_kind.Z3_BV_SORT:
if (kind === low_level_1.Z3_ast_kind.Z3_NUMERAL_AST) {
return new BitVecNumImpl(ast);
}
return new BitVecImpl(ast);
case low_level_1.Z3_sort_kind.Z3_ARRAY_SORT:
return new ArrayImpl(ast);
default:
return new ExprImpl(ast);
}
}
function _flattenArgs(args) {
const result = [];
for (const arg of args) {
if (isAstVector(arg)) {
result.push(...arg.values());
}
else {
result.push(arg);
}
}
return result;
}
function _toProbe(p) {
if (isProbe(p)) {
return p;
}
return new ProbeImpl(p);
}
function _probeNary(f, args) {
(0, utils_1.assert)(args.length > 0, 'At least one argument expected');
let r = _toProbe(args[0]);
for (let i = 1; i < args.length; i++) {
r = new ProbeImpl(check(f(contextPtr, r.ptr, _toProbe(args[i]).ptr)));
}
return r;
}
///////////////
// Functions //
///////////////
function interrupt() {
check(Z3.interrupt(contextPtr));
}
function isModel(obj) {
const r = obj instanceof ModelImpl;
r && _assertContext(obj);
return r;
}
function isAst(obj) {
const r = obj instanceof AstImpl;
r && _assertContext(obj);
return r;
}
function isSort(obj) {
const r = obj instanceof SortImpl;
r && _assertContext(obj);
return r;
}
function isFuncDecl(obj) {
const r = obj instanceof FuncDeclImpl;
r && _assertContext(obj);
return r;
}
function isFuncInterp(obj) {
const r = obj instanceof FuncInterpImpl;
r && _assertContext(obj);
return r;
}
function isApp(obj) {
if (!isExpr(obj)) {
return false;
}
const kind = check(Z3.get_ast_kind(contextPtr, obj.ast));
return kind === low_level_1.Z3_ast_kind.Z3_NUMERAL_AST || kind === low_level_1.Z3_ast_kind.Z3_APP_AST;
}
function isConst(obj) {
return isExpr(obj) && isApp(obj) && obj.numArgs() === 0;
}
function isExpr(obj) {
const r = obj instanceof ExprImpl;
r && _assertContext(obj);
return r;
}
function isVar(obj) {
return isExpr(obj) && check(Z3.get_ast_kind(contextPtr, obj.ast)) === low_level_1.Z3_ast_kind.Z3_VAR_AST;
}
function isAppOf(obj, kind) {
return isExpr(obj) && isApp(obj) && obj.decl().kind() === kind;
}
function isBool(obj) {
const r = obj instanceof ExprImpl && obj.sort.kind() === low_level_1.Z3_sort_kind.Z3_BOOL_SORT;
r && _assertContext(obj);
return r;
}
function isTrue(obj) {
return isAppOf(obj, low_level_1.Z3_decl_kind.Z3_OP_TRUE);
}
function isFalse(obj) {
return isAppOf(obj, low_level_1.Z3_decl_kind.Z3_OP_FALSE);
}
function isAnd(obj) {
return isAppOf(obj, low_level_1.Z3_decl_kind.Z3_OP_AND);
}
function isOr(obj) {
return isAppOf(obj, low_level_1.Z3_decl_kind.Z3_OP_OR);
}
function isImplies(obj) {
return isAppOf(obj, low_level_1.Z3_decl_kind.Z3_OP_IMPLIES);
}
function isNot(obj) {
return isAppOf(obj, low_level_1.Z3_decl_kind.Z3_OP_NOT);
}
function isEq(obj) {
return isAppOf(obj, low_level_1.Z3_decl_kind.Z3_OP_EQ);
}
function isDistinct(obj) {
return isAppOf(obj, low_level_1.Z3_decl_kind.Z3_OP_DISTINCT);
}
function isQuantifier(obj) {
const r = obj instanceof QuantifierImpl;
r && _assertContext(obj);
return r;
}
function isArith(obj) {
const r = obj instanceof ArithImpl;
r && _assertContext(obj);
return r;
}
function isArithSort(obj) {
const r = obj instanceof ArithSortImpl;
r && _assertContext(obj);
return r;
}
function isInt(obj) {
return isArith(obj) && isIntSort(obj.sort);
}
function isIntVal(obj) {
const r = obj instanceof IntNumImpl;
r && _assertContext(obj);
return r;
}
function isIntSort(obj) {
return isSort(obj) && obj.kind() === low_level_1.Z3_sort_kind.Z3_INT_SORT;
}
function isReal(obj) {
return isArith(obj) && isRealSort(obj.sort);
}
function isRealVal(obj) {
const r = obj instanceof RatNumImpl;
r && _assertContext(obj);
return r;
}
function isRealSort(obj) {
return isSort(obj) && obj.kind() === low_level_1.Z3_sort_kind.Z3_REAL_SORT;
}
function isBitVecSort(obj) {
const r = obj instanceof BitVecSortImpl;
r && _assertContext(obj);
return r;
}
function isBitVec(obj) {
const r = obj instanceof BitVecImpl;
r && _assertContext(obj);
return r;
}
function isBitVecVal(obj) {
const r = obj instanceof BitVecNumImpl;
r && _assertContext(obj);
return r;
}
function isArraySort(obj) {
const r = obj instanceof ArraySortImpl;
r && _assertContext(obj);
return r;
}
function isArray(obj) {
const r = obj instanceof ArrayImpl;
r && _assertContext(obj);
return r;
}
function isConstArray(obj) {
return isAppOf(obj, low_level_1.Z3_decl_kind.Z3_OP_CONST_ARRAY);
}
function isProbe(obj) {
const r = obj instanceof ProbeImpl;
r && _assertContext(obj);
return r;
}
function isTactic(obj) {
const r = obj instanceof TacticImpl;
r && _assertContext(obj);
return r;
}
function isAstVector(obj) {
const r = obj instanceof AstVectorImpl;
r && _assertContext(obj);
return r;
}
function eqIdentity(a, b) {
return a.eqIdentity(b);
}
function getVarIndex(obj) {
(0, utils_1.assert)(isVar(obj), 'Z3 bound variable expected');
return Z3.get_index_value(contextPtr, obj.ast);
}
function from(value) {
if (typeof value === 'boolean') {
return Bool.val(value);
}
else if (typeof value === 'number') {
if (!Number.isFinite(value)) {
throw new Error(`cannot represent infinity/NaN (got ${value})`);
}
if (Math.floor(value) === value) {
return Int.val(value);
}
return Real.val(value);
}
else if (isCoercibleRational(value)) {
return Real.val(value);
}
else if (typeof value === 'bigint') {
return Int.val(value);
}
else if (isExpr(value)) {
return value;
}
(0, utils_1.assert)(false);
}
async function solve(...assertions) {
const solver = new ctx.Solver();
solver.add(...assertions);
const result = await solver.check();
if (result === 'sat') {
return solver.model();
}
return result;
}
///////////////////////////////
// expression simplification //
///////////////////////////////
async function simplify(e) {
const result = await Z3.simplify(contextPtr, e.ast);
return _toExpr(check(result));
}
/////////////
// Objects //
/////////////
const Sort = {
declare: (name) => new SortImpl(Z3.mk_uninterpreted_sort(contextPtr, _toSymbol(name))),
};
const Function = {
declare: (name, ...signature) => {
const arity = signature.length - 1;
const rng = signature[arity];
_assertContext(rng);
const dom = [];
for (let i = 0; i < arity; i++) {
_assertContext(signature[i]);
dom.push(signature[i].ptr);
}
return new FuncDeclImpl(Z3.mk_func_decl(contextPtr, _toSymbol(name), dom, rng.ptr));
},
fresh: (...signature) => {
const arity = signature.length - 1;
const rng = signature[arity];
_assertContext(rng);
const dom = [];
for (let i = 0; i < arity; i++) {
_assertContext(signature[i]);
dom.push(signature[i].ptr);
}
return new FuncDeclImpl(Z3.mk_fresh_func_decl(contextPtr, 'f', dom, rng.ptr));
},
};
const RecFunc = {
declare: (name, ...signature) => {
const arity = signature.length - 1;
const rng = signature[arity];
_assertContext(rng);
const dom = [];
for (let i = 0; i < arity; i++) {
_assertContext(signature[i]);
dom.push(signature[i].ptr);
}
return new FuncDeclImpl(Z3.mk_rec_func_decl(contextPtr, _toSymbol(name), dom, rng.ptr));
},
addDefinition: (f, args, body) => {
_assertContext(f, ...args, body);
check(Z3.add_rec_def(contextPtr, f.ptr, args.map(arg => arg.ast), body.ast));
},
};
const Bool = {
sort: () => new BoolSortImpl(Z3.mk_bool_sort(contextPtr)),
const: (name) => new BoolImpl(Z3.mk_const(contextPtr, _toSymbol(name), Bool.sort().ptr)),
consts: (names) => {
if (typeof names === 'string') {
names = names.split(' ');
}
return names.map(name => Bool.const(name));
},
vector: (prefix, count) => {
const result = [];
for (let i = 0; i < count; i++) {
result.push(Bool.const(`${prefix}__${i}`));
}
return result;
},
fresh: (prefix = 'b') => new BoolImpl(Z3.mk_fresh_const(contextPtr, prefix, Bool.sort().ptr)),
val: (value) => {
if (value) {
return new BoolImpl(Z3.mk_true(contextPtr));
}
return new BoolImpl(Z3.mk_false(contextPtr));
},
};
const Int = {
sort: () => new ArithSortImpl(Z3.mk_int_sort(contextPtr)),
const: (name) => new ArithImpl(Z3.mk_const(contextPtr, _toSymbol(name), Int.sort().ptr)),
consts: (names) => {
if (typeof names === 'string') {
names = names.split(' ');
}
return names.map(name => Int.const(name));
},
vector: (prefix, count) => {
const result = [];
for (let i = 0; i < count; i++) {
result.push(Int.const(`${prefix}__${i}`));
}
return result;
},
fresh: (prefix = 'x') => new ArithImpl(Z3.mk_fresh_const(contextPtr, prefix, Int.sort().ptr)),
val: (value) => {
(0, utils_1.assert)(typeof value === 'bigint' || typeof value === 'string' || Number.isSafeInteger(value));
return new IntNumImpl(check(Z3.mk_numeral(contextPtr, value.toString(), Int.sort().ptr)));
},
};
const Real = {
sort: () => new ArithSortImpl(Z3.mk_real_sort(contextPtr)),
const: (name) => new ArithImpl(check(Z3.mk_const(contextPtr, _toSymbol(name), Real.sort().ptr))),
consts: (names) => {
if (typeof names === 'string') {
names = names.split(' ');
}
return names.map(name => Real.const(name));
},
vector: (prefix, count) => {
const result = [];
for (let i = 0; i < count; i++) {
result.push(Real.const(`${prefix}__${i}`));
}
return result;
},
fresh: (prefix = 'b') => new ArithImpl(Z3.mk_fresh_const(contextPtr, prefix, Real.sort().ptr)),
val: (value) => {
if (isCoercibleRational(value)) {
value = `${value.numerator}/${value.denominator}`;
}
return new RatNumImpl(Z3.mk_numeral(contextPtr, value.toString(), Real.sort().ptr));
},
};
const BitVec = {
sort(bits) {
(0, utils_1.assert)(Number.isSafeInteger(bits), 'number of bits must be an integer');
return new BitVecSortImpl(Z3.mk_bv_sort(contextPtr, bits));
},
const(name, bits) {
return new BitVecImpl(check(Z3.mk_const(contextPtr, _toSymbol(name), isBitVecSort(bits) ? bits.ptr : BitVec.sort(bits).ptr)));
},
consts(names, bits) {
if (typeof names === 'string') {
names = names.split(' ');
}
return names.map(name => BitVec.const(name, bits));
},
val(value, bits) {
if (value === true) {
return BitVec.val(1, bits);
}
else if (value === false) {
return BitVec.val(0, bits);
}
return new BitVecNumImpl(check(Z3.mk_numeral(contextPtr, value.toString(), isBitVecSort(bits) ? bits.ptr : BitVec.sort(bits).ptr)));
},
};
const Array = {
sort(...sig) {
const arity = sig.length - 1;
const r = sig[arity];
const d = sig[0];
if (arity === 1) {
return new ArraySortImpl(Z3.mk_array_sort(contextPtr, d.ptr, r.ptr));
}
const dom = sig.slice(0, arity);
return new ArraySortImpl(Z3.mk_array_sort_n(contextPtr, dom.map(s => s.ptr), r.ptr));
},
const(name, ...sig) {
return new ArrayImpl(check(Z3.mk_const(contextPtr, _toSymbol(name), Array.sort(...sig).ptr)));
},
consts(names, ...sig) {
if (typeof names === 'string') {
names = names.split(' ');
}
return names.map(name => Array.const(name, ...sig));
},
K(domain, value) {
return new ArrayImpl(check(Z3.mk_const_array(contextPtr, domain.ptr, value.ptr)));
},
};
const Set = {
// reference: https://z3prover.github.io/api/html/namespacez3py.html#a545f894afeb24caa1b88b7f2a324ee7e
sort(sort) {
return Array.sort(sort, Bool.sort());
},
const(name, sort) {
return new SetImpl(check(Z3.mk_const(contextPtr, _toSymbol(name), Array.sort(sort, Bool.sort()).ptr)));
},
consts(names, sort) {
if (typeof names === 'string') {
names = names.split(' ');
}
return names.map(name => Set.const(name, sort));
},
empty(sort) {
return EmptySet(sort);
},
val(values, sort) {
var result = EmptySet(sort);
for (const value of values) {
result = SetAdd(result, value);
}
return result;
}
};
function If(condition, onTrue, onFalse) {
if (isProbe(condition) && isTactic(onTrue) && isTactic(onFalse)) {
return Cond(condition, onTrue, onFalse);
}
(0, utils_1.assert)(!isProbe(condition) && !isTactic(onTrue) && !isTactic(onFalse), 'Mixed expressions and goals');
if (typeof condition === 'boolean') {
condition = Bool.val(condition);
}
onTrue = from(onTrue);
onFalse = from(onFalse);
return _toExpr(check(Z3.mk_ite(contextPtr, condition.ptr, onTrue.ast, onFalse.ast)));
}
function Distinct(...exprs) {
(0, utils_1.assert)(exprs.length > 0, "Can't make Distinct ouf of nothing");
return new BoolImpl(check(Z3.mk_distinct(contextPtr, exprs.map(expr => {
expr = from(expr);
_assertContext(expr);
return expr.ast;
}))));
}
function Const(name, sort) {
_assertContext(sort);
return _toExpr(check(Z3.mk_const(contextPtr, _toSymbol(name), sort.ptr)));
}
function Consts(names, sort) {
_assertContext(sort);
if (typeof names === 'string') {
names = names.split(' ');
}
return names.map(name => Const(name, sort));
}
function FreshConst(sort, prefix = 'c') {
_assertContext(sort);
return _toExpr(Z3.mk_fresh_const(sort.ctx.ptr, prefix, sort.ptr));
}
function Var(idx, sort) {
_assertContext(sort);
return _toExpr(Z3.mk_bound(sort.ctx.ptr, idx, sort.ptr));
}
function Implies(a, b) {
a = from(a);
b = from(b);
_assertContext(a, b);
return new BoolImpl(check(Z3.mk_implies(contextPtr, a.ptr, b.ptr)));
}
function Iff(a, b) {
a = from(a);
b = from(b);
_assertContext(a, b);
return new BoolImpl(check(Z3.mk_iff(contextPtr, a.ptr, b.ptr)));
}
function Eq(a, b) {
a = from(a);
b = from(b);
_assertContext(a, b);
return a.eq(b);
}
function Xor(a, b) {
a = from(a);
b = from(b);
_assertContext(a, b);
return new BoolImpl(check(Z3.mk_xor(contextPtr, a.ptr, b.ptr)));
}
function Not(a) {
if (typeof a === 'boolean') {
a = from(a);
}
_assertContext(a);
if (isProbe(a)) {
return new ProbeImpl(check(Z3.probe_not(contextPtr, a.ptr)));
}
return new BoolImpl(check(Z3.mk_not(contextPtr, a.ptr)));
}
function And(...args) {
if (args.length == 1 && args[0] instanceof ctx.AstVector) {
args = [...args[0].values()];
(0, utils_1.assert)((0, utils_1.allSatisfy)(args, isBool) ?? true, 'AstVector containing not bools');
}
const allProbes = (0, utils_1.allSatisfy)(args, isProbe) ?? false;
if (allProbes) {
return _probeNary(Z3.probe_and, args);
}
else {
const castArgs = args.map(from);
_assertContext(...castArgs);
return new BoolImpl(check(Z3.mk_and(contextPtr, castArgs.map(arg => arg.ptr))));
}
}
function Or(...args) {
if (args.length == 1 && args[0] instanceof ctx.AstVector) {
args = [...args[0].values()];
(0, utils_1.assert)((0, utils_1.allSatisfy)(args, isBool) ?? true, 'AstVector containing not bools');
}
const allProbes = (0, utils_1.allSatisfy)(args, isProbe) ?? false;
if (allProbes) {
return _probeNary(Z3.probe_or, args);
}
else {
const castArgs = args.map(from);
_assertContext(...castArgs);
return new BoolImpl(check(Z3.mk_or(contextPtr, castArgs.map(arg => arg.ptr))));
}
}
function PbEq(args, coeffs, k) {
_assertContext(...args);
if (args.length !== coeffs.length) {
throw new Error('Number of arguments and coefficients must match');
}
return new BoolImpl(check(Z3.mk_pbeq(contextPtr, args.map(arg => arg.ast), coeffs, k)));
}
function PbGe(args, coeffs, k) {
_assertContext(...args);
if (args.length !== coeffs.length) {
throw new Error('Number of arguments and coefficients must match');
}
return new BoolImpl(check(Z3.mk_pbge(contextPtr, args.map(arg => arg.ast), coeffs, k)));
}
function PbLe(args, coeffs, k) {
_assertContext(...args);
if (args.length !== coeffs.length) {
throw new Error('Number of arguments and coefficients must match');
}
return new BoolImpl(check(Z3.mk_pble(contextPtr, args.map(arg => arg.ast), coeffs, k)));
}
function ForAll(quantifiers, body, weight = 1) {
// Verify all quantifiers are constants
if (!(0, utils_1.allSatisfy)(quantifiers, isConst)) {
throw new Error('Quantifier variables must be constants');
}
return new NonLambdaQuantifierImpl(check(Z3.mk_quantifier_const_ex(contextPtr, true, weight, _toSymbol(''), _toSymbol(''), quantifiers.map(q => q.ptr), // The earlier check verifies these are all apps
[], [], body.ptr)));
}
function Exists(quantifiers, body, weight = 1) {
// Verify all quantifiers are constants
if (!(0, utils_1.allSatisfy)(quantifiers, isConst)) {
throw new Error('Quantifier variables must be constants');
}
return new NonLambdaQuantifierImpl(check(Z3.mk_quantifier_const_ex(contextPtr, false, weight, _toSymbol(''), _toSymbol(''), quantifiers.map(q => q.ptr), // The earlier check verifies these are all apps
[], [], body.ptr)));
}
function Lambda(quantifiers, expr) {
// TODO(walden): For some reason LambdaImpl<DomainSort, RangeSort> leads to type issues
// and Typescript won't build. I'm not sure why since the types seem to all match
// up. For now, we just use any for the domain sort
// Verify all quantifiers are constants
if (!(0, utils_1.allSatisfy)(quantifiers, isConst)) {
throw new Error('Quantifier variables must be constants');
}
return new LambdaImpl(check(Z3.mk_lambda_const(contextPtr, quantifiers.map(q => q.ptr), expr.ptr)));
}
function ToReal(expr) {
expr = from(expr);
_assertContext(expr);
(0, utils_1.assert)(isInt(expr), 'Int expression expected');
return new ArithImpl(check(Z3.mk_int2real(contextPtr, expr.ast)));
}
function ToInt(expr) {
if (!isExpr(expr)) {
expr = Real.val(expr);
}
_assertContext(expr);
(0, utils_1.assert)(isReal(expr), 'Real expression expected');
return new ArithImpl(check(Z3.mk_real2int(contextPtr, expr.ast)));
}
function IsInt(expr) {
if (!isExpr(expr)) {
expr = Real.val(expr);
}
_assertContext(expr);
(0, utils_1.assert)(isReal(expr), 'Real expression expected');
return new BoolImpl(check(Z3.mk_is_int(contextPtr, expr.ast)));
}
function Sqrt(a) {
if (!isExpr(a)) {
a = Real.val(a);
}
return a.pow('1/2');
}
function Cbrt(a) {
if (!isExpr(a)) {
a = Real.val(a);
}
return a.pow('1/3');
}
function BV2Int(a, isSigned) {
_assertContext(a);
return new ArithImpl(check(Z3.mk_bv2int(contextPtr, a.ast, isSigned)));
}
function Int2BV(a, bits) {
if (isArith(a)) {
(0, utils_1.assert)(isInt(a), 'parameter must be an integer');
}
else {
(0, utils_1.assert)(typeof a !== 'number' || Number.isSafeInteger(a), 'parameter must not have decimal places');
a = Int.val(a);
}
return new BitVecImpl(check(Z3.mk_int2bv(contextPtr, bits, a.ast)));
}
function Concat(...bitvecs) {
_assertContext(...bitvecs);
return bitvecs.reduce((prev, curr) => new BitVecImpl(check(Z3.mk_concat(contextPtr, prev.ast, curr.ast))));
}
function Cond(probe, onTrue, onFalse) {
_assertContext(probe, onTrue, onFalse);
return new TacticImpl(check(Z3.tactic_cond(contextPtr, probe.ptr, onTrue.ptr, onFalse.ptr)));
}
function LT(a, b) {
return new BoolImpl(check(Z3.mk_lt(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function GT(a, b) {
return new BoolImpl(check(Z3.mk_gt(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function LE(a, b) {
return new BoolImpl(check(Z3.mk_le(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function GE(a, b) {
return new BoolImpl(check(Z3.mk_ge(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function ULT(a, b) {
return new BoolImpl(check(Z3.mk_bvult(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function UGT(a, b) {
return new BoolImpl(check(Z3.mk_bvugt(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function ULE(a, b) {
return new BoolImpl(check(Z3.mk_bvule(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function UGE(a, b) {
return new BoolImpl(check(Z3.mk_bvuge(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function SLT(a, b) {
return new BoolImpl(check(Z3.mk_bvslt(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function SGT(a, b) {
return new BoolImpl(check(Z3.mk_bvsgt(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function SLE(a, b) {
return new BoolImpl(check(Z3.mk_bvsle(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function SGE(a, b) {
return new BoolImpl(check(Z3.mk_bvsge(contextPtr, a.ast, a.sort.cast(b).ast)));
}
function Extract(hi, lo, val) {
return new BitVecImpl(check(Z3.mk_extract(contextPtr, hi, lo, val.ast)));
}
function Select(array, ...indices) {
const args = indices.map((arg, i) => array.domain_n(i).cast(arg));
if (args.length === 1) {
return _toExpr(check(Z3.mk_select(contextPtr, array.ast, args[0].ast)));
}
const _args = args.map(arg => arg.ast);
return _toExpr(check(Z3.mk_select_n(contextPtr, array.ast, _args)));
}
function Store(array, ...indicesAndValue) {
const args = indicesAndValue.map((arg, i) => {
if (i === indicesAndValue.length - 1) {
return array.range().cast(arg);
}
return array.domain_n(i).cast(arg);
});
if (args.length <= 1) {
throw new Error('Array store requires both index and value arguments');
}
if (args.length === 2) {
return _toExpr(check(Z3.mk_store(contextPtr, array.ast, args[0].ast, args[1].ast)));
}
const _idxs = args.slice(0, args.length - 1).map(arg => arg.ast);
return _toExpr(check(Z3.mk_store_n(contextPtr, array.ast, _idxs, args[args.length - 1].ast)));
}
function SetUnion(...args) {
return new SetImpl(check(Z3.mk_set_union(contextPtr, args.map(arg => arg.ast))));
}
function SetIntersect(...args) {
return new SetImpl(check(Z3.mk_set_intersect(contextPtr, args.map(arg => arg.ast))));
}
function SetDifference(a, b) {
return new SetImpl(check(Z3.mk_set_difference(contextPtr, a.ast, b.ast)));
}
function SetHasSize(set, size) {
const a = typeof size === 'object' ? Int.sort().cast(size) : Int.sort().cast(size);
return new BoolImpl(check(Z3.mk_set_has_size(contextPtr, set.ast, a.ast)));
}
function SetAdd(set, elem) {
const arg = set.elemSort().cast(elem);
return new SetImpl(check(Z3.mk_set_add(contextPtr, set.ast, arg.ast)));
}
function SetDel(set, elem) {
const arg = set.elemSort().cast(elem);
return new SetImpl(check(Z3.mk_set_del(contextPtr, set.ast, arg.ast)));
}
function SetComplement(set) {
return new SetImpl(check(Z3.mk_set_complement(contextPtr, set.ast)));
}
function EmptySet(sort) {
return new SetImpl(check(Z3.mk_empty_set(contextPtr, sort.ptr)));
}
function FullSet(sort) {
return new SetImpl(check(Z3.mk_full_set(contextPtr, sort.ptr)));
}
function isMember(elem, set) {
const arg = set.elemSort().cast(elem);
return new BoolImpl(check(Z3.mk_set_member(contextPtr, arg.ast, set.ast)));
}
function isSubset(a, b) {
return new BoolImpl(check(Z3.mk_set_subset(contextPtr, a.ast, b.ast)));
}
class AstImpl {
constructor(ptr) {
this.ptr = ptr;
this.ctx = ctx;
const myAst = this.ast;
Z3.inc_ref(contextPtr, myAst);
cleanup.register(this, () => Z3.dec_ref(contextPtr, myAst), this);
}
get ast() {
return this.ptr;
}
id() {
return Z3.get_ast_id(contextPtr, this.ast);
}
eqIdentity(other) {
_assertContext(other);
return check(Z3.is_eq_ast(contextPtr, this.ast, other.ast));
}
neqIdentity(other) {
_assertContext(other);
return !this.eqIdentity(other);
}
sexpr() {
return Z3.ast_to_string(contextPtr, this.ast);
}
hash() {
return Z3.get_ast_hash(contextPtr, this.ast);
}
toString() {
return this.sexpr();
}
}
class SolverImpl {
get ptr() {
_assertPtr(this._ptr);
return this._ptr;
}
constructor(ptr = Z3.mk_solver(contextPtr)) {
this.ctx = ctx;
let myPtr;
if (typeof ptr === 'string') {
myPtr = check(Z3.mk_solver_for_logic(contextPtr, _toSymbol(ptr)));
}
else {
myPtr = ptr;
}
this._ptr = myPtr;
Z3.solver_inc_ref(contextPtr, myPtr);
cleanup.register(this, () => Z3.solver_dec_ref(contextPtr, myPtr), this);
}
set(key, value) {
Z3.solver_set_params(contextPtr, this.ptr, _toParams(key, value));
}
push() {
Z3.solver_push(contextPtr, this.ptr);
}
pop(num = 1) {
Z3.solver_pop(contextPtr, this.ptr, num);
}
numScopes() {
return Z3.solver_get_num_scopes(contextPtr, this.ptr);
}
reset() {
Z3.solver_reset(contextPtr, this.ptr);
}
add(...exprs) {
_flattenArgs(exprs).forEach(expr => {
_assertContext(expr);
check(Z3.solver_assert(contextPtr, this.ptr, expr.ast));
});
}
addAndTrack(expr, constant) {
if (typeof constant === 'string') {
constant = Bool.const(constant);
}
(0, utils_1.assert)(isConst(constant), 'Provided expression that is not a constant to addAndTrack');
check(Z3.solver_assert_and_track(contextPtr, this.ptr, expr.ast, constant.ast));
}
assertions() {
return new AstVectorImpl(check(Z3.solver_get_assertions(contextPtr, this.ptr)));
}
async check(...exprs) {
const assumptions = _flattenArgs(exprs).map(expr => {
_assertContext(expr);
return expr.ast;
});
const result = await asyncMutex.runExclusive(() => check(Z3.solver_check_assumptions(contextPtr, this.ptr, assumptions)));
switch (result) {
case low_level_1.Z3_lbool.Z3_L_FALSE:
return 'unsat';
case low_level_1.Z3_lbool.Z3_L_TRUE:
return 'sat';
case low_level_1.Z3_lbool.Z3_L_UNDEF:
return 'unknown';
default:
(0, utils_1.assertExhaustive)(result);
}
}
model() {
return new ModelImpl(check(Z3.solver_get_model(contextPtr, this.ptr)));
}
toString() {
return check(Z3.solver_to_string(contextPtr, this.ptr));
}
fromString(s) {
Z3.solver_from_string(contextPtr, this.ptr, s);
throwIfError();
}
release() {
Z3.solver_dec_ref(contextPtr, this.ptr);
// Mark the ptr as null to prevent double free
this._ptr = null;
cleanup.unregister(this);
}
}
class OptimizeImpl {
get ptr() {
_assertPtr(this._ptr);
return this._ptr;
}
constructor(ptr = Z3.mk_optimize(contextPtr)) {
this.ctx = ctx;
let myPtr;
myPtr = ptr;
this._ptr = myPtr;
Z3.optimize_inc_ref(contextPtr, myPtr);
cleanup.register(this, () => Z3.optimize_dec_ref(contextPtr, myPtr), this);
}
set(key, value) {
Z3.optimize_set_params(contextPtr, this.ptr, _toParams(key, value));
}
push() {
Z3.optimize_push(contextPtr, this.ptr);
}
pop() {
Z3.optimize_pop(contextPtr, this.ptr);
}
add(...exprs) {
_flattenArgs(exprs).forEach(expr => {
_assertContext(expr);
check(Z3.optimize_assert(contextPtr, this.ptr, expr.ast));
});
}
addSoft(expr, weight, id = '') {
if (isCoercibleRational(weight)) {
weight = `${weight.numerator}/${weight.denominator}`;
}
check(Z3.optimize_assert_soft(contextPtr, this.ptr, expr.ast, weight.toString(), _toSymbol(id)));
}
addAndTrack(expr, constant) {
if (typeof constant === 'string') {
constant = Bool.const(constant);
}
(0, utils_1.assert)(isConst(constant), 'Provided expression that is not a constant to addAndTrack');
check(Z3.optimize_assert_and_track(contextPtr, this.ptr, expr.ast, constant.ast));
}
assertions() {
return new AstVectorImpl(check(Z3.optimize_get_assertions(contextPtr, this.ptr)));
}
maximize(expr) {
check(Z3.optimize_maximize(contextPtr, this.ptr, expr.ast));
}
minimize(expr) {
check(Z3.optimize_minimize(contextPtr, this.ptr, expr.ast));
}
async check(...exprs) {
const assumptions = _flattenArgs(exprs).map(expr => {
_assertContext(expr);
return expr.ast;
});
const result = await asyncMutex.runExclusive(() => check(Z3.optimize_check(contextPtr, this.ptr, assumptions)));
switch (result) {
case low_level_1.Z3_lbool.Z3_L_FALSE:
return 'unsat';
case low_level_1.Z3_lbool.Z3_L_TRUE:
return 'sat';
case low_level_1.Z3_lbool.Z3_L_UNDEF:
return 'unknown';
default:
(0, utils_1.assertExhaustive)(result);
}
}
model() {
return new ModelImpl(check(Z3.optimize_get_model(contextPtr, this.ptr)));
}
toString() {
return check(Z3.optimize_to_string(contextPtr, this.ptr));
}
fromString(s) {
Z3.optimize_from_string(contextPtr, this.ptr, s);
throwIfError();
}
release() {
Z3.optimize_dec_ref(contextPtr, this.ptr);
this._ptr = null;
cleanup.unregister(this);
}
}
class ModelImpl {
get ptr() {
_assertPtr(this._ptr);
return this._ptr;
}
constructor(ptr = Z3.mk_model(contextPtr)) {
this.ctx = ctx;
this._ptr = ptr;
Z3.model_inc_ref(contextPtr, ptr);
cleanup.register(this, () => Z3.model_dec_ref(contextPtr, ptr), this);
}
length() {
return Z3.model_get_num_consts(contextPtr, this.ptr) + Z3.model_get_num_funcs(contextPtr, this.ptr);
}
[Symbol.iterator]() {
return this.values();
}
*entries() {
const length = this.length();
for (let i = 0; i < length; i++) {
yield [i, this.get(i)];
}
}
*keys() {
for (const [key] of this.entries()) {
yield key;
}
}
*values() {
for (const [, value] of this.entries()) {
yield value;
}
}
decls() {
return [...this.values()];
}
sexpr() {
return check(Z3.model_to_string(contextPtr, this.ptr));
}
toString() {
return this.sexpr();
}
eval(expr, modelCompletion = false) {
_assertContext(expr);
const r = check(Z3.model_eval(contextPtr, this.ptr, expr.ast, modelCompletion));
if (r === null) {
throw new types_1.Z3Error('Failed to evaluate expression in the model');
}
return _toExpr(r);
}
get(i, to) {
(0, utils_1.assert)(to === undefined || typeof i === 'number');
if (typeof i === 'number') {
const length = this.length();
if (i >= length) {
throw new RangeError(`expected index ${i} to be less than length ${length}`);
}
if (to === undefined) {
const numConsts = check(Z3.model_get_num_consts(contextPtr, this.ptr));
if (i < numConsts) {
return new FuncDeclImpl(check(Z3.model_get_const_decl(contextPtr, this.ptr, i)));
}
else {
return new FuncDeclImpl(check(Z3.model_get_func_decl(contextPtr, this.ptr, i - numConsts)));
}
}
if (to < 0) {
to += length;
}
if (to >= length) {
throw new RangeError(`expected index ${to} to be less than length ${length}`);
}
const result = [];
for (let j = i; j < to; j++) {
result.push(this.get(j));
}
return result;
}
else if (isFuncDecl(i) || (isExpr(i) && isConst(i))) {
const result = this.getInterp(i);
(0, utils_1.assert)(result !== null);
return result;
}
else if (isSort(i)) {
return this.getUniverse(i);
}
(0, utils_1.assert)(false, 'Number, declaration or constant expected');
}
updateValue(decl, a) {
_assertContext(decl);
_assertContext(a);
if (isExpr(decl)) {
decl = decl.decl();
}
if (isFuncDecl(decl) && decl.arity() !== 0 && isFuncInterp(a)) {
const funcInterp = this.addFuncInterp(decl, a.elseValue());
for (let i = 0; i < a.numEntries(); i++) {
const e = a.entry(i);
const n = e.numArgs();
const args = global.Array(n).map((_, i) => e.argValue(i));
funcInterp.addEntry(args, e.value());
}
return;
}
if (!isFuncDecl(decl) || decl.arity() !== 0) {
throw new types_1.Z3Error('Expecting 0-ary function or constant expression');
}
if (!isAst(a)) {
throw new types_1.Z3Error('Only func declarations can be assigned to func interpretations');
}
check(Z3.add_const_interp(contextPtr, this.ptr, decl.ptr, a.ast));
}
addFuncInterp(decl, defaultValue) {
const fi = check(Z3.add_func_interp(contextPtr, this.ptr, decl.ptr, decl.range().cast(defaultValue).ptr));
return new FuncInterpImpl(fi);
}
getInterp(expr) {
(0, utils_1.assert)(isFuncDecl(expr) || isConst(expr), 'Declaration expected');
if (isConst(expr)) {
(0, utils_1.assert)(isExpr(expr));
expr = expr.decl();
}
(0, utils_1.assert)(isFuncDecl(expr));
if (expr.arity() === 0) {
const result = check(Z3.model_get_const_interp(contextPtr, this.ptr, expr.ptr));
if (result === null) {
return null;
}
return _toExpr(result);
}
else {
const interp = check(Z3.model_get_func_interp(contextPtr, this.ptr, expr.ptr));
if (interp === null) {
return null;
}
return new FuncInterpImpl(interp);
}
}
getUniverse(sort) {
_assertContext(sort);
return new AstVectorImpl(check(Z3.model_get_sort_universe(contextPtr, this.ptr, sort.ptr)));
}
release() {
Z3.model_dec_ref(contextPtr, this.ptr);
this._ptr = null;
cleanup.unregister(this);
}
}
class FuncEntryImpl {
constructor(ptr) {
this.ptr = ptr;
this.ctx = ctx;
Z3.func_entry_inc_ref(contextPtr, ptr);
cleanup.register(this, () => Z3.func_entry_dec_ref(contextPtr, ptr), this);
}
numArgs() {
return check(Z3.func_entry_get_num_args(contextPtr, this.ptr));
}
argValue(i) {
return _toExpr(check(Z3.func_entry_get_arg(contextPtr, this.ptr, i)));
}
value() {
return _toExpr(check(Z3.func_entry_get_value(contextPtr, this.ptr)));
}
}
class FuncInterpImpl {
constructor(ptr) {
this.ptr = ptr;
this.ctx = ctx;
Z3.func_interp_inc_ref(contextPtr, ptr);
cleanup.register(this, () => Z3.func_interp_dec_ref(contextPtr, ptr), this);
}
elseValue() {
return _toExpr(check(Z3.func_interp_get_else(contextPtr, this.ptr)));
}
numEntries() {
return check(Z3.func_interp_get_num_entries(contextPtr, this.ptr));
}
arity() {
return check(Z3.func_interp_get_arity(contextPtr, this.ptr));
}
entry(i) {
return new FuncEntryImpl(check(Z3.func_interp_get_entry(contextPtr, this.ptr, i)));
}
addEntry(args, value) {
const argsVec = new AstVectorImpl();
for (const arg of args) {
argsVec.push(arg);
}
_assertContext(argsVec);
_assertContext(value);
(0, utils_1.assert)(this.arity() === argsVec.length(), "Number of arguments in entry doesn't match function arity");
check(Z3.func_interp_add_entry(contextPtr, this.ptr, argsVec.ptr, value.ptr));
}
}
class SortImpl extends AstImpl {
get ast() {
return Z3.sort_to_ast(contextPtr, this.ptr);
}
kind() {
return Z3.get_sort_kind(contextPtr, this.ptr);
}
subsort(other) {
_assertContext(other);
return false;
}
cast(expr) {
_assertContext(expr);
(0, utils_1.assert)(expr.sort.eqIdentity(expr.sort), 'Sort mismatch');
return expr;
}
name() {
return _fromSymbol(Z3.get_sort_name(contextPtr, this.ptr));
}
eqIdentity(other) {
_assertContext(other);
return check(Z3.is_eq_sort(contextPtr, this.ptr, other.ptr));
}
neqIdentity(other) {
return !this.eqIdentity(other);
}
}
class FuncDeclImpl extends AstImpl {
get ast() {
return Z3.func_decl_to_ast(contextPtr, this.ptr);
}
name() {
return _fromSymbol(Z3.get_decl_name(contextPtr, this.ptr));
}
arity() {
return Z3.get_arity(contextPtr, this.ptr);
}
domain(i) {
(0, utils_1.assert)(i < this.arity(), 'Index out of bounds');
return _toSort(Z3.get_domain(contextPtr, this.ptr, i));
}
range() {
return _toSort(Z3.get_range(contextPtr, this.ptr));
}
kind() {
return Z3.get_decl_kind(contextPtr, this.ptr);
}
params() {
const n = Z3.get_decl_num_parameters(contextPtr, this.ptr);
const result = [];
for (let i = 0; i < n; i++) {
const kind = check(Z3.get_decl_parameter_kind(contextPtr, this.ptr, i));
switch (kind) {
case low_level_1.Z3_parameter_kind.Z3_PARAMETER_INT:
result.push(check(Z3.get_decl_int_parameter(contextPtr, this.ptr, i)));
break;
case low_level_1.Z3_parameter_kind.Z3_PARAMETER_DOUBLE:
result.push(check(Z3.get_decl_double_parameter(contextPtr, this.ptr, i)));
break;
case low_level_1.Z3_parameter_kind.Z3_PARAMETER_RATIONAL:
result.push(check(Z3.get_decl_rational_parameter(contextPtr, this.ptr, i)));
break;
case low_level_1.Z3_parameter_kind.Z3_PARAMETER_SYMBOL:
result.push(_fromSymbol(check(Z3.get_decl_symbol_parameter(contextPtr, this.ptr, i))));
break;
case low_level_1.Z3_parameter_kind.Z3_PARAMETER_SORT:
result.push(new SortImpl(check(Z3.get_decl_sort_parameter(contextPtr, this.ptr, i))));
break;
case low_level_1.Z3_parameter_kind.Z3_PARAMETER_AST:
result.push(new ExprImpl(check(Z3.get_decl_ast_parameter(contextPtr, this.ptr, i))));
break;
case low_level_1.Z3_parameter_kind.Z3_PARAMETER_FUNC_DECL:
result.push(new FuncDeclImpl(check(Z3.get_decl_func_decl_parameter(contextPtr, this.ptr, i))));
break;
case low_level_1.Z3_parameter_kind.Z3_PARAMETER_INTERNAL:
break;
case low_level_1.Z3_parameter_kind.Z3_PARAMETER_ZSTRING:
break;
default:
(0, utils_1.assertExhaustive)(kind);
}
}
return result;
}
call(...args) {
(0, utils_1.assert)(args.length === this.arity(), `Incorrect number of arguments to ${this}`);
return _toExpr(check(Z3.mk_app(contextPtr, this.ptr, args.map((arg, i) => {
return this.domain(i).cast(arg).ast;
}))));
}
}
class ExprImpl extends AstImpl {
get sort() {
return _toSort(Z3.get_sort(contextPtr, this.ast));
}
eq(other) {
return new BoolImpl(check(Z3.mk_eq(contextPtr, this.ast, from(other).ast)));
}
neq(other) {
return new BoolImpl(check(Z3.mk_distinct(contextPtr, [this, other].map(expr => from(expr).ast))));
}
name() {
return this.decl().name();
}
params() {
return this.decl().params();
}
decl() {
(0, utils_1.assert)(isApp(this), 'Z3 application expected');
return new FuncDeclImpl(check(Z3.get_app_decl(contextPtr, check(Z3.to_app(contextPtr, this.ast)))));
}
numArgs() {
(0, utils_1.assert)(isApp(this), 'Z3 applicaiton expected');
return check(Z3.get_app_num_args(contextPtr, check(Z3.to_app(contextPtr, this.ast))));
}
arg(i) {
(0, utils_1.assert)(isApp(this), 'Z3 applicaiton expected');
(0, utils_1.assert)(i < this.numArgs(), `Invalid argument index - expected ${i} to be less than ${this.numArgs()}`);
return _toExpr(check(Z3.get_app_arg(contextPtr, check(Z3.to_app(contextPtr, this.ast)), i)));
}
children() {
const num_args = this.numArgs();
if (isApp(this)) {
const result = [];
for (let i = 0; i < num_args; i++) {
result.push(this.arg(i));
}
return result;
}
return [];
}
}
class PatternImpl {
constructor(ptr) {
this.ptr = ptr;
this.ctx = ctx;
// TODO: implement rest of Pattern
}
}
class BoolSortImpl extends SortImpl {
cast(other) {
if (typeof other === 'boolean') {
other = Bool.val(other);
}
(0, utils_1.assert)(isExpr(other), 'true, false or Z3 Boolean expression expected.');
(0, utils_1.assert)(this.eqIdentity(other.sort), 'Value cannot be converted into a Z3 Boolean value');
return other;
}
subsort(other) {
_assertContext(other.ctx);
return other instanceof ArithSortImpl;
}
}
class BoolImpl extends ExprImpl {
not() {
return Not(this);
}
and(other) {
return And(this, other);
}
or(other) {
return Or(this, other);
}
xor(other) {
return Xor(this, other);
}
implies(other) {
return Implies(this, other);
}
iff(other) {
return Iff(this, other);
}
}
class ProbeImpl {
constructor(ptr) {
this.ptr = ptr;
this.ctx = ctx;
}
}
class TacticImpl {
constructor(tactic) {
this.ctx = ctx;
let myPtr;
if (typeof tactic === 'string') {
myPtr = check(Z3.mk_tactic(contextPtr, tactic));
}
else {
myPtr = tactic;
}
this.ptr = myPtr;
Z3.tactic_inc_ref(contextPtr, myPtr);
cleanup.register(this, () => Z3.tactic_dec_ref(contextPtr, myPtr), this);
}
}
class ArithSortImpl extends SortImpl {
cast(other) {
const sortTypeStr = isIntSort(this) ? 'IntSort' : 'RealSort';
if (isExpr(other)) {
const otherS = other.sort;
if (isArith(other)) {
if (this.eqIdentity(otherS)) {
return other;
}
else if (isIntSort(otherS) && isRealSort(this)) {
return ToReal(other);
}
(0, utils_1.assert)(false, "Can't cast Real to IntSort without loss");
}
else if (isBool(other)) {
if (isIntSort(this)) {
return If(other, 1, 0);
}
else {
return ToReal(If(other, 1, 0));
}
}
(0, utils_1.assert)(false, `Can't cast expression to ${sortTypeStr}`);
}
else {
if (typeof other !== 'boolean') {
if (isIntSort(this)) {
(0, utils_1.assert)(!isCoercibleRational(other), "Can't cast fraction to IntSort");
return Int.val(other);
}
return Real.val(other);
}
(0, utils_1.assert)(false, `Can't cast primitive to ${sortTypeStr}`);
}
}
}
function Sum(arg0, ...args) {
if (arg0 instanceof BitVecImpl) {
// Assert only 2
if (args.length !== 1) {
throw new Error('BitVec add only supports 2 arguments');
}
return new BitVecImpl(check(Z3.mk_bvadd(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)));
}
else {
(0, utils_1.assert)(arg0 instanceof ArithImpl);
return new ArithImpl(check(Z3.mk_add(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))));
}
}
function Sub(arg0, ...args) {
if (arg0 instanceof BitVecImpl) {
// Assert only 2
if (args.length !== 1) {
throw new Error('BitVec sub only supports 2 arguments');
}
return new BitVecImpl(check(Z3.mk_bvsub(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)));
}
else {
(0, utils_1.assert)(arg0 instanceof ArithImpl);
return new ArithImpl(check(Z3.mk_sub(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))));
}
}
function Product(arg0, ...args) {
if (arg0 instanceof BitVecImpl) {
// Assert only 2
if (args.length !== 1) {
throw new Error('BitVec mul only supports 2 arguments');
}
return new BitVecImpl(check(Z3.mk_bvmul(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)));
}
else {
(0, utils_1.assert)(arg0 instanceof ArithImpl);
return new ArithImpl(check(Z3.mk_mul(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))));
}
}
function Div(arg0, arg1) {
if (arg0 instanceof BitVecImpl) {
return new BitVecImpl(check(Z3.mk_bvsdiv(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast)));
}
else {
(0, utils_1.assert)(arg0 instanceof ArithImpl);
return new ArithImpl(check(Z3.mk_div(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast)));
}
}
function BUDiv(arg0, arg1) {
return new BitVecImpl(check(Z3.mk_bvudiv(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast)));
}
function Neg(a) {
if (a instanceof BitVecImpl) {
return new BitVecImpl(check(Z3.mk_bvneg(contextPtr, a.ast)));
}
else {
(0, utils_1.assert)(a instanceof ArithImpl);
return new ArithImpl(check(Z3.mk_unary_minus(contextPtr, a.ast)));
}
}
function Mod(a, b) {
if (a instanceof BitVecImpl) {
return new BitVecImpl(check(Z3.mk_bvsrem(contextPtr, a.ast, a.sort.cast(b).ast)));
}
else {
(0, utils_1.assert)(a instanceof ArithImpl);
return new ArithImpl(check(Z3.mk_mod(contextPtr, a.ast, a.sort.cast(b).ast)));
}
}
class ArithImpl extends ExprImpl {
add(other) {
return Sum(this, other);
}
mul(other) {
return Product(this, other);
}
sub(other) {
return Sub(this, other);
}
pow(exponent) {
return new ArithImpl(check(Z3.mk_power(contextPtr, this.ast, this.sort.cast(exponent).ast)));
}
div(other) {
return Div(this, other);
}
mod(other) {
return Mod(this, other);
}
neg() {
return Neg(this);
}
le(other) {
return LE(this, other);
}
lt(other) {
return LT(this, other);
}
gt(other) {
return GT(this, other);
}
ge(other) {
return GE(this, other);
}
}
class IntNumImpl extends ArithImpl {
value() {
return BigInt(this.asString());
}
asString() {
return Z3.get_numeral_string(contextPtr, this.ast);
}
asBinary() {
return Z3.get_numeral_binary_string(contextPtr, this.ast);
}
}
class RatNumImpl extends ArithImpl {
value() {
return { numerator: this.numerator().value(), denominator: this.denominator().value() };
}
numerator() {
return new IntNumImpl(Z3.get_numerator(contextPtr, this.ast));
}
denominator() {
return new IntNumImpl(Z3.get_denominator(contextPtr, this.ast));
}
asNumber() {
const { numerator, denominator } = this.value();
const div = numerator / denominator;
return Number(div) + Number(numerator - div * denominator) / Number(denominator);
}
asDecimal(prec = Number.parseInt(getParam('precision') ?? FALLBACK_PRECISION.toString())) {
return Z3.get_numeral_decimal_string(contextPtr, this.ast, prec);
}
asString() {
return Z3.get_numeral_string(contextPtr, this.ast);
}
}
class BitVecSortImpl extends SortImpl {
size() {
return Z3.get_bv_sort_size(contextPtr, this.ptr);
}
subsort(other) {
return isBitVecSort(other) && this.size() < other.size();
}
cast(other) {
if (isExpr(other)) {
_assertContext(other);
return other;
}
(0, utils_1.assert)(!isCoercibleRational(other), "Can't convert rational to BitVec");
return BitVec.val(other, this.size());
}
}
class BitVecImpl extends ExprImpl {
size() {
return this.sort.size();
}
add(other) {
return Sum(this, other);
}
mul(other) {
return Product(this, other);
}
sub(other) {
return Sub(this, other);
}
sdiv(other) {
return Div(this, other);
}
udiv(other) {
return BUDiv(this, other);
}
smod(other) {
return Mod(this, other);
}
urem(other) {
return new BitVecImpl(check(Z3.mk_bvurem(contextPtr, this.ast, this.sort.cast(other).ast)));
}
srem(other) {
return new BitVecImpl(check(Z3.mk_bvsrem(contextPtr, this.ast, this.sort.cast(other).ast)));
}
neg() {
return Neg(this);
}
or(other) {
return new BitVecImpl(check(Z3.mk_bvor(contextPtr, this.ast, this.sort.cast(other).ast)));
}
and(other) {
return new BitVecImpl(check(Z3.mk_bvand(contextPtr, this.ast, this.sort.cast(other).ast)));
}
nand(other) {
return new BitVecImpl(check(Z3.mk_bvnand(contextPtr, this.ast, this.sort.cast(other).ast)));
}
xor(other) {
return new BitVecImpl(check(Z3.mk_bvxor(contextPtr, this.ast, this.sort.cast(other).ast)));
}
xnor(other) {
return new BitVecImpl(check(Z3.mk_bvxnor(contextPtr, this.ast, this.sort.cast(other).ast)));
}
shr(count) {
return new BitVecImpl(check(Z3.mk_bvashr(contextPtr, this.ast, this.sort.cast(count).ast)));
}
lshr(count) {
return new BitVecImpl(check(Z3.mk_bvlshr(contextPtr, this.ast, this.sort.cast(count).ast)));
}
shl(count) {
return new BitVecImpl(check(Z3.mk_bvshl(contextPtr, this.ast, this.sort.cast(count).ast)));
}
rotateRight(count) {
return new BitVecImpl(check(Z3.mk_ext_rotate_right(contextPtr, this.ast, this.sort.cast(count).ast)));
}
rotateLeft(count) {
return new BitVecImpl(check(Z3.mk_ext_rotate_left(contextPtr, this.ast, this.sort.cast(count).ast)));
}
not() {
return new BitVecImpl(check(Z3.mk_bvnot(contextPtr, this.ast)));
}
extract(high, low) {
return Extract(high, low, this);
}
signExt(count) {
return new BitVecImpl(check(Z3.mk_sign_ext(contextPtr, count, this.ast)));
}
zeroExt(count) {
return new BitVecImpl(check(Z3.mk_zero_ext(contextPtr, count, this.ast)));
}
repeat(count) {
return new BitVecImpl(check(Z3.mk_repeat(contextPtr, count, this.ast)));
}
sle(other) {
return SLE(this, other);
}
ule(other) {
return ULE(this, other);
}
slt(other) {
return SLT(this, other);
}
ult(other) {
return ULT(this, other);
}
sge(other) {
return SGE(this, other);
}
uge(other) {
return UGE(this, other);
}
sgt(other) {
return SGT(this, other);
}
ugt(other) {
return UGT(this, other);
}
redAnd() {
return new BitVecImpl(check(Z3.mk_bvredand(contextPtr, this.ast)));
}
redOr() {
return new BitVecImpl(check(Z3.mk_bvredor(contextPtr, this.ast)));
}
addNoOverflow(other, isSigned) {
return new BoolImpl(check(Z3.mk_bvadd_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned)));
}
addNoUnderflow(other) {
return new BoolImpl(check(Z3.mk_bvadd_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast)));
}
subNoOverflow(other) {
return new BoolImpl(check(Z3.mk_bvsub_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast)));
}
subNoUndeflow(other, isSigned) {
return new BoolImpl(check(Z3.mk_bvsub_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned)));
}
sdivNoOverflow(other) {
return new BoolImpl(check(Z3.mk_bvsdiv_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast)));
}
mulNoOverflow(other, isSigned) {
return new BoolImpl(check(Z3.mk_bvmul_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned)));
}
mulNoUndeflow(other) {
return new BoolImpl(check(Z3.mk_bvmul_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast)));
}
negNoOverflow() {
return new BoolImpl(check(Z3.mk_bvneg_no_overflow(contextPtr, this.ast)));
}
}
class BitVecNumImpl extends BitVecImpl {
value() {
return BigInt(this.asString());
}
asSignedValue() {
let val = this.value();
const size = BigInt(this.size());
if (val >= 2n ** (size - 1n)) {
val = val - 2n ** size;
}
if (val < (-2n) ** (size - 1n)) {
val = val + 2n ** size;
}
return val;
}
asString() {
return Z3.get_numeral_string(contextPtr, this.ast);
}
asBinaryString() {
return Z3.get_numeral_binary_string(contextPtr, this.ast);
}
}
class ArraySortImpl extends SortImpl {
domain() {
return _toSort(check(Z3.get_array_sort_domain(contextPtr, this.ptr)));
}
domain_n(i) {
return _toSort(check(Z3.get_array_sort_domain_n(contextPtr, this.ptr, i)));
}
range() {
return _toSort(check(Z3.get_array_sort_range(contextPtr, this.ptr)));
}
}
class ArrayImpl extends ExprImpl {
domain() {
return this.sort.domain();
}
domain_n(i) {
return this.sort.domain_n(i);
}
range() {
return this.sort.range();
}
select(...indices) {
return Select(this, ...indices);
}
store(...indicesAndValue) {
return Store(this, ...indicesAndValue);
}
}
class SetImpl extends ExprImpl {
elemSort() {
return this.sort.domain();
}
union(...args) {
return SetUnion(this, ...args);
}
intersect(...args) {
return SetIntersect(this, ...args);
}
diff(b) {
return SetDifference(this, b);
}
hasSize(size) {
return SetHasSize(this, size);
}
add(elem) {
return SetAdd(this, elem);
}
del(elem) {
return SetDel(this, elem);
}
complement() {
return SetComplement(this);
}
contains(elem) {
return isMember(elem, this);
}
subsetOf(b) {
return isSubset(this, b);
}
}
class QuantifierImpl extends ExprImpl {
is_forall() {
return Z3.is_quantifier_forall(contextPtr, this.ast);
}
is_exists() {
return Z3.is_quantifier_exists(contextPtr, this.ast);
}
is_lambda() {
return Z3.is_lambda(contextPtr, this.ast);
}
weight() {
return Z3.get_quantifier_weight(contextPtr, this.ast);
}
num_patterns() {
return Z3.get_quantifier_num_patterns(contextPtr, this.ast);
}
pattern(i) {
return new PatternImpl(check(Z3.get_quantifier_pattern_ast(contextPtr, this.ast, i)));
}
num_no_patterns() {
return Z3.get_quantifier_num_no_patterns(contextPtr, this.ast);
}
no_pattern(i) {
return _toExpr(check(Z3.get_quantifier_no_pattern_ast(contextPtr, this.ast, i)));
}
body() {
return _toExpr(check(Z3.get_quantifier_body(contextPtr, this.ast)));
}
num_vars() {
return Z3.get_quantifier_num_bound(contextPtr, this.ast);
}
var_name(i) {
return _fromSymbol(Z3.get_quantifier_bound_name(contextPtr, this.ast, i));
}
var_sort(i) {
return _toSort(check(Z3.get_quantifier_bound_sort(contextPtr, this.ast, i)));
}
children() {
return [this.body()];
}
}
class NonLambdaQuantifierImpl extends QuantifierImpl {
not() {
return Not(this);
}
and(other) {
return And(this, other);
}
or(other) {
return Or(this, other);
}
xor(other) {
return Xor(this, other);
}
implies(other) {
return Implies(this, other);
}
iff(other) {
return Iff(this, other);
}
}
// isBool will return false which is unlike the python API (but like the C API)
class LambdaImpl extends QuantifierImpl {
domain() {
return this.sort.domain();
}
domain_n(i) {
return this.sort.domain_n(i);
}
range() {
return this.sort.range();
}
select(...indices) {
return Select(this, ...indices);
}
store(...indicesAndValue) {
return Store(this, ...indicesAndValue);
}
}
class AstVectorImpl {
constructor(ptr = Z3.mk_ast_vector(contextPtr)) {
this.ptr = ptr;
this.ctx = ctx;
Z3.ast_vector_inc_ref(contextPtr, ptr);
cleanup.register(this, () => Z3.ast_vector_dec_ref(contextPtr, ptr), this);
}
length() {
return Z3.ast_vector_size(contextPtr, this.ptr);
}
[Symbol.iterator]() {
return this.values();
}
*entries() {
const length = this.length();
for (let i = 0; i < length; i++) {
yield [i, this.get(i)];
}
}
*keys() {
for (let [key] of this.entries()) {
yield key;
}
}
*values() {
for (let [, value] of this.entries()) {
yield value;
}
}
get(from, to) {
const length = this.length();
if (from < 0) {
from += length;
}
if (from >= length) {
throw new RangeError(`expected from index ${from} to be less than length ${length}`);
}
if (to === undefined) {
return _toAst(check(Z3.ast_vector_get(contextPtr, this.ptr, from)));
}
if (to < 0) {
to += length;
}
if (to >= length) {
throw new RangeError(`expected to index ${to} to be less than length ${length}`);
}
const result = [];
for (let i = from; i < to; i++) {
result.push(_toAst(check(Z3.ast_vector_get(contextPtr, this.ptr, i))));
}
return result;
}
set(i, v) {
_assertContext(v);
if (i >= this.length()) {
throw new RangeError(`expected index ${i} to be less than length ${this.length()}`);
}
check(Z3.ast_vector_set(contextPtr, this.ptr, i, v.ast));
}
push(v) {
_assertContext(v);
check(Z3.ast_vector_push(contextPtr, this.ptr, v.ast));
}
resize(size) {
check(Z3.ast_vector_resize(contextPtr, this.ptr, size));
}
has(v) {
_assertContext(v);
for (const item of this.values()) {
if (item.eqIdentity(v)) {
return true;
}
}
return false;
}
sexpr() {
return check(Z3.ast_vector_to_string(contextPtr, this.ptr));
}
}
class AstMapImpl {
constructor(ptr = Z3.mk_ast_map(contextPtr)) {
this.ptr = ptr;
this.ctx = ctx;
Z3.ast_map_inc_ref(contextPtr, ptr);
cleanup.register(this, () => Z3.ast_map_dec_ref(contextPtr, ptr), this);
}
[Symbol.iterator]() {
return this.entries();
}
get size() {
return Z3.ast_map_size(contextPtr, this.ptr);
}
*entries() {
for (const key of this.keys()) {
yield [key, this.get(key)];
}
}
keys() {
return new AstVectorImpl(Z3.ast_map_keys(contextPtr, this.ptr));
}
*values() {
for (const [_, value] of this.entries()) {
yield value;
}
}
get(key) {
return _toAst(check(Z3.ast_map_find(contextPtr, this.ptr, key.ast)));
}
set(key, value) {
check(Z3.ast_map_insert(contextPtr, this.ptr, key.ast, value.ast));
}
delete(key) {
check(Z3.ast_map_erase(contextPtr, this.ptr, key.ast));
}
clear() {
check(Z3.ast_map_reset(contextPtr, this.ptr));
}
has(key) {
return check(Z3.ast_map_contains(contextPtr, this.ptr, key.ast));
}
sexpr() {
return check(Z3.ast_map_to_string(contextPtr, this.ptr));
}
}
function substitute(t, ...substitutions) {
_assertContext(t);
const from = [];
const to = [];
for (const [f, t] of substitutions) {
_assertContext(f);
_assertContext(t);
from.push(f.ast);
to.push(t.ast);
}
return _toExpr(check(Z3.substitute(contextPtr, t.ast, from, to)));
}
function ast_from_string(s) {
const sort_names = [];
const sorts = [];
const decl_names = [];
const decls = [];
const v = new AstVectorImpl(check(Z3.parse_smtlib2_string(contextPtr, s, sort_names, sorts, decl_names, decls)));
if (v.length() !== 1) {
throw new Error('Expected exactly one AST. Instead got ' + v.length() + ': ' + v.sexpr());
}
return v.get(0);
}
const ctx = {
ptr: contextPtr,
name,
/////////////
// Classes //
/////////////
Solver: SolverImpl,
Optimize: OptimizeImpl,
Model: ModelImpl,
Tactic: TacticImpl,
AstVector: AstVectorImpl,
AstMap: AstMapImpl,
///////////////
// Functions //
///////////////
interrupt,
isModel,
isAst,
isSort,
isFuncDecl,
isFuncInterp,
isApp,
isConst,
isExpr,
isVar,
isAppOf,
isBool,
isTrue,
isFalse,
isAnd,
isOr,
isImplies,
isNot,
isEq,
isDistinct,
isQuantifier,
isArith,
isArithSort,
isInt,
isIntVal,
isIntSort,
isReal,
isRealVal,
isRealSort,
isBitVecSort,
isBitVec,
isBitVecVal, // TODO fix ordering
isArraySort,
isArray,
isConstArray,
isProbe,
isTactic,
isAstVector,
eqIdentity,
getVarIndex,
from,
solve,
/////////////
// Objects //
/////////////
Sort,
Function,
RecFunc,
Bool,
Int,
Real,
BitVec,
Array,
Set,
////////////////
// Operations //
////////////////
If,
Distinct,
Const,
Consts,
FreshConst,
Var,
Implies,
Iff,
Eq,
Xor,
Not,
And,
Or,
PbEq,
PbGe,
PbLe,
ForAll,
Exists,
Lambda,
ToReal,
ToInt,
IsInt,
Sqrt,
Cbrt,
BV2Int,
Int2BV,
Concat,
Cond,
LT,
GT,
LE,
GE,
ULT,
UGT,
ULE,
UGE,
SLT,
SGT,
SLE,
SGE,
Sum,
Sub,
Product,
Div,
BUDiv,
Neg,
Mod,
Select,
Store,
Extract,
substitute,
simplify,
/////////////
// Loading //
/////////////
ast_from_string,
SetUnion,
SetIntersect,
SetDifference,
SetHasSize,
SetAdd,
SetDel,
SetComplement,
EmptySet,
FullSet,
isMember,
isSubset,
};
cleanup.register(ctx, () => Z3.del_context(contextPtr));
return ctx;
}
return {
enableTrace,
disableTrace,
getVersion,
getVersionString,
getFullVersion,
openLog,
appendLog,
getParam,
setParam,
resetParams,
Context: createContext,
};
}