Skip to content

Commit

Permalink
wip: implement array ops with new representation
Browse files Browse the repository at this point in the history
  • Loading branch information
ekiwi committed Sep 23, 2024
1 parent 873d94e commit c5ef452
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 27 deletions.
16 changes: 3 additions & 13 deletions src/array/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -21,7 +21,7 @@ pub trait ArrayOps {
fn num_elements(&self) -> usize {
1usize << self.index_width()
}
fn select<'a>(&self, index: impl Into<BitVecValueRef<'a>>) -> BitVecValueRef;
fn select<'a>(&self, index: impl Into<BitVecValueRef<'a>>) -> BitVecValue;
// {
// let index = index.into();
// debug_assert!(self.index_width() <= DENSE_ARRAY_MAX_INDEX_WIDTH);
Expand All @@ -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<R: ArrayOps + ?Sized>(&self, rhs: &R) -> bool;
// fn is_equal<R: ArrayOps + ?Sized>(&self, rhs: &R) -> bool;

// {
// debug_assert_eq!(self.index_width(), rhs.index_width());
Expand Down Expand Up @@ -60,16 +60,6 @@ pub trait ArrayMutOps: ArrayOps {
// element.assign(data);
// }

// only works for dense arrays
// fn select_mut<I: BitVecOps>(&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<ArrayValueRef<'a>>) {
// let value = value.into();
Expand Down
149 changes: 137 additions & 12 deletions src/array/owned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,40 @@
// author: Kevin Laeufer <laeufer@cornell.edu>

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<Word>,
pub enum ArrayValue {
Sparse(SparseArrayValue),
Dense(DenseArrayValue),
}

impl ArrayOps for ArrayValue {
fn index_width(&self) -> WidthInt {
self.index_width
impl From<SparseArrayValue> for ArrayValue {
fn from(value: SparseArrayValue) -> Self {
ArrayValue::Sparse(value)
}
}

fn data_width(&self) -> WidthInt {
self.data_width
impl From<DenseArrayValue> 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))]
Expand Down Expand Up @@ -61,6 +72,120 @@ enum SparseArrayImpl {
BigBig(BitVecValue, HashMap<BitVecValue, BitVecValue>),
}

impl SparseArrayValue {
/// Checks equivalence for two arrays of the same type (index_width x data_width).
fn is_equal(&self, other: &SparseArrayValue) -> Option<bool> {
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<BitVecValueRef<'a>>) -> 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<BitVecValueRef<'a>>,
data: impl Into<BitVecValueRef<'b>>,
) {
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::*;
Expand Down
35 changes: 34 additions & 1 deletion src/bv/borrowed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -78,3 +79,35 @@ impl<'a> BitVecMutOps for BitVecValueMutRef<'a> {
self.words
}
}

impl Borrow<BitVecValueRef> 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<Q: ?Sized>(key: &Q) -> u64
where
BitVecValue: Borrow<Q>,
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() {}
}
2 changes: 1 addition & 1 deletion src/bv/owned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit c5ef452

Please # to comment.