use crate::error::GraphError;
use crate::{Vertex, Graph};
use std::collections::BinaryHeap;
use std::cmp::Ordering;
use std::ops::Add;
use crate::algorithms::definitions::Parents;
use std::marker::PhantomData;
pub struct Distance<'a, T> {
dist: Vec<Option<T>>,
phantom: PhantomData<&'a T>,
}
impl <'a, T> Distance<'a, T> where T: Copy {
pub fn get_distance(&self, target: &Vertex<T>) -> Option<T> {
self.dist[target.id()]
}
}
struct Pair<T> where T: PartialOrd + PartialEq + Copy + Default {
node: usize,
dist: T,
}
impl <T> PartialEq for Pair<T> where T: PartialOrd + PartialEq + Copy + Default {
fn eq(&self, other: &Pair<T>) -> bool {
self.dist == other.dist
}
}
impl <T> Eq for Pair<T> where T: PartialOrd + PartialEq + Copy + Default {}
impl<T> Ord for Pair<T> where T: PartialOrd + PartialEq + Copy + Default {
fn cmp(&self, other: &Self) -> Ordering {
other.dist.partial_cmp(&self.dist).unwrap()
}
}
impl<T> PartialOrd for Pair<T> where T: PartialOrd + PartialEq + Copy + Default {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(other.dist.partial_cmp(&self.dist).unwrap())
}
}
pub fn dijkstra<'a, T>(graph: &'a Graph<T>, from: &'a Vertex<T>) -> Result<(Parents<'a, T>, Distance<'a, T>), GraphError> where T: Default + Copy + PartialOrd + PartialEq + Add<Output = T> {
let mut parents = vec![None; graph.size()];
let mut visited = vec![false; graph.size()];
let mut distances = vec![None; graph.size()];
let from = from.id();
let mut heap = BinaryHeap::<Pair<T>>::new();
distances[from] = Some(Default::default());
heap.push(Pair { node: from, dist: T::default()});
while !heap.is_empty() {
let pair = heap.pop().unwrap();
visited[pair.node] = true;
for edge in &graph.adj[pair.node].edges {
if !visited[edge.to] && (distances[edge.to].is_none() || edge.weight + pair.dist < distances[edge.to].unwrap()) {
parents[edge.to] = Some(&graph.adj[pair.node]);
distances[edge.to] = Some(edge.weight + pair.dist);
heap.push(Pair {node: edge.to, dist: distances[edge.to].unwrap()});
}
}
}
Ok((parents, Distance{dist: distances, phantom: PhantomData }))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dijkstra(){
use crate::utils;
let mut graph = Graph::new(10);
graph.add_edge(1, 2, 2.0).unwrap();
graph.add_edge(2, 3, 5.0).unwrap();
graph.add_edge(3, 5, 7.0).unwrap();
graph.add_edge(1, 5, 19.0).unwrap();
let start = graph.get_vertex(1).unwrap();
let (parents, distances) = dijkstra(&graph, &start).unwrap();
let target = graph.get_vertex(5).unwrap();
let path = utils::find_path(&graph, &target, &parents).unwrap().unwrap();
assert_eq!(path.iter().map(|vertex|vertex.id()).collect::<Vec<usize>>(), vec![1, 2, 3, 5]);
let target = graph.get_vertex(3).unwrap();
let path = utils::find_path(&graph, &target, &parents).unwrap().unwrap();
assert_eq!(path.iter().map(|vertex|vertex.id()).collect::<Vec<usize>>(), vec![1, 2, 3]);
assert_eq!(distances.get_distance(graph.get_vertex(5).unwrap()).unwrap(), 14.0);
assert_eq!(distances.get_distance(graph.get_vertex(7).unwrap()), None);
}
}