use alloc::vec;
use puremp::{Int, Rational};
use crate::ast::manager::AstManager;
use crate::ast::node::{DeclInfo, FuncDeclFlags};
use crate::ast::parameter::Parameter;
use crate::ast::{AstId, DeclKind, FamilyId, SortSize};
use crate::util::symbol::Symbol;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[repr(i32)]
pub enum BvSortKind {
BitVec = 0,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[repr(i32)]
pub enum BvOp {
Num = 0,
Neg = 3,
Add = 4,
Sub = 5,
Mul = 6,
Udiv = 8,
Urem = 10,
Uleq = 22,
Sleq = 23,
Ult = 26,
Slt = 27,
BAnd = 30,
BOr = 31,
BNot = 32,
BXor = 33,
Concat = 37,
SignExt = 38,
ZeroExt = 39,
Extract = 40,
Shl = 45,
Lshr = 46,
Ashr = 47,
}
impl BvOp {
#[inline]
const fn kind(self) -> DeclKind {
self as DeclKind
}
}
impl AstManager {
fn bv_fid(&mut self) -> FamilyId {
self.mk_family_id(Symbol::new("bv"))
}
pub fn mk_bv_sort(&mut self, width: u32) -> AstId {
let fid = self.bv_fid();
let size = if width < 64 {
SortSize::finite(1u64 << width)
} else {
SortSize::very_big()
};
self.mk_sort(
Symbol::new(&alloc::format!("bv{width}")),
DeclInfo::new(
fid,
BvSortKind::BitVec as DeclKind,
vec![Parameter::Int(width as i32)],
),
size,
)
}
pub fn mk_bv_numeral(&mut self, value: Int, width: u32) -> AstId {
let sort = self.mk_bv_sort(width);
let fid = self.bv_fid();
let value = value.mod_2k(width); let name = Symbol::new(&alloc::format!("bv{value}"));
let info = DeclInfo::new(
fid,
BvOp::Num.kind(),
vec![Parameter::Rational(Rational::from_integer(value))],
);
let decl = self.mk_func_decl_full(name, &[], sort, info, FuncDeclFlags::default());
self.mk_const(decl)
}
pub fn mk_bv(&mut self, value: i64, width: u32) -> AstId {
self.mk_bv_numeral(Int::from(value), width)
}
pub fn mk_bv_const(&mut self, name: &str, width: u32) -> AstId {
let s = self.mk_bv_sort(width);
let d = self.mk_func_decl(Symbol::new(name), &[], s);
self.mk_const(d)
}
fn mk_bv_app(
&mut self,
name: &str,
op: BvOp,
domain: &[AstId],
range: AstId,
flags: FuncDeclFlags,
args: &[AstId],
) -> AstId {
let fid = self.bv_fid();
let info = DeclInfo::new(fid, op.kind(), alloc::vec::Vec::new());
let decl = self.mk_func_decl_full(Symbol::new(name), domain, range, info, flags);
self.mk_app(decl, args)
}
fn mk_bv_binop(
&mut self,
name: &str,
op: BvOp,
flags: FuncDeclFlags,
a: AstId,
b: AstId,
) -> AstId {
let sort = self.get_sort(a);
self.mk_bv_app(name, op, &[sort, sort], sort, flags, &[a, b])
}
pub fn mk_bvneg(&mut self, a: AstId) -> AstId {
let sort = self.get_sort(a);
self.mk_bv_app(
"bvneg",
BvOp::Neg,
&[sort],
sort,
FuncDeclFlags::default(),
&[a],
)
}
pub fn mk_bvadd(&mut self, a: AstId, b: AstId) -> AstId {
let flags = FuncDeclFlags {
left_assoc: true,
right_assoc: true,
commutative: true,
..FuncDeclFlags::default()
};
self.mk_bv_binop("bvadd", BvOp::Add, flags, a, b)
}
pub fn mk_bvsub(&mut self, a: AstId, b: AstId) -> AstId {
self.mk_bv_binop("bvsub", BvOp::Sub, FuncDeclFlags::default(), a, b)
}
pub fn mk_bvmul(&mut self, a: AstId, b: AstId) -> AstId {
let flags = FuncDeclFlags {
left_assoc: true,
right_assoc: true,
commutative: true,
..FuncDeclFlags::default()
};
self.mk_bv_binop("bvmul", BvOp::Mul, flags, a, b)
}
pub fn mk_bvudiv(&mut self, a: AstId, b: AstId) -> AstId {
self.mk_bv_binop("bvudiv", BvOp::Udiv, FuncDeclFlags::default(), a, b)
}
pub fn mk_bvurem(&mut self, a: AstId, b: AstId) -> AstId {
self.mk_bv_binop("bvurem", BvOp::Urem, FuncDeclFlags::default(), a, b)
}
pub fn mk_bvand(&mut self, a: AstId, b: AstId) -> AstId {
let flags = FuncDeclFlags {
commutative: true,
..FuncDeclFlags::default()
};
self.mk_bv_binop("bvand", BvOp::BAnd, flags, a, b)
}
pub fn mk_bvor(&mut self, a: AstId, b: AstId) -> AstId {
let flags = FuncDeclFlags {
commutative: true,
..FuncDeclFlags::default()
};
self.mk_bv_binop("bvor", BvOp::BOr, flags, a, b)
}
pub fn mk_bvxor(&mut self, a: AstId, b: AstId) -> AstId {
let flags = FuncDeclFlags {
commutative: true,
..FuncDeclFlags::default()
};
self.mk_bv_binop("bvxor", BvOp::BXor, flags, a, b)
}
pub fn mk_bv_concat(&mut self, a: AstId, b: AstId) -> AstId {
let wa = self
.bv_sort_width(self.get_sort(a))
.expect("concat: a not bv");
let wb = self
.bv_sort_width(self.get_sort(b))
.expect("concat: b not bv");
let sa = self.get_sort(a);
let sb = self.get_sort(b);
let range = self.mk_bv_sort(wa + wb);
self.mk_bv_app(
"concat",
BvOp::Concat,
&[sa, sb],
range,
FuncDeclFlags::default(),
&[a, b],
)
}
pub fn mk_bv_extract(&mut self, high: u32, low: u32, x: AstId) -> AstId {
assert!(high >= low, "extract: high < low");
let sx = self.get_sort(x);
let range = self.mk_bv_sort(high - low + 1);
let fid = self.bv_fid();
let info = DeclInfo::new(
fid,
BvOp::Extract as DeclKind,
vec![Parameter::Int(high as i32), Parameter::Int(low as i32)],
);
let decl = self.mk_func_decl_full(
Symbol::new("extract"),
&[sx],
range,
info,
FuncDeclFlags::default(),
);
self.mk_app(decl, &[x])
}
pub fn mk_bv_zero_extend(&mut self, k: u32, x: AstId) -> AstId {
self.mk_bv_extend("zero_extend", BvOp::ZeroExt, k, x)
}
pub fn mk_bv_sign_extend(&mut self, k: u32, x: AstId) -> AstId {
self.mk_bv_extend("sign_extend", BvOp::SignExt, k, x)
}
fn mk_bv_extend(&mut self, name: &str, op: BvOp, k: u32, x: AstId) -> AstId {
let w = self
.bv_sort_width(self.get_sort(x))
.expect("extend: not bv");
let sx = self.get_sort(x);
let range = self.mk_bv_sort(w + k);
let fid = self.bv_fid();
let info = DeclInfo::new(fid, op as DeclKind, vec![Parameter::Int(k as i32)]);
let decl = self.mk_func_decl_full(
Symbol::new(name),
&[sx],
range,
info,
FuncDeclFlags::default(),
);
self.mk_app(decl, &[x])
}
pub fn mk_bvnot(&mut self, a: AstId) -> AstId {
let sort = self.get_sort(a);
self.mk_bv_app(
"bvnot",
BvOp::BNot,
&[sort],
sort,
FuncDeclFlags::default(),
&[a],
)
}
pub fn mk_bvshl(&mut self, a: AstId, b: AstId) -> AstId {
self.mk_bv_binop("bvshl", BvOp::Shl, FuncDeclFlags::default(), a, b)
}
pub fn mk_bvlshr(&mut self, a: AstId, b: AstId) -> AstId {
self.mk_bv_binop("bvlshr", BvOp::Lshr, FuncDeclFlags::default(), a, b)
}
pub fn mk_bvashr(&mut self, a: AstId, b: AstId) -> AstId {
self.mk_bv_binop("bvashr", BvOp::Ashr, FuncDeclFlags::default(), a, b)
}
fn mk_bv_cmp(&mut self, name: &str, op: BvOp, a: AstId, b: AstId) -> AstId {
let sort = self.get_sort(a);
let bool_sort = self.mk_bool_sort();
self.mk_bv_app(
name,
op,
&[sort, sort],
bool_sort,
FuncDeclFlags::default(),
&[a, b],
)
}
pub fn mk_bvule(&mut self, a: AstId, b: AstId) -> AstId {
self.mk_bv_cmp("bvule", BvOp::Uleq, a, b)
}
pub fn mk_bvult(&mut self, a: AstId, b: AstId) -> AstId {
self.mk_bv_cmp("bvult", BvOp::Ult, a, b)
}
pub fn mk_bvslt(&mut self, a: AstId, b: AstId) -> AstId {
self.mk_bv_cmp("bvslt", BvOp::Slt, a, b)
}
pub fn mk_bvsle(&mut self, a: AstId, b: AstId) -> AstId {
self.mk_bv_cmp("bvsle", BvOp::Sleq, a, b)
}
pub fn bv_sort_width(&self, sort_id: AstId) -> Option<u32> {
let bvfid = self.get_family_id(Symbol::new("bv"))?;
let s = self.sort(sort_id)?;
if s.info.family_id == bvfid && s.info.decl_kind == BvSortKind::BitVec as DeclKind {
s.info.parameters.first()?.get_int().map(|w| w as u32)
} else {
None
}
}
pub fn bv_numeral_value(&self, id: AstId) -> Option<Int> {
let bvfid = self.get_family_id(Symbol::new("bv"))?;
let a = self.app(id)?;
if !a.args.is_empty() {
return None;
}
let d = self.func_decl(a.decl)?;
if d.info.family_id == bvfid && d.info.decl_kind == BvOp::Num as DeclKind {
d.info
.parameters
.first()?
.get_rational()
.map(|r| r.numerator().clone())
} else {
None
}
}
pub fn bv_op(&self, id: AstId) -> Option<BvOp> {
let bvfid = self.get_family_id(Symbol::new("bv"))?;
let a = self.app(id)?;
let d = self.func_decl(a.decl)?;
if d.info.family_id != bvfid {
return None;
}
let k = d.info.decl_kind;
[
BvOp::Num,
BvOp::Neg,
BvOp::Add,
BvOp::Sub,
BvOp::Mul,
BvOp::Udiv,
BvOp::Urem,
BvOp::Uleq,
BvOp::Ult,
BvOp::BAnd,
BvOp::BOr,
BvOp::BNot,
BvOp::BXor,
BvOp::Sleq,
BvOp::Slt,
BvOp::Concat,
BvOp::SignExt,
BvOp::ZeroExt,
BvOp::Extract,
BvOp::Shl,
BvOp::Lshr,
BvOp::Ashr,
]
.into_iter()
.find(|op| *op as DeclKind == k)
}
pub fn bv_extract_params(&self, id: AstId) -> Option<(u32, u32)> {
if self.bv_op(id)? != BvOp::Extract {
return None;
}
let d = self.func_decl(self.app(id)?.decl)?;
let high = d.info.parameters.first()?.get_int()? as u32;
let low = d.info.parameters.get(1)?.get_int()? as u32;
Some((high, low))
}
pub fn bv_extend_amount(&self, id: AstId) -> Option<u32> {
match self.bv_op(id)? {
BvOp::ZeroExt | BvOp::SignExt => {
let d = self.func_decl(self.app(id)?.decl)?;
Some(d.info.parameters.first()?.get_int()? as u32)
}
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bv_sort_carries_width() {
let mut m = AstManager::new();
let s8 = m.mk_bv_sort(8);
let s8b = m.mk_bv_sort(8);
let s16 = m.mk_bv_sort(16);
assert_eq!(s8, s8b);
assert_ne!(s8, s16);
assert_eq!(m.bv_sort_width(s8), Some(8));
assert_eq!(m.sort(s8).unwrap().num_elements, SortSize::finite(256));
}
#[test]
fn numerals_wrap_modulo_width() {
let mut m = AstManager::new();
let a = m.mk_bv(256, 8);
let z = m.mk_bv(0, 8);
assert_eq!(a, z);
let b = m.mk_bv(257, 8);
let one = m.mk_bv(1, 8);
assert_eq!(b, one);
}
#[test]
fn bitwise_ops_shapes() {
let mut m = AstManager::new();
let x = m.mk_bv_const("x", 8);
let y = m.mk_bv_const("y", 8);
let s8 = m.mk_bv_sort(8);
let and = m.mk_bvand(x, y);
let or = m.mk_bvor(x, y);
let xor = m.mk_bvxor(x, y);
let not = m.mk_bvnot(x);
for t in [and, or, xor, not] {
assert_eq!(m.get_sort(t), s8);
}
assert_eq!(m.bv_op(and), Some(BvOp::BAnd));
assert_eq!(m.bv_op(or), Some(BvOp::BOr));
assert_eq!(m.bv_op(xor), Some(BvOp::BXor));
assert_eq!(m.bv_op(not), Some(BvOp::BNot));
assert_eq!(m.bv_op(x), None);
}
#[test]
fn build_bitvector_term() {
let mut m = AstManager::new();
let x = m.mk_bv_const("x", 8);
let one = m.mk_bv(1, 8);
let sum = m.mk_bvadd(x, one);
assert_eq!(m.bv_sort_width(m.get_sort(sum)), Some(8));
assert_eq!(m.pp(sum), "(bvadd x bv1)");
let cmp = m.mk_bvult(x, sum);
assert_eq!(m.get_sort(cmp), m.mk_bool_sort());
}
}