use std::collections::HashSet;
use std::fmt::Debug;
use std::hash::{BuildHasherDefault, Hash};
use std::vec::IntoIter;
use dashmap::DashMap;
use itertools::Itertools;
use crate::collections::AppendOnlyVec;
use crate::formulas::formula_cache::hashable_formula_set::{HashableFormulaSet, SimpleHashAdder};
use crate::formulas::formula_cache::CACHE_INITIAL_CAPACITY;
use crate::formulas::{EncodedFormula, FormulaType};
use super::formula_encoding::{extend_32, Encoding, FormulaEncoding, SmallFormulaEncoding};
pub struct NaryFormulaCache {
vec32: AppendOnlyVec<Box<[SmallFormulaEncoding]>>,
vec64: AppendOnlyVec<Box<[FormulaEncoding]>>,
reverse_map32: DashMap<HashableFormulaSet<SmallFormulaEncoding>, FormulaEncoding, BuildHasherDefault<SimpleHashAdder>>,
reverse_map64: DashMap<HashableFormulaSet<FormulaEncoding>, FormulaEncoding, BuildHasherDefault<SimpleHashAdder>>,
representing_type: FormulaType,
}
impl NaryFormulaCache {
pub fn new(ty: FormulaType) -> Self {
Self {
vec32: AppendOnlyVec::new(),
vec64: AppendOnlyVec::new(),
reverse_map32: DashMap::with_capacity_and_hasher(CACHE_INITIAL_CAPACITY, BuildHasherDefault::default()),
reverse_map64: DashMap::default(),
representing_type: ty,
}
}
#[allow(dead_code, clippy::cast_possible_truncation)]
pub fn get(&self, index: FormulaEncoding) -> Vec<EncodedFormula> {
if index.is_large_cache() {
self.vec64[index.index() as usize].iter().map(|&encoding| encoding.to_formula()).collect()
} else {
self.vec32[index.index() as usize].iter().map(|&encoding| encoding.to_formula()).collect()
}
}
#[allow(clippy::cast_possible_truncation)]
pub fn get_iter(&self, index: FormulaEncoding) -> NaryIterator {
let container = if index.is_large_cache() {
let vec = &self.vec64[index.index() as usize];
NaryCacheContainer::Encoding64(vec)
} else {
let vec = &self.vec32[index.index() as usize];
NaryCacheContainer::Encoding32(vec)
};
NaryIterator::new(container)
}
pub fn get_or_insert(
&self,
elements32: Vec<SmallFormulaEncoding>,
element_set32: HashSet<SmallFormulaEncoding>,
mut elements64: Vec<FormulaEncoding>,
mut element_set64: HashSet<FormulaEncoding>,
) -> FormulaEncoding {
if elements64.is_empty() {
let set = HashableFormulaSet::new(element_set32);
*self.reverse_map32.entry(set).or_insert_with(|| {
let index = self.vec32.push(elements32.into());
FormulaEncoding::encode(index as u64, self.representing_type, false)
})
} else {
elements64.extend(elements32.iter().map(|e| e.as_64()));
element_set64.extend(elements32.iter().map(|e| e.as_64()));
let set = HashableFormulaSet::new(element_set64);
*self.reverse_map64.entry(set).or_insert_with(|| {
let index = self.vec64.push(elements64.into());
FormulaEncoding::encode(index as u64, self.representing_type, true)
})
}
}
pub fn shrink_to_fit(&self) {
self.reverse_map32.shrink_to_fit();
self.reverse_map64.shrink_to_fit();
}
pub fn len(&self) -> usize {
debug_assert_eq!(self.vec32.len(), self.reverse_map32.len());
debug_assert_eq!(self.vec64.len(), self.reverse_map64.len());
self.vec32.len() + self.vec64.len()
}
}
#[derive(Clone, Hash, Eq, PartialEq, Debug)]
enum NaryCacheContainer<'a> {
Encoding32(&'a [SmallFormulaEncoding]),
Encoding64(&'a [FormulaEncoding]),
}
#[derive(Clone, Hash, Eq, PartialEq, Debug)]
pub struct NaryIterator<'a> {
container: NaryCacheContainer<'a>,
len: usize,
current_index: usize,
}
impl<'a> NaryIterator<'a> {
const fn new(container: NaryCacheContainer<'a>) -> Self {
let len = match container {
NaryCacheContainer::Encoding32(v) => v.len(),
NaryCacheContainer::Encoding64(v) => v.len(),
};
NaryIterator { container, len, current_index: 0 }
}
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub fn into_owned(self) -> IntoIter<EncodedFormula> {
self.collect_vec().into_iter()
}
pub fn into_vec(self) -> Vec<EncodedFormula> {
self.collect_vec()
}
}
impl<'a> Iterator for NaryIterator<'a> {
type Item = EncodedFormula;
fn next(&mut self) -> Option<Self::Item> {
if self.current_index == self.len {
None
} else {
let res = match self.container {
NaryCacheContainer::Encoding32(vec) => EncodedFormula::from(extend_32(vec[self.current_index])),
NaryCacheContainer::Encoding64(vec) => EncodedFormula::from(vec[self.current_index]),
};
self.current_index += 1;
Some(res)
}
}
}