Skip to main content

dijkstra_suite/
compute.rs

1//! Core computing-related types
2//!
3//! ## The`compute` module
4//!
5//! This module contains all types and logic related to computing the dijkstra algorithm itself
6//! code from this module will be used primarily by `dijkstra` module in its logic
7//!
8//! ## Main structs
9//!
10//! The `DistanceFromSource` type keep tracks of each new discovered node shortest distance
11//! from the source (the starting node of the inquired path) and provides the final reconstructed
12//! path
13//!
14//! The `VisitedList` type keeps track of visited nodes as soon as the algorithm process it
15//!
16//! The `PriorityQueue` type implements the core pillar of implementeation, the min-heap
17//! priority queue to automatically sort out fastest node connections to choose from
18
19use std::{
20    cmp::Reverse,
21    collections::{BinaryHeap, HashMap, HashSet},
22    ops::{Deref, DerefMut},
23};
24
25use crate::{
26    error::DijkstraError,
27    node::{NodeId, NodeWeight},
28    path::Path,
29};
30
31// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
32// +++++++++++++++++++ DistanceFromSource +++++++++++++++++++
33/// keep track of shortest distances discovered and reconstruct shortest path
34
35/// ## The`DistanceFromSource` type
36///
37/// The `DistanceFromSource` type keep tracks of each new discovered node shortest distance
38/// from the source (the starting node of the inquired path)
39///
40/// This type implements the concrete graph representation upon which the dijkstra
41/// implementeation will run to compute the shortest path
42///
43/// ```rust
44/// use dijkstra_suite::path::Path;
45/// use dijkstra_suite::compute::DistanceFromSource;
46///
47/// let mut distances = DistanceFromSource::default();
48///
49/// distances.set_distance((0, 0), 0, None);
50/// distances.set_distance((1, 0), 3, Some((0, 0)));
51/// distances.set_distance((2, 0), 5, Some((0, 0)));
52/// distances.set_distance((3, 0), 7, Some((1, 0)));
53/// distances.set_distance((4, 0), 9, Some((3, 0)));
54/// distances.set_distance((5, 0), 9, Some((3, 0)));
55///
56/// println!("distances: {:?}", distances);
57///
58/// let check_computed_path: Path<(i32, i32), i32> =
59///     (9, vec![(0, 0), (1, 0), (3, 0), (5, 0)]).into();
60/// let computed_path = distances.compute_path((5, 0)).unwrap();
61///
62/// println!("computed weight: {:?}", computed_path.weight);
63/// println!("computed steps: {:?}", computed_path.steps);
64/// assert_eq!(computed_path.weight, check_computed_path.weight);
65/// assert_eq!(computed_path.steps, check_computed_path.steps);
66/// assert_eq!(computed_path, check_computed_path);
67/// ```
68
69#[derive(Debug, Default)]
70pub struct DistanceFromSource<I: NodeId, W: NodeWeight>(HashMap<I, (W, Option<I>)>);
71
72impl<I: NodeId, W: NodeWeight> Deref for DistanceFromSource<I, W> {
73    type Target = HashMap<I, (W, Option<I>)>;
74
75    fn deref(&self) -> &Self::Target {
76        &self.0
77    }
78}
79
80impl<I: NodeId, W: NodeWeight> DerefMut for DistanceFromSource<I, W> {
81    fn deref_mut(&mut self) -> &mut Self::Target {
82        &mut self.0
83    }
84}
85
86impl<I: NodeId, W: NodeWeight> DistanceFromSource<I, W> {
87    pub fn set_distance(
88        &mut self,
89        id: I,
90        distance: W,
91        previous_node: Option<I>,
92    ) -> Option<(W, Option<I>)> {
93        match self.get(&id) {
94            Some(val) => match val.0 < distance {
95                true => Some(val.clone()),
96                false => self.insert(id, (distance, previous_node)),
97            },
98            None => self.insert(id, (distance, previous_node)),
99        }
100        // self.insert(id, (distance, previous_node))
101    }
102
103    pub fn compute_path(&self, to: I) -> Result<Path<I, W>, DijkstraError> {
104        if self.get(&to).is_none() {
105            return Err(DijkstraError::ComputePathError("end node not found".into()));
106        }
107
108        let (cost, previous_node) = self.get(&to).unwrap();
109
110        // if node exists in the list, but has no previous node
111        // this node must be the starting one, so steps vec is empty
112        // and cost is the same as the starting node cost
113        if previous_node.is_none() {
114            // let (cost, _) = self.get(&to).unwrap();
115
116            return Ok(Path {
117                weight: cost.clone(),
118                steps: vec![],
119            });
120        }
121
122        let mut result_path: Vec<I> = vec![];
123        let result_cost = cost.clone();
124
125        result_path.push(to);
126
127        let mut next_node = previous_node.clone();
128
129        while let Some(node) = next_node {
130            // push the node in the resul list as a new step
131            // then check for its previous node, if any
132            // println!("node: {:?}", node);
133            result_path.push(node.clone());
134
135            match self.get(&node) {
136                Some(step) => {
137                    match &step.1 {
138                        Some(next_step) => {
139                            // there's a step behind this, let's
140                            // push it to the result and go ahead
141                            next_node = Some(next_step.clone());
142                        }
143                        None => {
144                            // println!("we reached the start of the path");
145                            next_node = None;
146                        }
147                    }
148                }
149                None => {
150                    return Err(DijkstraError::ComputePathError(
151                        "no valid path found".into(),
152                    ));
153                }
154            }
155        }
156
157        // println!("result cost: {}", result_cost);
158        // println!("result path: {:?}", result_path);
159
160        result_path.reverse();
161
162        Ok(Path {
163            weight: result_cost,
164            steps: result_path,
165        })
166    }
167}
168// +++++++++++++++++++ END DistanceFromSource +++++++++++++++++++
169
170// +++++++++++++++++++++++++++++++++++++++++++++++++++
171// +++++++++++++++++++ VisitedList +++++++++++++++++++
172/// keep track of visited nodes as soon as the algorithm process it
173///
174/// ## The`VisitedList` type
175///
176/// The `VisitedList` type keep tracks of visited nodes as soon as the algorithm process it
177/// from the source (the starting node of the inquired path)
178///
179/// This type is basically just a wrapper around HashSet, restricted to types that implements
180/// NodeId trait
181///
182/// ```rust
183/// use dijkstra_suite::compute::VisitedList;
184///
185/// let node_1 = "A";
186/// let node_2 = "B";
187/// let node_3 = "C";
188/// let node_4 = "D";
189/// let node_5 = "E";
190///
191/// let mut visited_list = VisitedList::default();
192///
193/// visited_list.insert(node_4);
194/// visited_list.insert(node_1);
195/// visited_list.insert(node_3);
196///
197/// assert!(visited_list.is_visited(&node_4));
198/// assert!(!visited_list.is_visited(&node_2));
199/// assert!(visited_list.is_visited(&node_1));
200/// ```
201
202#[derive(Debug, Default)]
203pub struct VisitedList<I: NodeId>(pub HashSet<I>);
204
205impl<I: NodeId> VisitedList<I> {
206    pub fn is_visited(&self, node_id: &I) -> bool {
207        self.contains(node_id)
208    }
209}
210
211impl<I: NodeId> Deref for VisitedList<I> {
212    type Target = HashSet<I>;
213
214    fn deref(&self) -> &Self::Target {
215        &self.0
216    }
217}
218
219impl<I: NodeId> DerefMut for VisitedList<I> {
220    fn deref_mut(&mut self) -> &mut Self::Target {
221        &mut self.0
222    }
223}
224
225// +++++++++++++++++++ END VisitedList +++++++++++++++++++
226
227// +++++++++++++++++++++++++++++++++++++++++++++++++++
228// +++++++++++++++++++ PriorityQueue +++++++++++++++++++
229/// min-heap queue to get the fastest connection to the next path node
230///
231/// ## The`PriorityQueue` type
232///
233/// The `PriorityQueue` type implements a priority queue using a min-heap
234/// binary tree, uses [`BinaryHeap`](std::collections::BinaryHeap) under
235/// the hood.
236///
237/// It's really just a small fancy newtype-style wrapper type around
238/// [`BinaryHeap`](std::collections::BinaryHeap), restricting it to
239/// `QueuedItem<T>` generic newtype to ensure proper min-like behaviour
240///
241/// ```rust
242/// use dijkstra_suite::compute::{QueuedItem, PriorityQueue};
243///
244/// let node_1: i32 = 1;
245/// let node_2: i32 = 2;
246/// let node_3: i32 = 3;
247/// let node_4: i32 = 4;
248/// let node_5: i32 = 5;
249///
250/// let mut queue: PriorityQueue<i32> = PriorityQueue::from(vec![
251///     // add explicitly trough QueuedItem
252///     QueuedItem::from(node_3),
253/// ]);
254///
255/// queue.push(node_5.into());
256/// queue.push(node_2.into());
257/// queue.push(node_1.into());
258/// queue.push(node_4.into());
259///
260/// println!("peek: {:?}", queue.peek().unwrap());
261///
262/// assert_eq!(queue.pop().unwrap(), QueuedItem::from(1));
263/// ```
264#[derive(Debug)]
265pub struct PriorityQueue<T: Default>(pub BinaryHeap<QueuedItem<T>>);
266
267impl<T: Default> Deref for PriorityQueue<T> {
268    type Target = BinaryHeap<QueuedItem<T>>;
269
270    fn deref(&self) -> &Self::Target {
271        &self.0
272    }
273}
274
275impl<T: Default> DerefMut for PriorityQueue<T> {
276    fn deref_mut(&mut self) -> &mut Self::Target {
277        &mut self.0
278    }
279}
280
281// impl<T: Default + Ord> From<T> for PriorityQueue<T> {
282//     fn from(value: T) -> Self {
283//         PriorityQueue(BinaryHeap::from([QueuedItem::from(value)]))
284//     }
285// }
286
287// impl<T: PartialOrd> PartialOrd for PriorityQueue<T> {
288//     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
289//         self.0.
290//     }
291// }
292//
293// +++++++++++++++++++ END PriorityQueue  +++++++++++++++++++
294
295// +++++++++++++++++++++++++++++++++++++++++++++++++++
296// +++++++++++++++++++ QueuedItem +++++++++++++++++++
297/// generic representation for an item queued onto priority queue
298///
299/// ## The`QueuedItem` type
300///
301/// It's really just a small fancy newtype-style wrapper type around
302/// [`Reverse`](std::cmp::Reverse), so that there's no need to mess with
303/// custom types Ord or PartialOrd impls for user's own types
304///
305/// ```rust
306/// use dijkstra_suite::compute::QueuedItem;
307/// use std::cmp::Reverse;
308///
309/// let item_1: QueuedItem<i32> = QueuedItem::from(1);
310/// let item_2 = QueuedItem::from(69_i32);
311///
312/// let reverse = item_1.0;
313/// let value = reverse.0;
314///
315/// assert_eq!(reverse, Reverse(1));
316/// assert_eq!(value, 1);
317///
318/// let reverse = item_2.0;
319/// let value = reverse.0;
320///
321/// assert_eq!(reverse, Reverse(69));
322/// assert_eq!(value, 69);
323/// ```
324#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
325pub struct QueuedItem<T>(pub Reverse<T>);
326
327impl<T> QueuedItem<T> {
328    pub fn item(&self) -> &T {
329        &self.0.0
330    }
331}
332
333impl<T> Deref for QueuedItem<T> {
334    type Target = Reverse<T>;
335
336    fn deref(&self) -> &Self::Target {
337        &self.0
338    }
339}
340
341impl<T> DerefMut for QueuedItem<T> {
342    fn deref_mut(&mut self) -> &mut Self::Target {
343        &mut self.0
344    }
345}
346
347impl<T> From<T> for QueuedItem<T> {
348    fn from(value: T) -> Self {
349        QueuedItem(Reverse(value))
350    }
351}
352
353// impl Ord for QueuedItem<f32> {
354//     fn cmp(&self, other: &Self) -> std::cmp::Ordering {
355//         self.0.0.total_cmp(&other.0.0)
356//     }
357// }
358
359// impl<T: PartialOrd> PartialOrd for QueuedItem<T> {
360//     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
361//         self.0.partial_cmp(&other.0)
362//     }
363// }
364
365impl<T: Default + Ord> From<Vec<QueuedItem<T>>> for PriorityQueue<T> {
366    fn from(value: Vec<QueuedItem<T>>) -> Self {
367        PriorityQueue(BinaryHeap::from(value))
368    }
369}
370//
371// +++++++++++++++++++ END QueuedItem  +++++++++++++++++++
372
373#[cfg(test)]
374mod test {
375    use super::*;
376
377    #[test]
378    fn test_distances_struct() {
379        let mut distances = DistanceFromSource::default();
380
381        let new_distance = distances.set_distance((0, 0), 0, None);
382        assert_eq!(new_distance, None);
383
384        println!("get invalid node (0, 1): {:?}", distances.get(&(0, 1)));
385        assert_eq!(distances.get(&(0, 1)), None);
386
387        // set the new value and returns the OLD value
388        let new_distance = distances.set_distance((0, 0), 1, None);
389        println!("new_distance: {:?}", new_distance);
390        println!("distances: {:?}", distances);
391        assert_eq!(new_distance, Some((0, None)));
392
393        let last_inserted_distance = distances.get(&(0, 0));
394        println!("last_inserted_distance: {:?}", last_inserted_distance);
395
396        // new_distance insertion FAILED because the list already had an entry
397        // with id (0, 0), and its value (0) is LOWER than the one we are trying to
398        // push (1)
399        assert_ne!(last_inserted_distance, Some(&(1, None)));
400        assert_eq!(last_inserted_distance, Some(&(0, None)));
401
402        // assert_eq!(1, 2)
403    }
404
405    #[test]
406    fn test_compute_path() {
407        let mut distances = DistanceFromSource::default();
408
409        distances.set_distance((0, 0), 0, None);
410        distances.set_distance((1, 0), 3, Some((0, 0)));
411        distances.set_distance((2, 0), 5, Some((0, 0)));
412        distances.set_distance((3, 0), 7, Some((1, 0)));
413        distances.set_distance((4, 0), 9, Some((3, 0)));
414        distances.set_distance((5, 0), 9, Some((3, 0)));
415
416        println!("distances: {:?}", distances);
417
418        let check_computed_path = Path::default();
419        let computed_path = distances.compute_path((0, 0)).unwrap();
420
421        assert_eq!(computed_path.weight, check_computed_path.weight);
422        assert_eq!(computed_path.steps, check_computed_path.steps);
423
424        let check_computed_path: Path<(i32, i32), i32> =
425            (9, vec![(0, 0), (1, 0), (3, 0), (5, 0)]).into();
426        let computed_path = distances.compute_path((5, 0)).unwrap();
427
428        println!("computed weight: {:?}", computed_path.weight);
429        println!("computed steps: {:?}", computed_path.steps);
430        assert_eq!(computed_path.weight, check_computed_path.weight);
431        assert_eq!(computed_path.steps, check_computed_path.steps);
432        assert_eq!(computed_path, check_computed_path);
433
434        // assert_eq!(1, 2)
435    }
436
437    #[test]
438    fn test_is_visited() {
439        let node_1 = "A";
440        let node_2 = "B";
441        let node_3 = "C";
442        let node_4 = "D";
443        let node_5 = "E";
444
445        let mut visited_list = VisitedList::default();
446
447        visited_list.insert(node_4);
448        visited_list.insert(node_1);
449        visited_list.insert(node_3);
450
451        assert!(visited_list.is_visited(&node_4));
452        assert!(!visited_list.is_visited(&node_2));
453        assert!(visited_list.is_visited(&node_1));
454    }
455
456    #[test]
457    fn test_priority_queue() {
458        let node_1 = "A";
459        let node_2 = "B";
460        let node_3 = "C";
461        let node_4 = "D";
462        let node_5 = "E";
463
464        // let mut queue: PriorityQueue<&str> = PriorityQueue::from(vec![
465        //     // add explicitly trough QueuedItem
466        //     QueuedItem::from(node_3),
467        //     // implicitly convert to QueuedItem
468        //     // node_2.into(),
469        //     // node_3.into(),
470        //     // node_4.into(),
471        //     // node_5.into(),
472        // ]);
473        //
474        // queue.push(node_5.into());
475        // queue.push(node_2.into());
476        // queue.push(node_1.into());
477        // queue.push(node_4.into());
478        //
479        // println!("peek: {:?}", queue.peek().unwrap());
480        //
481        // assert_eq!(queue.pop().unwrap(), QueuedItem::from("A"));
482
483        // assert_eq!(1, 2)
484    }
485
486    #[test]
487    fn test_priority_queue_i32() {
488        let node_1: i32 = 1;
489        let node_2: i32 = 2;
490        let node_3: i32 = 3;
491        let node_4: i32 = 4;
492        let node_5: i32 = 5;
493
494        let mut queue: PriorityQueue<i32> = PriorityQueue::from(vec![
495            // add explicitly trough QueuedItem
496            QueuedItem::from(node_1),
497            // implicitly convert to QueuedItem
498            node_2.into(),
499            node_3.into(),
500            node_4.into(),
501            node_5.into(),
502        ]);
503
504        println!("peek: {:?}", queue.peek().unwrap());
505
506        assert_eq!(queue.pop().unwrap(), QueuedItem::from(1));
507
508        // assert_eq!(1, 2)
509    }
510
511    #[test]
512    fn test_priority_queue_f32() {
513        let node_1: f32 = 1.0;
514        let node_2: f32 = 2.0;
515        let node_3: f32 = 3.0;
516        let node_4: f32 = 4.0;
517        let node_5: f32 = 5.0;
518
519        // let mut queue: PriorityQueue<f32> = PriorityQueue::from(vec![
520        //     // add explicitly trough QueuedItem
521        //     QueuedItem::from(node_1),
522        //     // implicitly convert to QueuedItem
523        //     node_2.into(),
524        //     node_3.into(),
525        //     node_4.into(),
526        //     node_5.into(),
527        // ]);
528        //
529        // println!("peek: {:?}", queue.peek().unwrap());
530        //
531        // assert_eq!(queue.pop().unwrap(), QueuedItem::from(1.0));
532
533        // assert_eq!(1, 2)
534    }
535}