Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Initial commit
  • Loading branch information
Brandon committed May 10, 2017
0 parents commit 248c455
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
# Rust
Cargo.lock
target
5 changes: 5 additions & 0 deletions Cargo.toml
@@ -0,0 +1,5 @@
[package]

name = "least_expensive_flight_cost_calculator"
version = "1.0.0"
authors = [ "Brandon Cheng <brandon@uconn.edu>"]
164 changes: 164 additions & 0 deletions README.md
@@ -0,0 +1,164 @@
# Least Expensive Flight Cost Calculator

A simple flight cost calculator done for an honors conversion with Gregory
Johnson in CSE 2500 at UConn.

This was my first venture into Rust. It will be very evident in this code that I
fought with the borrow checker. If anyone at UConn knows Rust well and would
like to show me how this code could be better, please send me an email. I will
give you cookies in return. :)

# Setup

Make sure [Rust](https://www.rust-lang.org) is installed.

```sh
$ cargo run
```

# Release

Binaries can be downloaded from the release page. Alternatively, the below can
be copied to [play.rust-lang.org](https://play.rust-lang.org).

```rust
use std::collections::HashMap;
use std::collections::HashSet;

pub struct Graph {
vertices: HashMap<String, HashMap<String, usize>>
}

impl Graph {
pub fn new() -> Graph {
Graph { vertices: HashMap::new() }
}

pub fn add_vertex(&mut self, name: &str) {
let name = name.to_string();
// This can be improved to allow for generic types rather than using strings as vertex
// identifiers
self.vertices.insert(name.to_string(), HashMap::new());
}

pub fn add_edge(&mut self, u: &str, v: &str, weight: usize) {
let u = u.to_string();
let v = v.to_string();

self.vertices.get_mut(&u).unwrap().insert(v.clone(), weight);
self.vertices.get_mut(&v).unwrap().insert(u, weight);
}

pub fn display(&self) {
for (vertex, neighbors) in &self.vertices {
println!("{}", vertex);
for (neighbor, weight) in neighbors {
println!(" {}: {}", neighbor, weight)
}
}
}

pub fn total_weight_of_path(&self, path: &[&str]) -> usize {
(0..path.len()-1).fold(0, |sum, i|
sum + self.vertices.get(path[i]).unwrap().get(path[i+1]).unwrap().clone())
}

pub fn shortest_path(&self, a: &str, z: &str) -> Vec<String> {
let mut tree = HashMap::new();
let mut dist: HashMap<String, usize> = HashMap::new();
let mut unvisited: HashSet<String> = HashSet::new();
for key in self.vertices.keys() {
unvisited.insert(key.to_string());
}

dist.insert(a.to_string(), 0);

while unvisited.contains(z) {
// We need to grab the unvisited node with the smallest distance. This is typically
// done with a priority queue, but we'll do it with a linear time search instead to
// avoid pulling in another dependency and making this proof of concept more complex.
let mut to_visit: HashSet<String> = dist.keys().cloned().collect();
to_visit = to_visit.intersection(&unvisited).cloned().collect();

let mut u = &"".to_string();
for v in &to_visit {
if u == "" {
u = v;
}

if dist.contains_key(v) && (dist.get(v).unwrap().clone() < dist.get(u).unwrap().clone()) {
u = v;
}
}

unvisited.remove(u);

for (v, weight) in self.vertices.get(u).unwrap() {
if !unvisited.contains(v) { continue };

let alt = dist.get(u).unwrap().clone() + weight;
if !dist.contains_key(v) || (alt < dist.get(v).unwrap().clone()) {
dist.insert(v.to_string(), alt);
tree.insert(v.to_string(), u.to_string());
}
}
}

let mut path: Vec<String> = Vec::new();
let mut current: &str = z;
while current != a {
path.push(current.to_string());
current = tree.get(current).unwrap();
}
path.push(a.to_string());
path.reverse();

path
}
}

fn main() {
let cities = [
"San Francisco",
"Denver",
"Chicago",
"Los Angeles",
"Boston",
"Atlanta",
"Orlando"
];

let costs = [
("San Francisco", "Boston", 350),
("San Francisco", "Denver", 140),
("San Francisco", "Los Angeles", 100),
("Denver", "Chicago", 110),
("Denver", "Los Angeles", 85),
("Chicago", "Boston", 125),
("Chicago", "Atlanta", 200),
("Los Angeles", "Boston", 365),
("Boston", "Atlanta", 145),
("Boston", "Orlando", 220),
("Atlanta", "Orlando", 80)
];

let mut flights = Graph::new();

for city in &cities {
flights.add_vertex(city);
}

for cost in &costs {
flights.add_edge(cost.0, cost.1, cost.2)
}

let path = flights.shortest_path("Los Angeles", "Boston");
println!("Best path from {} to {}: {}", "Los Angeles", "Boston", path.join(", "));

let path = flights.shortest_path("San Francisco", "Orlando");
println!("Best path from {} to {}: {}", "San Francisco", "Orlando", path.join(", "));

let path = flights.shortest_path("Denver", "Atlanta");
println!("Best path from {} to {}: {}", "Denver", "Atlanta", path.join(", "));
}
```
94 changes: 94 additions & 0 deletions src/graph.rs
@@ -0,0 +1,94 @@
use std::collections::HashMap;
use std::collections::HashSet;

pub struct Graph {
vertices: HashMap<String, HashMap<String, usize>>
}

impl Graph {
pub fn new() -> Graph {
Graph { vertices: HashMap::new() }
}

pub fn add_vertex(&mut self, name: &str) {
let name = name.to_string();
// This can be improved to allow for generic types rather than using strings as vertex
// identifiers
self.vertices.insert(name.to_string(), HashMap::new());
}

pub fn add_edge(&mut self, u: &str, v: &str, weight: usize) {
let u = u.to_string();
let v = v.to_string();

self.vertices.get_mut(&u).unwrap().insert(v.clone(), weight);
self.vertices.get_mut(&v).unwrap().insert(u, weight);
}

pub fn display(&self) {
for (vertex, neighbors) in &self.vertices {
println!("{}", vertex);
for (neighbor, weight) in neighbors {
println!(" {}: {}", neighbor, weight)
}
}
}

pub fn total_weight_of_path(&self, path: &[&str]) -> usize {
(0..path.len()-1).fold(0, |sum, i|
sum + self.vertices.get(path[i]).unwrap().get(path[i+1]).unwrap().clone())
}

pub fn shortest_path(&self, a: &str, z: &str) -> Vec<String> {
let mut tree = HashMap::new();
let mut dist: HashMap<String, usize> = HashMap::new();
let mut unvisited: HashSet<String> = HashSet::new();
for key in self.vertices.keys() {
unvisited.insert(key.to_string());
}

dist.insert(a.to_string(), 0);

while unvisited.contains(z) {
// We need to grab the unvisited node with the smallest distance. This is typically
// done with a priority queue, but we'll do it with a linear time search instead to
// avoid pulling in another dependency and making this proof of concept more complex.
let mut to_visit: HashSet<String> = dist.keys().cloned().collect();
to_visit = to_visit.intersection(&unvisited).cloned().collect();

let mut u = &"".to_string();
for v in &to_visit {
if u == "" {
u = v;
}

if dist.contains_key(v) && (dist.get(v).unwrap().clone() < dist.get(u).unwrap().clone()) {
u = v;
}
}

unvisited.remove(u);

for (v, weight) in self.vertices.get(u).unwrap() {
if !unvisited.contains(v) { continue };

let alt = dist.get(u).unwrap().clone() + weight;
if !dist.contains_key(v) || (alt < dist.get(v).unwrap().clone()) {
dist.insert(v.to_string(), alt);
tree.insert(v.to_string(), u.to_string());
}
}
}

let mut path: Vec<String> = Vec::new();
let mut current: &str = z;
while current != a {
path.push(current.to_string());
current = tree.get(current).unwrap();
}
path.push(a.to_string());
path.reverse();

path
}
}
46 changes: 46 additions & 0 deletions src/main.rs
@@ -0,0 +1,46 @@
mod graph;

fn main() {
let cities = [
"San Francisco",
"Denver",
"Chicago",
"Los Angeles",
"Boston",
"Atlanta",
"Orlando"
];

let costs = [
("San Francisco", "Boston", 350),
("San Francisco", "Denver", 140),
("San Francisco", "Los Angeles", 100),
("Denver", "Chicago", 110),
("Denver", "Los Angeles", 85),
("Chicago", "Boston", 125),
("Chicago", "Atlanta", 200),
("Los Angeles", "Boston", 365),
("Boston", "Atlanta", 145),
("Boston", "Orlando", 220),
("Atlanta", "Orlando", 80)
];

let mut flights = graph::Graph::new();

for city in &cities {
flights.add_vertex(city);
}

for cost in &costs {
flights.add_edge(cost.0, cost.1, cost.2)
}

let path = flights.shortest_path("Los Angeles", "Boston");
println!("Best path from {} to {}: {}", "Los Angeles", "Boston", path.join(", "));

let path = flights.shortest_path("San Francisco", "Orlando");
println!("Best path from {} to {}: {}", "San Francisco", "Orlando", path.join(", "));

let path = flights.shortest_path("Denver", "Atlanta");
println!("Best path from {} to {}: {}", "Denver", "Atlanta", path.join(", "));
}

0 comments on commit 248c455

Please sign in to comment.