#[cfg(feature = "mpi")]
pub mod mpi;
use std::collections::{BTreeMap, BTreeSet};
use std::hash::Hash;
use crate::{edge::InnerEdgeData, hash_id::RelRcHash, node::InnerData, RelRc};
use itertools::Itertools;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Detached<N, E> {
current: RelRcHash,
all_data: BTreeMap<RelRcHash, DetachedInnerData<N, E>>,
}
impl<N: Clone, E: Clone> RelRc<N, E> {
pub fn detach(&self, detach_from: &BTreeSet<RelRcHash>) -> Detached<N, E> {
Detached::new(self, detach_from)
}
pub fn attach(
detached: Detached<N, E>,
attach_to: impl IntoIterator<Item = RelRc<N, E>>,
) -> Self
where
N: Hash,
E: Hash,
{
let attach_to: BTreeMap<RelRcHash, RelRc<N, E>> =
attach_to.into_iter().map(|n| (n.hash_id(), n)).collect();
if attach_to.contains_key(&detached.current) {
return attach_to.get(&detached.current).unwrap().clone();
}
let mut all_new_relrc: BTreeMap<RelRcHash, RelRc<N, E>> = BTreeMap::new();
post_order_for_each(
detached.current,
|id| {
let data = detached.all_data.get(&id).cloned().unwrap();
let parents = data.incoming.into_iter().map(|(parent, edge_value)| {
let relrc = all_new_relrc
.get(&parent)
.or_else(|| attach_to.get(&parent))
.cloned()
.unwrap();
(relrc, edge_value)
});
let relrc = RelRc::with_parents(data.value, parents);
all_new_relrc.insert(id, relrc);
},
|id| {
let data = detached.all_data.get(&id).unwrap();
data.incoming.iter().map(|(parent, _)| *parent)
},
|id| !attach_to.contains_key(&id),
);
all_new_relrc.remove(&detached.current).unwrap()
}
}
impl<N: Clone, E: Clone> Detached<N, E> {
pub fn new(obj: &RelRc<N, E>, detach_from: &BTreeSet<RelRcHash>) -> Self {
let current = obj.hash_id();
let all_relrc = ancestors_upto(obj, detach_from);
let all_data = all_relrc
.into_iter()
.map(|(id, n)| {
let data = DetachedInnerData::new(n.value().clone(), n.all_incoming().to_vec());
(id, data)
})
.collect();
Self { current, all_data }
}
}
impl<N, E> Detached<N, E> {
#[cfg(feature = "mpi")]
fn empty(current: RelRcHash) -> Self {
Self {
current,
all_data: BTreeMap::new(),
}
}
pub fn n_ancestors(&self) -> usize {
self.all_data.len()
}
}
impl<N, E> Detached<N, E> {
pub fn required_hashes(&self) -> impl Iterator<Item = RelRcHash> + '_ {
self.all_data
.values()
.flat_map(|data| data.parents())
.filter(|hash| !self.all_data.contains_key(hash))
.unique()
}
pub fn attaches_to(&self, attach_to: &BTreeMap<RelRcHash, RelRc<N, E>>) -> bool {
self.required_hashes()
.all(|hash| attach_to.contains_key(&hash))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub(crate) struct DetachedInnerData<N, E> {
value: N,
incoming: Vec<(RelRcHash, E)>,
}
impl<'a, N, E> From<&'a InnerData<N, E>> for DetachedInnerData<&'a N, &'a E> {
fn from(value: &'a InnerData<N, E>) -> Self {
DetachedInnerData {
value: value.value(),
incoming: value
.all_incoming()
.iter()
.map(|e| (e.source().hash_id(), e.value()))
.collect(),
}
}
}
impl<N, E> DetachedInnerData<N, E> {
fn new(value: N, incoming: Vec<InnerEdgeData<N, E>>) -> Self {
DetachedInnerData {
value,
incoming: incoming
.into_iter()
.map(|InnerEdgeData { value, source, .. }| (source.hash_id(), value))
.collect(),
}
}
fn parents(&self) -> impl Iterator<Item = RelRcHash> + '_ {
self.incoming.iter().map(|(hash, _)| *hash)
}
}
fn ancestors_upto<N, E>(
obj: &RelRc<N, E>,
detach_from: &BTreeSet<RelRcHash>,
) -> BTreeMap<RelRcHash, RelRc<N, E>> {
let mut all_nodes: BTreeMap<RelRcHash, RelRc<N, E>> = Default::default();
let mut stack: Vec<_> = vec![obj.clone()];
while let Some(node) = stack.pop() {
let node_id = node.hash_id();
if !all_nodes.contains_key(&node_id) && !detach_from.contains(&node_id) {
stack.extend(node.all_parents().cloned());
all_nodes.insert(node_id, node);
}
}
all_nodes
}
fn post_order_for_each<V: Ord + Copy, I: IntoIterator<Item = V>>(
start: V,
mut f: impl FnMut(V),
successors: impl Fn(V) -> I,
visit: impl Fn(V) -> bool,
) {
enum DfsEvent<V> {
DfsEnter(V),
DfsExit(V),
}
let mut stack = vec![DfsEvent::DfsEnter(start)];
let mut visited = BTreeSet::new();
while let Some(event) = stack.pop() {
match event {
DfsEvent::DfsEnter(v) => {
stack.push(DfsEvent::DfsExit(v));
for u in successors(v) {
if visit(u) && visited.insert(u) {
stack.push(DfsEvent::DfsEnter(u));
}
}
}
DfsEvent::DfsExit(v) => {
f(v);
}
}
}
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use super::*;
use crate::RelRc;
use std::collections::BTreeSet;
#[test]
fn test_detach_and_attach_diamond() {
let root1 = RelRc::new("A");
let left_child1 = RelRc::with_parents("B", vec![(root1.clone(), "left")]);
let right_child1 = RelRc::with_parents("C", vec![(root1.clone(), "right")]);
let grandchild1 = RelRc::with_parents(
"D",
vec![
(left_child1.clone(), "left"),
(right_child1.clone(), "right"),
],
);
let root2 = RelRc::new("A");
let left_child2 = RelRc::with_parents("B", vec![(root2.clone(), "left")]);
let detach_from: BTreeSet<RelRcHash> =
BTreeSet::from_iter([root1.hash_id(), left_child1.hash_id()]);
let detached = grandchild1.detach(&detach_from);
assert_eq!(detached.all_data.len(), 2);
drop(grandchild1);
assert_eq!(left_child1.all_children().len(), 0);
assert_eq!(right_child1.all_children().len(), 0);
let attach_to = [root2.clone(), left_child2.clone()];
let grandchild2 = RelRc::attach(detached.clone(), attach_to);
assert_eq!(grandchild2.value(), &"D");
assert_eq!(
grandchild2.all_parents().map(|n| n.value()).collect_vec(),
vec![&"B", &"C"]
);
assert_eq!(root2.all_children().len(), 2);
let detached2 = grandchild2.detach(&BTreeSet::from_iter([
root2.hash_id(),
left_child2.hash_id(),
]));
assert_eq!(detached, detached2);
}
}