diff --git a/lib/dijkstra/Cargo.toml b/lib/dijkstra/Cargo.toml new file mode 100644 index 0000000..3736e86 --- /dev/null +++ b/lib/dijkstra/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "dijkstra" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/lib/dijkstra/src/lib.rs b/lib/dijkstra/src/lib.rs new file mode 100644 index 0000000..1e49f09 --- /dev/null +++ b/lib/dijkstra/src/lib.rs @@ -0,0 +1,144 @@ +use std::fmt::Debug; +use std::fmt::Display; +use std::{collections::HashSet, ops::Add}; + +type Coordinate = (usize, usize); + +const START: Coordinate = (0, 0); + +pub trait MatrixInner +where + T: Add + PartialOrd + Debug + Copy, +{ + fn height(&self) -> usize; + fn width(&self) -> usize; + fn get(&self, coordinate: &Coordinate) -> &Option>; + fn get_mut(&mut self, coordinate: &Coordinate) -> &mut Option>; +} + +pub trait Matrix: MatrixInner + Debug +where + T: Add + PartialOrd + Debug + Copy, +{ +} + +#[derive(Debug)] +pub struct Dijkstra { + matrix: Box>, + unvisited: HashSet, +} + +impl Dijkstra +where + T: PartialOrd + Add + Display + Copy + Debug, +{ + pub fn new(matrix: Box>) -> Self { + let unvisited = Self::initialize_unvisited_set(&*matrix, START); + Self { matrix, unvisited } + } + + pub fn solve(&mut self) { + while !self.unvisited.is_empty() { + let coord = self.get_unvisited_coord_with_smallest_distance_from_start(); + let neighbours = self.get_neighbours(&coord); + let previous_distance = *self + .matrix + .get(&coord) + .as_ref() + .unwrap() + .distance + .as_ref() + .unwrap(); + for neighbour in neighbours { + self.update_neighbour(neighbour, coord, previous_distance); + } + } + } + + fn get_unvisited_coord_with_smallest_distance_from_start(&mut self) -> Coordinate { + let coord = self + .unvisited + .iter() + .filter_map(|c| { + let node = self.matrix.get(c).as_ref().unwrap(); + node.distance.map(|value| (c, value)) + }) + .reduce(|(c0, v0), (c1, v1)| if v0 < v1 { (c0, v0) } else { (c1, v1) }) + .map(|(c, _)| *c) + .unwrap_or((0, 0)); + self.unvisited.remove(&coord); + coord + } + + fn get_neighbours(&self, coordinate: &Coordinate) -> Vec { + let (x0, y0) = coordinate; + let mut result = vec![]; + if *x0 < self.matrix.width() - 1 { + result.push((*x0 + 1, *y0)); + } + if *y0 < self.matrix.height() - 1 { + result.push((*x0, *y0 + 1)); + } + result + } + + fn update_neighbour(&mut self, next: Coordinate, previous: Coordinate, previous_distance: T) { + let mut next_node = self.matrix.get_mut(&next).as_mut().unwrap(); + let new_distance = previous_distance + next_node.value; + match next_node.distance { + None => { + next_node.distance = Some(new_distance); + next_node.previous = Some(previous); + } + Some(old_distance) => { + if new_distance < old_distance { + next_node.distance = Some(new_distance); + next_node.previous = Some(previous); + } + } + } + } + + pub fn show(&self) { + let result = self + .matrix + .get(&(self.matrix.height() - 1, self.matrix.width() - 1)) + .as_ref() + .unwrap() + .distance + .as_ref() + .unwrap(); + println!("{}", result); + } + + fn initialize_unvisited_set(matrix: &dyn Matrix, start: Coordinate) -> HashSet { + let unvisited: HashSet<_> = (0..matrix.height()) + .flat_map(|x| (0..matrix.width()).zip([x].into_iter().cycle())) + .filter(|value| *value != start) + .collect(); + unvisited + } +} + +#[derive(PartialEq, Eq, Hash, Debug)] +pub struct Node +where + T: Add + PartialOrd, +{ + value: T, + distance: Option, + previous: Option, +} + +impl From for Node +where + T: Add + PartialOrd, +{ + fn from(value: T) -> Self { + Node { + value, + distance: None, + previous: None, + } + } +}