use std::fmt::{Debug, Display};
use crate::{
abstr::{
three_valued::{CThreeValuedBitvector, InvalidZerosOnes, RThreeValuedBitvector},
BitvectorDisplay, BitvectorDomain, CBitvectorDomain, DomainDisplay,
},
bitvector::{compute_u64_mask, BitvectorBound},
concr::{CConcreteBitvector, ConcreteBitvector, SignedBitvector, UnsignedBitvector},
forward::Bitwise,
misc::{Join, RBound},
traits::misc::MetaEq,
};
use super::ThreeValuedBitvector;
impl<B: BitvectorBound> Join for ThreeValuedBitvector<B> {
fn join(self, other: &Self) -> Self {
assert_eq!(self.bound(), other.bound());
let zeros = self.zeros.bit_or(other.zeros);
let ones = self.ones.bit_or(other.ones);
Self::from_zeros_ones(zeros, ones)
}
}
impl<B: BitvectorBound> ThreeValuedBitvector<B> {
#[must_use]
pub fn new(value: u64, bound: B) -> Self {
Self::from_concrete_value(ConcreteBitvector::new(value, bound))
}
#[must_use]
pub fn from_zeros_ones(zeros: ConcreteBitvector<B>, ones: ConcreteBitvector<B>) -> Self {
match Self::try_from_zeros_ones(zeros, ones) {
Ok(ok) => ok,
Err(_) => panic!(
"Invalid zeros-ones with some unset bits (zeros {}, ones {})",
zeros, ones
),
}
}
pub fn try_from_zeros_ones(
zeros: ConcreteBitvector<B>,
ones: ConcreteBitvector<B>,
) -> Result<Self, InvalidZerosOnes> {
assert_eq!(zeros.bound(), ones.bound());
let bound = zeros.bound();
if Bitwise::bit_or(zeros, ones) != ConcreteBitvector::bit_mask(bound) {
return Err(InvalidZerosOnes);
}
Ok(Self { zeros, ones })
}
pub fn from_concrete_value(value: ConcreteBitvector<B>) -> Self {
let zeros = Bitwise::bit_not(value);
let ones = value;
Self::from_zeros_ones(zeros, ones)
}
#[must_use]
pub fn is_zeros_sign_bit_set(&self) -> bool {
self.zeros.is_sign_bit_set()
}
#[must_use]
pub fn is_ones_sign_bit_set(&self) -> bool {
self.ones.is_sign_bit_set()
}
#[must_use]
pub fn contains_concrete(&self, a: &ConcreteBitvector<B>) -> bool {
let excessive_rhs_zeros = a.bit_not().bit_and(self.zeros.bit_not());
let excessive_rhs_ones = a.bit_and(self.ones.bit_not());
excessive_rhs_zeros.is_zero() && excessive_rhs_ones.is_zero()
}
#[must_use]
pub fn get_possibly_one_flags(&self) -> ConcreteBitvector<B> {
self.ones
}
#[must_use]
pub fn get_possibly_zero_flags(&self) -> ConcreteBitvector<B> {
self.zeros
}
#[must_use]
pub(super) fn new_unknown(bound: B) -> Self {
let zeros = ConcreteBitvector::bit_mask(bound);
let ones = ConcreteBitvector::bit_mask(bound);
Self::from_zeros_ones(zeros, ones)
}
#[must_use]
pub fn new_value_known(value: ConcreteBitvector<B>, known: ConcreteBitvector<B>) -> Self {
let unknown = Bitwise::bit_not(known);
Self::new_value_unknown(value, unknown)
}
#[must_use]
pub fn new_value_unknown(value: ConcreteBitvector<B>, unknown: ConcreteBitvector<B>) -> Self {
let zeros = Bitwise::bit_or(Bitwise::bit_not(value), unknown);
let ones = Bitwise::bit_or(value, unknown);
Self::from_zeros_ones(zeros, ones)
}
#[must_use]
pub fn get_unknown_bits(&self) -> ConcreteBitvector<B> {
Bitwise::bit_and(self.zeros, self.ones)
}
#[must_use]
pub fn contains(&self, rhs: &Self) -> bool {
let excessive_rhs_zeros = rhs.zeros.bit_and(self.zeros.bit_not());
let excessive_rhs_ones = rhs.ones.bit_and(self.ones.bit_not());
excessive_rhs_zeros.is_zero() && excessive_rhs_ones.is_zero()
}
#[allow(dead_code)]
pub(crate) fn all_with_bound_iter(bound: B) -> impl Iterator<Item = Self> {
let zeros_iter = ConcreteBitvector::<B>::all_with_bound_iter(bound);
zeros_iter.flat_map(move |zeros| {
let ones_iter = ConcreteBitvector::<B>::all_with_bound_iter(bound);
ones_iter.filter_map(move |ones| Self::try_from_zeros_ones(zeros, ones).ok())
})
}
#[must_use]
pub fn from_unsigned_interval(min: UnsignedBitvector<B>, max: UnsignedBitvector<B>) -> Self {
assert_eq!(min.bound(), max.bound());
let bound = min.bound();
assert!(min <= max);
let min = min.cast_bitvector();
let max = max.cast_bitvector();
let xor = min.bit_xor(max);
let Some(unknown_positions) = xor.to_u64().checked_ilog2() else {
return Self::from_concrete_value(min);
};
let unknown_mask = ConcreteBitvector::new(compute_u64_mask(unknown_positions + 1), bound);
Self::new_value_unknown(min, unknown_mask)
}
}
impl<B: BitvectorBound> BitvectorDomain for ThreeValuedBitvector<B> {
type Bound = B;
type General<X: BitvectorBound> = ThreeValuedBitvector<X>;
fn single_value(value: ConcreteBitvector<Self::Bound>) -> Self {
Self::from_concrete_value(value)
}
fn top(bound: B) -> Self {
Self::new_unknown(bound)
}
fn meet(self, rhs: &Self) -> Option<Self> {
let zeros = self.zeros.bit_and(rhs.zeros);
let ones = self.ones.bit_and(rhs.ones);
Self::try_from_zeros_ones(zeros, ones).ok()
}
fn bound(&self) -> Self::Bound {
self.zeros.bound()
}
fn umin(&self) -> UnsignedBitvector<Self::Bound> {
Bitwise::bit_not(self.zeros).as_unsigned()
}
fn umax(&self) -> UnsignedBitvector<Self::Bound> {
self.ones.as_unsigned()
}
fn smin(&self) -> SignedBitvector<B> {
let bound = self.bound();
let sign_bit_mask = ConcreteBitvector::<B>::sign_bit_mask(bound);
let mut result = self.umin().cast_bitvector();
if self.is_ones_sign_bit_set() {
result = result.bit_or(sign_bit_mask)
}
result.as_signed()
}
fn smax(&self) -> SignedBitvector<B> {
let bound = self.bound();
let sign_bit_mask = ConcreteBitvector::<B>::sign_bit_mask(bound);
let mut result = self.umax().cast_bitvector();
if self.is_zeros_sign_bit_set() {
result = result.bit_and(sign_bit_mask.bit_not());
}
result.as_signed()
}
fn concrete_value(&self) -> Option<ConcreteBitvector<B>> {
let nxor = Bitwise::bit_not(Bitwise::bit_xor(self.ones, self.zeros));
if !nxor.is_zero() {
return None;
}
Some(self.ones)
}
fn display(&self) -> BitvectorDisplay {
let domains = vec![DomainDisplay::Value(format!("{}", self))];
BitvectorDisplay { domains }
}
}
impl<B: BitvectorBound> MetaEq for ThreeValuedBitvector<B> {
fn meta_eq(&self, other: &Self) -> bool {
self.ones == other.ones && self.zeros == other.zeros
}
}
impl<const W: u32> CBitvectorDomain for CThreeValuedBitvector<W> {
type Concrete = CConcreteBitvector<W>;
fn from_concrete_bitvector(value: Self::Concrete) -> Self {
let zeros = Bitwise::bit_not(value);
let ones = value;
Self::from_zeros_ones(zeros, ones)
}
fn from_runtime_bitvector(value: Self::General<RBound>) -> Self {
assert_eq!(value.bound().width(), W);
let zeros = ConcreteBitvector::from_runtime_bitvector(value.zeros);
let ones = ConcreteBitvector::from_runtime_bitvector(value.ones);
Self::from_zeros_ones(zeros, ones)
}
fn as_runtime_bitvector(&self) -> Self::General<RBound> {
RThreeValuedBitvector {
zeros: self.zeros.as_runtime_bitvector(),
ones: self.ones.as_runtime_bitvector(),
}
}
}
impl<B: BitvectorBound> Debug for ThreeValuedBitvector<B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let zeros = self.zeros.to_u64();
let ones = self.ones.to_u64();
format_zeros_ones(f, self.bound().width(), zeros, ones)
}
}
impl<B: BitvectorBound> Display for ThreeValuedBitvector<B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<Self as Debug>::fmt(self, f)
}
}
pub fn format_zeros_ones(
f: &mut std::fmt::Formatter<'_>,
bit_width: u32,
zeros: u64,
ones: u64,
) -> std::fmt::Result {
let nxor = !(ones ^ zeros);
if nxor == 0 {
return write!(f, "{:?}", ones);
}
write!(f, "\"")?;
for little_k in 0..bit_width {
let big_k = bit_width - little_k - 1;
let zero = (zeros >> (big_k as usize)) & 1 != 0;
let one = (ones >> (big_k as usize)) & 1 != 0;
let c = match (zero, one) {
(true, true) => 'X',
(true, false) => '0',
(false, true) => '1',
(false, false) => 'V',
};
write!(f, "{}", c)?;
}
write!(f, "\"")
}