use crate::{cell::Cell, Side};
use fasthash::{HasherExt, Murmur3HasherExt as ElmHasher};
use serde::{Deserialize, Serialize};
use std::{
collections::HashSet,
fmt::Debug,
hash::Hash,
ops::{BitXor, BitXorAssign, Sub},
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IBF<T>
where
T: Clone
+ std::hash::Hash
+ BitXor<Output = T>
+ BitXorAssign
+ Default
+ PartialEq
+ Eq
+ Debug,
{
cells: Box<[Cell<T>]>,
hash_count: usize,
size: usize,
}
impl<T> IBF<T>
where
T: Clone
+ std::hash::Hash
+ BitXor<Output = T>
+ BitXorAssign
+ Default
+ PartialEq
+ Eq
+ Debug,
{
pub fn new(size: usize) -> Self {
Self::new_with_hash_count(size, 3)
}
pub fn new_with_hash_count(size: usize, hash_count: usize) -> Self {
let buckets = vec![Cell::default(); size].into_boxed_slice();
Self {
cells: buckets,
hash_count,
size,
}
}
pub fn encode(&mut self, element: T) {
for i in 0..self.hash_count {
self.get_ith_cell(i, &element).encode(element.clone())
}
}
pub fn decode(mut self) -> Result<HashSet<Side<T>>, String> {
let mut set = HashSet::new();
loop {
if let Some(next_pure) = self.cells.iter().find(|cell| cell.is_pure()) {
let next_pure = next_pure.clone();
let element = next_pure.decode().expect("Only grabbing pure elements");
set.insert(element);
self.remove(next_pure);
} else {
if self.cells.iter().all(|cell| cell.is_empty()) {
return Ok(set);
} else {
let not_empty = self
.cells
.iter()
.filter(|cell| !cell.is_empty())
.collect::<Vec<_>>();
return Err(format!("Unable to fully decode: {:#?}", not_empty));
}
}
}
}
fn remove(&mut self, cell: Cell<T>) {
let element = &*cell.decode().expect("Only removing pure cells");
for i in 0..self.hash_count {
*self.get_ith_cell(i, &element) -= cell.clone();
}
}
fn get_ith_cell(&mut self, i: usize, element: &T) -> &mut Cell<T> {
let mut hasher: ElmHasher = Default::default();
element.hash(&mut hasher);
i.hash(&mut hasher);
let cell_idx = (hasher.finish_ext() % (self.size as u128)) as usize;
&mut self.cells[cell_idx]
}
}
impl<T> Sub for IBF<T>
where
T: Clone
+ std::hash::Hash
+ BitXor<Output = T>
+ BitXorAssign
+ Default
+ PartialEq
+ Eq
+ Debug,
{
type Output = Result<IBF<T>, String>;
fn sub(self, rhs: Self) -> Self::Output {
if self.hash_count != rhs.hash_count || self.size != rhs.size {
return Err("IBFs are not configured the same".to_string());
}
Ok(Self {
cells: self
.cells
.iter()
.zip(rhs.cells.iter())
.map(|(l, r)| l - r)
.collect(),
..self
})
}
}
impl<T> Sub for &IBF<T>
where
T: Clone
+ std::hash::Hash
+ BitXor<Output = T>
+ BitXorAssign
+ Default
+ PartialEq
+ Eq
+ Debug,
{
type Output = Result<IBF<T>, String>;
fn sub(self, rhs: Self) -> Self::Output {
if self.hash_count != rhs.hash_count || self.size != rhs.size {
return Err("IBFs are not configured the same".to_string());
}
Ok(IBF {
cells: self
.cells
.iter()
.zip(rhs.cells.iter())
.map(|(l, r)| l - r)
.collect(),
hash_count: self.hash_count,
size: self.size,
})
}
}