use crate::core::algo::traversal::TraversalError;
use crate::core::view::NodeId;
use crate::graph::graph::Graph;
use std::fmt::Debug;
use std::hash::Hash;
#[derive(Debug, thiserror::Error)]
pub enum ConnectivityError {
#[error("Invalid node reference: node ID {0} not found")]
InvalidNode(NodeId),
#[error("Graph transposition failed: {0}")]
TransposeError(String),
}
impl From<TraversalError> for ConnectivityError {
fn from(error: TraversalError) -> Self {
match error {
TraversalError::InvalidNode(id) => ConnectivityError::InvalidNode(id),
}
}
}
#[deprecated(
since = "3.0.0",
note = "superseded by `xgraph::core::Connectivity`, which is implemented for every \
graph view — including filtered ones — and carries no unused type parameters"
)]
pub trait Connectivity<W, N, E> {
fn find_weakly_connected_components(&self) -> Result<Vec<Vec<NodeId>>, ConnectivityError>;
fn find_strongly_connected_components(&self) -> Result<Vec<Vec<NodeId>>, ConnectivityError>;
fn find_connected_components(&self) -> Result<Vec<Vec<NodeId>>, ConnectivityError> {
if self.is_directed().unwrap_or(false) {
self.find_strongly_connected_components()
} else {
self.find_weakly_connected_components()
}
}
fn is_weakly_connected(&self) -> Result<bool, ConnectivityError>;
fn is_strongly_connected(&self) -> Result<bool, ConnectivityError>;
fn is_connected(&self) -> Result<bool, ConnectivityError> {
if self.is_directed().unwrap_or(false) {
self.is_strongly_connected()
} else {
self.is_weakly_connected()
}
}
fn is_directed(&self) -> Result<bool, ConnectivityError>;
}
#[allow(deprecated)]
impl<W, N, E> Connectivity<W, N, E> for Graph<W, N, E>
where
W: Copy + Default + PartialEq + Debug,
N: Clone + Eq + Hash + Debug,
E: Clone + Default + Debug,
{
fn find_weakly_connected_components(&self) -> Result<Vec<Vec<NodeId>>, ConnectivityError> {
crate::core::Connectivity::weakly_connected_components(self)
.map_err(ConnectivityError::from)
}
fn find_strongly_connected_components(&self) -> Result<Vec<Vec<NodeId>>, ConnectivityError> {
crate::core::Connectivity::strongly_connected_components(self)
.map_err(ConnectivityError::from)
}
fn is_weakly_connected(&self) -> Result<bool, ConnectivityError> {
crate::core::Connectivity::is_weakly_connected(self).map_err(ConnectivityError::from)
}
fn is_strongly_connected(&self) -> Result<bool, ConnectivityError> {
crate::core::Connectivity::is_strongly_connected(self).map_err(ConnectivityError::from)
}
fn is_directed(&self) -> Result<bool, ConnectivityError> {
Ok(crate::core::GraphView::is_directed(self))
}
}
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use super::*;
#[test]
fn test_strongly_connected() {
let mut graph = Graph::<u32, (), ()>::new(true);
let n0 = graph.add_node(());
let n1 = graph.add_node(());
let n2 = graph.add_node(());
graph.add_edge(n0, n1, 1, ()).unwrap();
graph.add_edge(n1, n2, 1, ()).unwrap();
graph.add_edge(n2, n0, 1, ()).unwrap();
assert!(graph.is_strongly_connected().unwrap());
let scc = graph.find_strongly_connected_components().unwrap();
assert_eq!(scc.len(), 1);
}
#[test]
fn test_weak_connectivity() {
let mut graph = Graph::<u32, (), ()>::new(true);
let n0 = graph.add_node(());
let n1 = graph.add_node(());
graph.add_edge(n0, n1, 1, ()).unwrap();
assert!(!graph.is_strongly_connected().unwrap());
assert!(graph.is_weakly_connected().unwrap());
}
#[test]
fn test_scc_with_sparse_ids() {
let mut graph = Graph::<u32, (), ()>::new(true);
let n0 = graph.add_node(());
let n1 = graph.add_node(());
let n2 = graph.add_node(());
let n3 = graph.add_node(());
graph.remove_node(n1).unwrap();
graph.add_edge(n0, n2, 1, ()).unwrap();
graph.add_edge(n2, n3, 1, ()).unwrap();
graph.add_edge(n3, n0, 1, ()).unwrap();
let scc = graph.find_strongly_connected_components().unwrap();
assert_eq!(scc.len(), 1);
assert_eq!(scc[0], vec![n0, n2, n3]);
assert!(graph.is_strongly_connected().unwrap());
}
#[test]
fn test_deep_chain_does_not_overflow_the_stack() {
let mut graph = Graph::<u32, usize, ()>::new(true);
let nodes: Vec<_> = (0..200_000).map(|i| graph.add_node(i)).collect();
for pair in nodes.windows(2) {
graph.add_edge(pair[0], pair[1], 1, ()).unwrap();
}
assert_eq!(
graph.find_strongly_connected_components().unwrap().len(),
nodes.len()
);
assert!(graph.is_weakly_connected().unwrap());
}
}