use std::fmt::{Debug, Display};
use std::ops::{Add, BitAnd, BitOr, BitXor, Mul, Not, Sub};
#[derive(Debug)]
pub enum SatResult<M> {
Unknown,
Sat(M),
Unsat,
}
pub trait SolverProvider: Send + Sync {
type Solver<'a>: SmtSolver<'a>;
fn with_solver<T>(&self, f: impl FnOnce(Self::Solver<'_>) -> T) -> T;
}
pub trait SmtSolver<'a>: Sized + Debug {
type BV: SmtBV<'a, Self>;
type Int: SmtInt<'a, Self>;
type Bool: SmtBool<'a, Self>;
type BvArray: SmtBVArray<'a, Self>;
type ModelRef<'r>: SmtModelRef<'a, Self>
where
Self: 'r;
type Model: SmtModel<'a, Self>;
fn bv_from_i64(&mut self, val: i64, size: u32) -> Self::BV;
fn bv_from_u64(&mut self, val: u64, size: u32) -> Self::BV;
fn bv_from_int(&mut self, int: Self::Int, size: u32) -> Self::BV;
fn new_bv_const(&mut self, name: impl AsRef<str>, size: u32) -> Self::BV;
fn new_bool_const(&mut self, name: impl AsRef<str>) -> Self::Bool;
fn new_bv_array_const(&mut self, name: impl AsRef<str>, index_size: u32, element_size: u32) -> Self::BvArray;
fn int_from_i64(&mut self, val: i64) -> Self::Int;
fn int_from_u64(&mut self, val: u64) -> Self::Int;
fn int_from_bv(&mut self, bv: Self::BV, signed: bool) -> Self::Int;
fn bool_from_bool(&mut self, val: bool) -> Self::Bool;
fn forall_const(&mut self, vals: &[Dynamic<'a, Self>], condition: Self::Bool) -> Self::Bool;
fn check_assertions<'me>(&'me mut self, assertions: &[Self::Bool]) -> SatResult<Self::ModelRef<'me>>;
fn bv_from_i128(&mut self, value: i128) -> Self::BV {
if let Ok(value) = i64::try_from(value) {
self.bv_from_i64(value, 128)
} else {
let value = value as u128;
let lower = self.bv_from_u64(value as u64, 64);
let higher = self.bv_from_u64((value >> 64) as u64, 64);
higher.concat(lower)
}
}
fn extract_bits<const N: u32>(&mut self, src: Self::BV, mask: Self::BV) -> Self::BV {
let mut dest = self.bv_from_i64(0, N);
for n in 0..N {
let mask_bit = mask.clone().extract(n, n);
let bit = src.clone().extract(n, n);
let index = if n > 0 {
self.popcount(&mask, n)
} else {
self.int_from_i64(0)
};
dest = dest | (bit & mask_bit).zero_ext(N - 1).bvshl(self.bv_from_int(index, N));
}
dest
}
fn popcount(&mut self, bv: &Self::BV, bv_size: u32) -> Self::Int {
let values = (0..bv_size)
.map(|n| bv.clone().extract(n, n))
.map(|bit| self.int_from_bv(bit, false))
.collect::<Vec<_>>();
values.into_iter().reduce(|a, b| a + b).unwrap()
}
fn deposit_bits<const N: u32>(&mut self, src: Self::BV, mask: Self::BV) -> Self::BV {
let mut dest = None;
for n in 0..N {
let mask_bit = mask.clone().extract(n, n);
let num = if n > 0 {
self.popcount(&mask, n)
} else {
self.int_from_i64(0)
};
let bit = src.clone().bvlshr(self.bv_from_int(num, N)).extract(0, 0);
let bit = mask_bit & bit;
dest = Some(match dest.take() {
Some(dest) => bit.concat(dest),
None => bit,
});
}
dest.unwrap()
}
fn count_trailing_zeros(&mut self, bv: Self::BV) -> Self::BV {
let mut result = self.bv_from_u64(0, 128);
for size in 1..=bv.get_size() {
let zero = self.bv_from_u64(0, size);
result = bv
.clone()
.extract(size - 1, 0)
._eq(zero)
.ite_bv(self.bv_from_u64(size as u64, 128), result);
}
result
}
fn count_leading_zeros(&mut self, bv: Self::BV) -> Self::BV {
let max = bv.get_size();
let mut result = self.bv_from_u64(0, 128);
for size in 1..=max {
let zero = self.bv_from_u64(0, size);
result = bv
.clone()
.extract(max - 1, max - size)
._eq(zero)
.ite_bv(self.bv_from_u64(size as u64, 128), result);
}
result
}
}
pub trait SmtModel<'ctx, S: SmtSolver<'ctx>>: Debug {
fn get_const_interp(&self, name: &S::BV) -> Option<S::BV>;
}
pub trait SmtModelRef<'ctx, S: SmtSolver<'ctx>>: Debug {
fn to_model(&self) -> Option<S::Model>;
}
#[allow(missing_docs)]
pub trait SmtBV<'a, S: SmtSolver<'a, BV = Self>>:
Clone
+ Debug
+ Display
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ BitOr<Output = Self>
+ BitAnd<Output = Self>
+ BitXor<Output = Self>
+ Not<Output = Self>
+ Sized
{
fn is_identical(&self, other: &Self) -> bool;
fn concat(self, other: Self) -> Self;
fn extract(self, hi: u32, lo: u32) -> Self;
fn zero_ext(self, num: u32) -> Self;
fn sign_ext(self, num: u32) -> Self;
fn bvshl(self, count: Self) -> Self;
fn bvlshr(self, count: Self) -> Self;
fn bvashr(self, count: Self) -> Self;
fn bvurem(self, n: Self) -> Self;
fn bvsrem(self, n: Self) -> Self;
fn bvudiv(self, n: Self) -> Self;
fn bvsdiv(self, n: Self) -> Self;
fn bvrotl(self, count: Self) -> Self;
fn bvrotr(self, count: Self) -> Self;
fn bvslt(self, other: Self) -> S::Bool;
fn bvsge(self, other: Self) -> S::Bool;
fn bvsgt(self, other: Self) -> S::Bool;
fn bvugt(self, other: Self) -> S::Bool;
fn bvult(self, other: Self) -> S::Bool;
fn bvule(self, other: Self) -> S::Bool;
fn bvuge(self, other: Self) -> S::Bool;
fn _eq(self, other: Self) -> S::Bool;
fn simplify(self) -> Self;
fn get_size(&self) -> u32;
fn as_u64(&self) -> Option<u64>;
fn into_dynamic(self) -> Dynamic<'a, S> {
Dynamic::BV(self)
}
fn swap_bytes(self, num_bytes: usize) -> Self {
(0..num_bytes as u32)
.map(|index| self.clone().extract(index * 8 + 7, index * 8))
.rev()
.reduce(|acc, el| el.concat(acc))
.unwrap()
}
fn swap_bytes_to_128bits(self, num_bytes: usize) -> Self {
self.swap_bytes(num_bytes).zero_ext(128 - num_bytes as u32 * 8)
}
}
pub trait SmtInt<'a, S: SmtSolver<'a, Int = Self>>:
Clone + Debug + Display + Add<Output = Self> + Sub<Output = Self> + Mul<Output = Self> + Sized
{
fn is_identical(&self, other: &Self) -> bool;
fn _eq(self, other: Self) -> S::Bool;
fn simplify(self) -> Self;
fn as_u64(&self) -> Option<u64>;
fn into_dynamic(self) -> Dynamic<'a, S> {
Dynamic::Int(self)
}
}
pub trait SmtBool<'a, S: SmtSolver<'a, Bool = Self>>:
Clone + Debug + Display + BitOr<Output = Self> + BitAnd<Output = Self> + BitXor<Output = Self> + Not<Output = Self>
{
fn is_identical(&self, other: &Self) -> bool;
fn _eq(self, other: Self) -> S::Bool;
fn simplify(self) -> Self;
fn ite_bv(self, lhs: S::BV, rhs: S::BV) -> S::BV;
fn ite_int(self, lhs: S::Int, rhs: S::Int) -> S::Int;
fn ite_bool(self, lhs: S::Bool, rhs: S::Bool) -> S::Bool;
fn ite_bv_array(self, lhs: S::BvArray, rhs: S::BvArray) -> S::BvArray;
fn ite_dynamic(self, lhs: Dynamic<'a, S>, rhs: Dynamic<'a, S>) -> Dynamic<'a, S> {
match (lhs, rhs) {
(Dynamic::BV(a), Dynamic::BV(b)) => self.ite_bv(a, b).into_dynamic(),
(Dynamic::Int(a), Dynamic::Int(b)) => self.ite_int(a, b).into_dynamic(),
(Dynamic::Bool(a), Dynamic::Bool(b)) => self.ite_bool(a, b).into_dynamic(),
(Dynamic::BvArray(a), Dynamic::BvArray(b)) => self.ite_bv_array(a, b).into_dynamic(),
(a, b) => panic!("ITE branches must have same sort: {a:?} vs {b:?}"),
}
}
fn as_bool(&self) -> Option<bool>;
fn into_dynamic(self) -> Dynamic<'a, S> {
Dynamic::Bool(self)
}
}
pub trait SmtBVArray<'a, S: SmtSolver<'a, BvArray = Self>>: Clone + Debug + Display {
fn is_identical(&self, other: &Self) -> bool;
fn _eq(self, other: Self) -> S::Bool;
fn simplify(self) -> Self;
fn select(self, index: S::BV) -> S::BV;
fn store(self, index: S::BV, value: S::BV) -> S::BvArray;
fn element_size(&self) -> u32;
fn index_size(&self) -> u32;
fn into_dynamic(self) -> Dynamic<'a, S> {
Dynamic::BvArray(self)
}
}
pub enum Dynamic<'a, S: SmtSolver<'a>> {
BV(S::BV),
Int(S::Int),
Bool(S::Bool),
BvArray(S::BvArray),
}
impl<'a, S: SmtSolver<'a>> Debug for Dynamic<'a, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BV(arg0) => f.debug_tuple("BV").field(arg0).finish(),
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
Self::BvArray(arg0) => f.debug_tuple("Array").field(arg0).finish(),
}
}
}
impl<'a, S: SmtSolver<'a>> Clone for Dynamic<'a, S> {
fn clone(&self) -> Self {
match self {
Self::BV(arg0) => Self::BV(arg0.clone()),
Self::Int(arg0) => Self::Int(arg0.clone()),
Self::Bool(arg0) => Self::Bool(arg0.clone()),
Self::BvArray(arg0) => Self::BvArray(arg0.clone()),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SortKind {
BV,
Int,
Bool,
Array,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Sort {
BV(u32),
Int,
Bool,
Array {
index: Box<Sort>,
element: Box<Sort>,
},
}
impl<'a, S: SmtSolver<'a>> Dynamic<'a, S> {
pub fn from_bv(bv: S::BV) -> Self {
Self::BV(bv)
}
pub fn as_bv(self) -> Option<S::BV> {
if let Dynamic::BV(bv) = self {
Some(bv)
} else {
None
}
}
pub fn as_int(self) -> Option<S::Int> {
if let Dynamic::Int(int) = self {
Some(int)
} else {
None
}
}
pub fn as_bool(self) -> Option<S::Bool> {
if let Dynamic::Bool(b) = self {
Some(b)
} else {
None
}
}
pub fn as_bv_array(self) -> Option<S::BvArray> {
if let Dynamic::BvArray(a) = self {
Some(a)
} else {
None
}
}
pub fn sort_kind(&self) -> SortKind {
match self {
Dynamic::BV(_) => SortKind::BV,
Dynamic::Int(_) => SortKind::Int,
Dynamic::Bool(_) => SortKind::Bool,
Dynamic::BvArray(_) => SortKind::Array,
}
}
pub fn sort(&self) -> Sort {
match self {
Dynamic::BV(bv) => Sort::BV(bv.get_size()),
Dynamic::Int(_) => Sort::Int,
Dynamic::Bool(_) => Sort::Bool,
Dynamic::BvArray(a) => Sort::Array {
index: Box::new(Sort::BV(a.index_size())),
element: Box::new(Sort::BV(a.element_size())),
},
}
}
pub fn simplify(self) -> Self {
match self {
Dynamic::BV(v) => Dynamic::BV(v.simplify()),
Dynamic::Int(v) => Dynamic::Int(v.simplify()),
Dynamic::Bool(v) => Dynamic::Bool(v.simplify()),
Dynamic::BvArray(v) => Dynamic::BvArray(v.simplify()),
}
}
pub fn is_identical(&self, other: &Self) -> bool {
match (self, other) {
(Dynamic::BV(v), Dynamic::BV(w)) => v.is_identical(w),
(Dynamic::Int(v), Dynamic::Int(w)) => v.is_identical(w),
(Dynamic::Bool(v), Dynamic::Bool(w)) => v.is_identical(w),
(Dynamic::BvArray(v), Dynamic::BvArray(w)) => v.is_identical(w),
(v, w) => panic!("Cannot compare {v:?} with {w:?}"),
}
}
pub fn _eq(self, other: Self) -> S::Bool {
match (self, other) {
(Dynamic::BV(v), Dynamic::BV(w)) => v._eq(w),
(Dynamic::Int(v), Dynamic::Int(w)) => v._eq(w),
(Dynamic::Bool(v), Dynamic::Bool(w)) => v._eq(w),
(Dynamic::BvArray(v), Dynamic::BvArray(w)) => v._eq(w),
(v, w) => panic!("Cannot compare {v:?} with {w:?}"),
}
}
}