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}