From ef3ea6d104d24a072cfa856e2f73b28126dfd0e3 Mon Sep 17 00:00:00 2001
From: Brady James Garvin <bgarvin@cse.unl.edu>
Date: Tue, 23 Oct 2018 06:45:01 -0500
Subject: [PATCH] Introduced starter code for Homework 3.6.

---
 index.html                  |   1 +
 js/heat_map.js              |  86 +++++++++++++++++++++++++++++++
 unit_tests/test_heat_map.js | 100 ++++++++++++++++++++++++++++++++++++
 unit_tests/unit_tests.html  |   3 ++
 4 files changed, 190 insertions(+)
 create mode 100644 js/heat_map.js
 create mode 100644 unit_tests/test_heat_map.js

diff --git a/index.html b/index.html
index a65528b..cf7d809 100644
--- a/index.html
+++ b/index.html
@@ -30,6 +30,7 @@
   <script src="js/undirected_graph.js"></script>
   <script src="js/transit.js"></script>
   <script src="js/patching.js"></script>
+  <script src="js/heat_map.js"></script>
   <script src="js/positioned_graph.js"></script>
   <script src="js/throttled_simulation.js"></script>
   <script src="js/visualization_style.js"></script>
diff --git a/js/heat_map.js b/js/heat_map.js
new file mode 100644
index 0000000..8a0ad23
--- /dev/null
+++ b/js/heat_map.js
@@ -0,0 +1,86 @@
+/* exported computeHeatMap */
+
+class EdgeLabeledGraph {
+  constructor(vertices, defaultLabel) {
+    const verticesList = [...vertices]; // copy the vertices to a list in case they can only be iterated over once
+    this.edges = new Map();
+    for (const source of verticesList) {
+      const adjacencies = new Map();
+      for (const destination of verticesList) {
+        adjacencies.set(destination, defaultLabel);
+      }
+      this.edges.set(source, adjacencies);
+    }
+  }
+
+  get vertices() {
+    return this.edges.keys();
+  }
+
+  getLabel(source, destination) {
+    return this.edges.get(source).get(destination);
+  }
+
+  setLabel(source, destination, label) {
+    return this.edges.get(source).set(destination, label);
+  }
+
+  capLabel(source, destination, label) {
+    const adjacencies = this.edges.get(source);
+    adjacencies.set(destination, Math.min(adjacencies.get(destination), label));
+  }
+
+  increaseLabel(source, destination, increase) {
+    const adjacencies = this.edges.get(source);
+    adjacencies.set(destination, adjacencies.get(destination) + increase);
+  }
+}
+
+function computeTransitGraph(city) {
+  return undefined; // TODO: stub
+}
+
+// Preliminaries:
+//   W[u][v] is the (possibly infinite) weight from u to v.
+//
+// Standard Floyd–Warshall Recurrence:
+//   D^(i)[u][v] is the distance from u to v using only the first i vertices as intermediates, so
+//   D^(n)[u][v] is the distance from u to v.
+//
+//   D^(0)[u][v] = …
+//   D^(i)[u][v] = …    for 1 ≤ i ≤ n
+//
+// Recurrence for Shortest-Path Successors:
+//   S^(i)[u][v] is a vertex that immediately follows u in a shortest path from u to v using only the first i vertices as intermediates, so
+//   S^(n)[u][v] is a vertex that immediately follows u in a shortest path from u to v.
+//
+//   S^(0)[u][v] = …    if …
+//                 …    otherwise
+//   S^(i)[u][v] = …    if …
+//                 …    if …
+//                 …    otherwise    for 1 ≤ i ≤ n
+function computeShortestPathSuccessors(transitGraph) {
+  return undefined; // TODO: stub
+}
+
+// Preliminaries:
+//   S^(n)[u][v] is defined as above computeShortestPathSuccessors.
+//
+// Recurrence for Traffic Matrix:
+//   T^(i)[u][v] is the number of distinct paths that visit a vertex \(u\) in their first \(i\) steps while traveling to a final destination \(v\), so
+//   T^(n-1)[u][v] is the number of distinct paths that travel via \(u\) to their final destination \(v\).
+//
+//   T^(0)[u][v] = 1
+//   T^(i)[u][v] = 1 + sum_{w|S^(n)[w][v]=u} T^(i-1)[w][v]    for 1 ≤ i ≤ n
+//
+function computeTrafficMatrix(successors) {
+  return undefined; // TODO: stub
+}
+
+function computeHeatFromTraffic(traffic) {
+  return undefined; // TODO: stub
+}
+
+function computeHeatMap(city) {
+  return undefined; // TODO: stub
+}
diff --git a/unit_tests/test_heat_map.js b/unit_tests/test_heat_map.js
new file mode 100644
index 0000000..d897773
--- /dev/null
+++ b/unit_tests/test_heat_map.js
@@ -0,0 +1,100 @@
+QUnit.module('heat_map.js');
+/* globals QUnit Vertex UndirectedEdge UndirectedGraph City Route Bus EdgeLabeledGraph */
+/* globals computeTransitGraph computeShortestPathSuccessors computeTrafficMatrix computeHeatFromTraffic computeHeatMap */
+/* eslint-disable no-magic-numbers */
+
+// This reformatting is not necessary, but makes the output from failed tests easier to read.
+function toTriples(edgeLabeledGraph) {
+  const accumulator = [];
+  for (const [source, adjacencies] of edgeLabeledGraph.edges) {
+    for (const [destination, label] of adjacencies) {
+      accumulator.push([source, destination, label]);
+    }
+  }
+  let result = '';
+  for (const triple of accumulator.sort()) {
+    result += `(${triple}); `;
+  }
+  return result.slice(0, -2);
+}
+
+// Building graphs from strings is not necessary, but makes tests easier to write.
+function fromTriples(triplesString) {
+  const triples = triplesString.split('; ').map((triple) => triple.slice(1, -1).split(','));
+  const vertices = new Set();
+  for (const [source, destination, _] of triples) {
+    vertices.add(source);
+    vertices.add(destination);
+  }
+  const result = new EdgeLabeledGraph(vertices, undefined);
+  for (const [source, destination, label] of triples) {
+    let cast = Number(label);
+    if (label === '') {
+      cast = undefined;
+    } else if (Number.isNaN(cast)){
+      cast = label;
+    }
+    result.setLabel(source, destination, cast);
+  }
+  return result;
+}
+
+const CITY_FOR_SMOKE_TESTS = (() => {
+  const a = new Vertex('a');
+  const b = new Vertex('b');
+  const c = new Vertex('c');
+  const walkGraph = new UndirectedGraph();
+  walkGraph.addVertex(a);
+  walkGraph.addVertex(b);
+  walkGraph.addVertex(c);
+  walkGraph.addEdge(a, new UndirectedEdge(2.0), b);
+  walkGraph.addEdge(a, new UndirectedEdge(4.0), c);
+  walkGraph.addEdge(b, new UndirectedEdge(8.0), c);
+  const driveGraph = new UndirectedGraph();
+  driveGraph.addVertex(a);
+  driveGraph.addVertex(b);
+  driveGraph.addVertex(c);
+  driveGraph.addEdge(a, new UndirectedEdge(1.0), c);
+  const city = new City(walkGraph, driveGraph);
+  const route = new Route(city, a, c);
+  const x = new Bus(route.getArc(a)); // eslint-disable-line no-unused-vars
+  const y = new Bus(route.getArc(c)); // eslint-disable-line no-unused-vars
+  return city;
+})();
+
+QUnit.test('smoke test computeTransitGraph', (assert) => {
+  const transitGraph = computeTransitGraph(CITY_FOR_SMOKE_TESTS);
+  assert.deepEqual(toTriples(transitGraph), '(a,a,Infinity); (a,b,2); (a,c,2); (b,a,2); (b,b,Infinity); (b,c,8); (c,a,2); (c,b,8); (c,c,Infinity)');
+});
+
+QUnit.test('smoke test computeShortestPathSuccessors', (assert) => {
+  const transitGraph = fromTriples('(a,a,Infinity); (a,b,2); (a,c,2); (b,a,2); (b,b,Infinity); (b,c,8); (c,a,2); (c,b,8); (c,c,Infinity)');
+  const successors = computeShortestPathSuccessors(transitGraph);
+  assert.deepEqual(toTriples(successors), '(a,a,); (a,b,b); (a,c,c); (b,a,a); (b,b,); (b,c,a); (c,a,a); (c,b,a); (c,c,)');
+});
+
+QUnit.test('smoke test computeTrafficMatrix', (assert) => {
+  const successors = fromTriples('(a,a,); (a,b,b); (a,c,c); (b,a,a); (b,b,); (b,c,a); (c,a,a); (c,b,a); (c,c,)');
+  const traffic = computeTrafficMatrix(successors);
+  assert.deepEqual(toTriples(traffic), '(a,a,3); (a,b,2); (a,c,2); (b,a,1); (b,b,3); (b,c,1); (c,a,1); (c,b,1); (c,c,3)');
+});
+
+QUnit.test('smoke test computeHeatFromTraffic', (assert) => {
+  const traffic = fromTriples('(a,a,3); (a,b,2); (a,c,2); (b,a,1); (b,b,3); (b,c,1); (c,a,1); (c,b,1); (c,c,3)');
+  const heat = computeHeatFromTraffic(traffic);
+  assert.deepEqual(heat.size, 3);
+  assert.deepEqual(heat.get('a'), 7); // fromTriples will give us string keys, not vertex keys
+  assert.deepEqual(heat.get('b'), 5);
+  assert.deepEqual(heat.get('c'), 5);
+});
+
+QUnit.test('smoke test computeHeatMap', (assert) => {
+  const a = CITY_FOR_SMOKE_TESTS.walkGraph.vertices.find((vertex) => vertex.name === 'a');
+  const b = CITY_FOR_SMOKE_TESTS.walkGraph.vertices.find((vertex) => vertex.name === 'b');
+  const c = CITY_FOR_SMOKE_TESTS.walkGraph.vertices.find((vertex) => vertex.name === 'c');
+  const heat = computeHeatMap(CITY_FOR_SMOKE_TESTS);
+  assert.deepEqual(heat.size, 3);
+  assert.deepEqual(heat.get(a), 7);
+  assert.deepEqual(heat.get(b), 5);
+  assert.deepEqual(heat.get(c), 5);
+});
diff --git a/unit_tests/unit_tests.html b/unit_tests/unit_tests.html
index 1ff7e4d..4681968 100644
--- a/unit_tests/unit_tests.html
+++ b/unit_tests/unit_tests.html
@@ -42,6 +42,9 @@
   <script src="../js/patching.js"></script>
   <script src="test_patching.js"></script>
 
+  <script src="../js/heat_map.js"></script>
+  <script src="test_heat_map.js"></script>
+
   <script src="../js/positioned_graph.js"></script>
   <script src="test_positioned_graph.js"></script>
 
-- 
GitLab