Compare commits
10 Commits
bc2f900f37
...
ba5642063e
Author | SHA1 | Date |
---|---|---|
Zynh0722 | ba5642063e | |
Zynh0722 | e922599372 | |
Zynh0722 | 5eeca21c44 | |
Zynh0722 | 3d14f3b3e1 | |
Zynh0722 | 876aa735dc | |
Zynh0722 | 36118c3299 | |
Zynh0722 | 85a553080a | |
Zynh0722 | 3f8a061404 | |
Zynh0722 | ce58011eb1 | |
Zynh0722 | 50419ff0e8 |
|
@ -28,9 +28,25 @@ name = "dijkstras-algo-wu"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"csv",
|
"csv",
|
||||||
|
"itertools",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
|
|
@ -7,4 +7,5 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
csv = "1.3.0"
|
csv = "1.3.0"
|
||||||
|
itertools = "0.12.1"
|
||||||
serde = { version = "1.0.198", features = ["derive"] }
|
serde = { version = "1.0.198", features = ["derive"] }
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
pub(crate) struct Airport {
|
pub(crate) struct Airport {
|
||||||
pub(crate) id: usize,
|
pub(crate) id: usize,
|
||||||
pub(crate) code: String,
|
pub(crate) code: String,
|
||||||
pub(crate) name: String,
|
pub(crate) _name: String,
|
||||||
pub(crate) city: String,
|
pub(crate) _city: String,
|
||||||
pub(crate) country: String,
|
pub(crate) _country: String,
|
||||||
pub(crate) lat: f64,
|
pub(crate) lat: f64,
|
||||||
pub(crate) lon: f64,
|
pub(crate) lon: f64,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
use std::{collections::HashMap, process::exit};
|
||||||
|
|
||||||
|
pub fn help() {
|
||||||
|
println!(
|
||||||
|
"usage:
|
||||||
|
airport-paths <aiport code> <airport code>
|
||||||
|
finds the shortest path between the first and second airport-paths
|
||||||
|
airport-paths -h
|
||||||
|
shows this help"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_args(args: Vec<String>, code_to_id: HashMap<String, usize>) -> (usize, usize) {
|
||||||
|
let (origin, destination) = match args.len() {
|
||||||
|
3 => {
|
||||||
|
let exit_handler = || {
|
||||||
|
println!("unable to find airport");
|
||||||
|
exit(2);
|
||||||
|
};
|
||||||
|
let origin = code_to_id.get(&args[1]).unwrap_or_else(exit_handler);
|
||||||
|
let destination = code_to_id.get(&args[2]).unwrap_or_else(exit_handler);
|
||||||
|
|
||||||
|
(origin, destination)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
help();
|
||||||
|
match args.get(1).map(String::as_str) {
|
||||||
|
Some("-h") => exit(0),
|
||||||
|
_ => exit(2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(*origin, *destination)
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialOrd)]
|
||||||
|
pub struct NonNan(pub f64);
|
||||||
|
|
||||||
|
impl Eq for NonNan {}
|
||||||
|
|
||||||
|
impl PartialEq for NonNan {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::derive_ord_xor_partial_ord)]
|
||||||
|
impl Ord for NonNan {
|
||||||
|
fn cmp(&self, other: &NonNan) -> Ordering {
|
||||||
|
self.partial_cmp(other).unwrap()
|
||||||
|
}
|
||||||
|
}
|
170
src/main.rs
170
src/main.rs
|
@ -1,26 +1,53 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
mod airport;
|
mod airport;
|
||||||
|
mod cmd;
|
||||||
mod distance;
|
mod distance;
|
||||||
|
mod float_ordering;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::{hash_map::Entry, HashMap},
|
collections::{hash_map::Entry, BTreeSet, HashMap},
|
||||||
|
env,
|
||||||
|
error::Error,
|
||||||
|
f64::INFINITY,
|
||||||
|
fs::File,
|
||||||
|
io::BufReader,
|
||||||
|
ops::Deref,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use float_ordering::NonNan;
|
||||||
|
use itertools::Itertools;
|
||||||
use parse::Record;
|
use parse::Record;
|
||||||
|
|
||||||
type Graph = HashMap<usize, Rc<RefCell<Node>>>;
|
type NodePointer = Rc<RefCell<Node>>;
|
||||||
|
type Graph = HashMap<usize, NodePointer>;
|
||||||
type Airports = HashMap<usize, Rc<airport::Airport>>;
|
type Airports = HashMap<usize, Rc<airport::Airport>>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Node {
|
struct Node {
|
||||||
origin: Rc<airport::Airport>,
|
origin: Rc<airport::Airport>,
|
||||||
destinations: Vec<Destination>,
|
destinations: Vec<Destination>,
|
||||||
source_distance: f64,
|
source_distance: NonNan,
|
||||||
visited: bool,
|
previous_node: Option<NodePointer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct PathFollower {
|
||||||
|
next_node: Option<NodePointer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for PathFollower {
|
||||||
|
type Item = NodePointer;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if let Some(node) = self.next_node.take() {
|
||||||
|
self.next_node = node.borrow().previous_node.clone();
|
||||||
|
return Some(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
|
@ -28,18 +55,50 @@ impl Node {
|
||||||
Node {
|
Node {
|
||||||
destinations,
|
destinations,
|
||||||
origin,
|
origin,
|
||||||
source_distance: 0.0,
|
source_distance: NonNan(INFINITY),
|
||||||
visited: false,
|
previous_node: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl Eq for Node {}
|
||||||
|
impl PartialEq for Node {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
other.origin.id == self.origin.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Node {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.source_distance
|
||||||
|
.cmp(&other.source_distance)
|
||||||
|
.then_with(|| self.origin.id.cmp(&other.origin.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Node {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Destination {
|
struct Destination {
|
||||||
airport: Rc<RefCell<Node>>,
|
node: NodePointer,
|
||||||
distance: f64,
|
distance: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Destination {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let node = self.node.as_ref().borrow();
|
||||||
|
|
||||||
|
f.debug_struct("Node")
|
||||||
|
.field("origin", &node.origin)
|
||||||
|
.field("distance", &self.distance)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Destination {
|
impl Destination {
|
||||||
fn new(source: &airport::Airport, destination: Rc<airport::Airport>, graph: &Graph) -> Self {
|
fn new(source: &airport::Airport, destination: Rc<airport::Airport>, graph: &Graph) -> Self {
|
||||||
Destination {
|
Destination {
|
||||||
|
@ -47,7 +106,7 @@ impl Destination {
|
||||||
(source.lat, source.lon),
|
(source.lat, source.lon),
|
||||||
(destination.lat, destination.lon),
|
(destination.lat, destination.lon),
|
||||||
),
|
),
|
||||||
airport: graph[&destination.id].clone(),
|
node: graph[&destination.id].clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,9 +116,9 @@ fn insert_airports_from_record(airports: &mut Airports, record: &Record) {
|
||||||
entry.insert(Rc::new(airport::Airport {
|
entry.insert(Rc::new(airport::Airport {
|
||||||
id: record.origin_airport_id,
|
id: record.origin_airport_id,
|
||||||
code: record.origin_airport_code.clone(),
|
code: record.origin_airport_code.clone(),
|
||||||
name: record.origin_airport.clone(),
|
_name: record.origin_airport.clone(),
|
||||||
city: record.origin_city.clone(),
|
_city: record.origin_city.clone(),
|
||||||
country: record.origin_country.clone(),
|
_country: record.origin_country.clone(),
|
||||||
lat: record.origin_airport_latitude,
|
lat: record.origin_airport_latitude,
|
||||||
lon: record.origin_airport_longitude,
|
lon: record.origin_airport_longitude,
|
||||||
}));
|
}));
|
||||||
|
@ -69,28 +128,29 @@ fn insert_airports_from_record(airports: &mut Airports, record: &Record) {
|
||||||
entry.insert(Rc::new(airport::Airport {
|
entry.insert(Rc::new(airport::Airport {
|
||||||
id: record.destination_airport_id,
|
id: record.destination_airport_id,
|
||||||
code: record.destination_airport_code.clone(),
|
code: record.destination_airport_code.clone(),
|
||||||
name: record.destination_airport.clone(),
|
_name: record.destination_airport.clone(),
|
||||||
city: record.destination_city.clone(),
|
_city: record.destination_city.clone(),
|
||||||
country: record.destination_country.clone(),
|
_country: record.destination_country.clone(),
|
||||||
lat: record.destination_airport_latitude,
|
lat: record.destination_airport_latitude,
|
||||||
lon: record.destination_airport_longitude,
|
lon: record.destination_airport_longitude,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
// Read all data lines in, ignoring errors
|
// Read all data lines in, ignoring errors
|
||||||
let data: Vec<Record> = csv::Reader::from_reader(std::io::stdin())
|
let data: Vec<Record> = csv::Reader::from_reader(BufReader::new(File::open("routes.csv")?))
|
||||||
.deserialize()
|
.deserialize()
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
// Storing airport data as a list of reference counted pointers
|
||||||
let mut airports: Airports = HashMap::new();
|
let mut airports: Airports = HashMap::new();
|
||||||
|
|
||||||
for record in &data {
|
for record in &data {
|
||||||
insert_airports_from_record(&mut airports, record);
|
insert_airports_from_record(&mut airports, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Index for converting airport codes to airport ids
|
||||||
let code_to_id: HashMap<String, usize> = HashMap::from_iter(
|
let code_to_id: HashMap<String, usize> = HashMap::from_iter(
|
||||||
airports
|
airports
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -110,10 +170,72 @@ fn main() {
|
||||||
// Populate Edges
|
// Populate Edges
|
||||||
for record in data {
|
for record in data {
|
||||||
let destination = record.get_destination(&airports, &graph);
|
let destination = record.get_destination(&airports, &graph);
|
||||||
if let Entry::Occupied(mut entry) = graph.entry(record.origin_airport_id) {
|
if let Entry::Occupied(entry) = graph.entry(record.origin_airport_id) {
|
||||||
let mut entry = entry.get_mut().borrow_mut();
|
let mut node = entry.get().deref().borrow_mut();
|
||||||
|
node.destinations.push(destination);
|
||||||
entry.destinations.push(destination);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using command line arguments as input
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
let (origin_id, destination_id) = cmd::handle_args(args, code_to_id);
|
||||||
|
|
||||||
|
// Fetch target nodes
|
||||||
|
let origin = graph[&origin_id].clone();
|
||||||
|
let destination = graph[&destination_id].clone();
|
||||||
|
|
||||||
|
origin.deref().borrow_mut().source_distance = NonNan(0.0);
|
||||||
|
|
||||||
|
#[allow(clippy::mutable_key_type)]
|
||||||
|
let mut unvisited: BTreeSet<NodePointer> = BTreeSet::new();
|
||||||
|
|
||||||
|
for node in graph.values() {
|
||||||
|
unvisited.insert(node.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(current_node) = unvisited.pop_first() {
|
||||||
|
if current_node.borrow().deref() == destination.borrow().deref() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for target in ¤t_node.borrow().destinations {
|
||||||
|
let new_distance = current_node.borrow().source_distance.0 + target.distance;
|
||||||
|
if new_distance < target.node.borrow().source_distance.0 {
|
||||||
|
let destination_node = unvisited.take(&target.node).unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut node_ref = destination_node.deref().borrow_mut();
|
||||||
|
node_ref.previous_node = Some(current_node.clone());
|
||||||
|
node_ref.source_distance = NonNan(new_distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
unvisited.insert(destination_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let follower = PathFollower {
|
||||||
|
next_node: Some(destination.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let reversed_path: Vec<NodePointer> = follower.collect();
|
||||||
|
let path: Vec<NodePointer> = reversed_path.into_iter().rev().collect();
|
||||||
|
|
||||||
|
if path.len() > 1 {
|
||||||
|
println!(
|
||||||
|
"Total Distance: {}",
|
||||||
|
path[path.len() - 1].borrow().source_distance.0
|
||||||
|
);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
path.iter()
|
||||||
|
.map(|node| node.borrow().origin.code.clone())
|
||||||
|
.join("->")
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("Path Not Found")
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue