tiny.smt.bitvec
Defined in tiny.smt.
Decides formulas over Booleans, fixed-width bit-vectors, arrays and uninterpreted functions by turning them into clauses for a SAT solver, and reads a satisfying assignment back as a value for each constant.
API (19)
Actions
Public operations.
Encoder.assertTerm: Encodes the Boolean termassertionand adds a unit clause that makes it true in every later solve.Encoder.assumeTerm: Encodes the Boolean termassumption, adds its literal to the solver's active assumptions and returns that literal.Encoder.deinit: Frees the bits and array cells of every encoded term and the table that held them.Encoder.encodeBits: Encodes the bit-vector termidand returns one literal per bit, least significant bit first.Encoder.encodeBool: Encodes the Boolean termidand returns the literal that is true exactly when the term is.Encoder.encodeSymbols: Encodes every named constant of theContext, so thatmodelfinds a value for each one.Encoder.init: Returns an encoder overctxandsolverthat allocates withallocator.Encoder.model: Returns aModelthat allocates withallocator, with the value of every named constant and one entry per encoded function application.Encoder.solve: Encodes every named constant of theContext, adds the equal-arguments clauses if no earlier call did, and solves under the active assumptions.FunctionModel.deinit: Frees the name, every entry and the entry list withallocator, forModel.deinitto call for each function.FunctionModelEntry.deinit: Frees every argument value, the argument slice and the result value withallocator, forFunctionModel.deinitto call for each entry.
Types and contracts
Public types and contracts.
EncodeError: The errors the encoder andEncoder.modelreturn besides the solver's and the allocator's, for code that reports why a formula could not be encoded or read back.Encoder: Turns the terms of oneContextinto clauses of onesat.Solver, and reads the solver's assignment back as aModel.Error: The error set of the encoder's functions: any error, returned by every public function of the encoder, so a caller can absorb it into its own error set.FunctionModel: The model of one uninterpreted function: its name and one entry per application the encoder encoded, returned byModel.getFunctionso code can check each application of one function.FunctionModelEntry: One application of an uninterpreted function in a model: its argument values and its result value, for code that checks a function's model.Model: The values a satisfying assignment gives: one per named constant, and one entry per encoded application of each uninterpreted function.ModelEntry: One named constant of a model and its value, for code that walks aModel.ModelValue: The value of a constant, a function argument or a function result in a model: a Boolean, a bit-vector or an array, for code that reads one from aModelto print it or compare it with an expected value.
Source
Source: lib/smt/src/bitvec.zig
zig
//! Decides formulas over Booleans, fixed-width bit-vectors, arrays and uninterpreted functions by//! turning them into clauses for a SAT solver, and reads a satisfying assignment back as a value//! for each constant.//!//! A caller holds a formula and needs to know whether some values of its constants make it true,//! and which values do. A caller also tests a condition without asserting it for good, and after a//! contradiction learns a set of its tested conditions that the formula refutes together.//!//! A SAT solver accepts only clauses over Boolean variables, so every bit-vector operation has to//! become Boolean relations between single bits. An array with an index of w bits has 2 to the//! power w cells, so its size doubles with each index bit. An uninterpreted function has no//! definition: the only thing known about it is that equal arguments give equal results.//!//! [SMT-LIB](https://smt-lib.org/) defines the fixed-size bit-vector operators on every input,//! division by zero and shifts past the width included. The encoder follows those definitions://! unsigned division by zero gives all ones, the remainder by zero is the dividend, a left shift or//! a logical right shift by at least the width gives zero, and an arithmetic right shift by at//! least the width copies the sign bit into every bit.//!//! The encoder (`Encoder`) gives each bit-vector one Boolean variable per bit, least significant//! bit first, and each operator the clauses of and, or and exclusive-or gates over those bits.//! Addition carries from each bit to the next, multiplication adds one shifted partial product per//! bit, and division finds its quotient one bit at a time from the most significant bit down. One//! variable fixed true by a unit clause stands for true, and its negation stands for false, so//! every bit of a Boolean constant or a bit-vector constant is one of those two literals.//!//! An array becomes one cell of element bits for each index value, so the encoder refuses an index//! wider than eight bits with `ArrayIndexTooWide` and an array has at most 256 cells. Two arrays//! are equal when every pair of cells is equal.//!//! For each pair of applications of one function, one clause makes their results equal whenever//! their arguments are equal. The encoder writes these clauses once, the first time a caller//! asserts, assumes or solves, over every application in the `Context`, asserted or not.//!//! The encoder encodes each term once and remembers the result, in a table sized to the `Context`//! at `Encoder.init`, so a term built after `init` fails with `TermOutOfRange`. Integer terms and//! `distinct` fail with `UnsupportedTerm`. `solve` encodes every named constant of the `Context`,//! so a single integer constant fails every solve.//!//! After a satisfiable answer, `Encoder.model` reads the solver's assignment into a `Model`: one//! value per named constant and one entry per encoded function application. A model value holds at//! most 128 bits, and a wider named constant, function argument, function result or array cell//! fails with `ModelValueTooWide`.//!//! A caller builds an `Encoder` over a `Context` and a `sat.Solver`, asserts terms with//! `assertTerm`, assumes terms with `assumeTerm`, and calls `solve`. On a satisfiable answer it//! reads `model`, and on an unsatisfiable one it asks the solver for the unsat core and the proof.const std = @import("std");const sat = @import("sat/root.zig");const term = @import("term.zig");/// The errors the encoder and `Encoder.model` return besides the solver's and the allocator's, for/// code that reports why a formula could not be encoded or read back. The encoder's functions/// return `Error`, which is `anyerror`, so the compiler does not check a switch on these tags for/// completeness.pub const EncodeError = error{ /// A term used as a condition, such as an assertion, an assumption or an operand of `and`, has /// another sort. ExpectedBool, /// A term used as a bit-vector operand has another sort. ExpectedBitVec, /// The operands of an operator have different widths or different kinds, or an application's /// argument count differs from its declaration. SortMismatch, /// The term is an integer term or `distinct`, which the encoder does not support, or it /// compares two values that were never encoded. UnsupportedTerm, /// `Encoder.model` found a constant with no value: the solver has not assigned it, or it was /// never encoded. The error follows a model read before a satisfiable answer, or of a constant /// that `encodeSymbols` has not reached. ModelUnavailable, /// `Encoder.model` found a value wider than 128 bits. ModelValueTooWide, /// The term's index is past the terms that existed when the `Encoder` was built. TermOutOfRange, /// An extract range lies outside its operand, or an operator that needs at least one bit got a /// bit-vector of width 0. InvalidBitVectorRange, /// A term used as an array has another sort. ExpectedArray, /// An array's index is wider than eight bits. ArrayIndexTooWide, /// An application names a function past the declarations of the `Context`. FunctionOutOfRange,};/// The error set of the encoder's functions: any error, returned by every public function of the/// encoder, so a caller can absorb it into its own error set. The set covers `EncodeError`, the/// solver's errors, `error.OutOfMemory` and the errors of `Context.sortOf`. Because it is/// `anyerror`, the compiler cannot list the errors a call returns.pub const Error = anyerror;const max_native_array_index_width = 8;const ArrayValue = struct { index_width: u32, element_width: u32, cells: [][]sat.Literal,};const Value = union(enum) { none, bool: sat.Literal, bits: []sat.Literal, array: ArrayValue,};/// The value of a constant, a function argument or a function result in a model: a Boolean, a/// bit-vector or an array, for code that reads one from a `Model` to print it or compare it with an/// expected value. A value that holds an array owns its cells, so its owner calls `deinit`.pub const ModelValue = union(enum) { /// A Boolean value. bool: bool, /// A bit-vector value: its bits as an unsigned number, least significant bit as bit 0, and its /// width. bitvec: struct { value: u128, width: u32, }, /// An array value: its index width, its element width and one number per cell, the cell for /// index i at position i. The cells are a slice the value owns. array: struct { index_width: u32, element_width: u32, cells: []u128, }, /// Frees an array value's cells with `allocator`, which has to be the allocator that made them, /// for a caller that holds a value outside a `Model`. The function frees nothing for a Boolean /// or a bit-vector. pub fn deinit(self: *ModelValue, allocator: std.mem.Allocator) void { switch (self.*) { .array => |array| allocator.free(array.cells), else => {}, } self.* = undefined; } /// Writes the value in SMT-LIB syntax: `true` or `false`, `(_ bvV W)`, or an array as /// `(array (_ BitVec i) (_ BitVec e) 0->(_ bvV e) ...)` with one index and value per cell, for /// `Model.write` to call for every value. The function returns only the writer's errors. pub fn write(self: ModelValue, writer: *std.Io.Writer) std.Io.Writer.Error!void { switch (self) { .bool => |value| try writer.writeAll(if (value) "true" else "false"), .bitvec => |value| try writer.print("(_ bv{d} {d})", .{ value.value, value.width }), .array => |value| { try writer.print("(array (_ BitVec {d}) (_ BitVec {d})", .{ value.index_width, value.element_width }); for (value.cells, 0..) |cell, index| { try writer.print(" {d}->(_ bv{d} {d})", .{ index, cell, value.element_width }); } try writer.writeAll(")"); }, } }};/// One named constant of a model and its value, for code that walks a `Model`.pub const ModelEntry = struct { /// The constant's name, a copy the `Model` owns and frees. name: []u8, /// The constant's value, which the `Model` owns and frees. value: ModelValue,};/// One application of an uninterpreted function in a model: its argument values and its result/// value, for code that checks a function's model.pub const FunctionModelEntry = struct { /// The argument values in declaration order, a slice the entry owns. arguments: []ModelValue, /// The value of the application's result, which the entry owns. result: ModelValue, /// Frees every argument value, the argument slice and the result value with `allocator`, for /// `FunctionModel.deinit` to call for each entry. pub fn deinit(self: *FunctionModelEntry, allocator: std.mem.Allocator) void { for (self.arguments) |*argument| { argument.deinit(allocator); } allocator.free(self.arguments); self.result.deinit(allocator); self.* = undefined; }};/// The model of one uninterpreted function: its name and one entry per application the encoder/// encoded, returned by `Model.getFunction` so code can check each application of one function. The/// model lists the applications in the formula, and it gives no value for other arguments.pub const FunctionModel = struct { /// The function's name, a copy the model owns. name: []u8, /// The entries in the order of their applications in the `Context`. Two applications with equal /// arguments give two entries with equal results. entries: std.ArrayList(FunctionModelEntry) = .empty, /// Frees the name, every entry and the entry list with `allocator`, for `Model.deinit` to call /// for each function. pub fn deinit(self: *FunctionModel, allocator: std.mem.Allocator) void { allocator.free(self.name); for (self.entries.items) |*entry| { entry.deinit(allocator); } self.entries.deinit(allocator); self.* = undefined; }};/// The values a satisfying assignment gives: one per named constant, and one entry per encoded/// application of each uninterpreted function. A caller reads a constant's value from it with `get`/// or prints it with `write` after `Encoder.model` returns one for a satisfiable answer. The model/// owns every name and value in it, and its owner frees them all with `deinit`.pub const Model = struct { allocator: std.mem.Allocator, /// The named constants and their values, in the order the constants were built. Two constants /// built with one name give two entries, and `get` returns the first. entries: std.ArrayList(ModelEntry) = .empty, /// The model of each uninterpreted function with an encoded application, one per function name. functions: std.ArrayList(FunctionModel) = .empty, /// Returns an empty model that allocates with `allocator`. A caller makes one to build an /// expected model by hand, and `Encoder.model` makes one. The call allocates nothing. pub fn init(allocator: std.mem.Allocator) Model { return .{ .allocator = allocator }; } /// Frees every name, every value and every function model. The owner calls it once when done /// with the values. A value returned by `get` and a pointer returned by `getFunction` are /// invalid afterward. pub fn deinit(self: *Model) void { for (self.entries.items) |*entry| { self.allocator.free(entry.name); entry.value.deinit(self.allocator); } self.entries.deinit(self.allocator); for (self.functions.items) |*function| { function.deinit(self.allocator); } self.functions.deinit(self.allocator); self.* = undefined; } /// Adds the constant `name` with value `value`. `Encoder.model` calls it once per named /// constant. The call copies `name` and takes ownership of `value`, and on failure it frees /// `value`. pub fn append(self: *Model, name: []const u8, value: ModelValue) !void { var owned_value = value; errdefer owned_value.deinit(self.allocator); const owned_name = try self.allocator.dupe(u8, name); errdefer self.allocator.free(owned_name); try self.entries.append(self.allocator, .{ .name = owned_name, .value = owned_value }); } /// Returns the value of the first constant named `name`, or `null` when the model has none. A /// caller reads with it a constant's value after a satisfiable answer, as every encoder test /// does. An array value's cells stay owned by the model, so the caller does not free them. The /// lookup checks each entry in turn. pub fn get(self: *const Model, name: []const u8) ?ModelValue { for (self.entries.items) |entry| { if (std.mem.eql(u8, entry.name, name)) return entry.value; } return null; } /// Adds an entry with argument values `arguments` and result value `result` to the model of the /// function `name`, and creates that function's model on first use. `Encoder.model` calls it /// once per encoded application. The call takes ownership of `arguments` and `result`, and on /// failure it frees them. pub fn appendFunctionApplication(self: *Model, name: []const u8, arguments: []ModelValue, result: ModelValue) !void { var owned_result = result; errdefer { for (arguments) |*argument| { argument.deinit(self.allocator); } self.allocator.free(arguments); owned_result.deinit(self.allocator); } const function = try self.functionModel(name); try function.entries.append(self.allocator, .{ .arguments = arguments, .result = owned_result }); } /// Returns the model of the function `name`, or `null` when no application of it was encoded. A /// caller checks the values an uninterpreted function took with it. The model keeps ownership, /// and the pointer is valid until the model changes or is freed. pub fn getFunction(self: *const Model, name: []const u8) ?*const FunctionModel { for (self.functions.items) |*function| { if (std.mem.eql(u8, function.name, name)) return function; } return null; } /// Writes one `name: value` line per constant, then per function a `name:` line followed by one /// indented `(arguments) -> result` line per entry, with each value in the syntax of /// `ModelValue.write`. A caller prints a model for a person to read with it. The call returns /// only the writer's errors. pub fn write(self: *const Model, writer: *std.Io.Writer) std.Io.Writer.Error!void { for (self.entries.items) |entry| { try writer.print("{s}: ", .{entry.name}); try entry.value.write(writer); try writer.writeAll("\n"); } for (self.functions.items) |function| { try writer.print("{s}:\n", .{function.name}); for (function.entries.items) |entry| { try writer.writeAll(" ("); for (entry.arguments, 0..) |argument, index| { if (index > 0) try writer.writeAll(", "); try argument.write(writer); } try writer.writeAll(") -> "); try entry.result.write(writer); try writer.writeAll("\n"); } } } fn functionModel(self: *Model, name: []const u8) !*FunctionModel { for (self.functions.items) |*item| { if (std.mem.eql(u8, item.name, name)) return item; } const owned_name = try self.allocator.dupe(u8, name); errdefer self.allocator.free(owned_name); try self.functions.append(self.allocator, .{ .name = owned_name }); return &self.functions.items[self.functions.items.len - 1]; }};const BitwiseKind = enum { and_, or_, xor,};const ShiftKind = enum { left, right,};const RotateKind = enum { left, right,};const DivRemResult = struct { quotient: []sat.Literal, remainder: []sat.Literal,};const ExtendKind = enum { zero, sign,};/// Turns the terms of one `Context` into clauses of one `sat.Solver`, and reads the solver's/// assignment back as a `Model`. A caller builds one per solve, over the `Context` that holds its/// terms and the solver that will decide them. The encoder borrows the `Context` and the solver, so/// both have to outlive it. The encoder encodes each term once and remembers the result, and the/// clauses it adds stay in the solver.pub const Encoder = struct { allocator: std.mem.Allocator, ctx: *const term.Context, solver: *sat.Solver, cache: []Value, true_literal: sat.Literal, false_literal: sat.Literal, function_congruence_encoded: bool = false, /// Returns an encoder over `ctx` and `solver` that allocates with `allocator`. A caller builds /// the encoder once the formula's terms exist and before asserting any of them. The call adds /// one variable to the solver and a unit clause that fixes it true, and the encoder uses it as /// the constant true. The encoder sizes its table of encoded terms to the terms of `ctx` at /// this call, so a term built later fails with `TermOutOfRange`. pub fn init(allocator: std.mem.Allocator, ctx: *const term.Context, solver: *sat.Solver) Error!Encoder { const true_variable = try solver.addVariable(); const true_literal = sat.Literal.positive(true_variable); try solver.addClause(&.{true_literal}); const cache = try allocator.alloc(Value, ctx.nodes.items.len); @memset(cache, .none); return .{ .allocator = allocator, .ctx = ctx, .solver = solver, .cache = cache, .true_literal = true_literal, .false_literal = true_literal.negated(), }; } /// Frees the bits and array cells of every encoded term and the table that held them. The owner /// calls it once when done with the encoder. The clauses already added to the solver stay /// there. pub fn deinit(self: *Encoder) void { for (self.cache) |value| { switch (value) { .bits => |bits| self.allocator.free(bits), .array => |array| self.freeArray(array), else => {}, } } self.allocator.free(self.cache); self.* = undefined; } /// Encodes the Boolean term `assertion` and adds a unit clause that makes it true in every /// later solve. A caller states with it each part of the formula that has to hold, as a caller /// does for every assertion of a `Script`. The first call of `assertTerm`, `assumeTerm` or /// `solve` also adds the equal-arguments clauses for every function application of the /// `Context`. The call returns `ExpectedBool` for a term of another sort. pub fn assertTerm(self: *Encoder, assertion: term.Term) Error!void { const literal = try self.encodeBool(assertion); try self.solver.addClause(&.{literal}); try self.encodeFunctionCongruence(); } /// Encodes the Boolean term `assumption`, adds its literal to the solver's active assumptions /// and returns that literal. A caller calls it to test a condition without asserting it for /// good, and matches the returned literal against the solver's unsat core, a set of tested /// conditions that the formula refutes together. The assumption holds in every solve until the /// caller pops its assumption frame or clears the assumptions. The clauses that encode the term /// stay in the solver after the assumption is removed. The first call of `assertTerm`, /// `assumeTerm` or `solve` also adds the equal-arguments clauses for every function /// application. The call returns `ExpectedBool` for a term of another sort. pub fn assumeTerm(self: *Encoder, assumption: term.Term) Error!sat.Literal { const literal = try self.encodeBool(assumption); try self.solver.assume(literal); try self.encodeFunctionCongruence(); return literal; } /// Encodes every named constant of the `Context`, adds the equal-arguments clauses if no /// earlier call did, and solves under the active assumptions. A caller decides the formula with /// it after asserting and assuming its terms. The call returns the solver's answer: /// satisfiable, unsatisfiable or unknown. Because the encoder encodes every named constant, a /// single integer constant in the `Context` makes the call fail with `UnsupportedTerm`, even if /// no assertion uses it. pub fn solve(self: *Encoder) Error!sat.Status { try self.encodeSymbols(); try self.encodeFunctionCongruence(); return try self.solver.solveWithActiveAssumptions(); } /// Encodes the Boolean term `id` and returns the literal that is true exactly when the term is. /// A caller that builds its own clauses around a term, or checks one term's value, gets the /// term's literal with it. The call returns `ExpectedBool` for a term of another sort. pub fn encodeBool(self: *Encoder, id: term.Term) Error!sat.Literal { switch (try self.encode(id)) { .bool => |literal| return literal, else => return EncodeError.ExpectedBool, } } /// Encodes the bit-vector term `id` and returns one literal per bit, least significant bit /// first. A caller that builds its own clauses over a bit-vector's bits gets them with it. The /// encoder owns the returned slice, which stays valid until `deinit`. The call returns /// `ExpectedBitVec` for a term of another sort. pub fn encodeBits(self: *Encoder, id: term.Term) Error![]const sat.Literal { switch (try self.encode(id)) { .bits => |bits| return bits, else => return EncodeError.ExpectedBitVec, } } fn encodeArray(self: *Encoder, id: term.Term) Error!ArrayValue { switch (try self.encode(id)) { .array => |array| return array, else => return EncodeError.ExpectedArray, } } /// Encodes every named constant of the `Context`, so that `model` finds a value for each one. A /// caller that calls `sat.Solver.solve` directly, as the encoder's tests do, calls it first so /// that every constant has a value to read. `solve` calls it. pub fn encodeSymbols(self: *Encoder) Error!void { for (self.ctx.nodes.items, 0..) |node, index| { switch (node) { .symbol => _ = try self.encode(@intCast(index)), else => {}, } } } /// Returns a `Model` that allocates with `allocator`, with the value of every named constant /// and one entry per encoded function application. A caller reads the satisfying values with it /// after a satisfiable answer. The caller owns the model and frees it with `Model.deinit`. The /// call returns `ModelUnavailable` when a constant has no value, as before a satisfiable answer /// or before `encodeSymbols`, `ModelValueTooWide` for a value above 128 bits, and /// `UnsupportedTerm` for an integer constant. pub fn model(self: *const Encoder, allocator: std.mem.Allocator) Error!Model { var result = Model.init(allocator); errdefer result.deinit(); for (self.ctx.nodes.items, 0..) |node, index| { switch (node) { .symbol => |symbol| try result.append(symbol.name, try self.modelValueFor(allocator, @intCast(index), symbol.sort)), .apply => |application| { if (self.cache[index] != .none) try self.appendFunctionApplicationModel(allocator, &result, @intCast(index), application); }, else => {}, } } return result; } fn encode(self: *Encoder, id: term.Term) Error!Value { if (id >= self.cache.len) return EncodeError.TermOutOfRange; if (self.cache[id] != .none) return self.cache[id]; const node = self.ctx.nodes.items[id]; const value = switch (node) { .symbol => |symbol| try self.encodeSymbol(symbol.sort), .apply => |item| try self.encodeApply(item), .bool => |value| Value{ .bool = if (value) self.true_literal else self.false_literal }, .int => return EncodeError.UnsupportedTerm, .bitvec => |value| Value{ .bits = try self.constantBits(value.value, value.width) }, .not => |operand| Value{ .bool = (try self.encodeBool(operand)).negated() }, .and_ => |operands| Value{ .bool = try self.encodeBoolNary(operands, true) }, .or_ => |operands| Value{ .bool = try self.encodeBoolNary(operands, false) }, .implies => |pair| Value{ .bool = try self.orGate((try self.encodeBool(pair.lhs)).negated(), try self.encodeBool(pair.rhs)) }, .eq => |pair| try self.encodeEq(pair.lhs, pair.rhs), .add => return EncodeError.UnsupportedTerm, .mul => return EncodeError.UnsupportedTerm, .le, .lt, .ge, .gt => return EncodeError.UnsupportedTerm, .distinct => return EncodeError.UnsupportedTerm, .bvadd => |pair| Value{ .bits = try self.encodeAdd(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvsub => |pair| Value{ .bits = try self.encodeSub(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvmul => |pair| Value{ .bits = try self.encodeMul(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvule => |pair| Value{ .bool = try self.encodeUle(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvult => |pair| Value{ .bool = try self.encodeUlt(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvsle => |pair| Value{ .bool = try self.encodeSle(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvslt => |pair| Value{ .bool = try self.encodeSlt(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvuaddo => |pair| Value{ .bool = try self.encodeUaddOverflow(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvsaddo => |pair| Value{ .bool = try self.encodeSaddOverflow(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvssubo => |pair| Value{ .bool = try self.encodeSsubOverflow(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvumulo => |pair| Value{ .bool = try self.encodeUmulOverflow(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvsmulo => |pair| Value{ .bool = try self.encodeSmulOverflow(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvnot => |operand| Value{ .bits = try self.encodeBitwiseNot(try self.encodeBits(operand)) }, .bvand => |pair| Value{ .bits = try self.encodeBitwiseBinary(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs), .and_) }, .bvor => |pair| Value{ .bits = try self.encodeBitwiseBinary(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs), .or_) }, .bvxor => |pair| Value{ .bits = try self.encodeBitwiseBinary(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs), .xor) }, .bvshl => |pair| Value{ .bits = try self.encodeShift(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs), .left) }, .bvlshr => |pair| Value{ .bits = try self.encodeShift(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs), .right) }, .bvashr => |pair| Value{ .bits = try self.encodeArithmeticShiftRight(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvrotl => |item| Value{ .bits = try self.encodeRotate(try self.encodeBits(item.operand), item.amount, .left) }, .bvrotr => |item| Value{ .bits = try self.encodeRotate(try self.encodeBits(item.operand), item.amount, .right) }, .bvudiv => |pair| blk: { const result = try self.encodeUnsignedDivRem(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)); self.allocator.free(result.remainder); break :blk Value{ .bits = result.quotient }; }, .bvurem => |pair| blk: { const result = try self.encodeUnsignedDivRem(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)); self.allocator.free(result.quotient); break :blk Value{ .bits = result.remainder }; }, .bvsdiv => |pair| Value{ .bits = try self.encodeSignedDiv(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvsrem => |pair| Value{ .bits = try self.encodeSignedRem(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvsmod => |pair| Value{ .bits = try self.encodeSignedMod(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .array_select => |item| Value{ .bits = try self.encodeArraySelect(try self.encodeArray(item.array), try self.encodeBits(item.index)) }, .array_store => |item| Value{ .array = try self.encodeArrayStore(try self.encodeArray(item.array), try self.encodeBits(item.index), try self.encodeBits(item.value)) }, .bvconcat => |pair| Value{ .bits = try self.encodeConcat(try self.encodeBits(pair.lhs), try self.encodeBits(pair.rhs)) }, .bvextract => |item| Value{ .bits = try self.encodeExtract(try self.encodeBits(item.operand), item.high, item.low) }, .bvzeroext => |item| Value{ .bits = try self.encodeExtend(try self.encodeBits(item.operand), item.extra, .zero) }, .bvsignext => |item| Value{ .bits = try self.encodeExtend(try self.encodeBits(item.operand), item.extra, .sign) }, }; self.cache[id] = value; return value; } fn encodeSymbol(self: *Encoder, sort: term.Sort) Error!Value { return switch (sort) { .bool => .{ .bool = try self.freshLiteral() }, .bitvec => |width| .{ .bits = try self.freshBits(width) }, .array => |array| .{ .array = try self.freshArray(array.index_width, array.element_width) }, .int => EncodeError.UnsupportedTerm, }; } fn encodeApply(self: *Encoder, item: term.ApplyExpr) Error!Value { if (item.function >= self.ctx.functions.items.len) return EncodeError.FunctionOutOfRange; const decl = self.ctx.functions.items[item.function]; if (decl.params.len != item.args.len) return EncodeError.SortMismatch; for (item.args) |argument| { _ = try self.encode(argument); } return try self.encodeSymbol(decl.result); } fn modelValueFor(self: *const Encoder, allocator: std.mem.Allocator, id: term.Term, sort: term.Sort) Error!ModelValue { if (id >= self.cache.len) return EncodeError.TermOutOfRange; return switch (sort) { .bool => switch (self.cache[id]) { .bool => |literal| switch (self.solver.literalValue(literal)) { .true => .{ .bool = true }, .false => .{ .bool = false }, .unset => EncodeError.ModelUnavailable, }, else => EncodeError.ModelUnavailable, }, .bitvec => |width| switch (self.cache[id]) { .bits => |bits| .{ .bitvec = .{ .value = try modelValue(self.solver, bits), .width = width } }, else => EncodeError.ModelUnavailable, }, .array => switch (self.cache[id]) { .array => |array| try self.modelArrayValue(allocator, array), else => EncodeError.ModelUnavailable, }, .int => EncodeError.UnsupportedTerm, }; } fn appendFunctionApplicationModel( self: *const Encoder, allocator: std.mem.Allocator, model_result: *Model, id: term.Term, application: term.ApplyExpr, ) Error!void { if (application.function >= self.ctx.functions.items.len) return EncodeError.FunctionOutOfRange; const decl = self.ctx.functions.items[application.function]; const arguments = try self.modelArguments(allocator, application.args); var arguments_handled = false; errdefer { if (!arguments_handled) { for (arguments) |*argument| { argument.deinit(allocator); } allocator.free(arguments); } } const value = try self.modelValueFor(allocator, id, decl.result); var value_handled = false; errdefer { if (!value_handled) { var owned_value = value; owned_value.deinit(allocator); } } arguments_handled = true; value_handled = true; try model_result.appendFunctionApplication(decl.name, arguments, value); } fn modelArguments(self: *const Encoder, allocator: std.mem.Allocator, arguments: []const term.Term) Error![]ModelValue { const values = try allocator.alloc(ModelValue, arguments.len); var initialized: usize = 0; errdefer { for (values[0..initialized]) |*value| { value.deinit(allocator); } allocator.free(values); } for (arguments, 0..) |argument, index| { values[index] = try self.modelValueFor(allocator, argument, try self.ctx.sortOf(argument)); initialized += 1; } return values; } fn encodeEq(self: *Encoder, lhs: term.Term, rhs: term.Term) Error!Value { const lhs_value = try self.encode(lhs); const rhs_value = try self.encode(rhs); return .{ .bool = try self.encodeValueEqual(lhs_value, rhs_value) }; } fn encodeValueEqual(self: *Encoder, lhs_value: Value, rhs_value: Value) Error!sat.Literal { return switch (lhs_value) { .bool => |lhs_literal| switch (rhs_value) { .bool => |rhs_literal| try self.xnorGate(lhs_literal, rhs_literal), else => EncodeError.SortMismatch, }, .bits => |lhs_bits| switch (rhs_value) { .bits => |rhs_bits| try self.bitsEqual(lhs_bits, rhs_bits), else => EncodeError.SortMismatch, }, .array => |lhs_array| switch (rhs_value) { .array => |rhs_array| try self.arraysEqual(lhs_array, rhs_array), else => EncodeError.SortMismatch, }, .none => EncodeError.UnsupportedTerm, }; } fn encodeFunctionCongruence(self: *Encoder) Error!void { if (self.function_congruence_encoded) return; for (self.ctx.nodes.items, 0..) |node, index| { switch (node) { .apply => |application| { const id: term.Term = @intCast(index); _ = try self.encode(id); var previous_index: usize = 0; while (previous_index < index) : (previous_index += 1) { switch (self.ctx.nodes.items[previous_index]) { .apply => |previous| { if (previous.function != application.function) continue; try self.encodeApplicationCongruence(previous, @intCast(previous_index), application, id); }, else => {}, } } }, else => {}, } } self.function_congruence_encoded = true; } fn encodeApplicationCongruence( self: *Encoder, lhs_application: term.ApplyExpr, lhs: term.Term, rhs_application: term.ApplyExpr, rhs: term.Term, ) Error!void { if (lhs_application.args.len != rhs_application.args.len) return EncodeError.SortMismatch; const clause = try self.allocator.alloc(sat.Literal, lhs_application.args.len + 1); defer self.allocator.free(clause); for (lhs_application.args, rhs_application.args, 0..) |lhs_arg, rhs_arg, index| { clause[index] = switch (try self.encodeEq(lhs_arg, rhs_arg)) { .bool => |literal| literal.negated(), else => return EncodeError.ExpectedBool, }; } clause[lhs_application.args.len] = try self.encodeValueEqual(try self.encode(lhs), try self.encode(rhs)); try self.solver.addClause(clause); } fn encodeBoolNary(self: *Encoder, operands: []const term.Term, comptime is_and: bool) Error!sat.Literal { if (operands.len == 0) return if (is_and) self.true_literal else self.false_literal; var result = try self.encodeBool(operands[0]); for (operands[1..]) |operand| { const rhs = try self.encodeBool(operand); result = if (is_and) try self.andGate(result, rhs) else try self.orGate(result, rhs); } return result; } fn constantBits(self: *Encoder, value: u128, width: u32) Error![]sat.Literal { const bits = try self.allocator.alloc(sat.Literal, width); for (bits, 0..) |*bit, index| { bit.* = if (((value >> @intCast(index)) & 1) == 1) self.true_literal else self.false_literal; } return bits; } fn freshBits(self: *Encoder, width: u32) Error![]sat.Literal { const bits = try self.allocator.alloc(sat.Literal, width); errdefer self.allocator.free(bits); for (bits) |*bit| { bit.* = try self.freshLiteral(); } return bits; } fn freshArray(self: *Encoder, index_width: u32, element_width: u32) Error!ArrayValue { const cell_count = try arrayCellCount(index_width); const cells = try self.allocator.alloc([]sat.Literal, cell_count); var initialized: usize = 0; errdefer { for (cells[0..initialized]) |cell| self.allocator.free(cell); self.allocator.free(cells); } for (cells) |*cell| { cell.* = try self.freshBits(element_width); initialized += 1; } return .{ .index_width = index_width, .element_width = element_width, .cells = cells }; } fn freeArray(self: *Encoder, array: ArrayValue) void { for (array.cells) |cell| self.allocator.free(cell); self.allocator.free(array.cells); } fn modelArrayValue(self: *const Encoder, allocator: std.mem.Allocator, array: ArrayValue) Error!ModelValue { const cells = try allocator.alloc(u128, array.cells.len); errdefer allocator.free(cells); for (cells, array.cells) |*target, bits| { target.* = try modelValue(self.solver, bits); } return .{ .array = .{ .index_width = array.index_width, .element_width = array.element_width, .cells = cells } }; } fn freshLiteral(self: *Encoder) Error!sat.Literal { return sat.Literal.positive(try self.solver.addVariable()); } fn encodeAdd(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error![]sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; const out = try self.allocator.alloc(sat.Literal, lhs.len); errdefer self.allocator.free(out); var carry = self.false_literal; for (lhs, rhs, 0..) |a, b, index| { const sum_ab = try self.xorGate(a, b); out[index] = try self.xorGate(sum_ab, carry); const carry_ab = try self.andGate(a, b); const carry_ac = try self.andGate(a, carry); const carry_bc = try self.andGate(b, carry); carry = try self.orGate(try self.orGate(carry_ab, carry_ac), carry_bc); } return out; } fn encodeSub(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error![]sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; const out = try self.allocator.alloc(sat.Literal, lhs.len); errdefer self.allocator.free(out); var carry = self.true_literal; for (lhs, rhs, 0..) |a, b, index| { const not_b = b.negated(); const sum_ab = try self.xorGate(a, not_b); out[index] = try self.xorGate(sum_ab, carry); const carry_ab = try self.andGate(a, not_b); const carry_ac = try self.andGate(a, carry); const carry_bc = try self.andGate(not_b, carry); carry = try self.orGate(try self.orGate(carry_ab, carry_ac), carry_bc); } return out; } fn encodeMul(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error![]sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; var result = try self.constantBits(0, @intCast(lhs.len)); errdefer self.allocator.free(result); for (rhs, 0..) |rhs_bit, shift| { const partial = try self.allocator.alloc(sat.Literal, lhs.len); defer self.allocator.free(partial); for (partial, 0..) |*bit, index| { bit.* = if (index < shift) self.false_literal else try self.andGate(lhs[index - shift], rhs_bit); } const next = try self.encodeAdd(result, partial); self.allocator.free(result); result = next; } return result; } fn encodeUlt(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error!sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; var equal_prefix = self.true_literal; var less = self.false_literal; var index = lhs.len; while (index > 0) { index -= 1; const bit_less = try self.andGate(lhs[index].negated(), rhs[index]); less = try self.orGate(less, try self.andGate(equal_prefix, bit_less)); equal_prefix = try self.andGate(equal_prefix, try self.xnorGate(lhs[index], rhs[index])); } return less; } fn encodeUle(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error!sat.Literal { return try self.orGate(try self.encodeUlt(lhs, rhs), try self.bitsEqual(lhs, rhs)); } fn encodeSlt(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error!sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; if (lhs.len == 0) return EncodeError.InvalidBitVectorRange; const lhs_sign = lhs[lhs.len - 1]; const rhs_sign = rhs[rhs.len - 1]; const signs_differ = try self.xorGate(lhs_sign, rhs_sign); const lhs_negative_rhs_positive = try self.andGate(lhs_sign, rhs_sign.negated()); const same_sign_less = try self.andGate(signs_differ.negated(), try self.encodeUlt(lhs, rhs)); return try self.orGate(lhs_negative_rhs_positive, same_sign_less); } fn encodeSle(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error!sat.Literal { return try self.orGate(try self.encodeSlt(lhs, rhs), try self.bitsEqual(lhs, rhs)); } fn bitsEqual(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error!sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; var result = self.true_literal; for (lhs, rhs) |a, b| { result = try self.andGate(result, try self.xnorGate(a, b)); } return result; } fn arraysEqual(self: *Encoder, lhs: ArrayValue, rhs: ArrayValue) Error!sat.Literal { if (lhs.index_width != rhs.index_width or lhs.element_width != rhs.element_width or lhs.cells.len != rhs.cells.len) return EncodeError.SortMismatch; var result = self.true_literal; for (lhs.cells, rhs.cells) |lhs_cell, rhs_cell| { result = try self.andGate(result, try self.bitsEqual(lhs_cell, rhs_cell)); } return result; } fn encodeUaddOverflow(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error!sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; var carry = self.false_literal; for (lhs, rhs) |a, b| { const carry_ab = try self.andGate(a, b); const carry_ac = try self.andGate(a, carry); const carry_bc = try self.andGate(b, carry); carry = try self.orGate(try self.orGate(carry_ab, carry_ac), carry_bc); } return carry; } fn encodeSaddOverflow(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error!sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; if (lhs.len == 0) return EncodeError.InvalidBitVectorRange; const sum = try self.encodeAdd(lhs, rhs); defer self.allocator.free(sum); const lhs_sign = lhs[lhs.len - 1]; const rhs_sign = rhs[rhs.len - 1]; const sum_sign = sum[sum.len - 1]; const same_input_sign = (try self.xorGate(lhs_sign, rhs_sign)).negated(); const result_sign_changed = try self.xorGate(lhs_sign, sum_sign); return try self.andGate(same_input_sign, result_sign_changed); } fn encodeSsubOverflow(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error!sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; if (lhs.len == 0) return EncodeError.InvalidBitVectorRange; const negated_rhs = try self.encodeNeg(rhs); defer self.allocator.free(negated_rhs); const difference = try self.encodeAdd(lhs, negated_rhs); defer self.allocator.free(difference); const lhs_sign = lhs[lhs.len - 1]; const rhs_sign = rhs[rhs.len - 1]; const difference_sign = difference[difference.len - 1]; const input_signs_differ = try self.xorGate(lhs_sign, rhs_sign); const result_sign_changed = try self.xorGate(lhs_sign, difference_sign); return try self.andGate(input_signs_differ, result_sign_changed); } fn encodeUmulOverflow(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error!sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; const double_width = lhs.len * 2; const lhs_extended = try self.allocator.alloc(sat.Literal, double_width); defer self.allocator.free(lhs_extended); const rhs_extended = try self.allocator.alloc(sat.Literal, double_width); defer self.allocator.free(rhs_extended); for (lhs, 0..) |bit, index| { lhs_extended[index] = bit; } for (rhs, 0..) |bit, index| { rhs_extended[index] = bit; } for (lhs.len..double_width) |index| { lhs_extended[index] = self.false_literal; rhs_extended[index] = self.false_literal; } const product = try self.encodeMul(lhs_extended, rhs_extended); defer self.allocator.free(product); return try self.orLiterals(product[lhs.len..]); } fn encodeSmulOverflow(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error!sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; if (lhs.len == 0) return EncodeError.InvalidBitVectorRange; const double_width = lhs.len * 2; const lhs_extended = try self.allocator.alloc(sat.Literal, double_width); defer self.allocator.free(lhs_extended); const rhs_extended = try self.allocator.alloc(sat.Literal, double_width); defer self.allocator.free(rhs_extended); @memcpy(lhs_extended[0..lhs.len], lhs); @memcpy(rhs_extended[0..rhs.len], rhs); for (lhs_extended[lhs.len..]) |*bit| { bit.* = lhs[lhs.len - 1]; } for (rhs_extended[rhs.len..]) |*bit| { bit.* = rhs[rhs.len - 1]; } const product = try self.encodeMul(lhs_extended, rhs_extended); defer self.allocator.free(product); const result_sign = product[lhs.len - 1]; var representable = self.true_literal; for (product[lhs.len..]) |upper_bit| { representable = try self.andGate(representable, try self.xnorGate(upper_bit, result_sign)); } return representable.negated(); } fn encodeNeg(self: *Encoder, bits: []const sat.Literal) Error![]sat.Literal { if (bits.len == 0) return EncodeError.InvalidBitVectorRange; const inverted = try self.encodeBitwiseNot(bits); errdefer self.allocator.free(inverted); const one = try self.constantBits(1, @intCast(bits.len)); defer self.allocator.free(one); const result = try self.encodeAdd(inverted, one); self.allocator.free(inverted); return result; } fn encodeBitwiseNot(self: *Encoder, bits: []const sat.Literal) Error![]sat.Literal { const out = try self.allocator.alloc(sat.Literal, bits.len); for (out, bits) |*target, bit| { target.* = bit.negated(); } return out; } fn encodeBitwiseBinary(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal, kind: BitwiseKind) Error![]sat.Literal { if (lhs.len != rhs.len) return EncodeError.SortMismatch; const out = try self.allocator.alloc(sat.Literal, lhs.len); errdefer self.allocator.free(out); for (out, lhs, rhs) |*target, a, b| { target.* = switch (kind) { .and_ => try self.andGate(a, b), .or_ => try self.orGate(a, b), .xor => try self.xorGate(a, b), }; } return out; } fn encodeShift(self: *Encoder, value: []const sat.Literal, amount: []const sat.Literal, kind: ShiftKind) Error![]sat.Literal { if (value.len != amount.len) return EncodeError.SortMismatch; const selectors = try self.allocator.alloc(sat.Literal, value.len); defer self.allocator.free(selectors); for (selectors, 0..) |*selector, shift| { selector.* = try self.bitsEqualConstant(amount, shift); } const out = try self.allocator.alloc(sat.Literal, value.len); errdefer self.allocator.free(out); for (out, 0..) |*target, output_index| { var result = self.false_literal; for (selectors, 0..) |selector, shift| { const source = switch (kind) { .left => if (output_index >= shift) value[output_index - shift] else self.false_literal, .right => if (output_index + shift < value.len) value[output_index + shift] else self.false_literal, }; if (source.raw == self.false_literal.raw) continue; result = try self.orGate(result, try self.andGate(selector, source)); } target.* = result; } return out; } fn encodeArithmeticShiftRight(self: *Encoder, value: []const sat.Literal, amount: []const sat.Literal) Error![]sat.Literal { if (value.len != amount.len) return EncodeError.SortMismatch; if (value.len == 0) return EncodeError.InvalidBitVectorRange; const logical = try self.encodeShift(value, amount, .right); errdefer self.allocator.free(logical); const inverted = try self.encodeBitwiseNot(value); defer self.allocator.free(inverted); const shifted_inverted = try self.encodeShift(inverted, amount, .right); defer self.allocator.free(shifted_inverted); const negative = try self.encodeBitwiseNot(shifted_inverted); defer self.allocator.free(negative); const sign = value[value.len - 1]; const out = try self.allocator.alloc(sat.Literal, value.len); errdefer self.allocator.free(out); for (out, logical, negative) |*target, positive_bit, negative_bit| { target.* = try self.muxGate(sign, negative_bit, positive_bit); } self.allocator.free(logical); return out; } fn encodeRotate(self: *Encoder, bits: []const sat.Literal, amount: u32, kind: RotateKind) Error![]sat.Literal { if (bits.len == 0) return EncodeError.InvalidBitVectorRange; const shift = @as(usize, @intCast(amount)) % bits.len; const out = try self.allocator.alloc(sat.Literal, bits.len); for (out, 0..) |*target, index| { const source_index = switch (kind) { .left => if (index >= shift) index - shift else bits.len - (shift - index), .right => if (index >= bits.len - shift) index - (bits.len - shift) else index + shift, }; target.* = bits[source_index]; } return out; } fn encodeUnsignedDivRem(self: *Encoder, dividend: []const sat.Literal, divisor: []const sat.Literal) Error!DivRemResult { if (dividend.len != divisor.len) return EncodeError.SortMismatch; if (dividend.len == 0) return EncodeError.InvalidBitVectorRange; const width = dividend.len; const extended_width = std.math.add(usize, width, 1) catch return EncodeError.InvalidBitVectorRange; var remainder = try self.allocator.alloc(sat.Literal, extended_width); errdefer self.allocator.free(remainder); @memset(remainder, self.false_literal); const divisor_extended = try self.allocator.alloc(sat.Literal, extended_width); defer self.allocator.free(divisor_extended); @memcpy(divisor_extended[0..width], divisor); divisor_extended[width] = self.false_literal; const quotient = try self.allocator.alloc(sat.Literal, width); errdefer self.allocator.free(quotient); var index = width; while (index > 0) { index -= 1; const shifted = try self.allocator.alloc(sat.Literal, extended_width); errdefer self.allocator.free(shifted); shifted[0] = dividend[index]; @memcpy(shifted[1..], remainder[0..width]); const can_subtract = try self.encodeUle(divisor_extended, shifted); const difference = try self.encodeSub(shifted, divisor_extended); errdefer self.allocator.free(difference); const next_remainder = try self.allocator.alloc(sat.Literal, extended_width); errdefer self.allocator.free(next_remainder); for (next_remainder, shifted, difference) |*target, shifted_bit, difference_bit| { target.* = try self.muxGate(can_subtract, difference_bit, shifted_bit); } quotient[index] = can_subtract; self.allocator.free(remainder); self.allocator.free(shifted); self.allocator.free(difference); remainder = next_remainder; } const result_remainder = try self.allocator.alloc(sat.Literal, width); errdefer self.allocator.free(result_remainder); @memcpy(result_remainder, remainder[0..width]); self.allocator.free(remainder); return .{ .quotient = quotient, .remainder = result_remainder }; } fn encodeSignedDiv(self: *Encoder, dividend: []const sat.Literal, divisor: []const sat.Literal) Error![]sat.Literal { if (dividend.len != divisor.len) return EncodeError.SortMismatch; if (dividend.len == 0) return EncodeError.InvalidBitVectorRange; const abs_dividend = try self.encodeAbs(dividend); defer self.allocator.free(abs_dividend); const abs_divisor = try self.encodeAbs(divisor); defer self.allocator.free(abs_divisor); const result = try self.encodeUnsignedDivRem(abs_dividend, abs_divisor); defer self.allocator.free(result.quotient); defer self.allocator.free(result.remainder); const negated_quotient = try self.encodeNeg(result.quotient); defer self.allocator.free(negated_quotient); const negative = try self.xorGate(dividend[dividend.len - 1], divisor[divisor.len - 1]); return try self.muxBits(negative, negated_quotient, result.quotient); } fn encodeSignedRem(self: *Encoder, dividend: []const sat.Literal, divisor: []const sat.Literal) Error![]sat.Literal { if (dividend.len != divisor.len) return EncodeError.SortMismatch; if (dividend.len == 0) return EncodeError.InvalidBitVectorRange; const abs_dividend = try self.encodeAbs(dividend); defer self.allocator.free(abs_dividend); const abs_divisor = try self.encodeAbs(divisor); defer self.allocator.free(abs_divisor); const result = try self.encodeUnsignedDivRem(abs_dividend, abs_divisor); defer self.allocator.free(result.quotient); defer self.allocator.free(result.remainder); const negated_remainder = try self.encodeNeg(result.remainder); defer self.allocator.free(negated_remainder); return try self.muxBits(dividend[dividend.len - 1], negated_remainder, result.remainder); } fn encodeSignedMod(self: *Encoder, dividend: []const sat.Literal, divisor: []const sat.Literal) Error![]sat.Literal { if (dividend.len != divisor.len) return EncodeError.SortMismatch; if (dividend.len == 0) return EncodeError.InvalidBitVectorRange; const abs_dividend = try self.encodeAbs(dividend); defer self.allocator.free(abs_dividend); const abs_divisor = try self.encodeAbs(divisor); defer self.allocator.free(abs_divisor); const result = try self.encodeUnsignedDivRem(abs_dividend, abs_divisor); defer self.allocator.free(result.quotient); defer self.allocator.free(result.remainder); const zero = try self.constantBits(0, @intCast(dividend.len)); defer self.allocator.free(zero); const remainder_is_zero = try self.bitsEqual(result.remainder, zero); const negated_remainder = try self.encodeNeg(result.remainder); defer self.allocator.free(negated_remainder); const negative_plus_divisor = try self.encodeAdd(negated_remainder, divisor); defer self.allocator.free(negative_plus_divisor); const positive_plus_divisor = try self.encodeAdd(result.remainder, divisor); defer self.allocator.free(positive_plus_divisor); const lhs_positive_branch = try self.muxBits(divisor[divisor.len - 1], positive_plus_divisor, result.remainder); defer self.allocator.free(lhs_positive_branch); const lhs_negative_branch = try self.muxBits(divisor[divisor.len - 1], negated_remainder, negative_plus_divisor); defer self.allocator.free(lhs_negative_branch); const adjusted = try self.muxBits(dividend[dividend.len - 1], lhs_negative_branch, lhs_positive_branch); defer self.allocator.free(adjusted); return try self.muxBits(remainder_is_zero, result.remainder, adjusted); } fn encodeAbs(self: *Encoder, bits: []const sat.Literal) Error![]sat.Literal { if (bits.len == 0) return EncodeError.InvalidBitVectorRange; const negated = try self.encodeNeg(bits); defer self.allocator.free(negated); return try self.muxBits(bits[bits.len - 1], negated, bits); } fn bitsEqualConstant(self: *Encoder, bits: []const sat.Literal, value: usize) Error!sat.Literal { var result = self.true_literal; for (bits, 0..) |bit, index| { const bit_is_set = index < @bitSizeOf(usize) and ((value >> @intCast(index)) & 1) == 1; const selected = if (bit_is_set) bit else bit.negated(); result = try self.andGate(result, selected); } return result; } fn encodeArraySelect(self: *Encoder, array: ArrayValue, index: []const sat.Literal) Error![]sat.Literal { const index_width: usize = @intCast(array.index_width); const element_width: usize = @intCast(array.element_width); if (index.len != index_width) return EncodeError.SortMismatch; const cell_count = try arrayCellCount(array.index_width); if (array.cells.len != cell_count) return EncodeError.SortMismatch; const out = try self.allocator.alloc(sat.Literal, element_width); errdefer self.allocator.free(out); @memset(out, self.false_literal); for (array.cells, 0..) |cell, cell_index| { if (cell.len != element_width) return EncodeError.SortMismatch; const selector = try self.bitsEqualConstant(index, cell_index); for (out, cell) |*target, bit| { if (bit.raw == self.false_literal.raw) continue; target.* = try self.orGate(target.*, try self.andGate(selector, bit)); } } return out; } fn encodeArrayStore(self: *Encoder, array: ArrayValue, index: []const sat.Literal, value: []const sat.Literal) Error!ArrayValue { const index_width: usize = @intCast(array.index_width); const element_width: usize = @intCast(array.element_width); if (index.len != index_width or value.len != element_width) return EncodeError.SortMismatch; const cell_count = try arrayCellCount(array.index_width); if (array.cells.len != cell_count) return EncodeError.SortMismatch; const cells = try self.allocator.alloc([]sat.Literal, cell_count); var initialized: usize = 0; errdefer { for (cells[0..initialized]) |cell| self.allocator.free(cell); self.allocator.free(cells); } for (cells, array.cells, 0..) |*target, cell, cell_index| { if (cell.len != element_width) return EncodeError.SortMismatch; const selector = try self.bitsEqualConstant(index, cell_index); target.* = try self.muxBits(selector, value, cell); initialized += 1; } return .{ .index_width = array.index_width, .element_width = array.element_width, .cells = cells }; } fn encodeConcat(self: *Encoder, lhs: []const sat.Literal, rhs: []const sat.Literal) Error![]sat.Literal { const out = try self.allocator.alloc(sat.Literal, lhs.len + rhs.len); @memcpy(out[0..rhs.len], rhs); @memcpy(out[rhs.len..], lhs); return out; } fn encodeExtract(self: *Encoder, bits: []const sat.Literal, high: u32, low: u32) Error![]sat.Literal { if (low > high or high >= bits.len) return EncodeError.InvalidBitVectorRange; const width = high - low + 1; const out = try self.allocator.alloc(sat.Literal, width); const base: usize = @intCast(low); for (out, 0..) |*target, index| { target.* = bits[base + index]; } return out; } fn encodeExtend(self: *Encoder, bits: []const sat.Literal, extra: u32, kind: ExtendKind) Error![]sat.Literal { if (bits.len == 0) return EncodeError.InvalidBitVectorRange; const extra_len: usize = @intCast(extra); const width = std.math.add(usize, bits.len, extra_len) catch return EncodeError.InvalidBitVectorRange; const out = try self.allocator.alloc(sat.Literal, width); @memcpy(out[0..bits.len], bits); const extension = switch (kind) { .zero => self.false_literal, .sign => bits[bits.len - 1], }; for (out[bits.len..]) |*target| { target.* = extension; } return out; } fn orLiterals(self: *Encoder, literals: []const sat.Literal) Error!sat.Literal { if (literals.len == 0) return self.false_literal; var result = literals[0]; for (literals[1..]) |literal| { result = try self.orGate(result, literal); } return result; } fn andGate(self: *Encoder, a: sat.Literal, b: sat.Literal) Error!sat.Literal { const out = try self.freshLiteral(); try self.solver.addClause(&.{ a.negated(), b.negated(), out }); try self.solver.addClause(&.{ a, out.negated() }); try self.solver.addClause(&.{ b, out.negated() }); return out; } fn orGate(self: *Encoder, a: sat.Literal, b: sat.Literal) Error!sat.Literal { const out = try self.freshLiteral(); try self.solver.addClause(&.{ a, b, out.negated() }); try self.solver.addClause(&.{ a.negated(), out }); try self.solver.addClause(&.{ b.negated(), out }); return out; } fn xorGate(self: *Encoder, a: sat.Literal, b: sat.Literal) Error!sat.Literal { const out = try self.freshLiteral(); try self.solver.addClause(&.{ a, b, out.negated() }); try self.solver.addClause(&.{ a.negated(), b.negated(), out.negated() }); try self.solver.addClause(&.{ a, b.negated(), out }); try self.solver.addClause(&.{ a.negated(), b, out }); return out; } fn xnorGate(self: *Encoder, a: sat.Literal, b: sat.Literal) Error!sat.Literal { return (try self.xorGate(a, b)).negated(); } fn muxGate(self: *Encoder, selector: sat.Literal, when_true: sat.Literal, when_false: sat.Literal) Error!sat.Literal { return try self.orGate(try self.andGate(selector, when_true), try self.andGate(selector.negated(), when_false)); } fn muxBits(self: *Encoder, selector: sat.Literal, when_true: []const sat.Literal, when_false: []const sat.Literal) Error![]sat.Literal { if (when_true.len != when_false.len) return EncodeError.SortMismatch; const out = try self.allocator.alloc(sat.Literal, when_true.len); errdefer self.allocator.free(out); for (out, when_true, when_false) |*target, true_bit, false_bit| { target.* = try self.muxGate(selector, true_bit, false_bit); } return out; }};fn modelValue(solver: *const sat.Solver, bits: []const sat.Literal) Error!u128 { if (bits.len > 128) return EncodeError.ModelValueTooWide; var value: u128 = 0; for (bits, 0..) |bit, index| { const bit_value = solver.literalValue(bit); if (bit_value == .true) value |= @as(u128, 1) << @intCast(index); } return value;}fn arrayCellCount(index_width: u32) Error!usize { if (index_width > max_native_array_index_width) return EncodeError.ArrayIndexTooWide; return @as(usize, 1) << @intCast(index_width);}test "bit-vector encoder proves unsigned self-less-than impossible" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const assertion = try ctx.bvult(x, x); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(assertion); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder proves signed self-less-than impossible" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const assertion = try ctx.bvslt(x, x); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(assertion); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder solves with Boolean term assumptions" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const p = try ctx.symbol("p", .bool); const q = try ctx.symbol("q", .bool); const r = try ctx.symbol("r", .bool); const operands = [_]term.Term{ p, q }; const disjunction = try ctx.or_(&operands); const not_p = try ctx.not(p); const not_q = try ctx.not(q); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(disjunction); const not_p_literal = try encoder.assumeTerm(not_p); _ = try encoder.assumeTerm(r); const not_q_literal = try encoder.assumeTerm(not_q); try std.testing.expectEqual(sat.Status.unsat, try encoder.solve()); try std.testing.expectEqualSlices(sat.Literal, &.{ not_p_literal, not_q_literal }, solver.lastUnsatCore()); var artifact = (try solver.lastProofArtifact(std.testing.allocator)).?; defer artifact.deinit(); try std.testing.expectEqualSlices(sat.Literal, &.{ not_p_literal, not_q_literal }, artifact.assumptions.items); try std.testing.expect(try artifact.valid());}test "bit-vector encoder solves signed comparison model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const negative = try ctx.symbol("negative", .{ .bitvec = 4 }); const positive = try ctx.symbol("positive", .{ .bitvec = 4 }); const minus_one = try ctx.bitvecValue(0xf, 4); const one = try ctx.bitvecValue(0x1, 4); const min_value = try ctx.bitvecValue(0x8, 4); const negative_is_minus_one = try ctx.eq(negative, minus_one); const positive_is_one = try ctx.eq(positive, one); const negative_less_positive = try ctx.bvslt(negative, positive); const negative_less_equal_negative = try ctx.bvsle(negative, negative); const min_less_negative = try ctx.bvslt(min_value, negative); const positive_not_less_negative = try ctx.not(try ctx.bvslt(positive, negative)); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(negative_is_minus_one); try encoder.assertTerm(positive_is_one); try encoder.assertTerm(negative_less_positive); try encoder.assertTerm(negative_less_equal_negative); try encoder.assertTerm(min_less_negative); try encoder.assertTerm(positive_not_less_negative); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0xf, .width = 4 } }, model_result.get("negative").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0x1, .width = 4 } }, model_result.get("positive").?);}test "bit-vector encoder finds wrapped increment model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const one = try ctx.bitvecValue(1, 4); const zero = try ctx.bitvecValue(0, 4); const assertion = try ctx.eq(try ctx.bvadd(x, one), zero); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(assertion); const bits = try encoder.encodeBits(x); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); try std.testing.expectEqual(@as(u128, 15), try modelValue(&solver, bits));}test "bit-vector encoder solves subtraction model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const three = try ctx.bitvecValue(3, 4); const twelve = try ctx.bitvecValue(12, 4); const assertion = try ctx.eq(try ctx.bvsub(x, three), twelve); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(assertion); const bits = try encoder.encodeBits(x); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); try std.testing.expectEqual(@as(u128, 15), try modelValue(&solver, bits));}test "bit-vector encoder extracts named model values" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const flag = try ctx.symbol("flag", .bool); const one = try ctx.bitvecValue(1, 4); const zero = try ctx.bitvecValue(0, 4); const assertion = try ctx.eq(try ctx.bvadd(x, one), zero); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(assertion); try encoder.assertTerm(flag); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(@as(usize, 2), model_result.entries.items.len); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 15, .width = 4 } }, model_result.get("x").?); try std.testing.expectEqual(ModelValue{ .bool = true }, model_result.get("flag").?); var buffer: [128]u8 = undefined; var stream = std.Io.Writer.fixed(&buffer); try model_result.write(&stream); try std.testing.expect(std.mem.indexOf(u8, stream.buffered(), "x: (_ bv15 4)") != null); try std.testing.expect(std.mem.indexOf(u8, stream.buffered(), "flag: true") != null);}test "bit-vector encoder solves finite array store select model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const memory = try ctx.symbol("memory", .{ .array = .{ .index_width = 2, .element_width = 4 } }); const index = try ctx.symbol("index", .{ .bitvec = 2 }); const other_index = try ctx.symbol("other_index", .{ .bitvec = 2 }); const value = try ctx.symbol("value", .{ .bitvec = 4 }); const other_value = try ctx.symbol("other_value", .{ .bitvec = 4 }); const index_one = try ctx.bitvecValue(1, 2); const index_two = try ctx.bitvecValue(2, 2); const ten = try ctx.bitvecValue(0xa, 4); const five = try ctx.bitvecValue(0x5, 4); const written = try ctx.arrayStore(memory, index, value); const other_written = try ctx.arrayStore(written, other_index, other_value); const index_is_one = try ctx.eq(index, index_one); const other_index_is_two = try ctx.eq(other_index, index_two); const value_is_ten = try ctx.eq(value, ten); const other_value_is_five = try ctx.eq(other_value, five); const stored_read = try ctx.eq(try ctx.arraySelect(written, index), value); const preserved_read = try ctx.eq(try ctx.arraySelect(written, other_index), try ctx.arraySelect(memory, other_index)); const nested_stored_read = try ctx.eq(try ctx.arraySelect(other_written, index), value); const nested_other_read = try ctx.eq(try ctx.arraySelect(other_written, other_index), other_value); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(index_is_one); try encoder.assertTerm(other_index_is_two); try encoder.assertTerm(value_is_ten); try encoder.assertTerm(other_value_is_five); try encoder.assertTerm(stored_read); try encoder.assertTerm(preserved_read); try encoder.assertTerm(nested_stored_read); try encoder.assertTerm(nested_other_read); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 1, .width = 2 } }, model_result.get("index").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 2, .width = 2 } }, model_result.get("other_index").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0xa, .width = 4 } }, model_result.get("value").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0x5, .width = 4 } }, model_result.get("other_value").?); switch (model_result.get("memory").?) { .array => |array| { try std.testing.expectEqual(@as(u32, 2), array.index_width); try std.testing.expectEqual(@as(u32, 4), array.element_width); try std.testing.expectEqual(@as(usize, 4), array.cells.len); }, else => return error.TestExpectedArrayModel, } var buffer: [512]u8 = undefined; var stream = std.Io.Writer.fixed(&buffer); try model_result.write(&stream); try std.testing.expect(std.mem.indexOf(u8, stream.buffered(), "memory: (array (_ BitVec 2) (_ BitVec 4)") != null);}test "bit-vector encoder rejects overwritten array read contradiction" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const memory = try ctx.symbol("memory", .{ .array = .{ .index_width = 2, .element_width = 4 } }); const index = try ctx.bitvecValue(1, 2); const ten = try ctx.bitvecValue(0xa, 4); const three = try ctx.bitvecValue(0x3, 4); const written = try ctx.arrayStore(memory, index, ten); const overwritten = try ctx.arrayStore(written, index, three); const contradiction = try ctx.eq(try ctx.arraySelect(overwritten, index), ten); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(contradiction); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder compares finite arrays extensionally" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const lhs = try ctx.symbol("lhs", .{ .array = .{ .index_width = 2, .element_width = 4 } }); const rhs = try ctx.symbol("rhs", .{ .array = .{ .index_width = 2, .element_width = 4 } }); const index = try ctx.bitvecValue(2, 2); const arrays_equal = try ctx.eq(lhs, rhs); const reads_differ = try ctx.not(try ctx.eq(try ctx.arraySelect(lhs, index), try ctx.arraySelect(rhs, index))); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(arrays_equal); try encoder.assertTerm(reads_differ); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder enforces uninterpreted function congruence" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const bv4 = term.Sort{ .bitvec = 4 }; const x = try ctx.symbol("x", bv4); const y = try ctx.symbol("y", bv4); const f = try ctx.function("f", &.{bv4}, bv4); const fx = try ctx.apply(f, &.{x}); const fy = try ctx.apply(f, &.{y}); const arguments_equal = try ctx.eq(x, y); const results_differ = try ctx.not(try ctx.eq(fx, fy)); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(arguments_equal); try encoder.assertTerm(results_differ); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder permits distinct uninterpreted function results" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const bv4 = term.Sort{ .bitvec = 4 }; const x = try ctx.symbol("x", bv4); const y = try ctx.symbol("y", bv4); const f = try ctx.function("f", &.{bv4}, bv4); const fx = try ctx.apply(f, &.{x}); const fy = try ctx.apply(f, &.{y}); const one = try ctx.bitvecValue(1, 4); const two = try ctx.bitvecValue(2, 4); const three = try ctx.bitvecValue(3, 4); const four = try ctx.bitvecValue(4, 4); const x_is_one = try ctx.eq(x, one); const y_is_two = try ctx.eq(y, two); const fx_is_three = try ctx.eq(fx, three); const fy_is_four = try ctx.eq(fy, four); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(x_is_one); try encoder.assertTerm(y_is_two); try encoder.assertTerm(fx_is_three); try encoder.assertTerm(fy_is_four); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 1, .width = 4 } }, model_result.get("x").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 2, .width = 4 } }, model_result.get("y").?); const f_model = model_result.getFunction("f").?; try std.testing.expectEqual(@as(usize, 2), f_model.entries.items.len); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 1, .width = 4 } }, f_model.entries.items[0].arguments[0]); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 3, .width = 4 } }, f_model.entries.items[0].result); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 2, .width = 4 } }, f_model.entries.items[1].arguments[0]); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 4, .width = 4 } }, f_model.entries.items[1].result);}test "bit-vector model skips unused uninterpreted function applications" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const bv4 = term.Sort{ .bitvec = 4 }; const x = try ctx.symbol("x", bv4); const f = try ctx.function("f", &.{bv4}, bv4); _ = try ctx.apply(f, &.{x}); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expect(model_result.getFunction("f") == null);}test "bit-vector encoder enforces bool-returning function congruence" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const bv4 = term.Sort{ .bitvec = 4 }; const x = try ctx.symbol("x", bv4); const y = try ctx.symbol("y", bv4); const p = try ctx.function("p", &.{bv4}, .bool); const px = try ctx.apply(p, &.{x}); const py = try ctx.apply(p, &.{y}); const arguments_equal = try ctx.eq(x, y); const py_false = try ctx.not(py); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(arguments_equal); try encoder.assertTerm(px); try encoder.assertTerm(py_false); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder rejects terms appended after initialization" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); const zero = try ctx.bitvecValue(0, 4); try std.testing.expectError(EncodeError.TermOutOfRange, encoder.assertTerm(try ctx.eq(x, zero)));}test "bit-vector encoder multiplies exactly modulo width" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const three = try ctx.bitvecValue(3, 4); const fifteen = try ctx.bitvecValue(15, 4); const assertion = try ctx.eq(try ctx.bvmul(x, three), fifteen); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(assertion); try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "bit-vector encoder solves unsigned division and remainder model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const dividend = try ctx.symbol("dividend", .{ .bitvec = 4 }); const divisor = try ctx.symbol("divisor", .{ .bitvec = 4 }); const thirteen = try ctx.bitvecValue(13, 4); const three = try ctx.bitvecValue(3, 4); const four = try ctx.bitvecValue(4, 4); const one = try ctx.bitvecValue(1, 4); const dividend_is_thirteen = try ctx.eq(dividend, thirteen); const divisor_is_three = try ctx.eq(divisor, three); const quotient_is_four = try ctx.eq(try ctx.bvudiv(dividend, divisor), four); const remainder_is_one = try ctx.eq(try ctx.bvurem(dividend, divisor), one); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(dividend_is_thirteen); try encoder.assertTerm(divisor_is_three); try encoder.assertTerm(quotient_is_four); try encoder.assertTerm(remainder_is_one); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 13, .width = 4 } }, model_result.get("dividend").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 3, .width = 4 } }, model_result.get("divisor").?);}test "bit-vector encoder follows unsigned division by zero semantics" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const dividend = try ctx.bitvecValue(10, 4); const zero = try ctx.bitvecValue(0, 4); const all_ones = try ctx.bitvecValue(15, 4); const quotient_is_all_ones = try ctx.eq(try ctx.bvudiv(dividend, zero), all_ones); const remainder_is_dividend = try ctx.eq(try ctx.bvurem(dividend, zero), dividend); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(quotient_is_all_ones); try encoder.assertTerm(remainder_is_dividend); try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "bit-vector encoder rejects wrapped unsigned division quotient" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const one = try ctx.bitvecValue(1, 4); const three = try ctx.bitvecValue(3, 4); const eleven = try ctx.bitvecValue(11, 4); const impossible = try ctx.eq(try ctx.bvudiv(one, three), eleven); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(impossible); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder solves signed division remainder and modulo" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const negative = try ctx.symbol("negative", .{ .bitvec = 4 }); const positive = try ctx.symbol("positive", .{ .bitvec = 4 }); const positive_divisor = try ctx.symbol("positive_divisor", .{ .bitvec = 4 }); const negative_divisor = try ctx.symbol("negative_divisor", .{ .bitvec = 4 }); const minus_seven = try ctx.bitvecValue(0b1001, 4); const plus_seven = try ctx.bitvecValue(0b0111, 4); const plus_three = try ctx.bitvecValue(0b0011, 4); const minus_three = try ctx.bitvecValue(0b1101, 4); const minus_two = try ctx.bitvecValue(0b1110, 4); const minus_one = try ctx.bitvecValue(0b1111, 4); const plus_one = try ctx.bitvecValue(0b0001, 4); const plus_two = try ctx.bitvecValue(0b0010, 4); const negative_is_minus_seven = try ctx.eq(negative, minus_seven); const positive_is_plus_seven = try ctx.eq(positive, plus_seven); const positive_divisor_is_plus_three = try ctx.eq(positive_divisor, plus_three); const negative_divisor_is_minus_three = try ctx.eq(negative_divisor, minus_three); const negative_quotient = try ctx.eq(try ctx.bvsdiv(negative, positive_divisor), minus_two); const negative_remainder = try ctx.eq(try ctx.bvsrem(negative, positive_divisor), minus_one); const negative_modulo = try ctx.eq(try ctx.bvsmod(negative, positive_divisor), plus_two); const positive_quotient = try ctx.eq(try ctx.bvsdiv(positive, negative_divisor), minus_two); const positive_remainder = try ctx.eq(try ctx.bvsrem(positive, negative_divisor), plus_one); const positive_modulo = try ctx.eq(try ctx.bvsmod(positive, negative_divisor), minus_two); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(negative_is_minus_seven); try encoder.assertTerm(positive_is_plus_seven); try encoder.assertTerm(positive_divisor_is_plus_three); try encoder.assertTerm(negative_divisor_is_minus_three); try encoder.assertTerm(negative_quotient); try encoder.assertTerm(negative_remainder); try encoder.assertTerm(negative_modulo); try encoder.assertTerm(positive_quotient); try encoder.assertTerm(positive_remainder); try encoder.assertTerm(positive_modulo); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0b1001, .width = 4 } }, model_result.get("negative").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0b0111, .width = 4 } }, model_result.get("positive").?);}test "bit-vector encoder follows signed division by zero semantics" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const negative = try ctx.bitvecValue(0b1001, 4); const positive = try ctx.bitvecValue(0b0111, 4); const zero = try ctx.bitvecValue(0, 4); const one = try ctx.bitvecValue(1, 4); const all_ones = try ctx.bitvecValue(0b1111, 4); const negative_division = try ctx.eq(try ctx.bvsdiv(negative, zero), one); const positive_division = try ctx.eq(try ctx.bvsdiv(positive, zero), all_ones); const negative_remainder = try ctx.eq(try ctx.bvsrem(negative, zero), negative); const positive_remainder = try ctx.eq(try ctx.bvsrem(positive, zero), positive); const negative_modulo = try ctx.eq(try ctx.bvsmod(negative, zero), negative); const positive_modulo = try ctx.eq(try ctx.bvsmod(positive, zero), positive); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(negative_division); try encoder.assertTerm(positive_division); try encoder.assertTerm(negative_remainder); try encoder.assertTerm(positive_remainder); try encoder.assertTerm(negative_modulo); try encoder.assertTerm(positive_modulo); try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "bit-vector encoder detects unsigned addition overflow" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const one = try ctx.bitvecValue(1, 4); const fifteen = try ctx.bitvecValue(15, 4); const is_fifteen = try ctx.eq(x, fifteen); const overflow = try ctx.bvuaddo(x, one); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(is_fifteen); try encoder.assertTerm(overflow); try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "bit-vector encoder proves bounded unsigned addition does not overflow" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const one = try ctx.bitvecValue(1, 4); const fifteen = try ctx.bitvecValue(15, 4); const assertion = try ctx.and_(&.{ try ctx.bvult(x, fifteen), try ctx.bvuaddo(x, one) }); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(assertion); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder detects signed addition overflow" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const seven = try ctx.bitvecValue(7, 4); const one = try ctx.bitvecValue(1, 4); const is_seven = try ctx.eq(x, seven); const overflow = try ctx.bvsaddo(x, one); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(is_seven); try encoder.assertTerm(overflow); try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "bit-vector encoder proves signed addition inside range does not overflow" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const minus_one = try ctx.bitvecValue(15, 4); const one = try ctx.bitvecValue(1, 4); const is_minus_one = try ctx.eq(x, minus_one); const overflow = try ctx.bvsaddo(x, one); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(is_minus_one); try encoder.assertTerm(overflow); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder detects signed subtraction overflow" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const min = try ctx.bitvecValue(8, 4); const one = try ctx.bitvecValue(1, 4); const is_min = try ctx.eq(x, min); const overflow = try ctx.bvssubo(x, one); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(is_min); try encoder.assertTerm(overflow); try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "bit-vector encoder proves signed subtraction inside range does not overflow" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const minus_one = try ctx.bitvecValue(15, 4); const one = try ctx.bitvecValue(1, 4); const is_minus_one = try ctx.eq(x, minus_one); const overflow = try ctx.bvssubo(x, one); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(is_minus_one); try encoder.assertTerm(overflow); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder detects unsigned multiplication overflow" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const six = try ctx.bitvecValue(6, 4); const three = try ctx.bitvecValue(3, 4); const is_six = try ctx.eq(x, six); const overflow = try ctx.bvumulo(x, three); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(is_six); try encoder.assertTerm(overflow); try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "bit-vector encoder proves bounded unsigned multiplication does not overflow" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const five = try ctx.bitvecValue(5, 4); const three = try ctx.bitvecValue(3, 4); const assertion = try ctx.and_(&.{ try ctx.eq(x, five), try ctx.bvumulo(x, three) }); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(assertion); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder detects signed multiplication overflow" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const minus_four = try ctx.bitvecValue(12, 4); const three = try ctx.bitvecValue(3, 4); const is_minus_four = try ctx.eq(x, minus_four); const overflow = try ctx.bvsmulo(x, three); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(is_minus_four); try encoder.assertTerm(overflow); try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "bit-vector encoder proves signed multiplication inside range does not overflow" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const minus_two = try ctx.bitvecValue(14, 4); const three = try ctx.bitvecValue(3, 4); const is_minus_two = try ctx.eq(x, minus_two); const overflow = try ctx.bvsmulo(x, three); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(is_minus_two); try encoder.assertTerm(overflow); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder solves bitwise mask model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const ten = try ctx.bitvecValue(0b1010, 4); const three = try ctx.bitvecValue(0b0011, 4); const one = try ctx.bitvecValue(0b0001, 4); const zero = try ctx.bitvecValue(0b0000, 4); const eight = try ctx.bitvecValue(0b1000, 4); const eleven = try ctx.bitvecValue(0b1011, 4); const mask_assertion = try ctx.eq(try ctx.bvand(x, ten), eight); const low_bit_assertion = try ctx.eq(try ctx.bvand(x, one), zero); const or_assertion = try ctx.eq(try ctx.bvor(x, three), eleven); const all_ones = try ctx.bitvecValue(0b1111, 4); const not_assertion = try ctx.eq(try ctx.bvxor(x, all_ones), try ctx.bvnot(x)); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(mask_assertion); try encoder.assertTerm(low_bit_assertion); try encoder.assertTerm(or_assertion); try encoder.assertTerm(not_assertion); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 8, .width = 4 } }, model_result.get("x").?);}test "bit-vector encoder proves xor self contradiction unsat" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const one = try ctx.bitvecValue(1, 4); const assertion = try ctx.eq(try ctx.bvxor(x, x), one); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(assertion); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder solves symbolic logical shift model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const amount = try ctx.symbol("amount", .{ .bitvec = 4 }); const two = try ctx.bitvecValue(2, 4); const three = try ctx.bitvecValue(3, 4); const twelve = try ctx.bitvecValue(12, 4); const shifted = try ctx.bvshl(x, amount); const roundtrip = try ctx.bvlshr(shifted, amount); const amount_is_two = try ctx.eq(amount, two); const shifted_is_twelve = try ctx.eq(shifted, twelve); const roundtrip_is_x = try ctx.eq(roundtrip, x); const x_is_three = try ctx.eq(x, three); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(amount_is_two); try encoder.assertTerm(shifted_is_twelve); try encoder.assertTerm(roundtrip_is_x); try encoder.assertTerm(x_is_three); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 3, .width = 4 } }, model_result.get("x").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 2, .width = 4 } }, model_result.get("amount").?);}test "bit-vector encoder treats large logical shift amounts as zero" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const one = try ctx.bitvecValue(1, 4); const four = try ctx.bitvecValue(4, 4); const zero = try ctx.bitvecValue(0, 4); const shl_is_zero = try ctx.eq(try ctx.bvshl(one, four), zero); const lshr_is_zero = try ctx.eq(try ctx.bvlshr(one, four), zero); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(shl_is_zero); try encoder.assertTerm(lshr_is_zero); try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "bit-vector encoder solves arithmetic shift model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const negative = try ctx.symbol("negative", .{ .bitvec = 4 }); const positive = try ctx.symbol("positive", .{ .bitvec = 4 }); const amount = try ctx.symbol("amount", .{ .bitvec = 4 }); const minus_four = try ctx.bitvecValue(0b1100, 4); const plus_six = try ctx.bitvecValue(0b0110, 4); const one = try ctx.bitvecValue(1, 4); const minus_two = try ctx.bitvecValue(0b1110, 4); const plus_three = try ctx.bitvecValue(0b0011, 4); const logical_negative = try ctx.bitvecValue(0b0110, 4); const negative_is_minus_four = try ctx.eq(negative, minus_four); const positive_is_plus_six = try ctx.eq(positive, plus_six); const amount_is_one = try ctx.eq(amount, one); const arithmetic_negative = try ctx.eq(try ctx.bvashr(negative, amount), minus_two); const logical_negative_match = try ctx.eq(try ctx.bvlshr(negative, amount), logical_negative); const arithmetic_positive = try ctx.eq(try ctx.bvashr(positive, amount), plus_three); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(negative_is_minus_four); try encoder.assertTerm(positive_is_plus_six); try encoder.assertTerm(amount_is_one); try encoder.assertTerm(arithmetic_negative); try encoder.assertTerm(logical_negative_match); try encoder.assertTerm(arithmetic_positive); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0b1100, .width = 4 } }, model_result.get("negative").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0b0110, .width = 4 } }, model_result.get("positive").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 1, .width = 4 } }, model_result.get("amount").?);}test "bit-vector encoder treats large arithmetic shift amounts as sign fill" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const negative = try ctx.bitvecValue(0b1001, 4); const positive = try ctx.bitvecValue(0b0111, 4); const four = try ctx.bitvecValue(4, 4); const all_ones = try ctx.bitvecValue(0b1111, 4); const zero = try ctx.bitvecValue(0, 4); const negative_is_all_ones = try ctx.eq(try ctx.bvashr(negative, four), all_ones); const positive_is_zero = try ctx.eq(try ctx.bvashr(positive, four), zero); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(negative_is_all_ones); try encoder.assertTerm(positive_is_zero); try std.testing.expectEqual(sat.Status.sat, try solver.solve());}test "bit-vector encoder solves fixed rotate model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const nine = try ctx.bitvecValue(0b1001, 4); const left_one = try ctx.bitvecValue(0b0011, 4); const right_one = try ctx.bitvecValue(0b1100, 4); const right_two = try ctx.bitvecValue(0b0110, 4); const x_is_nine = try ctx.eq(x, nine); const rotate_left_one = try ctx.eq(try ctx.bvrotl(x, 1), left_one); const rotate_left_five = try ctx.eq(try ctx.bvrotl(x, 5), left_one); const rotate_right_one = try ctx.eq(try ctx.bvrotr(x, 1), right_one); const rotate_right_two = try ctx.eq(try ctx.bvrotr(x, 2), right_two); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(x_is_nine); try encoder.assertTerm(rotate_left_one); try encoder.assertTerm(rotate_left_five); try encoder.assertTerm(rotate_right_one); try encoder.assertTerm(rotate_right_two); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0b1001, .width = 4 } }, model_result.get("x").?);}test "bit-vector encoder rejects impossible fixed rotate result" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const nine = try ctx.bitvecValue(0b1001, 4); const impossible = try ctx.eq(try ctx.bvrotl(nine, 1), nine); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(impossible); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder rejects impossible large shift result" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const one = try ctx.bitvecValue(1, 4); const four = try ctx.bitvecValue(4, 4); const impossible = try ctx.eq(try ctx.bvshl(one, four), one); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(impossible); try std.testing.expectEqual(sat.Status.unsat, try solver.solve());}test "bit-vector encoder solves concat and extract model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const high = try ctx.symbol("high", .{ .bitvec = 4 }); const low = try ctx.symbol("low", .{ .bitvec = 4 }); const word = try ctx.bvconcat(high, low); const high_value = try ctx.bitvecValue(0xa, 4); const low_value = try ctx.bitvecValue(0x5, 4); const packed_value = try ctx.bitvecValue(0xa5, 8); const high_assertion = try ctx.eq(try ctx.bvextract(word, 7, 4), high_value); const low_assertion = try ctx.eq(try ctx.bvextract(word, 3, 0), low_value); const packed_assertion = try ctx.eq(word, packed_value); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(high_assertion); try encoder.assertTerm(low_assertion); try encoder.assertTerm(packed_assertion); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0xa, .width = 4 } }, model_result.get("high").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0x5, .width = 4 } }, model_result.get("low").?);}test "bit-vector encoder solves zero and sign extension model" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const positive = try ctx.symbol("positive", .{ .bitvec = 4 }); const negative = try ctx.symbol("negative", .{ .bitvec = 4 }); const positive_value = try ctx.bitvecValue(0x5, 4); const negative_value = try ctx.bitvecValue(0xa, 4); const zero_extended = try ctx.bvzeroext(positive, 4); const positive_signed = try ctx.bvsignext(positive, 4); const negative_signed = try ctx.bvsignext(negative, 4); const zero_extended_value = try ctx.bitvecValue(0x05, 8); const positive_signed_value = try ctx.bitvecValue(0x05, 8); const negative_signed_value = try ctx.bitvecValue(0xfa, 8); const positive_is_value = try ctx.eq(positive, positive_value); const negative_is_value = try ctx.eq(negative, negative_value); const zero_extended_is_value = try ctx.eq(zero_extended, zero_extended_value); const positive_signed_is_value = try ctx.eq(positive_signed, positive_signed_value); const negative_signed_is_value = try ctx.eq(negative_signed, negative_signed_value); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try encoder.assertTerm(positive_is_value); try encoder.assertTerm(negative_is_value); try encoder.assertTerm(zero_extended_is_value); try encoder.assertTerm(positive_signed_is_value); try encoder.assertTerm(negative_signed_is_value); try encoder.encodeSymbols(); try std.testing.expectEqual(sat.Status.sat, try solver.solve()); var model_result = try encoder.model(std.testing.allocator); defer model_result.deinit(); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0x5, .width = 4 } }, model_result.get("positive").?); try std.testing.expectEqual(ModelValue{ .bitvec = .{ .value = 0xa, .width = 4 } }, model_result.get("negative").?);}test "bit-vector encoder rejects invalid extract range" { var ctx = term.Context.init(std.testing.allocator); defer ctx.deinit(); const x = try ctx.symbol("x", .{ .bitvec = 4 }); const invalid = try ctx.bvextract(x, 4, 0); var solver = sat.Solver.init(std.testing.allocator); defer solver.deinit(); var encoder = try Encoder.init(std.testing.allocator, &ctx, &solver); defer encoder.deinit(); try std.testing.expectError(EncodeError.InvalidBitVectorRange, encoder.encodeBits(invalid));}Source: lib/smt/src/root.zig:96
zig
pub const bitvec = @import("bitvec.zig");Complete caller list for bitvec.Encoder.assertTerm
41 direct callers.
lib.smt.src.bitvec.test_bit-vector_encoder_compares_finite_arrays_extensionally[function] — test source atlib/smt/src/bitvec.zig:1545in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_signed_addition_overflow[function] — test source atlib/smt/src/bitvec.zig:1854in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_signed_multiplication_overflow[function] — test source atlib/smt/src/bitvec.zig:1954in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_signed_subtraction_overflow[function] — test source atlib/smt/src/bitvec.zig:1888in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_unsigned_addition_overflow[function] — test source atlib/smt/src/bitvec.zig:1822in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_unsigned_multiplication_overflow[function] — test source atlib/smt/src/bitvec.zig:1922in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_enforces_bool-returning_function_congruence[function] — test source atlib/smt/src/bitvec.zig:1639in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_enforces_uninterpreted_function_congruence[function] — test source atlib/smt/src/bitvec.zig:1562in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_extracts_named_model_values[function] — test source atlib/smt/src/bitvec.zig:1443in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_finds_wrapped_increment_model[function] — test source atlib/smt/src/bitvec.zig:1409in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_follows_signed_division_by_zero_semantics[function] — test source atlib/smt/src/bitvec.zig:1795in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_follows_unsigned_division_by_zero_semantics[function] — test source atlib/smt/src/bitvec.zig:1716in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_multiplies_exactly_modulo_width[function] — test source atlib/smt/src/bitvec.zig:1672in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_permits_distinct_uninterpreted_function_results[function] — test source atlib/smt/src/bitvec.zig:1582in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_bounded_unsigned_addition_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1839in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_bounded_unsigned_multiplication_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1939in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_addition_inside_range_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1871in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_multiplication_inside_range_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1971in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_self-less-than_impossible[function] — test source atlib/smt/src/bitvec.zig:1338in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_subtraction_inside_range_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1905in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_unsigned_self-less-than_impossible[function] — test source atlib/smt/src/bitvec.zig:1325in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_xor_self_contradiction_unsat[function] — test source atlib/smt/src/bitvec.zig:2018in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_impossible_fixed_rotate_result[function] — test source atlib/smt/src/bitvec.zig:2164in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_impossible_large_shift_result[function] — test source atlib/smt/src/bitvec.zig:2177in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_overwritten_array_read_contradiction[function] — test source atlib/smt/src/bitvec.zig:1527in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_terms_appended_after_initialization[function] — test source atlib/smt/src/bitvec.zig:1660in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_wrapped_unsigned_division_quotient[function] — test source atlib/smt/src/bitvec.zig:1733in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_arithmetic_shift_model[function] — test source atlib/smt/src/bitvec.zig:2079in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_bitwise_mask_model[function] — test source atlib/smt/src/bitvec.zig:1988in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_concat_and_extract_model[function] — test source atlib/smt/src/bitvec.zig:2191in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_finite_array_store_select_model[function] — test source atlib/smt/src/bitvec.zig:1471in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_fixed_rotate_model[function] — test source atlib/smt/src/bitvec.zig:2135in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_signed_comparison_model[function] — test source atlib/smt/src/bitvec.zig:1377in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_signed_division_remainder_and_modulo[function] — test source atlib/smt/src/bitvec.zig:1748in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_subtraction_model[function] — test source atlib/smt/src/bitvec.zig:1426in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_symbolic_logical_shift_model[function] — test source atlib/smt/src/bitvec.zig:2032in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_unsigned_division_and_remainder_model[function] — test source atlib/smt/src/bitvec.zig:1687in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_with_Boolean_term_assumptions[function] — test source atlib/smt/src/bitvec.zig:1351in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_zero_and_sign_extension_model[function] — test source atlib/smt/src/bitvec.zig:2218in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_treats_large_arithmetic_shift_amounts_as_sign_fill[function] — test source atlib/smt/src/bitvec.zig:2116in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_treats_large_logical_shift_amounts_as_zero[function] — test source atlib/smt/src/bitvec.zig:2062in nearest public ownertiny.smt.bitvec
Complete caller list for bitvec.Encoder.deinit
43 direct callers.
lib.smt.src.bitvec.test_bit-vector_encoder_compares_finite_arrays_extensionally[function] — test source atlib/smt/src/bitvec.zig:1545in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_signed_addition_overflow[function] — test source atlib/smt/src/bitvec.zig:1854in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_signed_multiplication_overflow[function] — test source atlib/smt/src/bitvec.zig:1954in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_signed_subtraction_overflow[function] — test source atlib/smt/src/bitvec.zig:1888in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_unsigned_addition_overflow[function] — test source atlib/smt/src/bitvec.zig:1822in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_unsigned_multiplication_overflow[function] — test source atlib/smt/src/bitvec.zig:1922in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_enforces_bool-returning_function_congruence[function] — test source atlib/smt/src/bitvec.zig:1639in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_enforces_uninterpreted_function_congruence[function] — test source atlib/smt/src/bitvec.zig:1562in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_extracts_named_model_values[function] — test source atlib/smt/src/bitvec.zig:1443in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_finds_wrapped_increment_model[function] — test source atlib/smt/src/bitvec.zig:1409in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_follows_signed_division_by_zero_semantics[function] — test source atlib/smt/src/bitvec.zig:1795in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_follows_unsigned_division_by_zero_semantics[function] — test source atlib/smt/src/bitvec.zig:1716in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_multiplies_exactly_modulo_width[function] — test source atlib/smt/src/bitvec.zig:1672in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_permits_distinct_uninterpreted_function_results[function] — test source atlib/smt/src/bitvec.zig:1582in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_bounded_unsigned_addition_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1839in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_bounded_unsigned_multiplication_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1939in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_addition_inside_range_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1871in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_multiplication_inside_range_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1971in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_self-less-than_impossible[function] — test source atlib/smt/src/bitvec.zig:1338in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_subtraction_inside_range_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1905in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_unsigned_self-less-than_impossible[function] — test source atlib/smt/src/bitvec.zig:1325in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_xor_self_contradiction_unsat[function] — test source atlib/smt/src/bitvec.zig:2018in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_impossible_fixed_rotate_result[function] — test source atlib/smt/src/bitvec.zig:2164in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_impossible_large_shift_result[function] — test source atlib/smt/src/bitvec.zig:2177in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_invalid_extract_range[function] — test source atlib/smt/src/bitvec.zig:2253in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_overwritten_array_read_contradiction[function] — test source atlib/smt/src/bitvec.zig:1527in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_terms_appended_after_initialization[function] — test source atlib/smt/src/bitvec.zig:1660in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_wrapped_unsigned_division_quotient[function] — test source atlib/smt/src/bitvec.zig:1733in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_arithmetic_shift_model[function] — test source atlib/smt/src/bitvec.zig:2079in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_bitwise_mask_model[function] — test source atlib/smt/src/bitvec.zig:1988in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_concat_and_extract_model[function] — test source atlib/smt/src/bitvec.zig:2191in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_finite_array_store_select_model[function] — test source atlib/smt/src/bitvec.zig:1471in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_fixed_rotate_model[function] — test source atlib/smt/src/bitvec.zig:2135in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_signed_comparison_model[function] — test source atlib/smt/src/bitvec.zig:1377in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_signed_division_remainder_and_modulo[function] — test source atlib/smt/src/bitvec.zig:1748in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_subtraction_model[function] — test source atlib/smt/src/bitvec.zig:1426in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_symbolic_logical_shift_model[function] — test source atlib/smt/src/bitvec.zig:2032in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_unsigned_division_and_remainder_model[function] — test source atlib/smt/src/bitvec.zig:1687in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_with_Boolean_term_assumptions[function] — test source atlib/smt/src/bitvec.zig:1351in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_zero_and_sign_extension_model[function] — test source atlib/smt/src/bitvec.zig:2218in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_treats_large_arithmetic_shift_amounts_as_sign_fill[function] — test source atlib/smt/src/bitvec.zig:2116in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_treats_large_logical_shift_amounts_as_zero[function] — test source atlib/smt/src/bitvec.zig:2062in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_model_skips_unused_uninterpreted_function_applications[function] — test source atlib/smt/src/bitvec.zig:1621in nearest public ownertiny.smt.bitvec
Complete caller list for bitvec.Encoder.encodeSymbols
14 direct callers.
tiny.smt.bitvec.Encoder.solve[method] atlib/smt/src/bitvec.zig:434lib.smt.src.bitvec.test_bit-vector_encoder_extracts_named_model_values[function] — test source atlib/smt/src/bitvec.zig:1443in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_permits_distinct_uninterpreted_function_results[function] — test source atlib/smt/src/bitvec.zig:1582in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_arithmetic_shift_model[function] — test source atlib/smt/src/bitvec.zig:2079in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_bitwise_mask_model[function] — test source atlib/smt/src/bitvec.zig:1988in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_concat_and_extract_model[function] — test source atlib/smt/src/bitvec.zig:2191in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_finite_array_store_select_model[function] — test source atlib/smt/src/bitvec.zig:1471in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_fixed_rotate_model[function] — test source atlib/smt/src/bitvec.zig:2135in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_signed_comparison_model[function] — test source atlib/smt/src/bitvec.zig:1377in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_signed_division_remainder_and_modulo[function] — test source atlib/smt/src/bitvec.zig:1748in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_symbolic_logical_shift_model[function] — test source atlib/smt/src/bitvec.zig:2032in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_unsigned_division_and_remainder_model[function] — test source atlib/smt/src/bitvec.zig:1687in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_zero_and_sign_extension_model[function] — test source atlib/smt/src/bitvec.zig:2218in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_model_skips_unused_uninterpreted_function_applications[function] — test source atlib/smt/src/bitvec.zig:1621in nearest public ownertiny.smt.bitvec
Complete caller list for bitvec.Encoder.init
43 direct callers.
lib.smt.src.bitvec.test_bit-vector_encoder_compares_finite_arrays_extensionally[function] — test source atlib/smt/src/bitvec.zig:1545in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_signed_addition_overflow[function] — test source atlib/smt/src/bitvec.zig:1854in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_signed_multiplication_overflow[function] — test source atlib/smt/src/bitvec.zig:1954in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_signed_subtraction_overflow[function] — test source atlib/smt/src/bitvec.zig:1888in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_unsigned_addition_overflow[function] — test source atlib/smt/src/bitvec.zig:1822in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_detects_unsigned_multiplication_overflow[function] — test source atlib/smt/src/bitvec.zig:1922in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_enforces_bool-returning_function_congruence[function] — test source atlib/smt/src/bitvec.zig:1639in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_enforces_uninterpreted_function_congruence[function] — test source atlib/smt/src/bitvec.zig:1562in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_extracts_named_model_values[function] — test source atlib/smt/src/bitvec.zig:1443in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_finds_wrapped_increment_model[function] — test source atlib/smt/src/bitvec.zig:1409in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_follows_signed_division_by_zero_semantics[function] — test source atlib/smt/src/bitvec.zig:1795in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_follows_unsigned_division_by_zero_semantics[function] — test source atlib/smt/src/bitvec.zig:1716in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_multiplies_exactly_modulo_width[function] — test source atlib/smt/src/bitvec.zig:1672in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_permits_distinct_uninterpreted_function_results[function] — test source atlib/smt/src/bitvec.zig:1582in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_bounded_unsigned_addition_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1839in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_bounded_unsigned_multiplication_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1939in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_addition_inside_range_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1871in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_multiplication_inside_range_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1971in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_self-less-than_impossible[function] — test source atlib/smt/src/bitvec.zig:1338in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_signed_subtraction_inside_range_does_not_overflow[function] — test source atlib/smt/src/bitvec.zig:1905in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_unsigned_self-less-than_impossible[function] — test source atlib/smt/src/bitvec.zig:1325in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_proves_xor_self_contradiction_unsat[function] — test source atlib/smt/src/bitvec.zig:2018in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_impossible_fixed_rotate_result[function] — test source atlib/smt/src/bitvec.zig:2164in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_impossible_large_shift_result[function] — test source atlib/smt/src/bitvec.zig:2177in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_invalid_extract_range[function] — test source atlib/smt/src/bitvec.zig:2253in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_overwritten_array_read_contradiction[function] — test source atlib/smt/src/bitvec.zig:1527in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_terms_appended_after_initialization[function] — test source atlib/smt/src/bitvec.zig:1660in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_rejects_wrapped_unsigned_division_quotient[function] — test source atlib/smt/src/bitvec.zig:1733in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_arithmetic_shift_model[function] — test source atlib/smt/src/bitvec.zig:2079in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_bitwise_mask_model[function] — test source atlib/smt/src/bitvec.zig:1988in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_concat_and_extract_model[function] — test source atlib/smt/src/bitvec.zig:2191in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_finite_array_store_select_model[function] — test source atlib/smt/src/bitvec.zig:1471in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_fixed_rotate_model[function] — test source atlib/smt/src/bitvec.zig:2135in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_signed_comparison_model[function] — test source atlib/smt/src/bitvec.zig:1377in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_signed_division_remainder_and_modulo[function] — test source atlib/smt/src/bitvec.zig:1748in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_subtraction_model[function] — test source atlib/smt/src/bitvec.zig:1426in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_symbolic_logical_shift_model[function] — test source atlib/smt/src/bitvec.zig:2032in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_unsigned_division_and_remainder_model[function] — test source atlib/smt/src/bitvec.zig:1687in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_with_Boolean_term_assumptions[function] — test source atlib/smt/src/bitvec.zig:1351in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_zero_and_sign_extension_model[function] — test source atlib/smt/src/bitvec.zig:2218in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_treats_large_arithmetic_shift_amounts_as_sign_fill[function] — test source atlib/smt/src/bitvec.zig:2116in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_treats_large_logical_shift_amounts_as_zero[function] — test source atlib/smt/src/bitvec.zig:2062in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_model_skips_unused_uninterpreted_function_applications[function] — test source atlib/smt/src/bitvec.zig:1621in nearest public ownertiny.smt.bitvec
Complete caller list for bitvec.Encoder.model
13 direct callers.
lib.smt.src.bitvec.test_bit-vector_encoder_extracts_named_model_values[function] — test source atlib/smt/src/bitvec.zig:1443in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_permits_distinct_uninterpreted_function_results[function] — test source atlib/smt/src/bitvec.zig:1582in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_arithmetic_shift_model[function] — test source atlib/smt/src/bitvec.zig:2079in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_bitwise_mask_model[function] — test source atlib/smt/src/bitvec.zig:1988in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_concat_and_extract_model[function] — test source atlib/smt/src/bitvec.zig:2191in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_finite_array_store_select_model[function] — test source atlib/smt/src/bitvec.zig:1471in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_fixed_rotate_model[function] — test source atlib/smt/src/bitvec.zig:2135in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_signed_comparison_model[function] — test source atlib/smt/src/bitvec.zig:1377in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_signed_division_remainder_and_modulo[function] — test source atlib/smt/src/bitvec.zig:1748in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_symbolic_logical_shift_model[function] — test source atlib/smt/src/bitvec.zig:2032in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_unsigned_division_and_remainder_model[function] — test source atlib/smt/src/bitvec.zig:1687in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_encoder_solves_zero_and_sign_extension_model[function] — test source atlib/smt/src/bitvec.zig:2218in nearest public ownertiny.smt.bitveclib.smt.src.bitvec.test_bit-vector_model_skips_unused_uninterpreted_function_applications[function] — test source atlib/smt/src/bitvec.zig:1621in nearest public ownertiny.smt.bitvec
Audit
| Definitions | 18 |
|---|---|
| Public names | 18 |
| Members | 24 |
| Version | 26.7.0 |
| Revision | daab053ee433 |