Compare commits

...

10 Commits

Author SHA1 Message Date
Zynh0722 ba5642063e I am using code_to_id lmao 2024-04-25 19:09:42 -07:00
Zynh0722 e922599372 finished? 2024-04-24 12:34:07 -07:00
Zynh0722 5eeca21c44 non nan and stuff 2024-04-24 10:00:07 -07:00
Zynh0722 3d14f3b3e1 switch to NodePointer alias 2024-04-24 08:37:11 -07:00
Zynh0722 876aa735dc default source distance infinity 2024-04-24 08:19:04 -07:00
Zynh0722 36118c3299 better documentation 2024-04-24 08:16:53 -07:00
Zynh0722 85a553080a arg parsing 2024-04-24 08:14:36 -07:00
Zynh0722 3f8a061404 clippy 2024-04-24 07:40:49 -07:00
Zynh0722 ce58011eb1 remove unused import 2024-04-24 07:40:42 -07:00
Zynh0722 50419ff0e8 interior mutability woes 2024-04-24 00:29:21 -07:00
6 changed files with 220 additions and 27 deletions

16
Cargo.lock generated
View File

@ -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"

View File

@ -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"] }

View File

@ -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,
} }

35
src/cmd.rs Normal file
View File

@ -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)
}

19
src/float_ordering.rs Normal file
View File

@ -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()
}
}

View File

@ -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 &current_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(())
} }