/*++ Copyright (c) 2012 Microsoft Corporation Module Name: NativeContext.cs Abstract: Z3 Managed API: Native Context Author: Christoph Wintersteiger (cwinter) 2012-03-22 John Fleisher, Nikolaj Bjorner (nbjorner) 2022-03-01 --*/ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; namespace Microsoft.Z3 { using Z3_app = System.IntPtr; using Z3_ast = System.IntPtr; using Z3_ast_vector = System.IntPtr; using Z3_func_decl = System.IntPtr; using Z3_pattern = System.IntPtr; using Z3_solver = System.IntPtr; using Z3_sort = System.IntPtr; using Z3_stats = System.IntPtr; using Z3_symbol = System.IntPtr; /// /// The main interaction with Z3 happens via the Context. /// NativeContext allows for efficient wrapper-reduced interaction with Z3 /// expressions. /// public class NativeContext : IDisposable { /// /// Constructor. /// /// /// The following parameters can be set: /// - proof (Boolean) Enable proof generation /// - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting /// - trace (Boolean) Tracing support for VCC /// - trace_file_name (String) Trace out file for VCC traces /// - timeout (unsigned) default timeout (in milliseconds) used for solvers /// - well_sorted_check type checker /// - auto_config use heuristics to automatically select solver and configure it /// - model model generation for solvers, this parameter can be overwritten when creating a solver /// - model_validate validate models produced by solvers /// - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver /// Note that in previous versions of Z3, this constructor was also used to set global and module parameters. /// For this purpose we should now use /// public NativeContext(Dictionary settings) : base() { Debug.Assert(settings != null); lock (creation_lock) { IntPtr cfg = Native.Z3_mk_config(); foreach (KeyValuePair kv in settings) Native.Z3_set_param_value(cfg, kv.Key, kv.Value); m_ctx = Native.Z3_mk_context(cfg); Native.Z3_del_config(cfg); InitContext(); } } #region Arithmetic /// /// Create an expression representing t[0] + t[1] + .... /// public Z3_ast MkAdd(params Z3_ast[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != IntPtr.Zero)); return Native.Z3_mk_add(nCtx, (uint)(t?.Length ?? 0), t); } /// /// Create an expression representing t[0] * t[1] * .... /// public Z3_ast MkMul(params Z3_ast[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != IntPtr.Zero)); var ts = t.ToArray(); return Native.Z3_mk_mul(nCtx, (uint)(ts?.Length ?? 0), ts); } /// /// Create an expression representing t[0] - t[1] - .... /// public Z3_ast MkSub(params Z3_ast[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != IntPtr.Zero)); var ts = t.ToArray(); return Native.Z3_mk_sub(nCtx, (uint)(ts?.Length ?? 0), ts); } /// /// Create an expression representing t1 / t2. /// public Z3_ast MkDiv(Z3_ast t1, Z3_ast t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_div(nCtx, t1, t2); } /// /// Create an expression representing t1 <= t2 /// public Z3_ast MkLe(Z3_ast t1, Z3_ast t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_le(nCtx, t1, t2); } /// /// Create an expression representing t1 < t2 /// public Z3_ast MkLt(Z3_ast t1, Z3_ast t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_lt(nCtx, t1, t2); } /// /// Create an expression representing t1 >= t2 /// public Z3_ast MkGe(Z3_ast t1, Z3_ast t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_ge(nCtx, t1, t2); } /// /// Create an expression representing t1 > t2 /// public Z3_ast MkGt(Z3_ast t1, Z3_ast t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_gt(nCtx, t1, t2); } /// /// Unsigned less-than /// /// /// The arguments must have the same bit-vector sort. /// public Z3_ast MkBvUlt(Z3_ast t1, Z3_ast t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvult(nCtx, t1, t2); } /// /// Unsigned less-than-equal /// /// /// The arguments must have the same bit-vector sort. /// public Z3_ast MkBvUle(Z3_ast t1, Z3_ast t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvule(nCtx, t1, t2); } /// /// Creates the equality = . /// public Z3_ast MkEq(Z3_ast x, Z3_ast y) { Debug.Assert(x != IntPtr.Zero); Debug.Assert(y != IntPtr.Zero); return Native.Z3_mk_eq(nCtx, x, y); } /// /// Mk an expression representing not(a). /// public Z3_ast MkNot(Z3_ast a) { Debug.Assert(a != IntPtr.Zero); return Native.Z3_mk_not(nCtx, a); } /// /// Create an expression representing t[0] and t[1] and .... /// public Z3_ast MkAnd(params Z3_ast[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != IntPtr.Zero)); return Native.Z3_mk_and(nCtx, (uint)(t?.Length ?? 0), t); } /// /// Create an expression representing t[0] or t[1] or .... /// public Z3_ast MkOr(params Z3_ast[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != IntPtr.Zero)); return Native.Z3_mk_or(nCtx, (uint)(t?.Length ?? 0), t); } /// /// Create a real numeral. /// /// A string representing the Term value in decimal notation. /// A Term with value and sort Real public Z3_ast MkReal(string v) { Debug.Assert(!string.IsNullOrEmpty(v)); return Native.Z3_mk_numeral(nCtx, v, RealSort); } /// /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// /// Value of the numeral /// Sort of the numeral public Z3_ast MkNumeral(int v, Z3_sort sort) { Debug.Assert(sort != IntPtr.Zero); return Native.Z3_mk_int(nCtx, v, sort); } /// /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// /// Value of the numeral /// Sort of the numeral public Z3_ast MkNumeral(uint v, Z3_sort sort) { Debug.Assert(sort != null); return Native.Z3_mk_unsigned_int(nCtx, v, sort); } /// /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// /// Value of the numeral /// Sort of the numeral public Z3_ast MkNumeral(long v, Z3_sort sort) { Debug.Assert(sort != null); return Native.Z3_mk_int64(nCtx, v, sort); } /// /// Create an expression representing an if-then-else: ite(t1, t2, t3). /// /// An expression with Boolean sort /// An expression /// An expression with the same sort as public Z3_ast MkIte(Z3_ast t1, Z3_ast t2, Z3_ast t3) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); Debug.Assert(t3 != IntPtr.Zero); return Native.Z3_mk_ite(nCtx, t1, t2, t3); } /// /// Create an expression representing t1 -> t2. /// public Z3_ast MkImplies(Z3_ast t1, Z3_ast t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_implies(nCtx, t1, t2); } #endregion #region Sort /// /// Integer Sort /// public Z3_sort IntSort => Native.Z3_mk_int_sort(nCtx); /// /// Returns the "singleton" BoolSort for this NativeContext /// public Z3_sort BoolSort => Native.Z3_mk_bool_sort(nCtx); /// /// Returns the "singleton" RealSort for this NativeContext /// public Z3_sort RealSort => Native.Z3_mk_real_sort(nCtx); /// /// Returns the BvSort for size in this NativeContext /// public Z3_sort MkBvSort(uint size) => Native.Z3_mk_bv_sort(nCtx, size); /// /// returns ListSort /// /// /// /// /// /// /// /// /// /// The list algebraic datatype public Z3_sort MkListSort(string name, Z3_sort elemSort, out Z3_func_decl inil, out Z3_func_decl iisnil, out Z3_func_decl icons, out Z3_func_decl iiscons, out Z3_func_decl ihead, out Z3_func_decl itail) { Debug.Assert(!string.IsNullOrEmpty(name)); Debug.Assert(elemSort != IntPtr.Zero); IntPtr nil = IntPtr.Zero, isnil = IntPtr.Zero, cons = IntPtr.Zero, iscons = IntPtr.Zero, head = IntPtr.Zero, tail = IntPtr.Zero; var symbol = Native.Z3_mk_string_symbol(nCtx, name); var sort = Native.Z3_mk_list_sort(nCtx, symbol, elemSort, ref nil, ref isnil, ref cons, ref iscons, ref head, ref tail); inil = nil; iisnil = isnil; icons = cons; iiscons = iscons; ihead = head; itail = tail; return sort; } /// /// Create a new array sort. /// public Z3_sort MkArraySort(Z3_sort domain, Z3_sort range) { Debug.Assert(domain != IntPtr.Zero); Debug.Assert(range != IntPtr.Zero); return Native.Z3_mk_array_sort(nCtx, domain, range); } /// /// Create a new tuple sort. /// public Z3_sort MkTupleSort(Z3_symbol name, Z3_symbol[] fieldNames, Z3_sort[] fieldSorts, out Z3_func_decl constructor, Z3_func_decl[] projections) { Debug.Assert(name != IntPtr.Zero); Debug.Assert(fieldNames != null); Debug.Assert(fieldNames.All(fn => fn != IntPtr.Zero)); Debug.Assert(fieldSorts == null || fieldSorts.All(fs => fs != IntPtr.Zero)); var numFields = (uint)(fieldNames?.Length ?? 0); constructor = IntPtr.Zero; return Native.Z3_mk_tuple_sort(nCtx, name, numFields, fieldNames, fieldSorts, ref constructor, projections); } #endregion #region Propositional /// /// The true Term. /// public Z3_ast MkTrue() => Native.Z3_mk_true(nCtx); /// /// The false Term. /// public Z3_ast MkFalse() => Native.Z3_mk_false(nCtx); /// /// Creates a Boolean value. /// public Z3_ast MkBool(bool value) => value ? MkTrue() : MkFalse(); /// /// Create an expression representing t1 iff t2. /// public Z3_ast MkIff(Z3_ast t1, Z3_ast t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_iff(nCtx, t1, t2); } #endregion #region Constants /// /// Creates a new Constant of sort and named . /// public Z3_ast MkConst(string name, Z3_sort range) { Debug.Assert(!string.IsNullOrEmpty(name)); Debug.Assert(range != IntPtr.Zero); return Native.Z3_mk_const(nCtx, MkStringSymbol(name), range); } #endregion #region Symbol /// /// Return a ptr to symbol for string /// /// /// public Z3_symbol MkStringSymbol(string name) { Debug.Assert(!string.IsNullOrEmpty(name)); return Native.Z3_mk_string_symbol(nCtx, name); } #endregion #region Terms /// /// Create a new function application. /// public Z3_ast MkApp(Z3_func_decl f, params Z3_ast[] args) { Debug.Assert(f != IntPtr.Zero); Debug.Assert(args == null || args.All(a => a != IntPtr.Zero)); return Native.Z3_mk_app(nCtx, f, (uint)(args?.Length ?? 0), args); } #endregion #region Bound Variables /// /// Creates a new bound variable. /// /// The de-Bruijn index of the variable /// The sort of the variable public Z3_ast MkBound(uint index, Z3_sort sort) { Debug.Assert(sort != IntPtr.Zero); return Native.Z3_mk_bound(nCtx, index, sort); } #endregion #region Bit-vectors /// /// Bitwise conjunction. /// /// The arguments must have a bit-vector sort. public Z3_ast_vector MkBvAnd(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvand(nCtx, t1, t2); } /// /// Bitwise negation. /// /// The argument must have a bit-vector sort. public Z3_ast_vector MkBvNot(Z3_ast_vector t) { Debug.Assert(t != IntPtr.Zero); return Native.Z3_mk_bvnot(nCtx, t); } /// /// Standard two's complement unary minus. /// /// The arguments must have a bit-vector sort. public Z3_ast_vector MkBvNeg(Z3_ast_vector t) { Debug.Assert(t != IntPtr.Zero); return Native.Z3_mk_bvneg(nCtx, t); } /// /// Standard two's complement unary minus. /// /// The arguments must have a bit-vector sort. public Z3_ast_vector MkBVNeg(Z3_ast_vector t) { Debug.Assert(t != IntPtr.Zero); return Native.Z3_mk_bvneg(nCtx, t); } /// /// Two's complement addition. /// /// The arguments must have the same bit-vector sort. public Z3_ast_vector MkBvAdd(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvadd(nCtx, t1, t2); } /// /// Bit-vector extraction. /// /// /// Extract the bits down to from a bitvector of /// size m to yield a new bitvector of size n, where /// n = high - low + 1. /// The argument must have a bit-vector sort. /// public Z3_ast_vector MkBvExtract(uint high, uint low, Z3_ast_vector t) { Debug.Assert(t != IntPtr.Zero); return Native.Z3_mk_extract(nCtx, high, low, t); } /// /// Bit-vector sign extension. /// /// /// Sign-extends the given bit-vector to the (signed) equivalent bitvector of /// size m+i, where \c m is the size of the given bit-vector. /// The argument must have a bit-vector sort. /// public Z3_ast_vector MkBvSignExt(uint i, Z3_ast_vector t) { Debug.Assert(t != IntPtr.Zero); return Native.Z3_mk_sign_ext(nCtx, i, t); } /// /// Bit-vector zero extension. /// /// /// Extend the given bit-vector with zeros to the (unsigned) equivalent /// bitvector of size m+i, where \c m is the size of the /// given bit-vector. /// The argument must have a bit-vector sort. /// public Z3_ast_vector MkBvZeroExt(uint i, Z3_ast_vector t) { Debug.Assert(t != IntPtr.Zero); return Native.Z3_mk_zero_ext(nCtx, i, t); } /// /// Shift left. /// /// /// It is equivalent to multiplication by 2^x where \c x is the value of . /// /// NB. The semantics of shift operations varies between environments. This /// definition does not necessarily capture directly the semantics of the /// programming language or assembly architecture you are modeling. /// /// The arguments must have a bit-vector sort. /// public Z3_ast_vector MkBvShl(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvshl(nCtx, t1, t2); } /// /// Logical shift right /// /// /// It is equivalent to unsigned division by 2^x where \c x is the value of . /// /// NB. The semantics of shift operations varies between environments. This /// definition does not necessarily capture directly the semantics of the /// programming language or assembly architecture you are modeling. /// /// The arguments must have a bit-vector sort. /// public Z3_ast_vector MkBvLshr(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvlshr(nCtx, t1, t2); } /// /// Arithmetic shift right /// /// /// It is like logical shift right except that the most significant /// bits of the result always copy the most significant bit of the /// second argument. /// /// NB. The semantics of shift operations varies between environments. This /// definition does not necessarily capture directly the semantics of the /// programming language or assembly architecture you are modeling. /// /// The arguments must have a bit-vector sort. /// public Z3_ast_vector MkBvAshr(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvashr(nCtx, t1, t2); } /// /// Two's complement signed less-than /// /// /// The arguments must have the same bit-vector sort. /// public Z3_ast MkBvSlt(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvslt(nCtx, t1, t2); } /// /// Two's complement multiplication. /// /// The arguments must have the same bit-vector sort. public Z3_ast_vector MkBvMul(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvmul(nCtx, t1, t2); } /// /// Unsigned division. /// /// /// It is defined as the floor of t1/t2 if \c t2 is /// different from zero. If t2 is zero, then the result /// is undefined. /// The arguments must have the same bit-vector sort. /// public Z3_ast_vector MkBvUdiv(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvudiv(nCtx, t1, t2); } /// /// Signed division. /// /// /// It is defined in the following way: /// /// - The \c floor of t1/t2 if \c t2 is different from zero, and t1*t2 >= 0. /// /// - The \c ceiling of t1/t2 if \c t2 is different from zero, and t1*t2 < 0. /// /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public Z3_ast_vector MkBvSdiv(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvsdiv(nCtx, t1, t2); } /// /// Unsigned remainder. /// /// /// It is defined as t1 - (t1 /u t2) * t2, where /u represents unsigned division. /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public Z3_ast_vector MkBvUrem(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvurem(nCtx, t1, t2); } /// /// Signed remainder. /// /// /// It is defined as t1 - (t1 /s t2) * t2, where /s represents signed division. /// The most significant bit (sign) of the result is equal to the most significant bit of \c t1. /// /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public Z3_ast_vector MkBvSrem(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvsrem(nCtx, t1, t2); } /// /// Two's complement subtraction. /// /// The arguments must have the same bit-vector sort. public Z3_ast_vector MkBvSub(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvsub(nCtx, t1, t2); } /// /// Bitwise disjunction. /// /// The arguments must have a bit-vector sort. public Z3_ast_vector MkBvOr(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != IntPtr.Zero); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvor(nCtx, t1, t2); } /// /// Bitwise XOR. /// /// The arguments must have a bit-vector sort. public Z3_ast_vector MkBvXor(Z3_ast_vector t1, Z3_ast_vector t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != IntPtr.Zero); return Native.Z3_mk_bvxor(nCtx, t1, t2); } #endregion #region Quantifiers /// /// Create a universal Quantifier. /// /// /// Creates a forall formula, where is the weight, /// is an array of patterns, is an array /// with the sorts of the bound variables, is an array with the /// 'names' of the bound variables, and is the body of the /// quantifier. Quantifiers are associated with weights indicating the importance of /// using the quantifier during instantiation. /// Note that the bound variables are de-Bruijn indices created using . /// Z3 applies the convention that the last element in and /// refers to the variable with index 0, the second to last element /// of and refers to the variable /// with index 1, etc. /// /// the sorts of the bound variables. /// names of the bound variables /// the body of the quantifier. /// quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. /// array containing the patterns created using MkPattern. /// array containing the anti-patterns created using MkPattern. /// optional symbol to track quantifier. /// optional symbol to track skolem constants. public Z3_ast MkForall(Z3_sort[] sorts, Z3_symbol[] names, Z3_ast body, uint weight = 1, Z3_ast[] patterns = null, Z3_ast[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { return MkQuantifier(true, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Same as MkForAll but defaults to "forall" = false /// Create an existential Quantifier. /// /// /// /// /// /// /// /// /// /// public Z3_ast MkExists(Z3_sort[] sorts, Z3_symbol[] names, Z3_ast body, uint weight = 1, Z3_ast[] patterns = null, Z3_ast[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { return MkQuantifier(false, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create a quantified expression either forall or exists /// /// /// /// /// /// /// /// /// /// /// private Z3_ast MkQuantifier(bool is_forall, Z3_sort[] sorts, Z3_symbol[] names, Z3_ast body, uint weight, Z3_ast[] patterns, Z3_ast[] noPatterns, Symbol quantifierID, Symbol skolemID) { Debug.Assert(sorts != null); Debug.Assert(names != null); Debug.Assert(body != null); Debug.Assert(sorts.Length == names.Length); Debug.Assert(sorts.All(s => s != IntPtr.Zero)); Debug.Assert(names.All(n => n != IntPtr.Zero)); Debug.Assert(patterns == null || patterns.All(p => p != IntPtr.Zero)); Debug.Assert(noPatterns == null || noPatterns.All(np => np != IntPtr.Zero)); if (noPatterns == null && quantifierID == null && skolemID == null) { return Native.Z3_mk_quantifier(nCtx, (byte)(is_forall ? 1 : 0), weight, (uint)(patterns?.Length ?? 0), patterns, (uint)(sorts?.Length ?? 0), sorts, names, body); } else { return Native.Z3_mk_quantifier_ex(nCtx, (byte)(is_forall ? 1 : 0), weight, AST.GetNativeObject(quantifierID), AST.GetNativeObject(skolemID), (uint)(patterns?.Length ?? 0), patterns, (uint)(noPatterns?.Length ?? 0), noPatterns, (uint)(sorts?.Length ?? 0), sorts, names, body); } } #endregion #region Options /// /// Selects the format used for pretty-printing expressions. /// /// /// The default mode for pretty printing expressions is to produce /// SMT-LIB style output where common subexpressions are printed /// at each occurrence. The mode is called Z3_PRINT_SMTLIB_FULL. /// To print shared common subexpressions only once, /// use the Z3_PRINT_LOW_LEVEL mode. /// To print in way that conforms to SMT-LIB standards and uses let /// expressions to share common sub-expressions use Z3_PRINT_SMTLIB_COMPLIANT. /// /// /// /// /// public Z3_ast_print_mode PrintMode { set { Native.Z3_set_ast_print_mode(nCtx, (uint)value); } } #endregion #region Arrays /// /// Create a constant array. /// /// /// The resulting term is an array, such that a selecton an arbitrary index /// produces the value v. /// public Z3_ast MkConstArray(Z3_sort domain, Z3_ast v) { Debug.Assert(domain != IntPtr.Zero); Debug.Assert(v != IntPtr.Zero); return Native.Z3_mk_const_array(nCtx, domain, v); } /// /// Array update. /// /// /// The node a must have an array sort [domain -> range], /// i must have sort domain, /// v must have sort range. The sort of the result is [domain -> range]. /// The semantics of this function is given by the theory of arrays described in the SMT-LIB /// standard. See http://smtlib.org for more details. /// The result of this function is an array that is equal to a /// (with respect to select) /// on all indices except for i, where it maps to v /// (and the select of a with /// respect to i may be a different value). /// public Z3_ast MkStore(Z3_ast a, Z3_ast i, Z3_ast v) { Debug.Assert(a != IntPtr.Zero); Debug.Assert(i != IntPtr.Zero); Debug.Assert(v != IntPtr.Zero); return Native.Z3_mk_store(nCtx, a, i, v); } /// /// Array read. /// /// /// The argument array is the array and index is the index /// of the array that gets read. /// /// The node array must have an array sort [domain -> range], /// and index must have the sort domain. /// The sort of the result is range. /// public Z3_ast MkSelect(Z3_ast array, Z3_ast index) { Debug.Assert(array != IntPtr.Zero); Debug.Assert(index != IntPtr.Zero); return Native.Z3_mk_select(nCtx, array, index); } /// /// Access the array default value. /// /// /// Produces the default range value, for arrays that can be represented as /// finite maps with a default range value. /// public Z3_ast MkDefault(Z3_ast a) { Debug.Assert(a != null); return Native.Z3_mk_array_default(nCtx, a); } #endregion #region Function Declarations /// /// Creates a new function declaration. /// public Z3_func_decl MkFuncDecl(string name, Z3_sort[] domain, Z3_sort range) { Debug.Assert(!string.IsNullOrEmpty(name)); Debug.Assert(range != IntPtr.Zero); Debug.Assert(domain != null); Debug.Assert(domain.All(d => d != IntPtr.Zero)); var symbol = Native.Z3_mk_string_symbol(nCtx, name); return Native.Z3_mk_func_decl(nCtx, symbol, (uint)(domain?.Length ?? 0), domain, range); } /// /// Creates a new function declaration. /// public Z3_func_decl MkFuncDecl(string name, Z3_sort domain, Z3_sort range) { Debug.Assert(!string.IsNullOrEmpty(name)); Debug.Assert(range != IntPtr.Zero); Debug.Assert(domain != IntPtr.Zero); var symbol = Native.Z3_mk_string_symbol(nCtx, name); var q = new Z3_sort[] { domain }; return Native.Z3_mk_func_decl(nCtx, symbol, (uint)q.Length, q, range); } /// /// Creates a fresh function declaration with a name prefixed with . /// public Z3_func_decl MkFreshFuncDecl(string prefix, Z3_sort[] domain, Z3_sort range) { Debug.Assert(domain != null); Debug.Assert(range != IntPtr.Zero); Debug.Assert(domain.All(d => d != IntPtr.Zero)); return Native.Z3_mk_fresh_func_decl(nCtx, prefix, (uint)(domain?.Length ?? 0), domain, range); } /// /// Creates a new constant function declaration. /// public Z3_func_decl MkConstDecl(string name, Z3_sort range) { Debug.Assert(range != IntPtr.Zero); var symbol = Native.Z3_mk_string_symbol(nCtx, name); return Native.Z3_mk_func_decl(nCtx, symbol, 0, new IntPtr[0], range); } /// /// Get domain for a funcdecl /// /// /// public Z3_sort[] GetDomain(Z3_func_decl fdecl) { Debug.Assert(fdecl != IntPtr.Zero); var sz = Native.Z3_get_domain_size(nCtx, fdecl); var domain = new Z3_sort[sz]; for (uint i = 0; i < sz; i++) { domain[i] = Native.Z3_get_domain(nCtx, fdecl, i); } return domain; } /// /// Get range for a funcdecl /// /// /// public Z3_sort GetRange(Z3_func_decl fdecl) { Debug.Assert(fdecl != IntPtr.Zero); return Native.Z3_get_range(nCtx, fdecl); } #endregion #region Quantifier Patterns /// /// Create a quantifier pattern. /// public Z3_pattern MkPattern(params Z3_ast[] terms) { Debug.Assert(terms != null); if (terms == null || terms.Length == 0) throw new Z3Exception("Cannot create a pattern from zero terms"); return Native.Z3_mk_pattern(nCtx, (uint)terms.Length, terms); } #endregion #region Solver /// /// Creates a new (incremental) solver. /// public NativeSolver MkSimpleSolver() { Z3_solver nSolver = Native.Z3_mk_simple_solver(nCtx); return new NativeSolver(this, nSolver); } #endregion #region Utilities /// /// Get the sort kind from IntPtr /// public Z3_sort_kind GetSortKind(Z3_sort sort) { Debug.Assert(sort != IntPtr.Zero); return (Z3_sort_kind)Native.Z3_get_sort_kind(nCtx, sort); } /// /// Get the AST kind from IntPtr /// public Z3_ast_kind GetAstKind(Z3_ast ast) { Debug.Assert(ast != IntPtr.Zero); return (Z3_ast_kind)Native.Z3_get_ast_kind(nCtx, ast); } /// /// Get the Decl kind from IntPtr /// public Z3_decl_kind GetDeclKind(Z3_func_decl decl) { Debug.Assert(decl != IntPtr.Zero); return (Z3_decl_kind)Native.Z3_get_decl_kind(nCtx, decl); } /// /// Get Sort for AST /// public Z3_sort GetSort(Z3_ast ast) { Debug.Assert(ast != IntPtr.Zero); return Native.Z3_get_sort(nCtx, ast); } /// /// Get the arguments for app /// /// /// public Z3_ast[] GetAppArgs(Z3_app app) { var numArgs = GetNumArgs(app); var args = new Z3_ast[numArgs]; for (uint i = 0; i < numArgs; i++) { args[i] = GetAppArg(app, i); } return args; } /// /// Return number of arguments for app /// /// /// public uint GetNumArgs(Z3_app app) { Debug.Assert(app != IntPtr.Zero); return Native.Z3_get_app_num_args(nCtx, app); } internal Z3_ast GetAppArg(Z3_app app, uint i) => Native.Z3_get_app_arg(nCtx, app, i); /// /// Get App Decl from IntPtr /// public Z3_func_decl GetAppDecl(Z3_ast ast) { Debug.Assert(ast != IntPtr.Zero); return Native.Z3_get_app_decl(nCtx, ast); } /// /// Get string name for Decl /// /// /// public string GetDeclName(Z3_func_decl decl) { Debug.Assert(decl != IntPtr.Zero); var namePtr = Native.Z3_get_decl_name(nCtx, decl); return Marshal.PtrToStringAnsi(namePtr); } /// /// Get size of BitVector Sort /// public uint GetBvSortSize(Z3_sort bvSort) { Debug.Assert(bvSort != IntPtr.Zero); return Native.Z3_get_bv_sort_size(nCtx, bvSort); } /// /// Get the domain IntPtr for Sort /// public Z3_sort GetArraySortDomain(Z3_ast array) { Debug.Assert(array != IntPtr.Zero); return Native.Z3_get_array_sort_domain(nCtx, array); } /// /// Get the range IntPtr for Sort /// public Z3_sort GetArraySortRange(Z3_ast array) { Debug.Assert(array != IntPtr.Zero); return Native.Z3_get_array_sort_range(nCtx, array); } /// /// Try to get integer from AST /// /// /// /// public bool TryGetNumeralInt(Z3_ast v, out int i) { Debug.Assert(v != IntPtr.Zero); int result = i = 0; if (Native.Z3_get_numeral_int(nCtx, v, ref result) == 0) { return false; } i = result; return true; } /// /// Try to get uint from AST /// /// /// /// public bool TryGetNumeralUInt(Z3_ast v, out uint u) { Debug.Assert(v != IntPtr.Zero); uint result = u = 0; if (Native.Z3_get_numeral_uint(nCtx, v, ref result) == 0) { return false; } u = result; return true; } /// /// Try to get long from AST /// /// /// /// public bool TryGetNumeralInt64(Z3_ast v, out long i) { Debug.Assert(v != IntPtr.Zero); long result = i = 0; if (Native.Z3_get_numeral_int64(nCtx, v, ref result) == 0) { return false; } i = result; return true; } /// /// Try get ulong from AST /// /// /// /// public bool TryGetNumeralUInt64(Z3_ast v, out ulong u) { Debug.Assert(v != IntPtr.Zero); ulong result = u = 0; if (Native.Z3_get_numeral_uint64(nCtx, v, ref result) == 0) { return false; } u = result; return true; } /// /// Get string for numeral ast /// /// /// public string GetNumeralString(Z3_ast v) { Debug.Assert(v != IntPtr.Zero); return Native.Z3_get_numeral_string(nCtx, v); } /// /// Get printable string representing Z3_ast /// /// /// public string ToString(Z3_ast ast) { Debug.Assert(ast != IntPtr.Zero); return Native.Z3_ast_to_string(nCtx, ast); } /// /// Enable or disable warning messages /// /// public void ToggleWarningMessages(bool turnOn) => Native.Z3_toggle_warning_messages(turnOn ? (byte)1 : (byte)0); #endregion #region Internal internal static Object creation_lock = new Object(); internal IntPtr m_ctx = IntPtr.Zero; internal Native.Z3_error_handler m_n_err_handler = null; internal IntPtr nCtx { get { return m_ctx; } } internal void NativeErrorHandler(IntPtr ctx, Z3_error_code errorCode) { // Do-nothing error handler. The wrappers in Z3.Native will throw exceptions upon errors. } internal void InitContext() { PrintMode = Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT; m_n_err_handler = new Native.Z3_error_handler(NativeErrorHandler); // keep reference so it doesn't get collected. Native.Z3_set_error_handler(m_ctx, m_n_err_handler); GC.SuppressFinalize(this); } #endregion #region Tracing /// /// Enable trace to file /// /// Tag to trace public static void EnableTrace(string tag) { Debug.Assert(!string.IsNullOrEmpty(tag)); Native.Z3_enable_trace(tag); } #endregion #region Dispose /// /// Disposes of the context. /// public void Dispose() { if (m_ctx != IntPtr.Zero) { m_n_err_handler = null; IntPtr ctx = m_ctx; m_ctx = IntPtr.Zero; Native.Z3_del_context(ctx); } else GC.ReRegisterForFinalize(this); } #endregion /// /// Utility to convert a vector object of ast to a .Net array /// /// /// public Z3_ast[] ToArray(Z3_ast_vector vec) { Native.Z3_ast_vector_inc_ref(nCtx, vec); var sz = Native.Z3_ast_vector_size(nCtx, vec); var result = new Z3_ast[sz]; for (uint i = 0; i < sz; ++i) result[i] = Native.Z3_ast_vector_get(nCtx, vec, i); Native.Z3_ast_vector_dec_ref(nCtx, vec); return result; } /// /// Retrieve statistics as an array of entries /// /// /// public Statistics.Entry[] GetStatistics(Z3_stats stats) { Native.Z3_stats_inc_ref(nCtx, stats); var result = Statistics.NativeEntries(nCtx, stats); Native.Z3_stats_dec_ref(nCtx, stats); return result; } } }