use std::{cell::RefCell, rc::Rc};
use crate::{edge::InnerEdgeData, Edge, NodeId, Registry, RelRc};
use derive_more::{From, Into};
use derive_where::derive_where;
use slotmap_fork_lmondada::SecondaryMap;
#[derive(Debug)]
#[derive_where(Clone, Default)]
pub struct HistoryGraph<N, E> {
nodes: SecondaryMap<NodeId, RelRc<N, E>>,
registry: Rc<RefCell<Registry<N, E>>>,
}
impl<N, E> HistoryGraph<N, E> {
pub fn from_nodes(nodes: impl IntoIterator<Item = RelRc<N, E>>) -> Self {
Self::new(nodes, Registry::new())
}
pub fn with_registry(registry: impl Into<Rc<RefCell<Registry<N, E>>>>) -> Self {
Self::new([], registry)
}
pub fn new(
nodes: impl IntoIterator<Item = RelRc<N, E>>,
registry: impl Into<Rc<RefCell<Registry<N, E>>>>,
) -> Self {
let mut ret = Self {
nodes: Default::default(),
registry: registry.into(),
};
for node in nodes {
ret.insert_node(node);
}
ret
}
pub fn outgoing_edges(&self, node_id: NodeId) -> impl Iterator<Item = EdgeId> + '_ {
let source = self.get_node(node_id);
let edges = source.map(|n| n.all_outgoing());
let map_node_id = |Edge { target, index }| {
self.get_node_id(&target)
.map(|target| EdgeId { target, index })
};
edges.into_iter().flatten().filter_map(map_node_id)
}
pub fn incoming_edges(&self, node_id: NodeId) -> impl Iterator<Item = EdgeId> + '_ {
let target = self.get_node(node_id);
let n_incoming = target.map(|n| n.n_incoming()).unwrap_or_default();
(0..n_incoming)
.map(move |index| EdgeId {
target: node_id,
index,
})
.filter(|&e| self.contains_edge(e))
}
pub fn parents(&self, node_id: NodeId) -> impl Iterator<Item = NodeId> + '_ {
self.incoming_edges(node_id)
.filter_map(|edge_id| self.source(edge_id))
}
pub fn children(&self, node_id: NodeId) -> impl Iterator<Item = NodeId> + '_ {
self.outgoing_edges(node_id)
.filter_map(|edge_id| self.target(edge_id))
}
pub fn registry(&self) -> &Rc<RefCell<Registry<N, E>>> {
&self.registry
}
fn get_node_id(&self, node: &RelRc<N, E>) -> Option<NodeId> {
let id = self.registry.borrow().get_id(node)?;
self.nodes.contains_key(id).then_some(id)
}
pub fn all_node_ids(&self) -> impl Iterator<Item = NodeId> + Clone + '_ {
self.nodes.keys()
}
pub fn contains(&self, node: &RelRc<N, E>) -> bool {
let Some(id) = self.registry.borrow().get_id(node) else {
return false;
};
self.contains_id(id)
}
pub fn contains_id(&self, node_id: NodeId) -> bool {
self.nodes.contains_key(node_id)
}
pub fn get_node(&self, node_id: NodeId) -> Option<&RelRc<N, E>> {
self.nodes.get(node_id)
}
pub fn get_edge(&self, edge_id: EdgeId) -> Option<&InnerEdgeData<N, E>> {
self.get_node(edge_id.target)?
.incoming(edge_id.index)
.filter(|e| self.contains(e.source()))
}
pub fn contains_edge(&self, edge_id: EdgeId) -> bool {
self.get_edge(edge_id).is_some()
}
pub fn source(&self, edge_id: EdgeId) -> Option<NodeId> {
let edge = self.get_edge(edge_id)?;
let source_node = edge.source();
self.get_node_id(source_node)
}
pub fn target(&self, edge_id: EdgeId) -> Option<NodeId> {
self.contains_edge(edge_id).then_some(edge_id.target)
}
pub fn insert_node(&mut self, node: RelRc<N, E>) -> Option<NodeId> {
if let Some(id) = self.get_node_id(&node) {
return Some(id); }
let id = node.try_register_in(&self.registry)?;
self.nodes.insert(id, node);
Some(id)
}
pub fn insert_ancestors(&mut self, node: RelRc<N, E>) -> NodeId {
for parent in node.all_parents() {
if !self.contains(parent) {
self.insert_ancestors(parent.clone());
}
}
self.insert_node(node.clone()).expect("node not registered")
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, From, Into)]
pub struct EdgeId {
pub target: NodeId,
pub index: usize,
}
#[cfg(test)]
mod tests {
#![cfg(feature = "serde")]
use insta::assert_yaml_snapshot;
use itertools::Itertools;
use super::*;
use crate::RelRc;
#[test]
fn test_history_graph() {
let grandparent = RelRc::new((1, 10)); let parent = RelRc::with_parents((1, 20), vec![(grandparent.clone(), 10)]);
let child1 = RelRc::with_parents((2, 30), vec![(parent.clone(), 20)]);
let child2 = RelRc::with_parents((3, 35), vec![(parent.clone(), 25)]);
assert_eq!(child2.incoming(0).unwrap().value(), &25);
let mut graph = HistoryGraph::default();
let child1_id = graph.insert_ancestors(child1);
let node_ids: Vec<_> = graph.all_node_ids().collect();
assert_eq!(
node_ids.len(),
3,
"Should have 3 nodes after adding first family"
);
let parent_id = graph
.source(EdgeId {
target: child1_id,
index: 0,
})
.unwrap();
let grandparent_id = graph
.source(EdgeId {
target: parent_id,
index: 0,
})
.unwrap();
let out_grandparent = graph
.outgoing_edges(grandparent_id)
.exactly_one()
.ok()
.unwrap();
assert_eq!(out_grandparent.target, parent_id);
let out_parent = graph.outgoing_edges(parent_id).exactly_one().ok().unwrap();
assert_eq!(out_parent.target, child1_id);
let child2_id = graph.insert_ancestors(child2);
let node_ids: Vec<_> = graph.all_node_ids().sorted().collect();
assert_eq!(
node_ids.len(),
4,
"Should have 4 nodes after adding second family"
);
assert_eq!(node_ids, [grandparent_id, parent_id, child1_id, child2_id]);
}
#[test]
#[cfg(feature = "serde")]
fn test_history_graph_serialization() {
use std::collections::BTreeSet;
let grandparent1 = RelRc::new((1, 10)); let parent1 = RelRc::with_parents((1, 20), vec![(grandparent1.clone(), 10)]);
let child1 = RelRc::with_parents((2, 30), vec![(parent1.clone(), 20)]);
let grandparent2 = RelRc::new((1, 15)); let parent2 = RelRc::with_parents((1, 25), vec![(grandparent2.clone(), 15)]);
let child2 = RelRc::with_parents((3, 35), vec![(parent2.clone(), 25)]);
assert_eq!(child2.incoming(0).unwrap().value(), &25);
let mut graph = HistoryGraph::default();
graph.insert_ancestors(child1);
graph.insert_ancestors(child2);
let serialized = graph.to_serialized();
assert_yaml_snapshot!(serialized);
let deserialized = HistoryGraph::from_serialized(serialized);
assert_eq!(
graph.all_node_ids().collect::<BTreeSet<_>>(),
deserialized.all_node_ids().collect::<BTreeSet<_>>()
);
}
}