use crate::core::exceptions::GraphinaNoPath;
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>,
{
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>,
{
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>, GraphinaNoPath>
where
Ty: GraphConstructor<A, W>,
{
match iddfs(graph, start, target, max_depth) {
Some(path) => Ok(path),
None => Err(GraphinaNoPath::new(
"No path found using IDDFS within the given depth limit",
)),
}
}
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>,
{
visited.insert(current);
path.push(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();
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();
forward_queue.push_back(start);
forward_visited.insert(start);
forward_parents.insert(start, None);
backward_queue.push_back(target);
backward_visited.insert(target);
backward_parents.insert(target, None);
let mut meeting_node = None;
while !forward_queue.is_empty() && !backward_queue.is_empty() {
let forward_level = forward_queue.len();
for _ in 0..forward_level {
let current = forward_queue.pop_front().unwrap();
for neighbor in graph.neighbors(current) {
if forward_visited.insert(neighbor) {
forward_queue.push_back(neighbor);
forward_parents.insert(neighbor, Some(current));
}
}
}
if let Some(&meet) = forward_visited.intersection(&backward_visited).next() {
meeting_node = Some(meet);
break;
}
let backward_level = backward_queue.len();
for _ in 0..backward_level {
let current = backward_queue.pop_front().unwrap();
for neighbor in get_backward_neighbors(graph, current) {
if backward_visited.insert(neighbor) {
backward_queue.push_back(neighbor);
backward_parents.insert(neighbor, Some(current));
}
}
}
if let Some(&meet) = forward_visited.intersection(&backward_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>, GraphinaNoPath>
where
Ty: GraphConstructor<A, W>,
{
match bidis(graph, start, target) {
Some(path) => Ok(path),
None => Err(GraphinaNoPath::new(
"No path exists between the specified nodes",
)),
}
}
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()
}
}