There was a problem fetching linked pipelines.
Commit 0a900ed5 authored by Adam Blank's avatar Adam Blank
Browse files

Initial commit

parents
No related merge requests found
Pipeline #28879 canceled with stage
Showing with 2372 additions and 0 deletions
+2372 -0
package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.interfaces.*;
public class Graph<V, E> extends IGraph<V, E> {
@Override
public boolean addVertex(V vertex) {
return false;
}
@Override
public boolean addEdge(V src, V dest, E e) {
return false;
}
@Override
public boolean addUndirectedEdge(V n1, V n2, E e) {
return false;
}
@Override
public boolean removeEdge(V src, V dest) {
return false;
}
@Override
public ISet<V> vertices() {
return null;
}
@Override
public E adjacent(V i, V j) {
return null;
}
@Override
public ISet<V> neighbors(V vertex) {
return null;
}
}
\ No newline at end of file
package edu.caltech.cs2.datastructures;
import com.google.gson.JsonObject;
import java.util.List;
public class Location {
public enum Type {
BUILDING,
WAYPOINT;
}
public static final double EARTH_RAD_FT = 20925721.784777;
public final long id;
public final double lat;
public final double lon;
public final String amenity;
public String name;
public final String address;
public final String shop;
public final Type type;
public Location(long id, double latitude, double longitude,
String name, String addr, String amenity, String shop, String type) {
this.id = id;
this.lat = latitude;
this.lon = longitude;
this.type = type.equals("building") ? Type.BUILDING : Type.WAYPOINT;
this.amenity = amenity;
this.shop = shop;
this.name = name;
this.address = addr;
}
public Location(long id) {
this(id, 0, 0, null, null, null, null, "waypoint");
}
public Location(JsonObject e) {
this.id = Long.parseLong(e.get("id").getAsString());
this.lat = e.get("lat").getAsDouble();
this.lon = e.get("lon").getAsDouble();
String[] props = new String[5];
int i = 0;
for (String prop : List.of("name", "amenity", "shop", "type", "address")) {
if (e.has(prop)) {
props[i] = e.get(prop).getAsString();
}
i++;
}
this.name = props[0];
this.amenity = props[1];
this.shop = props[2];
this.type = props[3].equals("building") ? Type.BUILDING : Type.WAYPOINT;
this.address = props[4];
}
public String displayString() {
String s = this.name;
if (this.address != null) {
s += " (" + this.address + ")";
} else {
s += " (" + this.lat + ", " + this.lon + ")";
}
return s;
}
public double getDistance(double lat, double lon) {
double dLat = Math.toRadians(lat - this.lat);
double dLon = Math.toRadians(lon - this.lon);
return 2 * EARTH_RAD_FT * Math.asin(Math.sqrt(
Math.pow(Math.sin(dLat / 2), 2) + Math.pow(Math.sin(dLon / 2), 2) *
Math.cos(Math.toRadians(this.lat)) *
Math.cos(Math.toRadians(lat))));
}
public double getDistance(Location other) {
return this.getDistance(other.lat, other.lon);
}
public static double getDistance(double lat1, double lon1, double lat2, double lon2) {
return new Location(0, lat1, lon1, null, null, null, null, "building").getDistance(lat2, lon2);
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("{");
s.append("\"id\": " + "\"" + this.id + "\"");
s.append(", ");
s.append("\"lat\": " + this.lat);
s.append(", ");
s.append("\"lon\": " + this.lon);
s.append(", ");
s.append("\"type\": " + "\"" + (this.type == Type.BUILDING ? "building" : "footpath") + "\"");
if (this.amenity != null) {
s.append(", \"amenity\": " + "\"" + this.amenity + "\"");
}
if (this.address != null) {
s.append(", \"address\": " + "\"" + this.address + "\"");
}
if (this.shop != null) {
s.append(", \"shop\": " + "\"" + this.shop + "\"");
}
if (this.name != null) {
s.append(", \"name\": " + "\"" + this.name.replace("\"", "\\\"") + "\"");
}
s.append("}");
return s.toString();
}
@Override
public int hashCode() {
return Long.hashCode(this.id);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Location)) {
return false;
}
return this.id == ((Location)o).id;
}
}
package edu.caltech.cs2.interfaces;
import java.util.Iterator;
/**
* This interface represents some group or collection of elements.
* @param <E> Element type
*/
public interface ICollection<E> extends Iterable<E> {
/**
* Add an element to the collection.
* @param e Element to add
*/
public void add(E e);
/**
* Removes all elements from the collection.
*/
public void clear();
/**
* Generates an iterator over the collection.
* @return Iterator over the collection
*/
@Override
public Iterator<E> iterator();
/**
* Checks if an element is contained in the collection.
* @param e Element to check for
* @return True if element is contained in collection, false otherwise
*/
default public boolean contains(E e) {
for (E item : this) {
if (e == null ? item == null : e.equals(item)) {
return true;
}
}
return false;
}
/**
* Calculates the size of the collection.
* @return Number of elements in the collection
*/
public int size();
/**
* Checks whether the collection contains no elements.
* @return True if no elements are contained, false otherwise
*/
default public boolean isEmpty() {
return this.size() == 0;
}
}
package edu.caltech.cs2.interfaces;
/**
* This interface represents a deque - a data structure that can add and remove elements from either end of a list.
* @param <E> Element type
*/
public interface IDeque<E> extends ICollection<E> {
/**
* Add an element to the front end of the deque.
* @param e Element to add
*/
public void addFront(E e);
/**
* Add an element to the back end of the deque.
* @param e Element to add
*/
public void addBack(E e);
/**
* Adds an element to the collection.
* @param e Element to add
*/
@Override
default public void add(E e){
this.addFront(e);
}
/**
* Adds a collection of elements to this collection.
* @param c Collection of elements to add
*/
default public void addAll(ICollection<E> c) {
for (E e : c) {
this.add(e);
}
}
/**
* Removes and returns the element at the front end of the deque. Returns null if deque is empty.
* @return Element at front, if one exists; null otherwise
*/
public E removeFront();
/**
* Removes and returns the element at the back end of the deque. Returns null if deque is empty.
* @return Element at back, if one exists; null otherwise
*/
public E removeBack();
/**
* Removes all elements in the deque.
*/
@Override
default public void clear() {
while (!this.isEmpty()) {
this.removeBack();
}
}
/**
* Returns (but does not remove) the element at one end of the deque. Returns null if deque is empty.
* Note: The side you peek from should be chosen such that both the IQueue and IStack interfaces are implemented correctly.
* @return Element at one end, if one exists; null otherwise
*/
public E peek();
/**
* Returns (but does not remove) the element at the front end of the deque. Returns null if deque is empty.
* @return Element at front, if one exists; null otherwise
*/
public E peekFront();
/**
* Returns (but does not remove) the element at the back end of the deque. Returns null if deque is empty.
* @return Element at back, if one exists; null otherwise
*/
public E peekBack();
}
package edu.caltech.cs2.interfaces;
public interface IDictionary<K, V> extends Iterable<K> {
public static class Entry<K, V> {
public final K key;
public V value;
public Entry(K key) {
this(key, null);
}
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
}
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*/
public V get(K key);
/**
* Removes the mapping for the specified key from this map if present.
*
* @param key key whose mapping is to be removed from the map
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}.
*/
public V remove(K key);
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}.
*/
public V put(K key, V value);
/**
* Returns {@code true} if this map contains a mapping for the
* specified key.
*
* @param key The key whose presence in this map is to be tested
* @return {@code true} if this map contains a mapping for the specified
* key.
*/
public boolean containsKey(K key);
/**
* Returns {@code true} if this map maps one or more keys to the
* specified value.
*
* @param value value whose presence in this map is to be tested
* @return {@code true} if this map maps one or more keys to the
* specified value
*/
public boolean containsValue(V value);
/**
* Returns the number of key-value mappings in this map.
*
* @return the number of key-value mappings in this map
*/
public int size();
/**
* Returns {@code true} if this map contains no key-value mappings.
*
* @return {@code true} if this map contains no key-value mappings
*/
default public boolean isEmpty() {
return this.size() == 0;
}
/**
* Returns a {@link ICollection} of the keys contained in this map.
*
* @return a collection of the keys contained in this map
*/
public ICollection<K> keys();
/**
* Returns a {@link ICollection} of the values contained in this map
* which does not contain duplicates.
*
* @return a collection of the values contained in this map
*/
public ICollection<V> values();
default public ICollection<Entry<K, V>> entrySet() {
IDeque<Entry<K, V>> entries = new LinkedDeque<>();
for (K key : keySet()) {
entries.add(new Entry(key, this.get(key)));
}
return entries;
}
@SuppressWarnings("unchecked cast")
default ISet<K> keySet() {
return ISet.getBackingSet((IDictionary<K, Object>)this);
}
}
package edu.caltech.cs2.interfaces;
/**
* This interface represents a fixed-size queue - a queue with a constant and finite capacity.
* @param <E> Element type
*/
public interface IFixedSizeQueue<E> extends IQueue<E> {
/**
* Checks whether the fixed-size queue is full.
* @return True if full, false otherwise
*/
public boolean isFull();
/**
* Calculates the maximum capacity of the queue.
* @return Maximum capacity of the queue
*/
public int capacity();
}
\ No newline at end of file
package edu.caltech.cs2.interfaces;
public abstract class IGraph<V, E> {
/**
* Add a vertex to the graph.
* @param vertex The vertex to add
* @return true if vertex was not present already.
*/
public abstract boolean addVertex(V vertex);
/**
* Adds edge e to the graph.
*
* @param e The edge to add.
* @throws IllegalArgumentException
* If e is not a valid edge (eg. refers to vertices not in the graph).
* @return true If e was not already present; false if it was (in which case the graph is still updated).
*/
public abstract boolean addEdge(V src, V dest, E e);
/**
* Adds edge e to the graph in both directionns.
*
* @param e The edge to add.
* @throws IllegalArgumentException
* If e is not a valid edge (eg. refers to vertices not in the graph).
* @return true If e was not already present in either direction; false if it was (in which case the graph is still updated).
*/
public abstract boolean addUndirectedEdge(V src, V dest, E e);
/**
* Remove an edge from src to dest from the graph.
*
* @throws IllegalArgumentException if src or dest is not in the graph.
* @return true if an edge from src to dest edge was present.
*/
public abstract boolean removeEdge(V src, V dest);
/**
* Returns the set of vertices in the graph.
* @return The set of all vertices in the graph.
*/
public abstract ISet<V> vertices();
/**
* Tests if vertices i and j are adjacent, returning the edge between
* them if so.
*
* @throws IllegalArgumentException if i or j are not vertices in the graph.
* @return The edge from i to j if it exists in the graph;
* null otherwise.
*/
public abstract E adjacent(V i, V j);
/**
* Return the neighbours of a given vertex when this graph is treated as
* DIRECTED; that is, vertices to which vertex has an outgoing edge.
*
* @param vertex The vertex the neighbours of which to return.
* @throws IllegalArgumentException if vertex is not in the graph.
* @return The set of neighbors of vertex.
*/
public abstract ISet<V> neighbors(V vertex);
}
\ No newline at end of file
package edu.caltech.cs2.interfaces;
/**
* This interface represents a Priority Queue - a data structure that is very similar to a queue but
* stores values in ascending order.
* @param <E> Element type
*/
public interface IPriorityQueue<E> extends IQueue<IPriorityQueue.PQElement<E>> {
public static class PQElement<E> {
public final E data;
public final double priority;
public PQElement(E data, double priority) {
this.data = data;
this.priority = priority;
}
@Override
public int hashCode() {
return this.data.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof PQElement)) {
return false;
}
return this.data.equals(((PQElement)o).data);
}
@Override
public String toString() {
return "(" + this.data + ", " + this.priority + ")";
}
}
/**
* Increase the priority of the key at idx
* @param key - the new key with the new priority
*/
public void increaseKey(PQElement<E> key);
/**
* Decrease the priority of the key at idx
* @param key - the new key with the new priority
*/
public void decreaseKey(PQElement<E> key);
}
\ No newline at end of file
package edu.caltech.cs2.interfaces;
/**
* This interface represents a queue - a data structure that can add elements at one end and remove them from the other.
* @param <E> Element type
*/
public interface IQueue<E> extends Iterable<E> {
/**
* Adds an element to the back of the queue.
* @param e Element to add
* @return True if successful, false otherwise (i.e. fixed data structure is full)
*/
public boolean enqueue(E e);
/**
* Removes and returns the element at the front of the queue. Returns null if queue is empty.
* @return Element at front of queue, if one exists; null otherwise
*/
public E dequeue();
/**
* Returns (but does not remove) the element at the front of the queue. Returns null if queue is empty.
* @return Element at front of queue, if one exists; null otherwise
*/
public E peek();
/**
* Calculates the size of the queue.
* @return Number of elements in the queue
*/
public int size();
}
package edu.caltech.cs2.interfaces;
import java.util.Iterator;
public class ISet<E> implements Iterable<E> {
private IDictionary<E, Object> internalDictionary;
protected ISet(IDictionary<E, Object> dict) {
this.internalDictionary = dict;
}
public static <E> ISet<E> getBackingSet(IDictionary<E, Object> dict) {
return new ISet<E>(dict);
}
/**
* Returns the number of elements in this set (its cardinality). If this
* set contains more than {@code Integer.MAX_VALUE} elements, returns
* {@code Integer.MAX_VALUE}.
*
* @return the number of elements in this set (its cardinality)
*/
public int size() {
return this.internalDictionary.size();
}
/**
* Returns {@code true} if this set contains no elements.
*
* @return {@code true} if this set contains no elements
*/
public boolean isEmpty() {
return this.internalDictionary.isEmpty();
}
/**
* Returns {@code true} if this set contains the specified element.
* More formally, returns {@code true} if and only if this set
* contains an element {@code e} such that
* {@code Objects.equals(o, e)}.
*
* @param e element whose presence in this set is to be tested
* @return {@code true} if this set contains the specified element
*/
public boolean contains(E e) {
return this.internalDictionary.containsKey(e);
}
/**
* Returns an iterator over the elements in this set. The elements are
* returned in no particular order (unless this set is an instance of some
* class that provides a guarantee).
*
* @return an iterator over the elements in this set
*/
@Override
public Iterator<E> iterator() {
return this.internalDictionary.iterator();
}
// Modification Operations
/**
* Adds the specified element to this set if it is not already present
* (optional operation). More formally, adds the specified element
* {@code e} to this set if the set contains no element {@code e2}
* such that
* {@code Objects.equals(e, e2)}.
* If this set already contains the element, the call leaves the set
* unchanged and returns {@code false}. In combination with the
* restriction on constructors, this ensures that sets never contain
* duplicate elements.
*
* @param e element to be added to this set
* @return {@code true} if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return this.internalDictionary.put(e, new Object()) != null;
}
/**
* Removes the specified element from this set if it is present
* (optional operation). More formally, removes an element {@code e}
* such that
* {@code Objects.equals(o, e)}, if
* this set contains such an element. Returns {@code true} if this set
* contained the element (or equivalently, if this set changed as a
* result of the call). (This set will not contain the element once the
* call returns.)
*
* @param e object to be removed from this set, if present
* @return {@code true} if this set contained the specified element
*/
public boolean remove(E e) {
return this.internalDictionary.remove(e) != null;
}
/**
* Removes all of the elements from this set (optional operation).
* The set will be empty after this call returns.
*
* @throws UnsupportedOperationException if the {@code clear} method
* is not supported by this set
*/
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
for (E e : this) {
b.append(e + ", ");
}
if (b.length() > 0) {
b.delete(b.length() - 2, b.length());
}
return "[" + b.toString() + "]";
}
}
package edu.caltech.cs2.interfaces;
/**
* This interface represents a stack - a data structure that can add elements remove elements from a single end.
* @param <E> Element type
*/
public interface IStack<E> extends Iterable<E> {
/**
* Adds an element to the top of the stack.
* @param e Element to add
* @return True if successful, false otherwise (i.e. fixed data structure is full)
*/
public boolean push(E e);
/**
* Removes and returns the element at the top of the stack. Returns null if stack is empty.
* @return Element at top of the stack, if one exists; null otherwise
*/
public E pop();
/**
* Returns (but does not remove) the element at the top of the stack. Returns null if stack is empty.
* @return Element at top of the stack, if one exists; null otherwise
*/
public E peek();
/**
* Calculates the size of the stack.
* @return Number of elements in the stack
*/
public int size();
}
package edu.caltech.cs2.interfaces;
public interface ITrieMap<A, K extends Iterable<A>, V> extends IDictionary<K, V> {
/**
* Determines whether the given key is a prefix of a key in the trie.
* @param key the prefix to search for in the trie
* @return true if the trie has a key starting with the given key, false otherwise
*/
public boolean isPrefix(K key);
/**
* Gets the collection of values whose keys start with the given prefix.
* @param prefix the prefix to search for in the trie
* @return the values corresponding to the keys starting with the given prefix
*/
public IDeque<V> getCompletions(K prefix);
/**
* Removes all elements from the trie.
*/
public void clear();
}
package edu.caltech.cs2.project08;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import edu.caltech.cs2.datastructures.BeaverMapsGraph;
import edu.caltech.cs2.datastructures.Location;
import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.ISet;
import java.awt.*;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Stream;
public class BeaverMaps {
public static final int PORT = 8001;
public static final int NUMBER_OF_OPTIONS = 15;
private static BeaverMapsGraph graph;
private static MapsAutoCompleter COMPLETER;
static class LocalFile implements HttpHandler {
private String name;
public LocalFile(String name) {
this.name = name;
}
@Override
public void handle(HttpExchange t) throws IOException {
String response = new String(Files.readAllBytes(Paths.get(this.name)), StandardCharsets.UTF_8);
t.sendResponseHeaders(200, 0);
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
graph = new BeaverMapsGraph("data/pasadena/pasadena.buildings.json",
"data/pasadena/pasadena.waypoints.json",
"data/pasadena/pasadena.roads.json");
long end = System.currentTimeMillis();
System.out.println("Reading data took " + (end - start) + " millis.");
System.out.println("Populating autocomplete");
COMPLETER.populateLocations(graph.getBuildings());
System.out.println("Done populating autocomplete");
HttpServer server = HttpServer.create(new InetSocketAddress(PORT), 0);
server.createContext("/", new LocalFile("map.html"));
server.createContext("/css/map.css", new LocalFile("css/map.css"));
server.createContext("/js/map.js", new LocalFile("js/map.js"));
server.createContext("/js/autocomplete.js", new LocalFile("js/autocomplete.js"));
server.createContext("/js/typeahead.js", new LocalFile("js/typeahead.js"));
server.createContext("/byname", new AllWithNameSearch());
server.createContext("/nearby", new LocationSearch());
server.createContext("/pathfinder", new PathFinder());
server.createContext("/autocomplete", new Autocomplete());
server.createContext("/nearest", new NearestSearch());
server.setExecutor(null); // creates a default executor
server.start();
System.out.println("Server started!");
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().browse(new URI("http://localhost:" + PORT + "/"));
}
}
static class PathFinder implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
Optional<String> start = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("start=")).map(x -> x.split("=")[1]).findAny();
Optional<String> start_id = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("start-id=")).map(x -> x.split("=")[1]).findAny();
Optional<String> end = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("end=")).map(x -> x.split("=")[1]).findAny();
Optional<String> end_id = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("end-id=")).map(x -> x.split("=")[1]).findAny();
String response = "[]";
if (start.isPresent() && end.isPresent()) {
String startL = start.get();
String endL = end.get();
String startID = start_id.orElse(null);
String endID = end_id.orElse(null);
Location startLocation = startID != null ?
graph.getLocationByID(Long.parseLong(startID)) :
graph.getLocationByName(startL).peek();
Location endLocation = endID != null ?
graph.getLocationByID(Long.parseLong(endID)) :
graph.getLocationByName(endL).peek();
if (startLocation != null && endLocation != null) {
IDeque<Location> locs = graph.dijkstra(startLocation, endLocation);
IDeque<String> path = new LinkedDeque<>();
int i = 0;
for (Location loc : locs) {
String locStr = loc.lat + "::" + loc.lon + "::" + ("" + i + ":" + (loc.name != null ? " " + loc.name : "") + "::" + loc.id);
path.add(locStr);
i++;
}
response = path.toString();
}
}
t.sendResponseHeaders(200, 0);
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
static class NearestSearch implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
Optional<String> lat = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("lat=")).map(x -> x.split("=")[1]).findAny();
Optional<String> lon = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("lon=")).map(x -> x.split("=")[1]).findAny();
String response = "";
if (lat.isPresent() && lon.isPresent()) {
String latS = lat.get();
String lonS = lon.get();
Location l = graph.getClosestBuilding(Double.parseDouble(latS), Double.parseDouble(lonS));
if (l != null) {
response = "{\"id\": " + l.id + ", \"name\": \"" + l.name + "\"" + ", \"lat\":" + l.lat + ", \"lon\": " + l.lon + "}";
}
}
t.sendResponseHeaders(200, 0);
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
static class AllWithNameSearch implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
Optional<String> query = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("query=")).map(x -> x.split("=")[1]).findAny();
String response = "[]";
if (query.isPresent()) {
String location = query.get();
IDeque<Location> locs = graph.getLocationByName(location);
if (!locs.isEmpty()) {
IDeque<String> locList = new LinkedDeque<>();
for (Location loc : locs) {
if (loc.type == Location.Type.BUILDING) {
String locStr = loc.lat + "::" + loc.lon + "::" + loc.name + "::" + loc.id;
locList.add(locStr);
}
}
response = locList.toString();
}
}
t.sendResponseHeaders(200, 0);
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
static class LocationSearch implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
Optional<String> name = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("name=")).map(x -> x.split("=")[1]).findAny();
Optional<String> id = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("id=")).map(x -> x.split("=")[1]).findAny();
Optional<String> distance = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("distance=")).map(x -> x.split("=")[1]).findAny();
String response = "[]";
if (name.isPresent()) {
String nameS = name.get();
String idS = id.orElse(null);
Location l = idS != null ?
graph.getLocationByID(Long.parseLong(idS)) :
graph.getLocationByName(nameS).peek();
if (l != null) {
IDeque<String> locList = new LinkedDeque<>();
double dist = Double.parseDouble(distance.orElse("200"));
ISet<Location> closeLocs = graph.dfs(l, dist * 2);
closeLocs.remove(l);
for (Location loc : closeLocs) {
if (l.getDistance(loc) < dist && loc.type == Location.Type.BUILDING) {
String locStr = loc.lat + "::" + loc.lon + "::" + loc.name + "::" + loc.id;
locList.add(locStr);
}
}
locList.addFront(l.lat + "::" + l.lon + "::" + l.name);
response = locList.toString();
}
}
t.sendResponseHeaders(200, 0);
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
static class Autocomplete implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
Optional<String> query = Stream.of(t.getRequestURI().getQuery().split("\\&")).filter(x -> x.startsWith("query=")).map(x -> x.split("=")[1]).findAny();
String response = "[]";
if (query.isPresent()) {
long before = System.currentTimeMillis();
IDeque<Long> options = COMPLETER.complete(query.get());
String[] opts = new String[options.size()];
Long[] ids = new Long[options.size()];
for (int i = 0; i < opts.length; i++) {
long id = options.removeFront();
ids[i] = id;
opts[i] = graph.getLocationByID(id).displayString();
}
long after = System.currentTimeMillis();
StringBuilder result = new StringBuilder();
for (int i = 0; i < opts.length && i < NUMBER_OF_OPTIONS; i++) {
result.append("{\"value\": \"" + opts[i].replace("\"", "\\\"") + "\", \"id\": " + ids[i] + "}, ");
}
System.out.println("\"" + query.get() + "\" took " + (after - before) + " milliseconds!");
if (result.length() > 0) {
response = result.toString();
response = "[" + response.substring(0, response.length() - 2) + "]";
}
}
System.out.println(response);
t.sendResponseHeaders(200, 0);
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
}
package edu.caltech.cs2.project08;
import edu.caltech.cs2.datastructures.Location;
import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.ISet;
import edu.caltech.cs2.interfaces.ITrieMap;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class MapsAutoCompleter {
private static ITrieMap<String, IDeque<String>, IDeque<Location>> locs = new TrieMap<>((IDeque<String> s) -> s);
public static void populateLocations(ISet<Location> locations) {
locs.clear();
for (Location l : locations) {
if (l.name == null) {
continue;
}
String[] words = l.name.toLowerCase().split(" ");
char[] charArr = l.name.toLowerCase().toCharArray();
String[] strCharArr = new String[charArr.length];
for (int i = 0; i < charArr.length; i++) {
strCharArr[i] = Character.toString(charArr[i]);
}
for (int i = 1; i <= l.name.length(); i++) {
String[] subarr = Arrays.copyOfRange(strCharArr, 0, i);
IDeque<String> iterableSubarr = listFromArray(subarr);
if (!locs.containsKey(iterableSubarr)) {
locs.put(iterableSubarr, new LinkedDeque<>());
}
locs.get(iterableSubarr).add(l);
}
for (int i = 0; i < words.length; i++) {
String[] subarr = Arrays.copyOfRange(words, i, words.length);
IDeque<String> iterableSubarr = listFromArray(subarr);
if (!locs.containsKey(iterableSubarr)) {
locs.put(iterableSubarr, new LinkedDeque<>());
}
locs.get(iterableSubarr).add(l);
}
if (l.address == null) {
continue;
}
words = l.address.toLowerCase().split(" ");
for (int i = 0; i < words.length; i++) {
String[] subarr = Arrays.copyOfRange(words, i, words.length);
IDeque<String> iterableSubarr = listFromArray(subarr);
if (!locs.containsKey(iterableSubarr)) {
locs.put(iterableSubarr, new LinkedDeque<>());
}
locs.get(iterableSubarr).add(l);
}
}
}
private static IDeque<String> listFromArray(String[] arr) {
IDeque<String> lst = new LinkedDeque<>();
for (String s: arr) {
lst.addBack(s);
}
return lst;
}
public static IDeque<String> charArrToStringIterable(char[] ca) {
IDeque<String> d = new LinkedDeque<>();
for (int i = 0; i < ca.length; i++) {
d.addBack(Character.toString(ca[i]));
}
return d;
}
public static IDeque<Long> complete(String term) {
String[] keyPath = term.strip().toLowerCase().split("\\s");
IDeque<String> kpIterable = listFromArray(keyPath);
IDeque<IDeque<Location>> options = locs.getCompletions(kpIterable);
options.addAll(locs.getCompletions(charArrToStringIterable(term.toLowerCase().toCharArray())));
Set<Long> opts = new HashSet<>();
for (IDeque<Location> val : options) {
for (Location o : val) {
opts.add(o.id);
}
}
IDeque<Long> completions = new LinkedDeque<>();
for (Long o : opts) {
completions.add(o);
}
return completions;
}
}
package edu.caltech.cs2.helpers;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ConstructorDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.Statement;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.fail;
public class Inspection {
private static String getUsageOf(List<String> regexps, List<? extends Node> codeObjects) {
for (Node d : codeObjects) {
for (String regex : regexps) {
if (d.toString().replaceAll("\\R", "").matches(".*" + regex + ".*")) {
return regex;
}
}
}
return null;
}
public static void assertNoImportsOf(String filePath, List<String> regexps) {
try {
CompilationUnit cu = JavaParser.parse(new File(filePath));
String usage = getUsageOf(regexps, cu.getImports());
if (usage != null) {
fail("You may not import " + usage + " in " + Paths.get(filePath).getFileName() + ".");
}
} catch (FileNotFoundException e) {
fail("Missing Java file: " + Paths.get(filePath).getFileName());
}
}
private static class ConstructorCollector extends VoidVisitorAdapter<List<ConstructorDeclaration>> {
@Override
public void visit(ConstructorDeclaration md, List<ConstructorDeclaration> collector) {
super.visit(md, collector);
collector.add(md);
}
}
private static class MethodCollector extends VoidVisitorAdapter<List<MethodDeclaration>> {
@Override
public void visit(MethodDeclaration md, List<MethodDeclaration> collector) {
super.visit(md, collector);
collector.add(md);
}
}
private static MethodCollector METHOD_COLLECTOR = new MethodCollector();
private static ConstructorCollector CONSTRUCTOR_COLLECTOR = new ConstructorCollector();
public static void assertNoUsageOf(String filePath, List<String> regexps) {
try {
CompilationUnit cu = JavaParser.parse(new File(filePath));
List<ConstructorDeclaration> constructors = new ArrayList<>();
CONSTRUCTOR_COLLECTOR.visit(cu, constructors);
String usage = getUsageOf(regexps, constructors);
if (usage != null) {
fail("You may not use " + usage + " in " + Paths.get(filePath).getFileName() + ".");
}
List<MethodDeclaration> methods = new ArrayList<>();
METHOD_COLLECTOR.visit(cu, methods);
usage = getUsageOf(regexps, methods);
if (usage != null) {
fail("You may not use " + usage + " in " + Paths.get(filePath).getFileName() + ".");
}
} catch (FileNotFoundException e) {
fail("Missing Java file: " + Paths.get(filePath).getFileName());
}
}
public static void assertConstructorHygiene(String filePath) {
try {
CompilationUnit cu = JavaParser.parse(new File(filePath));
List<ConstructorDeclaration> constructors = new ArrayList<>();
CONSTRUCTOR_COLLECTOR.visit(cu, constructors);
int nonThisConstructors = 0;
for (ConstructorDeclaration c : constructors) {
BlockStmt body = c.getBody();
List<Statement> statements = body.getStatements();
if (statements.size() != 1) {
nonThisConstructors++;
}
else if (!statements.get(0).toString().startsWith("this(")) {
nonThisConstructors++;
}
if (nonThisConstructors > 1) {
fail("All but one of your constructors must use the this(...) notation.");
}
}
} catch (FileNotFoundException e) {
fail("Missing Java file: " + Paths.get(filePath).getFileName());
}
}
}
package edu.caltech.cs2.helpers;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import static org.junit.jupiter.api.Assertions.fail;
public class Reflection {
public static <T> T getFieldValue(Class clazz, String name, Object o) {
T result = null;
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return (T) field.get(o);
} catch (NoSuchFieldException | IllegalAccessException e) {
fail("Could not find field " + name + " in class " + clazz.getName());
return null;
}
}
public static Method getMethod(Class clazz, String name, Class<?>... params) {
Method method = null;
try {
method = clazz.getDeclaredMethod(name, params);
method.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
fail("Could not find method " + name + " in class " + clazz.getName());
return null;
}
return method;
}
public static Constructor getConstructor(Class clazz, Class<?>... params) {
Constructor c = null;
try {
c = clazz.getDeclaredConstructor(params);
c.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
fail("Could not find constructor " + clazz.getName() + "(" + String.join(", ", (String[])Stream.of(params).map(x -> x.getName()).collect(Collectors.toList()).toArray()) + ")" + " in class " + clazz.getName());
return null;
}
return c;
}
public static <T> T invoke(Method m, Object... args) {
T result = null;
try {
result = (T) m.invoke(args[0], Arrays.copyOfRange(args, 1, args.length));
} catch (IllegalAccessException | InvocationTargetException e) {
fail(e.getCause());
}
return result;
}
public static <T> T invokeStatic(Method m, Object... args) {
T result = null;
try {
result = (T) m.invoke(null, args);
} catch (IllegalAccessException | InvocationTargetException e) {
fail(e.getCause());
}
return result;
}
public static <T> T newInstance(Constructor c, Object... args) {
T result = null;
try {
result = (T) c.newInstance(args);
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
fail(e.getCause());
}
return result;
}
public static Stream<Field> getFields(Class clazz) {
return Stream.of(clazz.getDeclaredFields());
}
private static int stringToIntModifier(String modifier) {
switch (modifier.toLowerCase()) {
case "private": return Modifier.PRIVATE;
case "public": return Modifier.PUBLIC;
case "protected": return Modifier.PROTECTED;
case "static": return Modifier.STATIC;
case "final": return Modifier.FINAL;
default: fail("Unknown modifier test.");
}
/* Should never reach here... */
return -1;
}
public static Predicate<Member> hasModifier(String modifier) {
return (Member f) -> (f.getModifiers() & stringToIntModifier(modifier)) != 0;
}
public static Predicate<Member> doesNotHaveModifier(String modifier) {
return (Member f) -> (f.getModifiers() & stringToIntModifier(modifier)) == 0;
}
public static Predicate<Field> hasType(Class clazz) {
return (Field f) -> f.getType().equals(clazz);
}
public static Predicate<Field> doesNotHaveType(Class clazz) {
return (Field f) -> !f.getType().equals(clazz);
}
public static void assertFieldsLessThan(Class clazz, Class FieldType, int x) {
assertFieldsLessThan(clazz, null, FieldType, x );
}
public static void assertFieldsLessThan(Class clazz, String modifier, int x) {
assertFieldsLessThan(clazz, modifier, null, x);
}
public static void assertFieldsLessThan(Class clazz, Stream<Field> fields, int x) {
assertFieldsLessThan(clazz, fields, null, null, x );
}
public static void assertFieldsLessThan(Class clazz, String modifier, Class FieldType, int x) {
assertFieldsLessThan(clazz, getFields(clazz), modifier, FieldType, x);
}
public static void assertFieldsLessThan(Class clazz, Stream<Field> fields, String modifier, Class FieldType, int x) {
if (modifier != null) {
fields = fields.filter(hasModifier(modifier)).filter(doesNotHaveModifier("static"));
}
if (FieldType != null) {
fields = fields.filter(hasType(FieldType));
}
if (fields.count() >= x) {
fail(clazz.getName() + " has too many fields" +
(modifier != null ? " with modifier " + modifier : "") + "" +
(FieldType != null ? " of type " + FieldType.getName() : "")
);
}
}
public static void assertFieldsGreaterThan(Class clazz, Class FieldType, int x) {
assertFieldsGreaterThan(clazz, null, FieldType, x );
}
public static void assertFieldsGreaterThan(Class clazz, String modifier, int x) {
assertFieldsGreaterThan(clazz, modifier, null, x);
}
public static void assertFieldsGreaterThan(Class clazz, Stream<Field> fields, int x) {
assertFieldsGreaterThan(clazz, fields, null, null, x );
}
public static void assertFieldsGreaterThan(Class clazz, String modifier, Class FieldType, int x) {
assertFieldsGreaterThan(clazz, getFields(clazz), modifier, FieldType, x);
}
public static void assertFieldsGreaterThan(Class clazz, Stream<Field> fields, String modifier, Class FieldType, int x) {
if (modifier != null) {
fields = fields.filter(hasModifier(modifier));
}
if (FieldType != null) {
fields = fields.filter(hasType(FieldType));
}
if (fields.count() <= x) {
fail(clazz.getName() + " has too few fields" +
(modifier != null ? " with modifier " + modifier : "") + " " +
(FieldType != null ? " of type " + FieldType.getName() : "")
);
}
}
public static void assertFieldsEqualTo(Class clazz, Class FieldType, int x) {
assertFieldsEqualTo(clazz, null, FieldType, x );
}
public static void assertFieldsEqualTo(Class clazz, String modifier, int x) {
assertFieldsEqualTo(clazz, modifier, null, x );
}
public static void assertFieldsEqualTo(Class clazz, Stream<Field> fields, int x) {
assertFieldsEqualTo(clazz, fields, null, null, x );
}
public static void assertFieldsEqualTo(Class clazz, String modifier, Class FieldType, int x) {
assertFieldsEqualTo(clazz, getFields(clazz), modifier, FieldType, x);
}
public static void assertFieldsEqualTo(Class clazz, Stream<Field> fields, String modifier, Class FieldType, int x) {
if (modifier != null) {
fields = fields.filter(hasModifier(modifier));
}
if (FieldType != null) {
fields = fields.filter(hasType(FieldType));
}
if (fields.count() != x) {
fail(clazz.getName() + " has the wrong number of fields" +
(modifier != null ? " with modifier " + modifier : "") + " " +
(FieldType != null ? "of type " + FieldType.getName() : "")
);
}
}
public static void assertNoPublicFields(Class clazz) {
assertFieldsEqualTo(clazz, getFields(clazz).filter(doesNotHaveModifier("static")),
"public", null, 0);
}
public static Field getFieldByName(Class clazz, String name) {
try {
return clazz.getDeclaredField(name);
} catch (NoSuchFieldException e) {
fail(clazz.getName() + " should have a field named '" + name + "', but does not.");
// Should not reach here!
return null;
}
}
public static Field getNonStaticFieldByType(Class clazz, Class FieldType) {
Stream<Field> fields = getFields(clazz).filter(hasType(FieldType)).filter(doesNotHaveModifier("static"));
List<Field> fieldsList = fields.collect(Collectors.toList());
if (fieldsList.isEmpty()) {
fail(clazz.getName() +
" should have a field with the type '" + FieldType.getName() +
"', but does not."
);
// Should not reach here!
return null;
}
if (fieldsList.size() > 1) {
fail(clazz.getName() +
" should only have one field with the type '" +
FieldType.getName() +
"', but has more."
);
// Should not reach here
return null;
}
return fieldsList.get(0);
}
public static Field getFieldByType(Class clazz, Class FieldType) {
Stream<Field> fields = getFields(clazz).filter(hasType(FieldType));
List<Field> fieldsList = fields.collect(Collectors.toList());
if (fieldsList.isEmpty()) {
fail(clazz.getName() +
" should have a field with the type '" + FieldType.getName() +
"', but does not."
);
// Should not reach here!
return null;
}
if (fieldsList.size() > 1) {
fail(clazz.getName() +
" should only have one field with the type '" +
FieldType.getName() +
"', but has more."
);
// Should not reach here
return null;
}
return fieldsList.get(0);
}
public static Field getFieldByModifiers(Class clazz, String modifier) {
return getFieldByModifiers(clazz, List.of(modifier));
}
public static Field getFieldByModifiers(Class clazz, List<String> modifiers) {
Stream<Field> fields = getFields(clazz);
for (String m : modifiers) {
fields = fields.filter(hasModifier(m));
}
List<Field> fieldsList = fields.collect(Collectors.toList());
if (fieldsList.isEmpty()) {
fail(clazz.getName() +
" should have a field with the modifiers '" +
String.join(", ", modifiers) +
"', but does not."
);
// Should not reach here!
return null;
}
if (fieldsList.size() > 1) {
fail(clazz.getName() +
" should only have one field with the modifiers '" +
String.join(", ", modifiers) +
"', but has more."
);
// Should not reach here
return null;
}
return fieldsList.get(0);
}
public static void checkFieldModifiers(Field f, String modifier) {
checkFieldModifiers(f, List.of(modifier));
}
public static void checkFieldModifiers(Field f, List<String> modifiers) {
if (!modifiers.stream().allMatch(m -> hasModifier(m).test(f))) {
fail(f.getName() + " is missing at least one of the modifiers: " + String.join(", ", modifiers));
}
}
public static void assertPublicInterface(Class clazz, List<String> methods) {
SortedSet<String> expected = new TreeSet<>(methods);
SortedSet<String> actual = new TreeSet<>(Stream.of(clazz.getDeclaredMethods())
.filter(hasModifier("public"))
.map(x -> x.getName())
.collect(Collectors.toList()));
if (!expected.equals(actual)) {
String diff = "expected: " + expected + "\nactual: " + actual;
fail("The public interface of " + clazz.getName() + " has incorrect functionality.\n" + diff);
}
}
public static void assertMethodCorrectlyOverridden(Class clazz, String method, Class<?>... params) {
Method studentc = getMethod(clazz, method, params);
Method superc = getMethod(clazz.getSuperclass(), method, params);
if (!studentc.getReturnType().equals(superc.getReturnType())) {
fail("You should be overriding the " + method + "method, but your signature wasn't correct.");
}
}
}
package edu.caltech.cs2.helpers;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import static org.junit.jupiter.api.Assertions.fail;
public class RuntimeInstrumentation {
private static final int SKIP = 8;
private static final int ITERATIONS = 100;
public enum ComplexityType {
CONSTANT(0, "constant"),
LOGARITHMIC(1, "logarithmic"),
LINEAR(2, "linear"),
QUADRATIC(3, "quadratic"),
WORSE(4, "worse than quadratic");
private final String name;
private int size;
ComplexityType(int size, String name) {
this.size = size;
this.name = name;
}
public String toString() {
return this.name;
}
public boolean isSlowerThan(ComplexityType other) {
return this.size > other.size;
}
}
public static <DS> long timeFunction(DS ds, Consumer<DS> function) {
long startTime = System.nanoTime();
function.accept(ds);
long endTime = System.nanoTime();
return endTime - startTime;
}
public static <DS> ComplexityType getEmpiricalComplexity(Function<Integer, DS> provideDSOfSize, Consumer<DS> functionToTest, int numberOfDoubles) {
List<Long> times = new ArrayList<>();
int maxSize = (1 << (numberOfDoubles + SKIP));
for (int currentSize = 1; currentSize < maxSize; currentSize *= 2) {
long totalTime = 0;
DS ds = provideDSOfSize.apply(maxSize);
timeFunction(ds, functionToTest);
for (int i = 0; i < ITERATIONS; i++) {
ds = provideDSOfSize.apply(currentSize);
// Bring ds into cache! Make sure we're only clocking
// the function, and not JVM operations on the heap / cache
timeFunction(ds, functionToTest);
totalTime += timeFunction(ds, functionToTest);
}
times.add(Math.round((double)totalTime / ITERATIONS));
}
for (int i = 0; i < SKIP; i++) {
times.remove(0);
}
if (isApproximately(ComplexityType.CONSTANT, times)) {
return ComplexityType.CONSTANT;
}
else if (isApproximately(ComplexityType.LOGARITHMIC, times)) {
return ComplexityType.LOGARITHMIC;
}
else if (isApproximately(ComplexityType.LINEAR, times)) {
return ComplexityType.LINEAR;
}
else if (isApproximately(ComplexityType.QUADRATIC, times)) {
return ComplexityType.QUADRATIC;
}
else {
return ComplexityType.WORSE;
}
}
private static Set<Integer> findMaxes(List<Long> times) {
int maxIdx = 0;
int sndIdx = -1;
for (int i = 0; i < times.size(); i++) {
if (times.get(i) > times.get(maxIdx)) {
sndIdx = maxIdx;
maxIdx = i;
}
}
return Set.of(maxIdx, sndIdx);
}
private static boolean isApproximately(ComplexityType type, List<Long> times) {
List<Double> y = new ArrayList<>();
List<Double> x = new ArrayList<>();
Set<Integer> skips = findMaxes(times);
for (int i = 0; i < times.size(); i++) {
if (skips.contains(i)) {
continue;
}
int numElements = (1 << (i + SKIP));
x.add((double) numElements);
double d = 0.0;
switch (type) {
case CONSTANT:
d = times.get(i);
break;
case LOGARITHMIC:
d = times.get(i) / (Math.log10(numElements) / Math.log10(2));
break;
case LINEAR:
d = ((double)times.get(i)) / numElements;
break;
case QUADRATIC:
d = ((double)times.get(i)) / (numElements * numElements);
break;
default:
throw new RuntimeException("unimplemented isApproximately for " + type);
}
y.add(d);
}
// Store sums
double sumX = 0;
double sumY = 0;
for (int i = 0; i < x.size(); i ++) {
sumX += x.get(i);
sumY += y.get(i);
}
// Calc standard deviation of numElements
double std = 0;
for (int i = 0; i < x.size(); i ++) {
std += (Math.pow(x.get(i) - sumX / x.size(), 2));
}
std /= times.size();
double slope;
double inter;
// If constant, no slope - best fit is mean of times
if (type == ComplexityType.CONSTANT) {
slope = 0;
inter = sumY / y.size();
}
// Otherwise, do least squares regression to find the best
// linear fit for the transformed times
else {
double cov = 0;
for (int i = 0; i < y.size(); i ++) {
cov += (x.get(i) - sumX / y.size()) * (y.get(i) - sumY / y.size());
}
cov /= y.size() - 1;
slope = cov / std;
inter = sumY / y.size() - slope * sumX / y.size();
}
// Calculate mean squared error
double mse = 0;
for (int i = 0; i < y.size(); i ++) {
mse += Math.pow(y.get(i) - inter + slope * x.get(i), 2);
}
// Use R^2 measure to check fit
double rSq = 1 - mse / std;
// Tune depending on strictness - 0.95 accounts for variations
// *Should* actually be like 0.99, but sometimes weird heap operations
// happen and make certain runs take longer
//System.out.println(type + ", " + y + ", MSE: " + mse + ", R^2: " + rSq);
return rSq >= 0.90;
}
public static <DS> void assertAtMost(String whatIsBeingTested, ComplexityType expected, Function<Integer, DS> provideDSOfSize, Consumer<DS> functionToTest, int numberOfDoubles) {
ComplexityType calculated = getEmpiricalComplexity(provideDSOfSize, functionToTest, numberOfDoubles);
if (calculated.isSlowerThan(expected)) {
fail(whatIsBeingTested + " is expected to be " + expected + " time or better. The actual calculated time is " + calculated + ".\nThis test is non-deterministic which means it might not always be correct. If you run it multiple times and it usually passes, that's probably fine.");
}
}
}
package edu.caltech.cs2.project08;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import edu.caltech.cs2.datastructures.BeaverMapsGraph;
import edu.caltech.cs2.datastructures.Location;
import edu.caltech.cs2.helpers.Inspection;
import edu.caltech.cs2.helpers.Reflection;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.ISet;
import org.hamcrest.MatcherAssert;
import org.hamcrest.collection.IsIterableContainingInAnyOrder;
import org.hamcrest.collection.IsIterableContainingInOrder;
import org.hamcrest.core.IsCollectionContaining;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.*;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BeavermapTests {
private static String BEAVERMAP_GRAPH_SOURCE = "src/edu/caltech/cs2/datastructures/BeaverMapsGraph.java";
private static JsonParser JSON_PARSER = new JsonParser();
private static JsonElement fromFile(String filename) {
try {
return JSON_PARSER.parse(
new FileReader(
new File(filename)
)
);
} catch (IOException e) {
return null;
}
}
@Tag("C")
@DisplayName("Does not use or import disallowed classes")
@Test
public void testForInvalidClasses() {
List<String> graphDisallow = List.of("java\\.util\\.(?!Iterator)", "java\\.lang\\.reflect");
Inspection.assertNoImportsOf(BEAVERMAP_GRAPH_SOURCE, graphDisallow);
Inspection.assertNoUsageOf(BEAVERMAP_GRAPH_SOURCE, graphDisallow);
}
// Only use Caltech map and buildings to test for correctness
@Tag("C")
@Test
public void testGetLocationByID() {
BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/caltech/caltech.buildings.json",
"data/caltech/caltech.waypoints.json",
"data/caltech/caltech.roads.json");
JsonElement bs = fromFile("data/caltech/caltech.buildings.json");
for (JsonElement b : bs.getAsJsonArray()) {
Location loc = new Location(b.getAsJsonObject());
assertNotNull(bmg.getLocationByID(loc.id), "Location id " + loc.id + " not found by id");
}
}
@Tag("C")
@Test
public void testGetLocationByName() {
BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/caltech/caltech.buildings.json",
"data/caltech/caltech.waypoints.json",
"data/caltech/caltech.roads.json");
JsonElement bs = fromFile("data/caltech/caltech.buildings.json");
for (JsonElement b : bs.getAsJsonArray()) {
Location loc = new Location(b.getAsJsonObject());
if (loc.name != null) {
assertNotNull(bmg.getLocationByName(loc.name), "Location " + loc.name + " not found by name");
}
}
}
@Tag("C")
@DisplayName("Test getBuildings()")
@ParameterizedTest(name = "Test getBuildings() on {0}")
@CsvSource({
"caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json",
"pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json",
})
public void testGetBuildings(String buildingsFile, String waypointsFile, String roadsFile) {
BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/" + buildingsFile, "data/" + waypointsFile, "data/" + roadsFile);
Set<Location> buildings = new HashSet<>();
JsonElement bs = fromFile("data/" + buildingsFile);
for (JsonElement b : bs.getAsJsonArray()) {
Location loc = new Location(b.getAsJsonObject());
buildings.add(loc);
}
MatcherAssert.assertThat(bmg.getBuildings(),
IsIterableContainingInAnyOrder.containsInAnyOrder(buildings.toArray()));
}
@Tag("C")
@DisplayName("Test getClosestBuilding()")
@ParameterizedTest(name = "Test getClosestBuilding() on {0}")
@CsvSource({
"caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.closest_trace.json",
"pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json, pasadena/pasadena.closest_trace.json",
})
public void testGetClosestLocation(String buildingsFile, String waypointsFile, String roadsFile, String traceFile) {
BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/" + buildingsFile, "data/" + waypointsFile, "data/" + roadsFile);
JsonElement bs = fromFile("data/" + traceFile);
for (JsonElement b : bs.getAsJsonArray()) {
JsonObject curr = b.getAsJsonObject();
Location center = bmg.getLocationByID(curr.get("center").getAsLong());
Location closestExpected = bmg.getLocationByID(curr.get("closest").getAsLong());
Location closestActual = bmg.getClosestBuilding(center.lat, center.lon);
assertEquals(closestExpected.lat, closestActual.lat, "Latitudes differ");
assertEquals(closestExpected.lon, closestActual.lon, "Longitudes differ");
}
}
@Tag("C")
@DisplayName("BeaverMapsGraph implements required public methods")
@Test
public void testMethodsBeaverMapsGraph() {
SortedSet<String> expected = new TreeSet<>(List.of(
"getLocationByName", "getBuildings", "getClosestBuilding", "dfs", "dijkstra", "addVertex"
));
SortedSet<String> actual = new TreeSet<>(
Stream.of(BeaverMapsGraph.class.getDeclaredMethods())
.filter(Reflection.hasModifier("public"))
.map(x -> x.getName())
.collect(Collectors.toList()));
MatcherAssert.assertThat(new ArrayList<>(actual),
IsCollectionContaining.hasItems((expected.toArray())));
}
// Note: Pasadena map is WAY TOO LARGE to test all edges, don't try
@Tag("C")
@DisplayName("Tests nodes and edges in map for filename")
@ParameterizedTest(name = "Test nodes in file {0}")
@CsvSource({
"caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.neighbors_trace.json"
})
public void testNodesEdgesInMap(String bFile, String wFile, String roadsFile, String traceFile) {
BeaverMapsGraph bmg = new BeaverMapsGraph("data/" + bFile, "data/" + wFile, "data/" + roadsFile);
List<Long> actualNodeIDs = new ArrayList<>();
for (long nid : bmg.vertices()) {
actualNodeIDs.add(nid);
}
JsonElement s = fromFile("data/" + traceFile);
for (JsonElement b : s.getAsJsonArray()) {
JsonObject curr = b.getAsJsonObject();
long locID = curr.get("id").getAsLong();
Location loc = bmg.getLocationByID(locID);
assertNotNull(loc, locID + " should be in graph, but is not");
actualNodeIDs.remove(locID);
JsonArray neighbors = curr.get("neighbors").getAsJsonArray();
ISet<Long> actualNeighbors = bmg.neighbors(locID);
List<Long> missingNeighbors = new ArrayList<>();
for (JsonElement e : neighbors) {
long neighborID = e.getAsLong();
if (!actualNeighbors.remove(neighborID)) {
missingNeighbors.add(neighborID);
}
}
// Use this instead of MatcherAssert to provide better errors (though I doubt they'll be needed)
if (missingNeighbors.size() > 0) {
fail(locID + " missing neighbors " + missingNeighbors);
} else if (actualNeighbors.size() != 0) {
fail(locID + " has extra neighbors " + actualNeighbors);
}
}
assertEquals(0, actualNodeIDs.size(), "Graph has extra nodes: " + actualNodeIDs);
}
@Tag("B")
@DisplayName("Test DFS radius search")
@ParameterizedTest(name = "Test DFS on graph {0}")
@CsvSource({
"caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.radius_trace.json",
"pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json, pasadena/pasadena.radius_trace.json",
})
public void testDFSRadius(String buildingsFile, String waypointsFile, String roadsFile, String traceFile) {
BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/" + buildingsFile, "data/" + waypointsFile, "data/" + roadsFile);
JsonElement s = fromFile("data/" + traceFile);
for (JsonElement b : s.getAsJsonArray()) {
JsonObject curr = b.getAsJsonObject();
long locID = curr.get("center").getAsLong();
Location loc = bmg.getLocationByID(locID);
double dist = curr.get("radius").getAsDouble();
JsonArray locList = curr.get("locations").getAsJsonArray();
Set<Long> expectedLocIDs = new HashSet<>();
for (JsonElement e : locList) {
expectedLocIDs.add(e.getAsLong());
}
ISet<Location> actualLoc = bmg.dfs(loc, dist);
Set<Long> locIDs = new HashSet<>(actualLoc.size());
for (Location l : actualLoc) {
locIDs.add(l.id);
}
MatcherAssert.assertThat(locIDs,
IsIterableContainingInAnyOrder.containsInAnyOrder(expectedLocIDs.toArray()));
}
}
@Tag("A")
@DisplayName("Test Dijkstra")
@ParameterizedTest(name = "Test Dijkstra on graph {0}")
@CsvSource({
"caltech/caltech.buildings.json, caltech/caltech.waypoints.json, caltech/caltech.roads.json, caltech/caltech.paths_trace.json",
"pasadena/pasadena.buildings.json, pasadena/pasadena.waypoints.json, pasadena/pasadena.roads.json, pasadena/pasadena.paths_trace.json",
})
public void testDijkstraBeaverMap(String buildingsFile, String waypointsFile, String roadsFile, String traceFile) throws FileNotFoundException {
BeaverMapsGraph bmg = new BeaverMapsGraph(
"data/" + buildingsFile, "data/" + waypointsFile, "data/" + roadsFile);
JsonElement s = fromFile("data/" + traceFile);
for (JsonElement b : s.getAsJsonArray()) {
JsonObject curr = b.getAsJsonObject();
Location start = bmg.getLocationByID(curr.get("start").getAsLong());
Location target = bmg.getLocationByID(curr.get("target").getAsLong());
// Build expected list
JsonArray pathList = curr.get("path").getAsJsonArray();
List<Long> expectedPathIDs = new ArrayList<>();
for (JsonElement e : pathList) {
expectedPathIDs.add(e.getAsLong());
}
IDeque<Location> actualPath = bmg.dijkstra(start, target);
List<Long> actualPathIDs = new ArrayList<>();
if (actualPath != null) {
for (Location l : actualPath) {
actualPathIDs.add(l.id);
}
// Check that path is *exactly* equivalent
MatcherAssert.assertThat(actualPathIDs,
IsIterableContainingInOrder.contains(expectedPathIDs.toArray()));
}
else if (expectedPathIDs.size() != 0) {
fail("Found path from " + start.id + " to " + target.id + " where there is none.");
}
}
}
}
package edu.caltech.cs2.project08;
import edu.caltech.cs2.datastructures.BeaverMapsGraph;
import edu.caltech.cs2.datastructures.Graph;
import edu.caltech.cs2.datastructures.Location;
import edu.caltech.cs2.helpers.Reflection;
import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.IGraph;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Random;
import java.util.Scanner;
import static org.junit.jupiter.api.Assertions.*;
@Tag("A")
public class DijkstraTest {
@Order(0)
@DisplayName("Loop path should be singleton")
@Test
public void dijkstraShortTripTest() {
IDeque<Location> res = GraphMaker.transformToLocations(GraphMaker.graph4(10)).dijkstra(new Location(1), new Location(1));
assertEquals(1, res.size(), "Path from a node to itself should only include the node once");
assertEquals(res.peek(), new Location(1), "Path from a node to itself should only include the node once");
}
@Order(1)
@DisplayName("Disconnected graph should not have a path")
@Test
public void dijkstraDisconnectedTest() {
IDeque<Location> res = GraphMaker.transformToLocations(GraphMaker.graph5(10)).dijkstra(new Location(1), new Location(9));
assertNull(res, "Disconnected graph should give null path");
}
@Order(2)
@ParameterizedTest
@DisplayName("Tests correctness of Dijkstra implementation")
@CsvSource({
"graph1, 1, 3, simple_1_3",
"graph2, 0, 1, line_0_1",
"graph2, 0, 5, line_0_5",
"graph2, 0, 20, line_0_20",
"graph2, 0, 99, line_0_99",
"graph2, 1, 0, line_1_0",
"graph3, 0, 1, graph3_0_1",
"graph3, 99, 0, graph3_99_0"
})
public void dijkstraTestGraph(String graphName, int start, int end, String traceFile)
throws IllegalAccessException, InvocationTargetException, FileNotFoundException {
BeaverMapsGraph bmg;
if (graphName.equals("graph1")) {
Method graphGen = Reflection.getMethod(GraphMaker.class, graphName);
bmg = GraphMaker.transformToLocations(
(IGraph<Integer, Integer>) graphGen.invoke(null));
}
else {
Method graphGen = Reflection.getMethod(GraphMaker.class, graphName, int.class);
bmg = GraphMaker.transformToLocations(
(IGraph<Integer, Integer>) graphGen.invoke(null, 100));
}
IDeque<Location> res = bmg.dijkstra(new Location(start), new Location(end));
double pathLen = 0;
Location prev = null;
Scanner s = new Scanner(new File("data/dijkstra/" + traceFile));
if (res == null) {
assertEquals(s.nextLine(), "null", "Path exists but was not found");
}
else {
for (Location l : res) {
if (prev != null) {
pathLen += bmg.adjacent(prev.id, l.id);
}
prev = l;
}
double expectedLen = s.nextDouble();
assertEquals(expectedLen, pathLen, "Path lengths are not equivalent");
}
}
@Order(3)
@DisplayName("Tests Dijkstra on random graph and paths")
@Test
public void dijkstraStressTest() throws FileNotFoundException{
final int num_tests = 1000;
IGraph<Integer, Integer> refg = new Graph<Integer, Integer>();
Scanner s = new Scanner(new File("data/dijkstra/random_graph"));
Random r = new Random(69420);
int num_vertices = r.nextInt(100);
for (int i = 0; i < num_vertices; i++) {
refg.addVertex(i);
}
for (int i = 0; i < num_tests; i++) {
// It will be handy to have two unique vertex ids below
int one = r.nextInt(num_vertices);
int two = r.nextInt(num_vertices);
if (one == two)
two = (two + 1) % num_vertices;
int dice = r.nextInt(5);
if (dice <= 4) {
refg.addEdge(one, two, r.nextInt(100));
} else if (dice <= 5) {
refg.removeEdge(one, two);
}
int startvertex = r.nextInt(num_vertices);
int endvertex = r.nextInt(num_vertices);
BeaverMapsGraph bmg = GraphMaker.transformToLocations(refg);
IDeque<Location> res = bmg.dijkstra(new Location(startvertex), new Location(endvertex));
if (res == null) {
assertEquals(s.nextLine(), "null", "Path exists but was not found");
}
else {
double pathLen = 0;
Location prev = null;
for (Location l : res) {
if (prev != null) {
pathLen += bmg.adjacent(prev.id, l.id);
}
prev = l;
}
double expectedLen = s.nextDouble();
s.nextLine();
assertEquals(expectedLen, pathLen, "Path lengths are not equivalent");
}
}
}
}
package edu.caltech.cs2.project08;
import edu.caltech.cs2.datastructures.BeaverMapsGraph;
import edu.caltech.cs2.datastructures.Graph;
import edu.caltech.cs2.datastructures.Location;
import edu.caltech.cs2.interfaces.IGraph;
import static org.junit.jupiter.api.Assertions.*;
public class GraphMaker {
public static BeaverMapsGraph transformToLocations(IGraph<Integer, Integer> g) {
BeaverMapsGraph ng = new BeaverMapsGraph();
for (Integer v : g.vertices()) {
ng.addVertex(new Location((long)v));
}
for (Integer v : g.vertices()) {
for (Integer v2 : g.neighbors(v)) {
ng.addEdge((long)v, (long)v2, (double)g.adjacent(v, v2));
}
}
return ng;
}
// A very simple graph.
public static IGraph<Integer, Integer> graph1() {
IGraph<Integer, Integer> g = new Graph<>();
g.addVertex(1);
g.addVertex(2);
g.addVertex(3);
g.addEdge(1, 2, 10);
g.addEdge(2, 3, 20);
return g;
}
// A linear graph on n vertices.
public static IGraph<Integer, Integer> graph2(int n) {
IGraph<Integer, Integer> g = new Graph<>();
for (int i = 0; i < n; i++) {
assertTrue(g.addVertex(i), "Adding a new vertex should return true");
}
for (int i = 0; i < n - 1; i++) {
assertTrue(g.addEdge(i, i + 1, i),
"Adding a new edge should return true"
);
}
return g;
}
public static void vertexTest(IGraph<Integer, Integer> g, int n) {
for (int i = 0; i < n; i++)
assertTrue(g.vertices().contains(i), "Graphs should contain added vertices");
}
// Verify that a graph we're given looks reasonably like graph 2
public static void graph2Test(IGraph<Integer, Integer> g, int n) {
vertexTest(g, n);
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1; j++) {
Integer e = g.adjacent(i, j);
if (j == i + 1)
assertEquals(i, e, "Retrieved edge weight is not correct");
else
assertNull(e, "An edge that was never added returned a non-null weight");
}
}
}
// A tournament on n vertices; has an edge from i to j iff j<i
public static IGraph<Integer, Integer> graph3(int n) {
IGraph<Integer, Integer> g = new Graph<>();
for (int i = 0; i < n; i++) {
assertTrue(g.addVertex(i), "Adding a new vertex should return true");
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
assertTrue(g.addEdge(i, j, i), "Adding a new edge should return true");
}
}
return g;
}
// Verify that a graph we're given looks reasonably like graph 3
public static void graph3Test(IGraph<Integer, Integer> g, int n) {
vertexTest(g, n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
Integer e = g.adjacent(i, j);
if (j < i) {
assertEquals(i, e, "Retrieved edge weight is not correct");
}
else {
assertNull(e, "An edge that was never added returned a non-null weight");
}
}
}
}
// A complete graph on n vertices
public static IGraph<Integer, Integer> graph4(int n) {
IGraph<Integer, Integer> g = new Graph<>();
for (int i = 0; i < n; i++) {
assertTrue(g.addVertex(i), "Adding a new vertex should return true");
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (j == i) continue;
assertTrue(g.addEdge(i, j, i),
"Adding a new edge should return true"
);
}
}
return g;
}
// Verify that a graph we're given looks reasonably like graph 4
public static void graph4Test(IGraph<Integer, Integer> g, int n) {
vertexTest(g, n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
Integer e = g.adjacent(i,
j);
if (i != j) {
assertEquals(i, e, "Retrieved edge weight is not correct");
}
else {
assertNull(e, "An edge that was never added returned a non-null weight");
}
}
}
}
// Two disjoint complete graphs.
public static IGraph<Integer, Integer> graph5(int n) {
IGraph<Integer, Integer> g = new Graph<>();
for (int i = 0; i < n; i++) {
assertTrue(g.addVertex(i), "Adding a new vertex should return true");
}
for (int i = 0; i < n / 2; i++) {
for (int j = 0; j < n / 2; j++) {
if (i != j) {
assertTrue(g.addEdge(i, j, i),
"Adding a new edge should return true"
);
}
}
}
for (int i = n / 2; i < n; i++) {
for (int j = n / 2; j < n; j++) {
if (i != j) {
assertTrue(g.addEdge(i, j, i),
"Adding a new edge should return true"
);
}
}
}
return g;
}
// Verify that a graph we're given looks reasonably like graph 5
public static void graph5Test(IGraph<Integer, Integer> g, int n) {
vertexTest(g, n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
Integer e = g.adjacent(i, j);
if ((i < n / 2 && j < n / 2 || i >= n / 2 && j >= n / 2) && i != j) {
assertEquals(i, e, "Retrieved edge weight is not correct");
}
else {
assertNull(e, "An edge that was never added returned a non-null weight");
}
}
}
}
/* A graph on which Dijkstra's should fail on a search from
* vertex 0 to vertex 2. Optimal path is 0->3->2 (length 1), but Dijkstra's
* should give us 0->1->2 (length 2).
*/
public static IGraph<Integer, Integer> dijkstraFail() {
IGraph<Integer, Integer> g = new Graph<>();
g.addVertex(0);
g.addVertex(1);
g.addVertex(2);
g.addVertex(3);
g.addEdge(0, 1, 1);
g.addEdge(1, 2, 1);
g.addEdge(0, 3, 5);
g.addEdge(3, 2, -4);
return g;
}
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment