use crate::cs::error::{Error, Result};
use std::{
collections::{HashMap, HashSet, VecDeque},
hash::Hash,
};
#[derive(Debug, Clone)]
pub struct Graph<T> {
edges: HashMap<T, Vec<T>>,
}
impl<T> Default for Graph<T>
where
T: Eq + Hash + Clone,
{
fn default() -> Self {
Self::new()
}
}
impl<T> Graph<T>
where
T: Eq + Hash + Clone,
{
pub fn new() -> Self {
Graph {
edges: HashMap::new(),
}
}
pub fn add_vertex(&mut self, vertex: T) {
self.edges.entry(vertex).or_default();
}
pub fn add_edge(&mut self, source: T, destination: T) {
self.edges
.entry(source.clone())
.or_default()
.push(destination.clone());
self.edges.entry(destination).or_default();
}
pub fn search(&self, start: &T, target: &T) -> Result<Option<Vec<T>>> {
if !self.edges.contains_key(start) {
return Err(Error::invalid_input("Start vertex not found in graph"));
}
if start == target {
return Ok(Some(vec![start.clone()]));
}
let mut queue = VecDeque::new();
let mut visited = HashSet::new();
let mut parent = HashMap::new();
queue.push_back(start.clone());
visited.insert(start.clone());
while let Some(current) = queue.pop_front() {
if let Some(neighbors) = self.edges.get(¤t) {
for neighbor in neighbors {
if !visited.contains(neighbor) {
visited.insert(neighbor.clone());
parent.insert(neighbor.clone(), current.clone());
queue.push_back(neighbor.clone());
if neighbor == target {
return Ok(Some(self.reconstruct_path(&parent, start, target)));
}
}
}
}
}
Ok(None)
}
fn reconstruct_path(&self, parent: &HashMap<T, T>, start: &T, target: &T) -> Vec<T> {
let mut path = Vec::new();
let mut current = target.clone();
path.push(current.clone());
while current != *start {
current = parent[¤t].clone();
path.push(current.clone());
}
path.reverse();
path
}
pub fn traverse(&self, start: &T) -> Result<Vec<T>> {
if !self.edges.contains_key(start) {
return Err(Error::invalid_input("Start vertex not found in graph"));
}
let mut result = Vec::new();
let mut queue = VecDeque::new();
let mut visited = HashSet::new();
queue.push_back(start.clone());
visited.insert(start.clone());
result.push(start.clone());
while let Some(current) = queue.pop_front() {
if let Some(neighbors) = self.edges.get(¤t) {
for neighbor in neighbors {
if !visited.contains(neighbor) {
visited.insert(neighbor.clone());
queue.push_back(neighbor.clone());
result.push(neighbor.clone());
}
}
}
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_graph() {
let graph: Graph<i32> = Graph::new();
assert!(matches!(graph.search(&1, &2), Err(Error::InvalidInput(_))));
}
#[test]
fn test_single_vertex() {
let mut graph = Graph::new();
graph.add_vertex(1);
assert!(matches!(graph.search(&1, &1).unwrap(), Some(path) if path == vec![1]));
}
#[test]
fn test_direct_edge() {
let mut graph = Graph::new();
graph.add_edge(1, 2);
assert!(matches!(graph.search(&1, &2).unwrap(), Some(path) if path == vec![1, 2]));
}
#[test]
fn test_path_not_found() {
let mut graph = Graph::new();
graph.add_edge(1, 2);
graph.add_edge(2, 3);
assert!(matches!(graph.search(&1, &4).unwrap(), None));
}
#[test]
fn test_shortest_path() {
let mut graph = Graph::new();
graph.add_edge(1, 2);
graph.add_edge(2, 3);
graph.add_edge(3, 4);
graph.add_edge(1, 5);
graph.add_edge(5, 4);
assert!(matches!(graph.search(&1, &4).unwrap(), Some(path) if path == vec![1, 5, 4]));
}
#[test]
fn test_cyclic_graph() {
let mut graph = Graph::new();
graph.add_edge(1, 2);
graph.add_edge(2, 3);
graph.add_edge(3, 1);
graph.add_edge(2, 4);
assert!(matches!(graph.search(&1, &4).unwrap(), Some(path) if path == vec![1, 2, 4]));
}
#[test]
fn test_with_strings() {
let mut graph = Graph::new();
graph.add_edge("A", "B");
graph.add_edge("B", "C");
graph.add_edge("A", "D");
assert!(
matches!(graph.search(&"A", &"C").unwrap(), Some(path) if path == vec!["A", "B", "C"])
);
}
#[test]
fn test_disconnected_components() {
let mut graph = Graph::new();
graph.add_edge(1, 2);
graph.add_edge(3, 4);
assert!(matches!(graph.search(&1, &4).unwrap(), None));
}
#[test]
fn test_invalid_start_vertex() {
let mut graph = Graph::new();
graph.add_edge(1, 2);
assert!(matches!(graph.search(&3, &2), Err(Error::InvalidInput(_))));
}
#[test]
fn test_bfs_traversal_order() {
let mut graph = Graph::new();
graph.add_edge(1, 2);
graph.add_edge(1, 3);
graph.add_edge(2, 4);
graph.add_edge(2, 5);
graph.add_edge(3, 6);
let traversal = graph.traverse(&1).unwrap();
assert_eq!(traversal[0], 1); assert!(traversal[1..3].contains(&2) && traversal[1..3].contains(&3)); assert!(traversal[3..].iter().all(|&x| x >= 4)); }
#[test]
fn test_multiple_paths_same_length() {
let mut graph = Graph::new();
graph.add_edge(1, 2);
graph.add_edge(2, 4);
graph.add_edge(1, 3);
graph.add_edge(3, 4);
let path = graph.search(&1, &4).unwrap().unwrap();
assert_eq!(path.len(), 3); assert!(
(path == vec![1, 2, 4]) || (path == vec![1, 3, 4]),
"Path should be either [1,2,4] or [1,3,4]"
);
}
}