3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-14 09:56:15 +00:00

JS/TS API Array support (#6393)

* feat: basic array support

Still need deeper type support for Arrays

* fixed broken format rules

* spaces inside curly

* feat: range sort type inference

* feat: better type inference in model eval

* doc: fixed some incorrect documentation

* feat: domain type inference

* feat: addressed suggestions

* feat: addressed suggestions

* chore: moved ts-expect from deps to dev-deps

* test: added z3guide examples

* fix: removed ts-expect from dependencies again

* docs: fixed some documentation
This commit is contained in:
Walden Yan 2022-10-17 14:10:36 -04:00 committed by GitHub
parent d4885abdc0
commit f175fcbb54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 1767 additions and 7335 deletions

File diff suppressed because it is too large Load diff

View file

@ -35,7 +35,8 @@
"contributors": [ "contributors": [
"Kevin Gibbons <bakkot@gmail.com>", "Kevin Gibbons <bakkot@gmail.com>",
"Nikolaj Bjorner", "Nikolaj Bjorner",
"Olaf Tomalka <olaf@tomalka.me>" "Olaf Tomalka <olaf@tomalka.me>",
"Walden Yan <waldenyan20@gmail.com>"
], ],
"devDependencies": { "devDependencies": {
"@types/jest": "^27.5.1", "@types/jest": "^27.5.1",
@ -49,6 +50,7 @@
"prettier": "^2.5.1", "prettier": "^2.5.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sprintf-js": "^1.1.2", "sprintf-js": "^1.1.2",
"ts-expect": "^1.3.0",
"ts-jest": "^28.0.3", "ts-jest": "^28.0.3",
"ts-node": "^10.8.0", "ts-node": "^10.8.0",
"typedoc": "^0.22.17", "typedoc": "^0.22.17",

View file

@ -2,6 +2,7 @@ import assert from 'assert';
import asyncToArray from 'iter-tools/methods/async-to-array'; import asyncToArray from 'iter-tools/methods/async-to-array';
import { init, killThreads } from '../jest'; import { init, killThreads } from '../jest';
import { Arith, Bool, Model, Z3AssertionError, Z3HighLevel } from './types'; import { Arith, Bool, Model, Z3AssertionError, Z3HighLevel } from './types';
import { expectType } from "ts-expect";
/** /**
* Generate all possible solutions from given assumptions. * Generate all possible solutions from given assumptions.
@ -113,7 +114,7 @@ describe('high-level', () => {
const { Solver, Not, Int } = api.Context('main'); const { Solver, Not, Int } = api.Context('main');
const solver = new Solver(); const solver = new Solver();
solver.fromString("(declare-const x Int) (assert (and (< x 2) (> x 0)))") solver.fromString("(declare-const x Int) (assert (and (< x 2) (> x 0)))")
expect(await solver.check()).toStrictEqual('sat') expect(await solver.check()).toStrictEqual('sat')
const x = Int.const('x') const x = Int.const('x')
solver.add(Not(x.eq(1))) solver.add(Not(x.eq(1)))
expect(await solver.check()).toStrictEqual('unsat') expect(await solver.check()).toStrictEqual('unsat')
@ -211,6 +212,7 @@ describe('high-level', () => {
} }
return cells; return cells;
} }
const INSTANCE = toSudoku(` const INSTANCE = toSudoku(`
....94.3. ....94.3.
...51...7 ...51...7
@ -390,6 +392,106 @@ describe('high-level', () => {
}); });
}); });
describe('arrays', () => {
it('Example 1', async () => {
const Z3 = api.Context('main');
const arr = Z3.Array.const('arr', Z3.Int.sort(), Z3.Int.sort());
const [idx, val] = Z3.Int.consts('idx val');
const conjecture = (arr.store(idx, val).select(idx).eq(val));
await prove(conjecture);
});
it('domain and range type inference', async () => {
const { BitVec, Array, isArray, isArraySort } = api.Context('main');
const arr = Array.const('arr', BitVec.sort(160), BitVec.sort(256));
const domain = arr.domain();
expect(domain.size()).toStrictEqual(160);
expect(arr.domain_n(0).size()).toStrictEqual(160);
const range = arr.range();
expect(range.size()).toStrictEqual(256);
assert(isArray(arr) && isArraySort(arr.sort));
const arr2 = Array.const('arr2', BitVec.sort(1), BitVec.sort(2), BitVec.sort(3));
const dom2 = arr2.domain_n(1);
// We can call size() on dom2 and see that it is two bits
// purely from type inference
expectType<2>(dom2.size());
// Won't let us create an array constant with only one of domain/range
// and is detected at compile time
// @ts-expect-error
const arr3 = Array.const('arr3', BitVec.sort(1));
})
it('can do simple proofs', async () => {
const { BitVec, Array, isArray, isArraySort, isConstArray, Eq, Not } = api.Context('main');
const idx = BitVec.const('idx', 160);
const val = BitVec.const('val', 256);
const FIVE_VAL = BitVec.val(5, 256);
const arr = Array.const('arr', BitVec.sort(160), BitVec.sort(256));
const constArr = Array.K(BitVec.sort(160), FIVE_VAL);
assert(isArray(arr) && isArraySort(arr.sort) && isConstArray(constArr));
const arr2 = arr.store(0, 5);
await prove(Eq(arr2.select(0), FIVE_VAL));
await prove(Not(Eq(arr2.select(0), BitVec.val(6, 256))));
await prove(Eq(arr2.store(idx, val).select(idx), constArr.store(idx, val).select(idx)));
// TODO: add in Quantifiers and better typing of arrays
// await prove(
// ForAll([idx], idx.add(1).ugt(idx).and(arr.select(idx.add(1)).ugt(arr.select(idx)))).implies(
// arr.select(0).ult(arr.select(1000))
// )
// );
});
it('Finds arrays that differ but that sum to the same', async () => {
const Z3 = api.Context('main');
const { Array, BitVec } = Z3;
const mod = 1n << 32n;
const arr1 = Array.const('arr', BitVec.sort(2), BitVec.sort(32));
const arr2 = Array.const('arr2', BitVec.sort(2), BitVec.sort(32));
const same_sum = arr1.select(0)
.add(arr1.select(1))
.add(arr1.select(2))
.add(arr1.select(3))
.eq(
arr2.select(0)
.add(arr2.select(1))
.add(arr2.select(2))
.add(arr2.select(3))
);
const different = arr1.select(0).neq(arr2.select(0))
.or(arr1.select(1).neq(arr2.select(1)))
.or(arr1.select(2).neq(arr2.select(2)))
.or(arr1.select(3).neq(arr2.select(3)));
const model = await solve(same_sum.and(different));
const arr1Vals = [0, 1, 2, 3].map(i => model.eval(arr1.select(i)).value());
const arr2Vals = [0, 1, 2, 3].map(i => model.eval(arr2.select(i)).value());
expect((arr1Vals.reduce((a, b) => a + b, 0n) % mod) === arr2Vals.reduce((a, b) => a + b, 0n) % mod);
for (let i = 0; i < 4; i++) {
expect(arr1Vals[i] !== arr2Vals[i]);
}
});
});
describe('Solver', () => { describe('Solver', () => {
it('can use push and pop', async () => { it('can use push and pop', async () => {
const { Solver, Int } = api.Context('main'); const { Solver, Int } = api.Context('main');

View file

@ -37,7 +37,7 @@ import {
AnyExpr, AnyExpr,
AnySort, AnySort,
Arith, Arith,
ArithSort, ArithSort, ArrayIndexType,
Ast, Ast,
AstMap, AstMap,
AstMapCtor, AstMapCtor,
@ -48,7 +48,7 @@ import {
BitVecSort, BitVecSort,
Bool, Bool,
BoolSort, BoolSort,
CheckSatResult, CheckSatResult, CoercibleFromMap,
CoercibleRational, CoercibleRational,
CoercibleToBitVec, CoercibleToBitVec,
CoercibleToExpr, CoercibleToExpr,
@ -63,6 +63,8 @@ import {
Model, Model,
Probe, Probe,
RatNum, RatNum,
SMTArray,
SMTArraySort,
Solver, Solver,
Sort, Sort,
SortToExprMap, SortToExprMap,
@ -86,12 +88,11 @@ function isCoercibleRational(obj: any): obj is CoercibleRational {
(obj.denominator !== null && (obj.denominator !== null &&
(typeof obj.denominator === 'number' || typeof obj.denominator === 'bigint')) (typeof obj.denominator === 'number' || typeof obj.denominator === 'bigint'))
); );
r && r && assert(
assert( (typeof obj!.numerator !== 'number' || Number.isSafeInteger(obj!.numerator)) &&
(typeof obj.numerator !== 'number' || Number.isSafeInteger(obj.numerator)) && (typeof obj!.denominator !== 'number' || Number.isSafeInteger(obj!.denominator)),
(typeof obj.denominator !== 'number' || Number.isSafeInteger(obj.denominator)), 'Fraction numerator and denominator must be integers',
'Fraction numerator and denominator must be integers', );
);
return r; return r;
} }
@ -151,7 +152,9 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
function createContext<Name extends string>(name: Name, options?: Record<string, any>): Context<Name> { function createContext<Name extends string>(name: Name, options?: Record<string, any>): Context<Name> {
const cfg = Z3.mk_config(); const cfg = Z3.mk_config();
if (options != null) { if (options != null) {
Object.entries(options).forEach(([key, value]) => check(Z3.set_param_value(cfg, key, value.toString()))); Object.entries(options).forEach(
([key, value]) => check(Z3.set_param_value(cfg, key, value.toString()))
);
} }
const contextPtr = Z3.mk_context_rc(cfg); const contextPtr = Z3.mk_context_rc(cfg);
Z3.set_ast_print_mode(contextPtr, Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT); Z3.set_ast_print_mode(contextPtr, Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT);
@ -216,20 +219,22 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return new ArithSortImpl(ast); return new ArithSortImpl(ast);
case Z3_sort_kind.Z3_BV_SORT: case Z3_sort_kind.Z3_BV_SORT:
return new BitVecSortImpl(ast); return new BitVecSortImpl(ast);
case Z3_sort_kind.Z3_ARRAY_SORT:
return new ArraySortImpl(ast);
default: default:
return new SortImpl(ast); return new SortImpl(ast);
} }
} }
function _toExpr(ast: Z3_ast): Bool<Name> | IntNum<Name> | RatNum<Name> | Arith<Name> | Expr<Name> { function _toExpr(ast: Z3_ast): AnyExpr<Name> {
const kind = check(Z3.get_ast_kind(contextPtr, ast)); const kind = check(Z3.get_ast_kind(contextPtr, ast));
if (kind === Z3_ast_kind.Z3_QUANTIFIER_AST) { if (kind === Z3_ast_kind.Z3_QUANTIFIER_AST) {
if (Z3.is_quantifier_forall(contextPtr, ast)) if (Z3.is_quantifier_forall(contextPtr, ast))
return new BoolImpl(ast); return new BoolImpl(ast);
if (Z3.is_quantifier_exists(contextPtr, ast)) if (Z3.is_quantifier_exists(contextPtr, ast))
return new BoolImpl(ast); return new BoolImpl(ast);
if (Z3.is_lambda(contextPtr, ast)) if (Z3.is_lambda(contextPtr, ast))
return new ExprImpl(ast); return new ExprImpl(ast);
assert(false); assert(false);
} }
const sortKind = check(Z3.get_sort_kind(contextPtr, Z3.get_sort(contextPtr, ast))); const sortKind = check(Z3.get_sort_kind(contextPtr, Z3.get_sort(contextPtr, ast)));
@ -251,6 +256,8 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return new BitVecNumImpl(ast); return new BitVecNumImpl(ast);
} }
return new BitVecImpl(ast); return new BitVecImpl(ast);
case Z3_sort_kind.Z3_ARRAY_SORT:
return new ArrayImpl(ast);
default: default:
return new ExprImpl(ast); return new ExprImpl(ast);
} }
@ -440,6 +447,22 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return r; return r;
} }
function isArraySort(obj: unknown): obj is SMTArraySort<Name> {
const r = obj instanceof ArraySortImpl;
r && _assertContext(obj);
return r;
}
function isArray(obj: unknown): obj is SMTArray<Name> {
const r = obj instanceof ArrayImpl;
r && _assertContext(obj);
return r;
}
function isConstArray(obj: unknown): boolean {
return isAppOf(obj, Z3_decl_kind.Z3_OP_CONST_ARRAY);
}
function isProbe(obj: unknown): obj is Probe<Name> { function isProbe(obj: unknown): obj is Probe<Name> {
const r = obj instanceof ProbeImpl; const r = obj instanceof ProbeImpl;
r && _assertContext(obj); r && _assertContext(obj);
@ -508,9 +531,9 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
// expression simplification // // expression simplification //
/////////////////////////////// ///////////////////////////////
async function simplify(e : Expr<Name>) { async function simplify(e: Expr<Name>) {
const result = await Z3.simplify(contextPtr, e.ast) const result = await Z3.simplify(contextPtr, e.ast)
return _toExpr(check(result)); return _toExpr(check(result));
} }
///////////// /////////////
@ -677,6 +700,44 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
); );
}, },
}; };
const Array = {
sort<DomainSort extends [AnySort<Name>, ...AnySort<Name>[]], RangeSort extends AnySort<Name>>(
...sig: [...DomainSort, RangeSort]
): SMTArraySort<Name, DomainSort, RangeSort> {
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<DomainSort extends [AnySort<Name>, ...AnySort<Name>[]], RangeSort extends AnySort<Name>>(
name: string, ...sig: [...DomainSort, RangeSort]
): SMTArray<Name, DomainSort, RangeSort> {
return new ArrayImpl<DomainSort, RangeSort>(
check(Z3.mk_const(contextPtr, _toSymbol(name), Array.sort(...sig).ptr))
);
},
consts<DomainSort extends [AnySort<Name>, ...AnySort<Name>[]], RangeSort extends AnySort<Name>>(
names: string | string[],
...sig: [...DomainSort, RangeSort]
): SMTArray<Name, DomainSort, RangeSort>[] {
if (typeof names === 'string') {
names = names.split(' ');
}
return names.map(name => Array.const(name, ...sig));
},
K<DomainSort extends AnySort<Name>, RangeSort extends AnySort<Name>>(
domain: DomainSort,
value: SortToExprMap<RangeSort, Name>
): SMTArray<Name, [DomainSort], RangeSort> {
return new ArrayImpl<[DomainSort], RangeSort>(
check(Z3.mk_const_array(contextPtr, domain.ptr, value.ptr))
);
}
}
//////////////// ////////////////
// Operations // // Operations //
@ -948,6 +1009,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
readonly ptr: Z3_solver; readonly ptr: Z3_solver;
readonly ctx: Context<Name>; readonly ctx: Context<Name>;
constructor(ptr: Z3_solver | string = Z3.mk_solver(contextPtr)) { constructor(ptr: Z3_solver | string = Z3.mk_solver(contextPtr)) {
this.ctx = ctx; this.ctx = ctx;
let myPtr: Z3_solver; let myPtr: Z3_solver;
@ -964,21 +1026,26 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
push() { push() {
Z3.solver_push(contextPtr, this.ptr); Z3.solver_push(contextPtr, this.ptr);
} }
pop(num: number = 1) { pop(num: number = 1) {
Z3.solver_pop(contextPtr, this.ptr, num); Z3.solver_pop(contextPtr, this.ptr, num);
} }
numScopes() { numScopes() {
return Z3.solver_get_num_scopes(contextPtr, this.ptr); return Z3.solver_get_num_scopes(contextPtr, this.ptr);
} }
reset() { reset() {
Z3.solver_reset(contextPtr, this.ptr); Z3.solver_reset(contextPtr, this.ptr);
} }
add(...exprs: (Bool<Name> | AstVector<Name, Bool<Name>>)[]) { add(...exprs: (Bool<Name> | AstVector<Name, Bool<Name>>)[]) {
_flattenArgs(exprs).forEach(expr => { _flattenArgs(exprs).forEach(expr => {
_assertContext(expr); _assertContext(expr);
check(Z3.solver_assert(contextPtr, this.ptr, expr.ast)); check(Z3.solver_assert(contextPtr, this.ptr, expr.ast));
}); });
} }
addAndTrack(expr: Bool<Name>, constant: Bool<Name> | string) { addAndTrack(expr: Bool<Name>, constant: Bool<Name> | string) {
if (typeof constant === 'string') { if (typeof constant === 'string') {
constant = Bool.const(constant); constant = Bool.const(constant);
@ -1019,7 +1086,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return check(Z3.solver_to_string(contextPtr, this.ptr)); return check(Z3.solver_to_string(contextPtr, this.ptr));
} }
fromString(s : string) { fromString(s: string) {
Z3.solver_from_string(contextPtr, this.ptr, s); Z3.solver_from_string(contextPtr, this.ptr, s);
throwIfError(); throwIfError();
} }
@ -1043,20 +1110,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return this.values(); return this.values();
} }
*entries(): IterableIterator<[number, FuncDecl<Name>]> { * entries(): IterableIterator<[number, FuncDecl<Name>]> {
const length = this.length(); const length = this.length();
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
yield [i, this.get(i)]; yield [i, this.get(i)];
} }
} }
*keys(): IterableIterator<number> { * keys(): IterableIterator<number> {
for (const [key] of this.entries()) { for (const [key] of this.entries()) {
yield key; yield key;
} }
} }
*values(): IterableIterator<FuncDecl<Name>> { * values(): IterableIterator<FuncDecl<Name>> {
for (const [, value] of this.entries()) { for (const [, value] of this.entries()) {
yield value; yield value;
} }
@ -1076,11 +1143,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
eval(expr: Bool<Name>, modelCompletion?: boolean): Bool<Name>; eval(expr: Bool<Name>, modelCompletion?: boolean): Bool<Name>;
eval(expr: Arith<Name>, modelCompletion?: boolean): Arith<Name>; eval(expr: Arith<Name>, modelCompletion?: boolean): Arith<Name>;
eval<Bits extends number = number>(expr: BitVec<Bits, Name>, modelCompletion?: boolean): BitVecNum<Bits, Name>;
eval(expr: Expr<Name>, modelCompletion: boolean = false) { eval(expr: Expr<Name>, modelCompletion: boolean = false) {
_assertContext(expr); _assertContext(expr);
const r = check(Z3.model_eval(contextPtr, this.ptr, expr.ast, modelCompletion)); const r = check(Z3.model_eval(contextPtr, this.ptr, expr.ast, modelCompletion));
if (r === null) { if (r === null) {
throw new Z3Error('Failed to evaluatio expression in the model'); throw new Z3Error('Failed to evaluate expression in the model');
} }
return _toExpr(r); return _toExpr(r);
} }
@ -1092,7 +1160,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
get(sort: Sort<Name>): AstVector<Name, AnyExpr<Name>>; get(sort: Sort<Name>): AstVector<Name, AnyExpr<Name>>;
get( get(
i: number | FuncDecl<Name> | Expr<Name> | Sort<Name>, i: number | FuncDecl<Name> | Expr<Name> | Sort<Name>,
to?: number, to?: number
): FuncDecl<Name> | FuncInterp<Name> | Expr<Name> | AstVector<Name, AnyAst<Name>> | FuncDecl<Name>[] { ): FuncDecl<Name> | FuncInterp<Name> | Expr<Name> | AstVector<Name, AnyAst<Name>> | FuncDecl<Name>[] {
assert(to === undefined || typeof i === 'number'); assert(to === undefined || typeof i === 'number');
if (typeof i === 'number') { if (typeof i === 'number') {
@ -1362,15 +1430,22 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
not(): Bool<Name> { not(): Bool<Name> {
return Not(this); return Not(this);
} }
and(other: Bool<Name> | boolean): Bool<Name> { and(other: Bool<Name> | boolean): Bool<Name> {
return And(this, other); return And(this, other);
} }
or(other: Bool<Name> | boolean): Bool<Name> { or(other: Bool<Name> | boolean): Bool<Name> {
return Or(this, other); return Or(this, other);
} }
xor(other: Bool<Name> | boolean): Bool<Name> { xor(other: Bool<Name> | boolean): Bool<Name> {
return Xor(this, other); return Xor(this, other);
} }
implies(other: Bool<Name> | boolean): Bool<Name> {
return Implies(this, other);
}
} }
class ProbeImpl implements Probe<Name> { class ProbeImpl implements Probe<Name> {
@ -1571,27 +1646,35 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
add(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { add(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvadd(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvadd(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
mul(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { mul(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvmul(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvmul(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
sub(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { sub(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvsub(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvsub(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
sdiv(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { sdiv(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvsdiv(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvsdiv(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
udiv(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { udiv(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvudiv(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvudiv(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
smod(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { smod(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvsmod(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvsmod(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
urem(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { urem(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvurem(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvurem(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
srem(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { srem(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvsrem(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvsrem(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
neg(): BitVec<Bits, Name> { neg(): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvneg(contextPtr, this.ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvneg(contextPtr, this.ast)));
} }
@ -1599,33 +1682,43 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
or(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { or(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvor(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvor(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
and(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { and(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvand(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvand(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
nand(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { nand(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvnand(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvnand(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
xor(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { xor(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvxor(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvxor(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
xnor(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { xnor(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvxnor(contextPtr, this.ast, this.sort.cast(other).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvxnor(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
shr(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { shr(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvashr(contextPtr, this.ast, this.sort.cast(count).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvashr(contextPtr, this.ast, this.sort.cast(count).ast)));
} }
lshr(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { lshr(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvlshr(contextPtr, this.ast, this.sort.cast(count).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvlshr(contextPtr, this.ast, this.sort.cast(count).ast)));
} }
shl(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { shl(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvshl(contextPtr, this.ast, this.sort.cast(count).ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvshl(contextPtr, this.ast, this.sort.cast(count).ast)));
} }
rotateRight(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { rotateRight(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_ext_rotate_right(contextPtr, this.ast, this.sort.cast(count).ast))); return new BitVecImpl<Bits>(check(Z3.mk_ext_rotate_right(contextPtr, this.ast, this.sort.cast(count).ast)));
} }
rotateLeft(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> { rotateLeft(count: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_ext_rotate_left(contextPtr, this.ast, this.sort.cast(count).ast))); return new BitVecImpl<Bits>(check(Z3.mk_ext_rotate_left(contextPtr, this.ast, this.sort.cast(count).ast)));
} }
not(): BitVec<Bits, Name> { not(): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvnot(contextPtr, this.ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvnot(contextPtr, this.ast)));
} }
@ -1633,12 +1726,15 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
extract(high: number, low: number): BitVec<Bits, Name> { extract(high: number, low: number): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_extract(contextPtr, high, low, this.ast))); return new BitVecImpl<Bits>(check(Z3.mk_extract(contextPtr, high, low, this.ast)));
} }
signExt(count: number): BitVec<Bits, Name> { signExt(count: number): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_sign_ext(contextPtr, count, this.ast))); return new BitVecImpl<Bits>(check(Z3.mk_sign_ext(contextPtr, count, this.ast)));
} }
zeroExt(count: number): BitVec<Bits, Name> { zeroExt(count: number): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_zero_ext(contextPtr, count, this.ast))); return new BitVecImpl<Bits>(check(Z3.mk_zero_ext(contextPtr, count, this.ast)));
} }
repeat(count: number): BitVec<Bits, Name> { repeat(count: number): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_repeat(contextPtr, count, this.ast))); return new BitVecImpl<Bits>(check(Z3.mk_repeat(contextPtr, count, this.ast)));
} }
@ -1646,24 +1742,31 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
sle(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { sle(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvsle(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvsle(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
ule(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { ule(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvule(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvule(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
slt(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { slt(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvslt(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvslt(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
ult(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { ult(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvult(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvult(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
sge(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { sge(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvsge(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvsge(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
uge(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { uge(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvuge(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvuge(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
sgt(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { sgt(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvsgt(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvsgt(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
ugt(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { ugt(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvugt(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvugt(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
@ -1671,6 +1774,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
redAnd(): BitVec<Bits, Name> { redAnd(): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvredand(contextPtr, this.ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvredand(contextPtr, this.ast)));
} }
redOr(): BitVec<Bits, Name> { redOr(): BitVec<Bits, Name> {
return new BitVecImpl<Bits>(check(Z3.mk_bvredor(contextPtr, this.ast))); return new BitVecImpl<Bits>(check(Z3.mk_bvredor(contextPtr, this.ast)));
} }
@ -1678,24 +1782,31 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
addNoOverflow(other: CoercibleToBitVec<Bits, Name>, isSigned: boolean): Bool<Name> { addNoOverflow(other: CoercibleToBitVec<Bits, Name>, isSigned: boolean): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvadd_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned))); return new BoolImpl(check(Z3.mk_bvadd_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned)));
} }
addNoUnderflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { addNoUnderflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvadd_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvadd_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
subNoOverflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { subNoOverflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvsub_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvsub_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
subNoUndeflow(other: CoercibleToBitVec<Bits, Name>, isSigned: boolean): Bool<Name> { subNoUndeflow(other: CoercibleToBitVec<Bits, Name>, isSigned: boolean): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvsub_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned))); return new BoolImpl(check(Z3.mk_bvsub_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned)));
} }
sdivNoOverflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { sdivNoOverflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvsdiv_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvsdiv_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
mulNoOverflow(other: CoercibleToBitVec<Bits, Name>, isSigned: boolean): Bool<Name> { mulNoOverflow(other: CoercibleToBitVec<Bits, Name>, isSigned: boolean): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvmul_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned))); return new BoolImpl(check(Z3.mk_bvmul_no_overflow(contextPtr, this.ast, this.sort.cast(other).ast, isSigned)));
} }
mulNoUndeflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> { mulNoUndeflow(other: CoercibleToBitVec<Bits, Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvmul_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast))); return new BoolImpl(check(Z3.mk_bvmul_no_underflow(contextPtr, this.ast, this.sort.cast(other).ast)));
} }
negNoOverflow(): Bool<Name> { negNoOverflow(): Bool<Name> {
return new BoolImpl(check(Z3.mk_bvneg_no_overflow(contextPtr, this.ast))); return new BoolImpl(check(Z3.mk_bvneg_no_overflow(contextPtr, this.ast)));
} }
@ -1703,6 +1814,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
class BitVecNumImpl<Bits extends number> extends BitVecImpl<Bits> implements BitVecNum<Bits, Name> { class BitVecNumImpl<Bits extends number> extends BitVecImpl<Bits> implements BitVecNum<Bits, Name> {
declare readonly __typename: BitVecNum['__typename']; declare readonly __typename: BitVecNum['__typename'];
value() { value() {
return BigInt(this.asString()); return BigInt(this.asString());
} }
@ -1718,14 +1830,87 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
} }
return val; return val;
} }
asString() { asString() {
return Z3.get_numeral_string(contextPtr, this.ast); return Z3.get_numeral_string(contextPtr, this.ast);
} }
asBinaryString() { asBinaryString() {
return Z3.get_numeral_binary_string(contextPtr, this.ast); return Z3.get_numeral_binary_string(contextPtr, this.ast);
} }
} }
class ArraySortImpl<DomainSort extends [AnySort<Name>, ...AnySort<Name>[]] = [Sort<Name>, ...Sort<Name>[]],
RangeSort extends AnySort<Name> = Sort<Name>>
extends SortImpl
implements SMTArraySort<Name, DomainSort, RangeSort> {
declare readonly __typename: SMTArraySort['__typename'];
domain(): DomainSort[0] {
return _toSort(check(Z3.get_array_sort_domain(contextPtr, this.ptr)));
}
domain_n<T extends number>(i: T): DomainSort[T] {
return _toSort(check(Z3.get_array_sort_domain_n(contextPtr, this.ptr, i)));
}
range(): RangeSort {
return _toSort(check(Z3.get_array_sort_range(contextPtr, this.ptr))) as RangeSort;
}
}
class ArrayImpl<
DomainSort extends [AnySort<Name>, ...AnySort<Name>[]] = [Sort<Name>, ...Sort<Name>[]],
RangeSort extends AnySort<Name> = Sort<Name>
> extends ExprImpl<Z3_ast, ArraySortImpl<DomainSort, RangeSort>>
implements SMTArray<Name, DomainSort, RangeSort> {
declare readonly __typename: SMTArray['__typename'];
domain(): DomainSort[0] {
return this.sort.domain();
}
domain_n<T extends number>(i: T): DomainSort[T] {
return this.sort.domain_n(i);
}
range(): RangeSort {
return this.sort.range();
}
select(...indices: ArrayIndexType<Name, DomainSort>): SortToExprMap<RangeSort, Name> {
const args = indices.map((arg, i) => this.domain_n(i).cast(arg as any));
if (args.length === 1) {
return _toExpr(check(Z3.mk_select(contextPtr, this.ast, args[0].ast))) as SortToExprMap<RangeSort, Name>;
}
const _args = args.map(arg => arg.ast);
return _toExpr(check(Z3.mk_select_n(contextPtr, this.ast, _args))) as SortToExprMap<RangeSort, Name>;
}
store(
...indicesAndValue: [
...ArrayIndexType<Name, DomainSort>,
CoercibleFromMap<SortToExprMap<RangeSort, Name>, Name>
]
): SMTArray<Name, DomainSort, RangeSort> {
const args = indicesAndValue.map((arg, i) => {
if (i === indicesAndValue.length - 1) {
return this.range().cast(arg as CoercibleFromMap<SortToExprMap<RangeSort, Name>, Name>);
}
return this.domain_n(i).cast(arg as any);
});
if (args.length <= 1) {
throw new Z3Error("Array store requires both index and value arguments");
}
if (args.length === 2) {
return _toExpr(check(Z3.mk_store(contextPtr, this.ast, args[0].ast, args[1].ast))) as SMTArray<Name, DomainSort, RangeSort>;
}
const _idxs = args.slice(0, args.length - 1).map(arg => arg.ast);
return _toExpr(check(Z3.mk_store_n(contextPtr, this.ast, _idxs, args[args.length - 1].ast))) as SMTArray<Name, DomainSort, RangeSort>;
}
}
class AstVectorImpl<Item extends AnyAst<Name>> { class AstVectorImpl<Item extends AnyAst<Name>> {
declare readonly __typename: AstVector['__typename']; declare readonly __typename: AstVector['__typename'];
readonly ctx: Context<Name>; readonly ctx: Context<Name>;
@ -1744,20 +1929,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return this.values(); return this.values();
} }
*entries(): IterableIterator<[number, Item]> { * entries(): IterableIterator<[number, Item]> {
const length = this.length(); const length = this.length();
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
yield [i, this.get(i)]; yield [i, this.get(i)];
} }
} }
*keys(): IterableIterator<number> { * keys(): IterableIterator<number> {
for (let [key] of this.entries()) { for (let [key] of this.entries()) {
yield key; yield key;
} }
} }
*values(): IterableIterator<Item> { * values(): IterableIterator<Item> {
for (let [, value] of this.entries()) { for (let [, value] of this.entries()) {
yield value; yield value;
} }
@ -1842,7 +2027,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return Z3.ast_map_size(contextPtr, this.ptr); return Z3.ast_map_size(contextPtr, this.ptr);
} }
*entries(): IterableIterator<[Key, Value]> { * entries(): IterableIterator<[Key, Value]> {
for (const key of this.keys()) { for (const key of this.keys()) {
yield [key, this.get(key)]; yield [key, this.get(key)];
} }
@ -1852,11 +2037,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
return new AstVectorImpl(Z3.ast_map_keys(contextPtr, this.ptr)); return new AstVectorImpl(Z3.ast_map_keys(contextPtr, this.ptr));
} }
*values(): IterableIterator<Value> { * values(): IterableIterator<Value> {
for (const [_, value] of this.entries()) { for (const [_, value] of this.entries()) {
yield value; yield value;
} }
} }
get(key: Key): Value { get(key: Key): Value {
return _toAst(check(Z3.ast_map_find(contextPtr, this.ptr, key.ast))) as Value; return _toAst(check(Z3.ast_map_find(contextPtr, this.ptr, key.ast))) as Value;
} }
@ -1928,6 +2114,9 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
isBitVecSort, isBitVecSort,
isBitVec, isBitVec,
isBitVecVal, // TODO fix ordering isBitVecVal, // TODO fix ordering
isArraySort,
isArray,
isConstArray,
isProbe, isProbe,
isTactic, isTactic,
isAstVector, isAstVector,
@ -1946,6 +2135,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
Int, Int,
Real, Real,
BitVec, BitVec,
Array,
//////////////// ////////////////
// Operations // // Operations //

File diff suppressed because it is too large Load diff