use super::core::{Graph, GraphError, GraphType, NodeId};
use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::Debug;
#[derive(Debug, Clone)]
pub struct BfsResult {
pub visit_order: Vec<NodeId>,
pub parent: HashMap<NodeId, Option<NodeId>>,
pub distance: HashMap<NodeId, usize>,
}
#[derive(Debug, Clone)]
pub struct DfsResult {
pub discovery_order: Vec<NodeId>,
pub finish_order: Vec<NodeId>,
pub parent: HashMap<NodeId, Option<NodeId>>,
pub discovery_time: HashMap<NodeId, usize>,
pub finish_time: HashMap<NodeId, usize>,
}
pub fn bfs<N, W>(graph: &Graph<N, W>, source: NodeId) -> BfsResult
where
N: Clone + Debug,
W: Clone + Debug,
{
let mut visit_order = Vec::new();
let mut parent: HashMap<NodeId, Option<NodeId>> = HashMap::new();
let mut distance: HashMap<NodeId, usize> = HashMap::new();
let mut visited: HashSet<NodeId> = HashSet::new();
let mut queue: VecDeque<NodeId> = VecDeque::new();
visited.insert(source);
parent.insert(source, None);
distance.insert(source, 0);
queue.push_back(source);
while let Some(current) = queue.pop_front() {
visit_order.push(current);
if let Some(neighbors) = graph.neighbors(current) {
let current_dist = distance[¤t];
for neighbor in neighbors {
if !visited.contains(&neighbor) {
visited.insert(neighbor);
parent.insert(neighbor, Some(current));
distance.insert(neighbor, current_dist + 1);
queue.push_back(neighbor);
}
}
}
}
BfsResult {
visit_order,
parent,
distance,
}
}
pub fn bfs_reachable<N, W>(graph: &Graph<N, W>, source: NodeId) -> HashSet<NodeId>
where
N: Clone + Debug,
W: Clone + Debug,
{
bfs(graph, source).visit_order.into_iter().collect()
}
pub fn shortest_path_bfs<N, W>(
graph: &Graph<N, W>,
source: NodeId,
target: NodeId,
) -> Option<Vec<NodeId>>
where
N: Clone + Debug,
W: Clone + Debug,
{
let result = bfs(graph, source);
if !result.parent.contains_key(&target) {
return None; }
let mut path = Vec::new();
let mut current = target;
while let Some(parent) = result.parent.get(¤t) {
path.push(current);
match parent {
Some(p) => current = *p,
None => break, }
}
path.reverse();
Some(path)
}
pub fn dfs<N, W>(graph: &Graph<N, W>) -> DfsResult
where
N: Clone + Debug,
W: Clone + Debug,
{
let mut discovery_order = Vec::new();
let mut finish_order = Vec::new();
let mut parent: HashMap<NodeId, Option<NodeId>> = HashMap::new();
let mut discovery_time: HashMap<NodeId, usize> = HashMap::new();
let mut finish_time: HashMap<NodeId, usize> = HashMap::new();
let mut visited: HashSet<NodeId> = HashSet::new();
let mut time = 0;
for node_id in graph.node_ids() {
if !visited.contains(&node_id) {
dfs_visit(
graph,
node_id,
None,
&mut visited,
&mut discovery_order,
&mut finish_order,
&mut parent,
&mut discovery_time,
&mut finish_time,
&mut time,
);
}
}
DfsResult {
discovery_order,
finish_order,
parent,
discovery_time,
finish_time,
}
}
pub fn dfs_from<N, W>(graph: &Graph<N, W>, source: NodeId) -> DfsResult
where
N: Clone + Debug,
W: Clone + Debug,
{
let mut discovery_order = Vec::new();
let mut finish_order = Vec::new();
let mut parent: HashMap<NodeId, Option<NodeId>> = HashMap::new();
let mut discovery_time: HashMap<NodeId, usize> = HashMap::new();
let mut finish_time: HashMap<NodeId, usize> = HashMap::new();
let mut visited: HashSet<NodeId> = HashSet::new();
let mut time = 0;
dfs_visit(
graph,
source,
None,
&mut visited,
&mut discovery_order,
&mut finish_order,
&mut parent,
&mut discovery_time,
&mut finish_time,
&mut time,
);
DfsResult {
discovery_order,
finish_order,
parent,
discovery_time,
finish_time,
}
}
fn dfs_visit<N, W>(
graph: &Graph<N, W>,
node: NodeId,
node_parent: Option<NodeId>,
visited: &mut HashSet<NodeId>,
discovery_order: &mut Vec<NodeId>,
finish_order: &mut Vec<NodeId>,
parent: &mut HashMap<NodeId, Option<NodeId>>,
discovery_time: &mut HashMap<NodeId, usize>,
finish_time: &mut HashMap<NodeId, usize>,
time: &mut usize,
) where
N: Clone + Debug,
W: Clone + Debug,
{
visited.insert(node);
parent.insert(node, node_parent);
*time += 1;
discovery_time.insert(node, *time);
discovery_order.push(node);
if let Some(neighbors) = graph.neighbors(node) {
for neighbor in neighbors {
if !visited.contains(&neighbor) {
dfs_visit(
graph,
neighbor,
Some(node),
visited,
discovery_order,
finish_order,
parent,
discovery_time,
finish_time,
time,
);
}
}
}
*time += 1;
finish_time.insert(node, *time);
finish_order.push(node);
}
pub fn dfs_iterative<N, W>(graph: &Graph<N, W>, source: NodeId) -> Vec<NodeId>
where
N: Clone + Debug,
W: Clone + Debug,
{
let mut visit_order = Vec::new();
let mut visited: HashSet<NodeId> = HashSet::new();
let mut stack: Vec<NodeId> = vec![source];
while let Some(current) = stack.pop() {
if visited.contains(¤t) {
continue;
}
visited.insert(current);
visit_order.push(current);
if let Some(neighbors) = graph.neighbors(current) {
for neighbor in neighbors.into_iter().rev() {
if !visited.contains(&neighbor) {
stack.push(neighbor);
}
}
}
}
visit_order
}
pub fn topological_sort<N, W>(graph: &Graph<N, W>) -> Result<Vec<NodeId>, GraphError>
where
N: Clone + Debug,
W: Clone + Debug,
{
if graph.graph_type() != GraphType::Directed {
return Err(GraphError::InvalidOperation(
"Topological sort requires a directed graph".to_string(),
));
}
let mut in_degree: HashMap<NodeId, usize> = HashMap::new();
let mut result = Vec::new();
let mut queue: VecDeque<NodeId> = VecDeque::new();
for node_id in graph.node_ids() {
in_degree.insert(node_id, graph.in_degree(node_id).unwrap_or(0));
}
for (&node_id, °ree) in &in_degree {
if degree == 0 {
queue.push_back(node_id);
}
}
while let Some(current) = queue.pop_front() {
result.push(current);
if let Some(neighbors) = graph.neighbors(current) {
for neighbor in neighbors {
if let Some(degree) = in_degree.get_mut(&neighbor) {
*degree -= 1;
if *degree == 0 {
queue.push_back(neighbor);
}
}
}
}
}
if result.len() != graph.node_count() {
return Err(GraphError::CycleDetected);
}
Ok(result)
}
pub fn has_cycle<N, W>(graph: &Graph<N, W>) -> bool
where
N: Clone + Debug,
W: Clone + Debug,
{
if graph.graph_type() != GraphType::Directed {
return graph.edge_count() > 0;
}
topological_sort(graph).is_err()
}
pub fn nodes_at_distance<N, W>(graph: &Graph<N, W>, source: NodeId, distance: usize) -> Vec<NodeId>
where
N: Clone + Debug,
W: Clone + Debug,
{
let result = bfs(graph, source);
result
.distance
.iter()
.filter_map(|(&node, &dist)| if dist == distance { Some(node) } else { None })
.collect()
}
pub fn eccentricity<N, W>(graph: &Graph<N, W>, node: NodeId) -> Option<usize>
where
N: Clone + Debug,
W: Clone + Debug,
{
let result = bfs(graph, node);
if result.distance.len() != graph.node_count() {
return None;
}
result.distance.values().copied().max()
}
pub fn diameter<N, W>(graph: &Graph<N, W>) -> Option<usize>
where
N: Clone + Debug,
W: Clone + Debug,
{
let mut max_eccentricity = 0;
for node_id in graph.node_ids() {
match eccentricity(graph, node_id) {
Some(e) => max_eccentricity = max_eccentricity.max(e),
None => return None, }
}
Some(max_eccentricity)
}
pub fn radius<N, W>(graph: &Graph<N, W>) -> Option<usize>
where
N: Clone + Debug,
W: Clone + Debug,
{
let mut min_eccentricity = usize::MAX;
for node_id in graph.node_ids() {
match eccentricity(graph, node_id) {
Some(e) => min_eccentricity = min_eccentricity.min(e),
None => return None, }
}
if min_eccentricity == usize::MAX {
None
} else {
Some(min_eccentricity)
}
}
pub fn center<N, W>(graph: &Graph<N, W>) -> Option<Vec<NodeId>>
where
N: Clone + Debug,
W: Clone + Debug,
{
let r = radius(graph)?;
let center_nodes: Vec<NodeId> = graph
.node_ids()
.filter(|&node_id| eccentricity(graph, node_id) == Some(r))
.collect();
Some(center_nodes)
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_graph() -> Graph<&'static str, f64> {
let mut graph = Graph::new(GraphType::Undirected);
let a = graph.add_node("A");
let b = graph.add_node("B");
let c = graph.add_node("C");
let d = graph.add_node("D");
let e = graph.add_node("E");
graph
.add_edge(a, b, None)
.expect("operation should succeed");
graph
.add_edge(b, c, None)
.expect("operation should succeed");
graph
.add_edge(c, d, None)
.expect("operation should succeed");
graph
.add_edge(d, e, None)
.expect("operation should succeed");
graph
}
#[test]
fn test_bfs() {
let graph = create_test_graph();
let a = NodeId(0);
let result = bfs(&graph, a);
assert_eq!(result.visit_order.len(), 5);
assert_eq!(result.visit_order[0], a);
assert_eq!(result.distance[&a], 0);
assert_eq!(result.distance[&NodeId(4)], 4); }
#[test]
fn test_shortest_path_bfs() {
let graph = create_test_graph();
let a = NodeId(0);
let e = NodeId(4);
let path = shortest_path_bfs(&graph, a, e).expect("operation should succeed");
assert_eq!(path.len(), 5);
assert_eq!(path[0], a);
assert_eq!(path[4], e);
}
#[test]
fn test_dfs() {
let graph = create_test_graph();
let result = dfs(&graph);
assert_eq!(result.discovery_order.len(), 5);
assert_eq!(result.finish_order.len(), 5);
}
#[test]
fn test_topological_sort() {
let mut graph: Graph<&str, f64> = Graph::new(GraphType::Directed);
let a = graph.add_node("A");
let b = graph.add_node("B");
let c = graph.add_node("C");
let d = graph.add_node("D");
graph
.add_edge(a, b, None)
.expect("operation should succeed");
graph
.add_edge(a, c, None)
.expect("operation should succeed");
graph
.add_edge(b, d, None)
.expect("operation should succeed");
graph
.add_edge(c, d, None)
.expect("operation should succeed");
let sorted = topological_sort(&graph).expect("operation should succeed");
let pos_a = sorted
.iter()
.position(|&n| n == a)
.expect("operation should succeed");
let pos_b = sorted
.iter()
.position(|&n| n == b)
.expect("operation should succeed");
let pos_c = sorted
.iter()
.position(|&n| n == c)
.expect("operation should succeed");
let pos_d = sorted
.iter()
.position(|&n| n == d)
.expect("operation should succeed");
assert!(pos_a < pos_b);
assert!(pos_a < pos_c);
assert!(pos_b < pos_d);
assert!(pos_c < pos_d);
}
#[test]
fn test_has_cycle() {
let mut graph: Graph<&str, f64> = Graph::new(GraphType::Directed);
let a = graph.add_node("A");
let b = graph.add_node("B");
let c = graph.add_node("C");
graph
.add_edge(a, b, None)
.expect("operation should succeed");
graph
.add_edge(b, c, None)
.expect("operation should succeed");
assert!(!has_cycle(&graph));
graph
.add_edge(c, a, None)
.expect("operation should succeed");
assert!(has_cycle(&graph));
}
#[test]
fn test_eccentricity_diameter_radius() {
let graph = create_test_graph();
let a = NodeId(0);
let c = NodeId(2);
assert_eq!(eccentricity(&graph, a), Some(4));
assert_eq!(eccentricity(&graph, c), Some(2));
assert_eq!(diameter(&graph), Some(4));
assert_eq!(radius(&graph), Some(2));
}
}