1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! Dijkstra path search implementation.

use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::ops::Add;

use super::frontier::Frontier;
use super::graph::{GraphBuilder, GraphSolver, PathSearchTree};
use super::numtraits::{IntoOrd, Zero};

/// Dijkstra path search.
pub struct Dijkstra<T, W> {
    /// Links node with tag `<T>` to the list of ajacent nodest with corresponding weights `<W>`.
    nodes: HashMap<T, Vec<(T, W)>>,
}

impl<T, W> Dijkstra<T, W> {
    /// Create new instance with empty graph.
    pub fn new() -> Self {
        Dijkstra { nodes: HashMap::new() }
    }
}

impl<T, W> GraphBuilder<T, W> for Dijkstra<T, W>
where
    T: Eq + Hash,
    W: PartialEq + PartialOrd + Zero,
{
    fn add_node<I: IntoIterator<Item = (T, W)>>(&mut self, node_tag: T, links: I) {
        let links = links.into_iter().collect::<Vec<(T, W)>>();
        debug_assert!(links.iter().all(|(_, w)| *w >= W::ZERO), "Negative weight detected");
        self.nodes.insert(node_tag, links);
    }
}

impl<T, W> GraphSolver<T, W> for Dijkstra<T, W>
where
    T: Clone + Eq + Ord + Hash,
    W: Clone + PartialEq + PartialOrd + IntoOrd + Add<Output = W> + Zero,
{
    fn path(&self, from: &T, to: &T) -> Vec<T> {
        let mut path = self.reverse_path(from, to);

        // Reverse the path, so the result will be from `from` to `to`
        path.reverse();

        path
    }

    fn reverse_path(&self, from: &T, to: &T) -> Vec<T> {
        // Don't run when we don't have nodes set
        if self.nodes.is_empty() {
            return Vec::new();
        }

        // Algorithm state
        let mut explored = HashSet::<T>::new();
        let mut frontier = Frontier::<T, W>::new();
        let mut previous = HashMap::<T, T>::new();

        // The resulting reversed path
        let mut rev_path = Vec::<T>::new();

        // Add the starting point to the frontier, it will be the first node visited
        frontier.push(from.clone(), W::ZERO);

        // Run until we have visited every node in the frontier
        while let Some((tag, cost)) = frontier.pop() {
            // When the node with the lowest cost in the frontier is our goal node, we're done.
            if tag == *to {
                let mut cur_tag = tag;
                while let Some(prev_tag) = previous.get(&cur_tag) {
                    rev_path.push(cur_tag.clone());
                    cur_tag = prev_tag.clone();
                }
                break;
            }

            // Add the current node to the explored set
            explored.insert(tag.clone());

            // Loop all the neighboring nodes
            if let Some(neighbors) = self.nodes.get(&tag) {
                for (n_tag, n_cost) in neighbors.iter() {
                    // If we already explored the node - skip it
                    if explored.contains(n_tag) {
                        continue;
                    }

                    let node_cost = cost.clone() + n_cost.clone();

                    // If the neighboring node is not yet in the frontier, we add it with the correct cost.
                    // Otherwise we only update the cost of this node in the frontier when it's below what's currently set.
                    let updated = frontier.try_insert_or_decrease_cost(n_tag, node_cost);
                    if updated {
                        previous.insert(n_tag.clone(), tag.clone());
                    }
                }
            }
        }

        // Check if path not found
        if rev_path.is_empty() {
            return rev_path;
        }

        // Add the origin waypoint at the end of the array
        rev_path.push(from.clone());

        rev_path
    }

    fn path_search_tree(&self, start: &T) -> PathSearchTree<T> {
        // Prepare empty tree
        let mut res = PathSearchTree {
            start_node: start.clone(),
            paths: Vec::new(),
        };

        // Don't run when we don't have nodes set
        if self.nodes.is_empty() {
            return res;
        }

        // Algorithm state
        let mut explored = HashSet::<T>::new();
        let mut frontier = Frontier::<T, W>::new();
        let mut previous = HashMap::<T, T>::new();

        // Add the starting point to the frontier, it will be the first node visited
        frontier.push(start.clone(), W::ZERO);

        // Run until we have visited every node in the frontier
        while let Some((tag, cost)) = frontier.pop() {
            // Add the current node to the explored set
            explored.insert(tag.clone());

            // Loop all the neighboring nodes
            if let Some(neighbors) = self.nodes.get(&tag) {
                for (n_tag, n_cost) in neighbors.iter() {
                    // If we already explored the node - skip it
                    if explored.contains(n_tag) {
                        continue;
                    }

                    let node_cost = cost.clone() + n_cost.clone();

                    // If the neighboring node is not yet in the frontier, we add it with the correct cost.
                    // Otherwise we only update the cost of this node in the frontier when it's below what's currently set.
                    let updated = frontier.try_insert_or_decrease_cost(n_tag, node_cost);
                    if updated {
                        previous.insert(n_tag.clone(), tag.clone());
                    }
                }
            }
        }

        // Free some memory
        drop(explored);
        drop(frontier);

        // Build the search tree
        for end_tag in previous.keys() {
            // The reversed path without starting and ending nodes
            let mut rev_path = Vec::<T>::new();

            let mut cur_tag = end_tag;
            while let Some(prev_tag) = previous.get(&cur_tag) {
                cur_tag = prev_tag;
                if *prev_tag != *start {
                    let waypoint = prev_tag.clone();
                    rev_path.push(waypoint);
                }
            }

            // Convert reversed path into forward path
            let path = {
                rev_path.reverse();
                rev_path
            };

            // Store that path in the tree
            res.paths.push((end_tag.clone(), path));
        }

        res
    }
}