use alloc::vec::Vec;
use crate::{EMPTY_WORD, Map, Set, Word, merkle::smt::large_forest::root::LineageId};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ForestOperation {
Insert { key: Word, value: Word },
Remove { key: Word },
}
impl ForestOperation {
pub fn insert(key: Word, value: Word) -> Self {
Self::Insert { key, value }
}
pub fn remove(key: Word) -> Self {
Self::Remove { key }
}
pub fn key(&self) -> Word {
match self {
ForestOperation::Insert { key, .. } => *key,
ForestOperation::Remove { key } => *key,
}
}
}
impl From<ForestOperation> for (Word, Word) {
fn from(value: ForestOperation) -> Self {
match value {
ForestOperation::Insert { key, value } => (key, value),
ForestOperation::Remove { key } => (key, EMPTY_WORD),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SmtUpdateBatch {
operations: Vec<ForestOperation>,
}
impl SmtUpdateBatch {
pub fn empty() -> Self {
Self { operations: vec![] }
}
pub fn new(operations: impl Iterator<Item = ForestOperation>) -> Self {
Self {
operations: operations.collect::<Vec<_>>(),
}
}
pub fn add_operations(&mut self, operations: impl Iterator<Item = ForestOperation>) {
self.operations.extend(operations);
}
pub fn add_insert(&mut self, key: Word, value: Word) {
self.operations.push(ForestOperation::insert(key, value));
}
pub fn add_remove(&mut self, key: Word) {
self.operations.push(ForestOperation::remove(key));
}
pub fn consume(self) -> Vec<ForestOperation> {
let mut seen_keys: Set<Word> = Set::new();
let mut ops = self
.operations
.into_iter()
.rev()
.filter(|o| seen_keys.insert(o.key()))
.collect::<Vec<_>>();
ops.sort_by_key(ForestOperation::key);
ops
}
}
impl IntoIterator for SmtUpdateBatch {
type Item = ForestOperation;
type IntoIter = alloc::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.consume().into_iter()
}
}
impl From<SmtUpdateBatch> for Vec<(Word, Word)> {
fn from(value: SmtUpdateBatch) -> Self {
value
.consume()
.into_iter()
.map(|op| match op {
ForestOperation::Insert { key, value } => (key, value),
ForestOperation::Remove { key } => (key, EMPTY_WORD),
})
.collect()
}
}
impl<I> From<I> for SmtUpdateBatch
where
I: Iterator<Item = (Word, Word)>,
{
fn from(value: I) -> Self {
Self::new(value.map(|(k, v)| {
if v.is_empty() {
ForestOperation::Remove { key: k }
} else {
ForestOperation::Insert { key: k, value: v }
}
}))
}
}
impl Default for SmtUpdateBatch {
fn default() -> Self {
Self::empty()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SmtForestUpdateBatch {
operations: Map<LineageId, SmtUpdateBatch>,
}
impl SmtForestUpdateBatch {
pub fn empty() -> Self {
Self { operations: Map::new() }
}
pub fn add_operations(
&mut self,
lineage: LineageId,
operations: impl Iterator<Item = ForestOperation>,
) {
let batch = self.operations.entry(lineage).or_insert_with(SmtUpdateBatch::empty);
batch.add_operations(operations);
}
pub fn operations(&mut self, lineage: LineageId) -> &mut SmtUpdateBatch {
self.operations.entry(lineage).or_insert_with(SmtUpdateBatch::empty)
}
pub fn lineages(&self) -> impl Iterator<Item = &LineageId> {
self.operations.keys()
}
pub fn consume(self) -> Map<LineageId, Vec<ForestOperation>> {
self.operations.into_iter().map(|(k, v)| (k, v.consume())).collect()
}
}
impl IntoIterator for SmtForestUpdateBatch {
type Item = (LineageId, Vec<ForestOperation>);
type IntoIter = crate::MapIntoIter<LineageId, Vec<ForestOperation>>;
fn into_iter(self) -> Self::IntoIter {
self.consume().into_iter()
}
}
#[cfg(test)]
mod test {
use itertools::Itertools;
use super::*;
use crate::rand::test_utils::ContinuousRng;
#[test]
fn tree_batch() {
let mut rng = ContinuousRng::new([0x12; 32]);
let mut batch = SmtUpdateBatch::empty();
let o1_key: Word = rng.value();
let o1_value: Word = rng.value();
let o2_key: Word = rng.value();
let o3_key: Word = rng.value();
let o3_value: Word = rng.value();
let o1 = ForestOperation::insert(o1_key, o1_value);
let o2 = ForestOperation::remove(o2_key);
let o3 = ForestOperation::insert(o3_key, o3_value);
batch.add_operations(vec![o1.clone()].into_iter());
batch.add_remove(o2_key);
batch.add_insert(o3_key, o3_value);
let batch_tmp = batch.clone();
let ops = batch.consume();
assert!(ops.is_sorted_by_key(ForestOperation::key));
let o4_key = o2_key;
let o4_value: Word = rng.value();
let o5_key = o1_key;
let o4 = ForestOperation::insert(o4_key, o4_value);
let o5 = ForestOperation::remove(o5_key);
let mut batch = batch_tmp;
batch.add_operations(vec![o4.clone(), o5.clone()].into_iter());
let ops = batch.consume();
assert_eq!(ops.len(), 3);
assert!(ops.is_sorted_by_key(ForestOperation::key));
assert!(ops.contains(&o3));
assert!(ops.contains(&o4));
assert!(!ops.contains(&o2));
assert!(ops.contains(&o5));
assert!(!ops.contains(&o1));
}
#[test]
fn forest_batch() {
let mut rng = ContinuousRng::new([0x13; 32]);
let mut batch = SmtForestUpdateBatch::empty();
let t1_lineage: LineageId = rng.value();
let t1_o1 = ForestOperation::insert(rng.value(), rng.value());
let t1_o2 = ForestOperation::remove(rng.value());
batch.add_operations(t1_lineage, vec![t1_o1, t1_o2].into_iter());
let t2_lineage: LineageId = rng.value();
let t2_o1 = ForestOperation::remove(rng.value());
let t2_o2 = ForestOperation::insert(rng.value(), rng.value());
batch.operations(t2_lineage).add_operations(vec![t2_o1, t2_o2].into_iter());
let ops = batch.consume();
assert_eq!(ops.len(), 2);
let t1_ops = ops.get(&t1_lineage).unwrap();
assert!(t1_ops.is_sorted_by_key(ForestOperation::key));
assert_eq!(t1_ops.iter().unique_by(|o| o.key()).count(), 2);
let t2_ops = ops.get(&t2_lineage).unwrap();
assert!(t2_ops.is_sorted_by_key(ForestOperation::key));
assert_eq!(t2_ops.iter().unique_by(|o| o.key()).count(), 2);
}
}