use crate::algorithm::graph::heuristics::dijkstra_heuristic;
use crate::structure::graph::graph_trait::GraphWeightTrait;
use crate::structure::graph::Graph;
use std::collections::{BTreeMap, HashMap, VecDeque};
pub mod heuristics;
fn traversal<W: GraphWeightTrait, const NODES: usize>(
graph: &Graph<W, NODES>,
source: usize,
destination: usize,
push: fn(&mut VecDeque<usize>, usize),
pop: fn(&mut VecDeque<usize>) -> Option<usize>,
) -> Vec<(usize, String)> {
if source >= NODES || destination >= NODES {
panic!(
"Invalid start({}) or end({}) node, available nodes (0 - {})",
source,
destination,
NODES - 1
);
}
let mut visited = vec![false; NODES];
let mut parent: BTreeMap<usize, usize> = BTreeMap::new();
let mut queue = VecDeque::new();
visited[source] = true;
push(&mut queue, source);
while let Some(current) = pop(&mut queue) {
if current == destination {
let mut path = Vec::<(usize, String)>::new();
let mut current = destination;
while parent.contains_key(¤t) {
path.push((current, graph[current].clone()));
current = parent[¤t];
}
path.push((current, graph[current].clone()));
path.reverse();
return path;
}
for next in 0..NODES {
if !visited[next] && graph[(current, next)].is_some() {
visited[next] = true;
parent.insert(next, current);
push(&mut queue, next);
}
}
}
vec![]
}
fn reconstruct_path<W: GraphWeightTrait, const NODES: usize>(
graph: &Graph<W, NODES>,
parents: HashMap<usize, usize>,
costs: [Option<W>; NODES],
node: usize,
) -> Vec<(usize, String, W)> {
let mut current = node;
let mut path = vec![(current, graph[current].clone(), costs[current].unwrap())];
while let Some(&parent) = parents.get(¤t) {
path.push((parent, graph[parent].clone(), costs[parent].unwrap()));
current = parent;
}
path.reverse();
path
}
pub fn dfs<W: GraphWeightTrait, const NODES: usize>(
graph: &Graph<W, NODES>,
source: usize,
destination: usize,
) -> Vec<(usize, String)> {
fn push(q: &mut VecDeque<usize>, v: usize) {
q.push_back(v)
}
fn pop(q: &mut VecDeque<usize>) -> Option<usize> {
q.pop_back()
}
traversal(graph, source, destination, push, pop)
}
pub fn bfs<W: GraphWeightTrait, const NODES: usize>(
graph: &Graph<W, NODES>,
source: usize,
destination: usize,
) -> Vec<(usize, String)> {
fn push(q: &mut VecDeque<usize>, v: usize) {
q.push_front(v)
}
fn pop(q: &mut VecDeque<usize>) -> Option<usize> {
q.pop_back()
}
traversal(graph, source, destination, push, pop)
}
pub fn dijkstra<W: GraphWeightTrait, const NODES: usize>(
graph: &Graph<W, NODES>,
source: usize,
destination: usize,
) -> Vec<(usize, String, W)> {
a_star(graph, source, destination, dijkstra_heuristic)
}
pub fn a_star<W: GraphWeightTrait, const NODES: usize, H: Fn(usize, usize, usize) -> W>(
graph: &Graph<W, NODES>,
source: usize,
destination: usize,
heu: H,
) -> Vec<(usize, String, W)> {
if source >= NODES || destination >= NODES {
panic!(
"Invalid start({}) or end({}) node, available nodes (0 - {})",
source,
destination,
NODES - 1
);
}
let mut parents = HashMap::with_capacity(NODES);
let mut costs = [None; NODES];
let mut explored = [false; NODES];
costs[source] = Some(W::zero());
let mut current = source;
while current < NODES {
if current == destination {
return reconstruct_path(graph, parents, costs, current);
}
explored[current] = true;
let mut next = usize::MAX;
let mut next_heu = None;
for adj in 0..NODES {
if let Some(weight) = graph[(current, adj)] {
let new_cost = costs[current].unwrap() + weight;
let new_heu = new_cost + heu(adj, destination, NODES);
match costs[adj] {
None => {
costs[adj] = Some(new_cost);
parents.insert(adj, current);
}
Some(prev_cost) if new_cost < prev_cost => {
costs[adj] = Some(new_cost);
parents.insert(adj, current);
}
_ => {}
}
if !explored[adj] && (next_heu.is_none() || new_heu < next_heu.unwrap()) {
next_heu = Some(new_heu);
next = adj;
}
}
}
current = next;
}
Vec::new()
}