From c5ef4529fc9467b3f875417fc89e48fd1d360a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20L=C3=A4ufer?= Date: Mon, 23 Sep 2024 16:32:40 -0400 Subject: [PATCH] wip: implement array ops with new representation --- src/array/ops.rs | 16 +---- src/array/owned.rs | 149 +++++++++++++++++++++++++++++++++++++++++---- src/bv/borrowed.rs | 35 ++++++++++- src/bv/owned.rs | 2 +- 4 files changed, 175 insertions(+), 27 deletions(-) diff --git a/src/array/ops.rs b/src/array/ops.rs index 713085d..48f0907 100644 --- a/src/array/ops.rs +++ b/src/array/ops.rs @@ -5,7 +5,7 @@ // // Traits for operations on arrays. -use crate::{BitVecValueRef, WidthInt, Word}; +use crate::{BitVecValue, BitVecValueRef, WidthInt, Word}; pub const DENSE_ARRAY_MAX_INDEX_WIDTH: WidthInt = 48; @@ -21,7 +21,7 @@ pub trait ArrayOps { fn num_elements(&self) -> usize { 1usize << self.index_width() } - fn select<'a>(&self, index: impl Into>) -> BitVecValueRef; + fn select<'a>(&self, index: impl Into>) -> BitVecValue; // { // let index = index.into(); // debug_assert!(self.index_width() <= DENSE_ARRAY_MAX_INDEX_WIDTH); @@ -31,7 +31,7 @@ pub trait ArrayOps { // let end = start + self.words_per_element(); // BitVecValueRef::new(self.data_width(), &self.words()[start..end]) // } - fn is_equal(&self, rhs: &R) -> bool; + // fn is_equal(&self, rhs: &R) -> bool; // { // debug_assert_eq!(self.index_width(), rhs.index_width()); @@ -60,16 +60,6 @@ pub trait ArrayMutOps: ArrayOps { // element.assign(data); // } - // only works for dense arrays - // fn select_mut(&mut self, index: I) -> BitVecValueMutRef { - // debug_assert!(self.index_width() <= DENSE_ARRAY_MAX_INDEX_WIDTH); - // debug_assert_eq!(self.index_width(), index.width()); - // debug_assert_eq!(index.words().len(), 1); - // let start = self.words_per_element() * index.words()[0] as usize; - // let end = start + self.words_per_element(); - // BitVecValueMutRef::new(self.data_width(), &mut self.words_mut()[start..end]) - // } - // only performs well for dense arrays // fn assign<'a>(&mut self, value: impl Into>) { // let value = value.into(); diff --git a/src/array/owned.rs b/src/array/owned.rs index 285d694..2208f85 100644 --- a/src/array/owned.rs +++ b/src/array/owned.rs @@ -4,29 +4,40 @@ // author: Kevin Laeufer use crate::array::ops::{ArrayMutOps, ArrayOps}; -use crate::{BitVecValue, WidthInt, Word}; +use crate::{BitVecOps, BitVecValue, BitVecValueRef, WidthInt, Word}; use std::collections::HashMap; -/// Owned dense bit-vector array. +/// Owned Array Container. #[derive(Clone)] #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] -pub struct ArrayValue { - pub(crate) index_width: WidthInt, - pub(crate) data_width: WidthInt, - pub(crate) words: Vec, +pub enum ArrayValue { + Sparse(SparseArrayValue), + Dense(DenseArrayValue), } -impl ArrayOps for ArrayValue { - fn index_width(&self) -> WidthInt { - self.index_width +impl From for ArrayValue { + fn from(value: SparseArrayValue) -> Self { + ArrayValue::Sparse(value) } +} - fn data_width(&self) -> WidthInt { - self.data_width +impl From for ArrayValue { + fn from(value: DenseArrayValue) -> Self { + ArrayValue::Dense(value) } } -impl ArrayMutOps for ArrayValue {} +// impl ArrayOps for ArrayValue { +// fn index_width(&self) -> WidthInt { +// self.index_width +// } +// +// fn data_width(&self) -> WidthInt { +// self.data_width +// } +// } +// +// impl ArrayMutOps for ArrayValue {} #[derive(Clone)] #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] @@ -61,6 +72,120 @@ enum SparseArrayImpl { BigBig(BitVecValue, HashMap), } +impl SparseArrayValue { + /// Checks equivalence for two arrays of the same type (index_width x data_width). + fn is_equal(&self, other: &SparseArrayValue) -> Option { + if other.index_width != self.index_width || other.data_width != self.data_width { + return None; + } + match (&self.data, &other.data) { + ( + SparseArrayImpl::U64U64(default_a, map_a), + SparseArrayImpl::U64U64(default_b, map_b), + ) => { + todo!() + } + ( + SparseArrayImpl::U64Big(default_a, map_a), + SparseArrayImpl::U64Big(default_b, map_b), + ) => { + todo!() + } + ( + SparseArrayImpl::BigBig(default_a, map_a), + SparseArrayImpl::BigBig(default_b, map_b), + ) => { + todo!() + } + _ => unreachable!( + "the representation for two arrays of the same type should always be the same!" + ), + } + } +} + +impl ArrayOps for SparseArrayValue { + fn index_width(&self) -> WidthInt { + self.index_width + } + fn data_width(&self) -> WidthInt { + self.data_width + } + fn select<'a>(&self, index: impl Into>) -> BitVecValue { + let index = index.into(); + debug_assert_eq!(index.width(), self.index_width); + match &self.data { + SparseArrayImpl::U64U64(default, map) => { + let index = index.to_u64().unwrap(); + let value = map.get(&index).copied().unwrap_or(*default); + BitVecValue::from_u64(value, self.data_width) + } + SparseArrayImpl::U64Big(default, map) => { + let index = index.to_u64().unwrap(); + let value = map.get(&index).cloned().unwrap_or_else(|| default.clone()); + value + } + SparseArrayImpl::BigBig(default, map) => { + todo!("index with BitVecRef into map!") + } + } + } +} + +impl ArrayMutOps for SparseArrayValue { + fn store<'a, 'b>( + &mut self, + index: impl Into>, + data: impl Into>, + ) { + let index = index.into(); + debug_assert_eq!(index.width(), self.index_width); + let data = data.into(); + debug_assert_eq!(data.width(), self.data_width); + match &mut self.data { + SparseArrayImpl::U64U64(default, map) => { + let index = index.to_u64().unwrap(); + let data = data.to_u64().unwrap(); + if data == *default { + // ensures that the default value is used for the given index + map.remove(&index); + } else { + map.insert(index, data); + } + } + SparseArrayImpl::U64Big(default, map) => { + let index = index.to_u64().unwrap(); + if data.is_equal(default) { + // ensures that the default value is used for the given index + map.remove(&index); + } else { + map.insert(index, data.into()); + } + } + SparseArrayImpl::BigBig(default, map) => { + todo!("index with BitVecRef into map!") + } + } + } + + fn clear(&mut self) { + match &mut self.data { + SparseArrayImpl::U64U64(default, map) => { + *default = 0; + map.clear(); + } + SparseArrayImpl::U64Big(default, map) => { + *default = BitVecValue::zero(self.data_width); + map.clear(); + } + SparseArrayImpl::BigBig(default, map) => { + *default = BitVecValue::zero(self.data_width); + map.clear(); + } + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/bv/borrowed.rs b/src/bv/borrowed.rs index 91f94e8..dd42485 100644 --- a/src/bv/borrowed.rs +++ b/src/bv/borrowed.rs @@ -5,9 +5,10 @@ // Borrowed bit-vector and array values. use crate::{BitVecMutOps, BitVecOps, BitVecValue, WidthInt, Word}; +use std::borrow::Borrow; /// Bit-vector value that does not own its storage. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Hash)] pub struct BitVecValueRef<'a> { pub(crate) width: WidthInt, pub(crate) words: &'a [Word], @@ -78,3 +79,35 @@ impl<'a> BitVecMutOps for BitVecValueMutRef<'a> { self.words } } + +impl Borrow for BitVecValue {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{BitVecMutOps, BitVecValue}; + use std::borrow::Borrow; + use std::hash::{DefaultHasher, Hash, Hasher}; + + /// Signature is copied from HashTable::get + fn get_hash(key: &Q) -> u64 + where + BitVecValue: Borrow, + Q: Hash + Eq, + { + let mut hasher = DefaultHasher::new(); + key.borrow().hash(&mut hasher); + hasher.finish() + } + + fn check_hash(value: BitVecValue) { + let value_hash = get_hash(&value); + let re = BitVecValueRef::from(&value); + let re_hash = get_hash(re); + assert_eq!(value, re); + assert_eq!(value_hash, re_hash); + } + + #[test] + fn borrowed_hash() {} +} diff --git a/src/bv/owned.rs b/src/bv/owned.rs index 7c720fb..be55eea 100644 --- a/src/bv/owned.rs +++ b/src/bv/owned.rs @@ -10,7 +10,7 @@ use smallvec::{smallvec, SmallVec}; pub(crate) type ValueVec = SmallVec<[Word; 2]>; /// Owned bit-vector value. -#[derive(Clone)] +#[derive(Clone, Hash)] #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] pub struct BitVecValue { pub(crate) width: WidthInt,