use crate::core::error::{GraphinaError, Result};
use crate::core::types::{BaseGraph, GraphConstructor, NodeId};
use std::collections::{HashMap, HashSet, VecDeque};
pub fn bfs<A, W, Ty>(graph: &BaseGraph<A, W, Ty>, start: NodeId) -> Vec<NodeId>
where
Ty: GraphConstructor<A, W>,
{
if graph.node_attr(start).is_none() {
return Vec::new();
}
let mut visited = HashSet::new();
let mut order = Vec::new();
let mut queue = VecDeque::new();
visited.insert(start);
queue.push_back(start);
while let Some(node) = queue.pop_front() {
order.push(node);
for neighbor in graph.neighbors(node) {
if visited.insert(neighbor) {
queue.push_back(neighbor);
}
}
}
order
}
pub fn dfs<A, W, Ty>(graph: &BaseGraph<A, W, Ty>, start: NodeId) -> Vec<NodeId>
where
Ty: GraphConstructor<A, W>,
{
if graph.node_attr(start).is_none() {
return Vec::new();
}
let mut visited = HashSet::new();
let mut order = Vec::new();
dfs_util(graph, start, &mut visited, &mut order);
order
}
fn dfs_util<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
node: NodeId,
visited: &mut HashSet<NodeId>,
order: &mut Vec<NodeId>,
) where
Ty: GraphConstructor<A, W>,
{
if !visited.insert(node) {
return;
}
order.push(node);
for neighbor in graph.neighbors(node) {
if !visited.contains(&neighbor) {
dfs_util(graph, neighbor, visited, order);
}
}
}
pub fn iddfs<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
start: NodeId,
target: NodeId,
max_depth: usize,
) -> Option<Vec<NodeId>>
where
Ty: GraphConstructor<A, W>,
{
for depth in 0..=max_depth {
let mut path = Vec::new();
let mut visited = HashSet::new();
if dls(graph, start, target, depth, &mut visited, &mut path) {
return Some(path);
}
}
None
}
pub fn try_iddfs<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
start: NodeId,
target: NodeId,
max_depth: usize,
) -> Result<Vec<NodeId>>
where
Ty: GraphConstructor<A, W>,
{
if graph.node_attr(start).is_none() || graph.node_attr(target).is_none() {
return Err(GraphinaError::node_not_found(
"Start or target node not found",
));
}
for depth in 0..=max_depth {
let mut path = Vec::new();
let mut visited = HashSet::new();
if dls(graph, start, target, depth, &mut visited, &mut path) {
return Ok(path);
}
}
Err(GraphinaError::no_path(format!(
"No path found from {} to {} within depth {}",
start.index(),
target.index(),
max_depth
)))
}
fn dls<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
current: NodeId,
target: NodeId,
depth: usize,
visited: &mut HashSet<NodeId>,
path: &mut Vec<NodeId>,
) -> bool
where
Ty: GraphConstructor<A, W>,
{
path.push(current);
visited.insert(current);
if current == target {
return true;
}
if depth > 0 {
for neighbor in graph.neighbors(current) {
if !visited.contains(&neighbor)
&& dls(graph, neighbor, target, depth - 1, visited, path)
{
return true;
}
}
}
path.pop();
visited.remove(¤t);
false
}
pub fn bidis<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
start: NodeId,
target: NodeId,
) -> Option<Vec<NodeId>>
where
Ty: GraphConstructor<A, W>,
{
if start == target {
return Some(vec![start]);
}
let mut forward_queue = VecDeque::new();
let mut backward_queue = VecDeque::new();
let mut forward_parents: HashMap<NodeId, Option<NodeId>> = HashMap::new();
let mut backward_parents: HashMap<NodeId, Option<NodeId>> = HashMap::new();
let mut forward_visited = HashSet::new();
let mut backward_visited = HashSet::new();
let mut forward_frontier = HashSet::new();
let mut backward_frontier = HashSet::new();
forward_queue.push_back(start);
forward_visited.insert(start);
forward_frontier.insert(start);
forward_parents.insert(start, None);
backward_queue.push_back(target);
backward_visited.insert(target);
backward_frontier.insert(target);
backward_parents.insert(target, None);
let mut meeting_node = None;
while !forward_queue.is_empty() && !backward_queue.is_empty() {
if let Some(&meet) = forward_frontier.intersection(&backward_frontier).next() {
meeting_node = Some(meet);
break;
}
forward_frontier.clear();
let forward_level = forward_queue.len();
for _ in 0..forward_level {
if let Some(current) = forward_queue.pop_front() {
for neighbor in graph.neighbors(current) {
if forward_visited.insert(neighbor) {
forward_queue.push_back(neighbor);
forward_frontier.insert(neighbor);
forward_parents.insert(neighbor, Some(current));
}
}
}
}
if let Some(&meet) = forward_frontier.intersection(&backward_visited).next() {
meeting_node = Some(meet);
break;
}
backward_frontier.clear();
let backward_level = backward_queue.len();
for _ in 0..backward_level {
if let Some(current) = backward_queue.pop_front() {
for neighbor in get_backward_neighbors(graph, current) {
if backward_visited.insert(neighbor) {
backward_queue.push_back(neighbor);
backward_frontier.insert(neighbor);
backward_parents.insert(neighbor, Some(current));
}
}
}
}
if let Some(&meet) = backward_frontier.intersection(&forward_visited).next() {
meeting_node = Some(meet);
break;
}
}
if let Some(meet) = meeting_node {
let mut forward_path = Vec::new();
let mut cur = meet;
while let Some(&Some(parent)) = forward_parents.get(&cur) {
forward_path.push(cur);
cur = parent;
}
forward_path.push(start);
forward_path.reverse();
let mut backward_path = Vec::new();
cur = meet;
while let Some(&Some(parent)) = backward_parents.get(&cur) {
backward_path.push(parent);
cur = parent;
}
let mut full_path = forward_path;
full_path.extend(backward_path);
return Some(full_path);
}
None
}
pub fn try_bidirectional_search<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
start: NodeId,
target: NodeId,
) -> Result<Vec<NodeId>>
where
Ty: GraphConstructor<A, W>,
{
if graph.node_attr(start).is_none() || graph.node_attr(target).is_none() {
return Err(GraphinaError::node_not_found(
"Start or target node not found",
));
}
bidis(graph, start, target).ok_or_else(|| {
GraphinaError::no_path(format!(
"No path found from {} to {}",
start.index(),
target.index()
))
})
}
fn get_backward_neighbors<A, W, Ty>(graph: &BaseGraph<A, W, Ty>, node: NodeId) -> Vec<NodeId>
where
Ty: GraphConstructor<A, W>,
{
if <Ty as GraphConstructor<A, W>>::is_directed() {
graph
.edges()
.filter(|(_, tgt, _)| *tgt == node)
.map(|(src, _, _)| src)
.collect()
} else {
graph.neighbors(node).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::types::Graph;
#[test]
fn test_bfs() {
let mut graph = Graph::<i32, ()>::new();
let n1 = graph.add_node(1);
let n2 = graph.add_node(2);
let n3 = graph.add_node(3);
graph.add_edge(n1, n2, ());
graph.add_edge(n2, n3, ());
let visited = bfs(&graph, n1);
assert_eq!(visited.len(), 3);
assert!(visited.contains(&n1));
assert!(visited.contains(&n2));
assert!(visited.contains(&n3));
}
#[test]
fn test_dfs() {
let mut graph = Graph::<i32, ()>::new();
let n1 = graph.add_node(1);
let n2 = graph.add_node(2);
let n3 = graph.add_node(3);
graph.add_edge(n1, n2, ());
graph.add_edge(n2, n3, ());
let visited = dfs(&graph, n1);
assert_eq!(visited.len(), 3);
assert!(visited.contains(&n1));
assert!(visited.contains(&n2));
assert!(visited.contains(&n3));
}
#[test]
fn test_bidis() {
let mut graph = Graph::<i32, ()>::new();
let n1 = graph.add_node(1);
let n2 = graph.add_node(2);
let n3 = graph.add_node(3);
graph.add_edge(n1, n2, ());
graph.add_edge(n2, n3, ());
let path = bidis(&graph, n1, n3);
assert!(path.is_some());
let path = path.unwrap();
assert_eq!(path[0], n1);
assert_eq!(path[path.len() - 1], n3);
}
}