Implement simple dijkstra
This commit is contained in:
parent
8b62ff78bd
commit
179e6b74a5
2 changed files with 125 additions and 8 deletions
|
|
@ -1,8 +1,9 @@
|
||||||
|
use std::collections::BinaryHeap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, BufReader};
|
use std::io::{self, BufReader};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::data::{AdjacencyList, Page, PageInfo};
|
use crate::data::{AdjacencyList, LinkInfo, Page, PageInfo};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
fn find_index_of_title(pages: &[Page<PageInfo>], title: &str) -> u32 {
|
fn find_index_of_title(pages: &[Page<PageInfo>], title: &str) -> u32 {
|
||||||
|
|
@ -17,25 +18,121 @@ fn find_index_of_title(pages: &[Page<PageInfo>], title: &str) -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DijkstraPageInfo {
|
struct DijkstraPageInfo {
|
||||||
distance: u32,
|
cost: u32,
|
||||||
prev_page_idx: u32,
|
prev_page_idx: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DijkstraPageInfo {
|
impl Default for DijkstraPageInfo {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
distance: u32::MAX,
|
cost: u32::MAX,
|
||||||
prev_page_idx: u32::MAX,
|
prev_page_idx: u32::MAX,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DijkstraLinkInfo {
|
||||||
|
cost: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DijkstraLinkInfo {
|
||||||
|
const BASE_COST: u32 = 1000;
|
||||||
|
|
||||||
|
fn from_link_info(info: LinkInfo) -> Self {
|
||||||
|
DijkstraLinkInfo {
|
||||||
|
cost: Self::BASE_COST + info.start,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
struct Entry {
|
||||||
|
cost: u32,
|
||||||
|
page_idx: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entry {
|
||||||
|
pub fn new(cost: u32, page_idx: u32) -> Self {
|
||||||
|
Self { cost, page_idx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manual implementation so the queue is a min-heap instead of a max-heap.
|
||||||
|
impl Ord for Entry {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
other
|
||||||
|
.cost
|
||||||
|
.cmp(&self.cost)
|
||||||
|
.then_with(|| self.page_idx.cmp(&other.page_idx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Entry {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closely matches the dijkstra example in [std::collections::binary_heap].
|
||||||
fn dijkstra(
|
fn dijkstra(
|
||||||
mut data: AdjacencyList<DijkstraPageInfo, ()>,
|
data: AdjacencyList<PageInfo, LinkInfo>,
|
||||||
from_idx: u32,
|
from_idx: u32,
|
||||||
to_idx: u32,
|
to_idx: u32,
|
||||||
) -> Option<Vec<u32>> {
|
) -> Option<Vec<u32>> {
|
||||||
todo!()
|
println!("> Prepare state");
|
||||||
|
let mut data = data
|
||||||
|
.change_page_data(&|_| DijkstraPageInfo::default())
|
||||||
|
.change_link_data(&DijkstraLinkInfo::from_link_info);
|
||||||
|
let mut queue = BinaryHeap::new();
|
||||||
|
data.page_mut(from_idx).data.cost = 0;
|
||||||
|
queue.push(Entry::new(0, from_idx));
|
||||||
|
|
||||||
|
println!("> Run dijkstra");
|
||||||
|
while let Some(Entry { cost, page_idx }) = queue.pop() {
|
||||||
|
if page_idx == to_idx {
|
||||||
|
// We've found the shortest path to our target
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if cost > data.page(page_idx).data.cost {
|
||||||
|
// This queue entry is outdated
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for link_idx in data.link_range(page_idx) {
|
||||||
|
let link = data.link(link_idx);
|
||||||
|
|
||||||
|
let next = Entry {
|
||||||
|
cost: cost + link.data.cost,
|
||||||
|
page_idx: link.to,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut target_page = data.page_mut(link.to);
|
||||||
|
if next.cost < target_page.data.cost {
|
||||||
|
target_page.data.cost = next.cost;
|
||||||
|
target_page.data.prev_page_idx = page_idx;
|
||||||
|
queue.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("> Collect results");
|
||||||
|
let mut steps = vec![];
|
||||||
|
let mut at_idx = to_idx;
|
||||||
|
loop {
|
||||||
|
steps.push(at_idx);
|
||||||
|
at_idx = data.page(at_idx).data.prev_page_idx;
|
||||||
|
if at_idx == u32::MAX {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
steps.reverse();
|
||||||
|
Some(steps)
|
||||||
|
// if steps.first() == Some(&from_idx) {
|
||||||
|
// Some(steps)
|
||||||
|
// } else {
|
||||||
|
// None
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path(datafile: &Path, from: &str, to: &str) -> io::Result<()> {
|
pub fn path(datafile: &Path, from: &str, to: &str) -> io::Result<()> {
|
||||||
|
|
@ -43,9 +140,6 @@ pub fn path(datafile: &Path, from: &str, to: &str) -> io::Result<()> {
|
||||||
let mut databuf = BufReader::new(File::open(datafile)?);
|
let mut databuf = BufReader::new(File::open(datafile)?);
|
||||||
let data = AdjacencyList::read(&mut databuf)?;
|
let data = AdjacencyList::read(&mut databuf)?;
|
||||||
let pages = data.pages.clone();
|
let pages = data.pages.clone();
|
||||||
let data = data
|
|
||||||
.change_page_data(&|_| DijkstraPageInfo::default())
|
|
||||||
.change_link_data(&|_| ());
|
|
||||||
|
|
||||||
println!(">> Locate from and to");
|
println!(">> Locate from and to");
|
||||||
let from_idx = find_index_of_title(&pages, from);
|
let from_idx = find_index_of_title(&pages, from);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
mod ioutil {
|
mod ioutil {
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
@ -217,6 +218,28 @@ impl AdjacencyList<PageInfo, LinkInfo> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, L> AdjacencyList<P, L> {
|
impl<P, L> AdjacencyList<P, L> {
|
||||||
|
pub fn page(&self, idx: u32) -> &Page<P> {
|
||||||
|
&self.pages[idx as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn page_mut(&mut self, idx: u32) -> &mut Page<P> {
|
||||||
|
&mut self.pages[idx as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn link_range(&self, page_idx: u32) -> Range<u32> {
|
||||||
|
let start_idx = self.page(page_idx).link_idx;
|
||||||
|
let end_idx = self.page(page_idx + 1).link_idx;
|
||||||
|
start_idx..end_idx
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn link(&self, idx: u32) -> &Link<L> {
|
||||||
|
&self.links[idx as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn link_mut(&mut self, idx: u32) -> &mut Link<L> {
|
||||||
|
&mut self.links[idx as usize]
|
||||||
|
}
|
||||||
|
|
||||||
pub fn change_page_data<P2>(self, page_f: &impl Fn(P) -> P2) -> AdjacencyList<P2, L> {
|
pub fn change_page_data<P2>(self, page_f: &impl Fn(P) -> P2) -> AdjacencyList<P2, L> {
|
||||||
let pages = self
|
let pages = self
|
||||||
.pages
|
.pages
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue