use crate::core::view::{EdgeId, NodeId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum GraphEvent {
NodeAdded {
node: NodeId,
},
NodeRemoved {
node: NodeId,
},
EdgeAdded {
edge: EdgeId,
from: NodeId,
to: NodeId,
},
EdgeRemoved {
edge: EdgeId,
from: NodeId,
to: NodeId,
},
}
pub trait GraphObserver {
fn on_event(&mut self, event: GraphEvent);
}
impl GraphObserver for () {
fn on_event(&mut self, _event: GraphEvent) {}
}
impl<O: GraphObserver + ?Sized> GraphObserver for Box<O> {
fn on_event(&mut self, event: GraphEvent) {
(**self).on_event(event);
}
}
#[derive(Debug, Clone, Default)]
pub struct EventLog {
events: Vec<GraphEvent>,
}
impl EventLog {
pub fn new() -> Self {
Self::default()
}
pub fn events(&self) -> &[GraphEvent] {
&self.events
}
pub fn take(&mut self) -> Vec<GraphEvent> {
std::mem::take(&mut self.events)
}
}
impl GraphObserver for EventLog {
fn on_event(&mut self, event: GraphEvent) {
self.events.push(event);
}
}
pub trait MutableGraph {
type NodeData;
type EdgeData;
type EdgeWeight;
type Error;
fn add_node(&mut self, data: Self::NodeData) -> NodeId;
fn add_edge(
&mut self,
from: NodeId,
to: NodeId,
weight: Self::EdgeWeight,
data: Self::EdgeData,
) -> Result<EdgeId, Self::Error>;
fn remove_node(&mut self, node: NodeId) -> Result<bool, Self::Error>;
fn remove_edge_by_id(&mut self, edge: EdgeId) -> Result<(), Self::Error>;
}
#[derive(Debug, Clone, Default)]
pub struct Observed<G, O> {
graph: G,
observer: O,
}
impl<G, O> Observed<G, O> {
pub fn new(graph: G, observer: O) -> Self {
Self { graph, observer }
}
pub fn graph(&self) -> &G {
&self.graph
}
pub fn observer(&self) -> &O {
&self.observer
}
pub fn observer_mut(&mut self) -> &mut O {
&mut self.observer
}
pub fn into_parts(self) -> (G, O) {
(self.graph, self.observer)
}
pub fn graph_mut_unobserved(&mut self) -> &mut G {
&mut self.graph
}
}
impl<G, O> Observed<G, O>
where
G: MutableGraph + crate::core::view::GraphView,
O: GraphObserver,
{
pub fn add_node(&mut self, data: G::NodeData) -> NodeId {
let node = self.graph.add_node(data);
self.observer.on_event(GraphEvent::NodeAdded { node });
node
}
pub fn add_edge(
&mut self,
from: NodeId,
to: NodeId,
weight: G::EdgeWeight,
data: G::EdgeData,
) -> Result<EdgeId, G::Error> {
let edge = self.graph.add_edge(from, to, weight, data)?;
let (from, to) = self.graph.edge_endpoints(edge).unwrap_or((from, to));
self.observer
.on_event(GraphEvent::EdgeAdded { edge, from, to });
Ok(edge)
}
pub fn remove_edge_by_id(&mut self, edge: EdgeId) -> Result<(), G::Error> {
let endpoints = self.graph.edge_endpoints(edge);
self.graph.remove_edge_by_id(edge)?;
if let Some((from, to)) = endpoints {
self.observer
.on_event(GraphEvent::EdgeRemoved { edge, from, to });
}
Ok(())
}
pub fn remove_node(&mut self, node: NodeId) -> Result<bool, G::Error> {
let incident: Vec<(EdgeId, NodeId, NodeId)> = self
.graph
.node_id_vec()
.into_iter()
.flat_map(|other| {
self.graph
.links(other)
.filter(|link| other == node || link.target == node)
.filter_map(|link| {
self.graph
.edge_endpoints(link.edge)
.map(|(from, to)| (link.edge, from, to))
})
.collect::<Vec<_>>()
})
.collect();
let removed = self.graph.remove_node(node)?;
if !removed {
return Ok(false);
}
let mut reported = std::collections::HashSet::new();
for (edge, from, to) in incident {
if reported.insert(edge) {
self.observer
.on_event(GraphEvent::EdgeRemoved { edge, from, to });
}
}
self.observer.on_event(GraphEvent::NodeRemoved { node });
Ok(true)
}
}
impl<G, O> crate::core::view::GraphView for Observed<G, O>
where
G: crate::core::view::GraphView,
{
type Weight = G::Weight;
type NodeIds<'a>
= G::NodeIds<'a>
where
Self: 'a;
type Links<'a>
= G::Links<'a>
where
Self: 'a;
fn is_directed(&self) -> bool {
self.graph.is_directed()
}
fn node_count(&self) -> usize {
self.graph.node_count()
}
fn edge_count(&self) -> usize {
self.graph.edge_count()
}
fn contains_node(&self, node: NodeId) -> bool {
self.graph.contains_node(node)
}
fn node_ids(&self) -> Self::NodeIds<'_> {
self.graph.node_ids()
}
fn links(&self, node: NodeId) -> Self::Links<'_> {
self.graph.links(node)
}
fn edge_endpoints(&self, edge: EdgeId) -> Option<(NodeId, NodeId)> {
self.graph.edge_endpoints(edge)
}
}
#[derive(Debug, Default)]
pub struct IncrementalComponents {
sets: crate::core::algo::spanning_tree::DisjointSets,
stale: bool,
}
impl IncrementalComponents {
pub fn new() -> Self {
Self::default()
}
pub fn connected(&mut self, left: NodeId, right: NodeId) -> bool {
self.sets.connected(left, right)
}
pub fn is_current(&self) -> bool {
!self.stale
}
pub fn rebuild<G: crate::core::view::GraphView + ?Sized>(&mut self, graph: &G) {
self.sets = crate::core::algo::spanning_tree::DisjointSets::new();
for node in graph.node_ids() {
self.sets.insert(node);
}
for node in graph.node_id_vec() {
for link in graph.links(node) {
self.sets.union(node, link.target);
}
}
self.stale = false;
}
}
impl GraphObserver for IncrementalComponents {
fn on_event(&mut self, event: GraphEvent) {
match event {
GraphEvent::NodeAdded { node } => self.sets.insert(node),
GraphEvent::EdgeAdded { from, to, .. } => {
self.sets.union(from, to);
}
GraphEvent::NodeRemoved { .. } | GraphEvent::EdgeRemoved { .. } => self.stale = true,
}
}
}
#[cfg(test)]
#[cfg(feature = "graph")]
mod tests {
use super::*;
use crate::core::{Connectivity, GraphView};
use crate::graph::Graph;
fn observed() -> Observed<Graph<u32, u32, &'static str>, EventLog> {
Observed::new(Graph::<u32, u32, &str>::new(false), EventLog::new())
}
#[test]
fn test_every_mutation_is_reported() {
let mut graph = observed();
let a = graph.add_node(0);
let b = graph.add_node(1);
let edge = graph.add_edge(a, b, 1, "rail").unwrap();
graph.remove_edge_by_id(edge).unwrap();
assert_eq!(
graph.observer().events(),
&[
GraphEvent::NodeAdded { node: a },
GraphEvent::NodeAdded { node: b },
GraphEvent::EdgeAdded {
edge,
from: a,
to: b
},
GraphEvent::EdgeRemoved {
edge,
from: a,
to: b
},
]
);
}
#[test]
fn test_removing_a_node_reports_its_edges_first() {
let mut graph = observed();
let a = graph.add_node(0);
let b = graph.add_node(1);
let c = graph.add_node(2);
let ab = graph.add_edge(a, b, 1, "x").unwrap();
let bc = graph.add_edge(b, c, 1, "y").unwrap();
graph.observer_mut().take();
graph.remove_node(b).unwrap();
let events = graph.observer().events();
let removed_edges: Vec<_> = events
.iter()
.filter_map(|event| match event {
GraphEvent::EdgeRemoved { edge, .. } => Some(*edge),
_ => None,
})
.collect();
assert_eq!(removed_edges.len(), 2);
assert!(removed_edges.contains(&ab) && removed_edges.contains(&bc));
assert_eq!(events.last(), Some(&GraphEvent::NodeRemoved { node: b }));
}
#[test]
fn test_a_rejected_mutation_reports_nothing() {
let mut graph = observed();
let a = graph.add_node(0);
graph.observer_mut().take();
assert!(graph.add_edge(a, NodeId::from_index(999), 1, "x").is_err());
assert!(graph.observer().events().is_empty());
}
#[test]
fn test_algorithms_run_over_the_wrapper() {
let mut graph = observed();
let a = graph.add_node(0);
let b = graph.add_node(1);
graph.add_edge(a, b, 1, "rail").unwrap();
assert_eq!(graph.node_count(), 2);
assert!(graph.is_connected().unwrap());
}
#[test]
fn test_components_are_maintained_incrementally() {
let mut graph = Observed::new(
Graph::<u32, u32, &str>::new(false),
IncrementalComponents::new(),
);
let nodes: Vec<_> = (0..6).map(|i| graph.add_node(i)).collect();
graph.add_edge(nodes[0], nodes[1], 1, "x").unwrap();
graph.add_edge(nodes[1], nodes[2], 1, "x").unwrap();
graph.add_edge(nodes[3], nodes[4], 1, "x").unwrap();
assert!(graph.observer().is_current());
assert!(graph.observer_mut().connected(nodes[0], nodes[2]));
assert!(!graph.observer_mut().connected(nodes[0], nodes[3]));
assert!(!graph.observer_mut().connected(nodes[0], nodes[5]));
let components = graph.weakly_connected_components().unwrap();
for component in &components {
for &node in component {
assert!(graph.observer_mut().connected(component[0], node));
}
}
}
#[test]
fn test_a_removal_marks_the_tracker_stale() {
let mut graph = Observed::new(
Graph::<u32, u32, &str>::new(false),
IncrementalComponents::new(),
);
let a = graph.add_node(0);
let b = graph.add_node(1);
let edge = graph.add_edge(a, b, 1, "x").unwrap();
assert!(graph.observer().is_current());
graph.remove_edge_by_id(edge).unwrap();
assert!(!graph.observer().is_current());
let (graph_inner, mut observer) = graph.into_parts();
observer.rebuild(&graph_inner);
assert!(observer.is_current());
assert!(!observer.connected(a, b));
}
#[test]
fn test_the_default_observer_observes_nothing() {
let mut graph = Observed::new(Graph::<u32, u32, &str>::new(false), ());
let a = graph.add_node(0);
let b = graph.add_node(1);
graph.add_edge(a, b, 1, "x").unwrap();
assert_eq!(graph.node_count(), 2);
}
}