use std::collections::{BTreeMap, BTreeSet};
use std::num::NonZeroUsize;
use lru::LruCache;
use crate::error::RetrievalError;
use crate::event_dag::layers::EventLayers;
use crate::event_dag::relation::AbstractCausalRelation;
use crate::retrieval::GetEvents;
use ankurah_proto::{Event, EventId};
pub(crate) struct EventAccumulator<E: GetEvents> {
dag: BTreeMap<EventId, Vec<EventId>>,
cache: LruCache<EventId, Event>,
event_getter: E,
}
impl<E: GetEvents> EventAccumulator<E> {
pub(crate) fn new(event_getter: E) -> Self {
Self { dag: BTreeMap::new(), cache: LruCache::new(NonZeroUsize::new(1000).unwrap()), event_getter }
}
pub(crate) fn accumulate(&mut self, event: &Event) {
let id = event.id();
let parents: Vec<EventId> = event.parent.as_slice().to_vec();
self.dag.insert(id, parents);
self.cache.put(event.id(), event.clone());
}
pub(crate) async fn get_event(&mut self, id: &EventId) -> Result<Event, RetrievalError> {
if let Some(event) = self.cache.get(id) {
return Ok(event.clone());
}
let event = self.event_getter.get_event(id).await?;
self.cache.put(id.clone(), event.clone());
Ok(event)
}
pub(crate) fn dag(&self) -> &BTreeMap<EventId, Vec<EventId>> { &self.dag }
pub(crate) fn into_layers(self, meet: Vec<EventId>, current_head: Vec<EventId>) -> EventLayers<E> {
EventLayers::new(self, meet, current_head)
}
}
pub(crate) struct ComparisonResult<E: GetEvents> {
pub(crate) relation: AbstractCausalRelation<EventId>,
accumulator: EventAccumulator<E>,
}
impl<E: GetEvents> ComparisonResult<E> {
pub(crate) fn new(relation: AbstractCausalRelation<EventId>, accumulator: EventAccumulator<E>) -> Self {
Self { relation, accumulator }
}
pub(crate) fn into_layers(self, current_head: Vec<EventId>) -> Option<EventLayers<E>> {
match &self.relation {
AbstractCausalRelation::DivergedSince { meet, .. } => Some(self.accumulator.into_layers(meet.clone(), current_head)),
_ => None,
}
}
pub(crate) fn accumulator(&self) -> &EventAccumulator<E> { &self.accumulator }
pub(crate) fn into_parts(self) -> (AbstractCausalRelation<EventId>, EventAccumulator<E>) { (self.relation, self.accumulator) }
}
pub(crate) fn compute_ancestry_from_dag(dag: &BTreeMap<EventId, Vec<EventId>>, head: &[EventId]) -> BTreeSet<EventId> {
let mut ancestry = BTreeSet::new();
let mut frontier: Vec<EventId> = head.to_vec();
while let Some(id) = frontier.pop() {
if !ancestry.insert(id.clone()) {
continue;
}
if let Some(parents) = dag.get(&id) {
for parent in parents {
if !ancestry.contains(parent) {
frontier.push(parent.clone());
}
}
}
}
ancestry
}
pub(crate) fn is_descendant_dag(dag: &BTreeMap<EventId, Vec<EventId>>, descendant: &EventId, ancestor: &EventId) -> bool {
let mut visited = BTreeSet::new();
let mut frontier = vec![descendant.clone()];
while let Some(id) = frontier.pop() {
if !visited.insert(id.clone()) {
continue;
}
if &id == ancestor {
return true;
}
let Some(parents) = dag.get(&id) else {
continue;
};
for parent in parents {
if !visited.contains(parent) {
frontier.push(parent.clone());
}
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compute_ancestry_from_dag() {
let mut dag = BTreeMap::new();
let a = EventId::from_bytes([1; 32]);
let b = EventId::from_bytes([2; 32]);
let c = EventId::from_bytes([3; 32]);
let d = EventId::from_bytes([4; 32]);
dag.insert(a.clone(), vec![]); dag.insert(b.clone(), vec![a.clone()]);
dag.insert(c.clone(), vec![a.clone()]);
dag.insert(d.clone(), vec![b.clone()]);
let ancestry = compute_ancestry_from_dag(&dag, &[d.clone()]);
assert!(ancestry.contains(&a));
assert!(ancestry.contains(&b));
assert!(ancestry.contains(&d));
assert!(!ancestry.contains(&c));
let ancestry = compute_ancestry_from_dag(&dag, &[d.clone(), c.clone()]);
assert!(ancestry.contains(&a));
assert!(ancestry.contains(&b));
assert!(ancestry.contains(&c));
assert!(ancestry.contains(&d));
}
#[test]
fn test_is_descendant_dag() {
let mut dag = BTreeMap::new();
let a = EventId::from_bytes([1; 32]);
let b = EventId::from_bytes([2; 32]);
let c = EventId::from_bytes([3; 32]);
dag.insert(a.clone(), vec![]);
dag.insert(b.clone(), vec![a.clone()]);
dag.insert(c.clone(), vec![b.clone()]);
assert!(is_descendant_dag(&dag, &c, &a)); assert!(is_descendant_dag(&dag, &c, &b)); assert!(!is_descendant_dag(&dag, &a, &c)); assert!(!is_descendant_dag(&dag, &b, &c)); }
}