Routing-Packets-In-A-Network-Overlay / src / main / java / csx55 / overlay / dijkstra / ShortestPath.java
ShortestPath.java
Raw
package csx55.overlay.dijkstra;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.PriorityQueue;
import csx55.overlay.wireformats.LinkWeights;

/**
 * This class is used to compute the shortest path from a node to every other node in the overlay.
 * When the messaging nodes receive the link weights, it creates a RoutingCache object. This object
 * will call the createSP method of this class to find the shortest path, which is then cached in
 * RoutingCache. To calculate the shortest path, Dijkstra's algorithm is utilized. Therefore,
 * the algorithm will not work for negative edge weights.
 */
public class ShortestPath {
    private int adjMatrix[][];
    /*
     * An inner class that extends Comparable. It will be used for the priority queue so that Dijkstra's
     * algorithm is efficient.
     */
    class Vertex implements Comparable<Vertex> {
        String node;
        int distance;

        public Vertex(String node, int distance) {
            this.node = node;
            this.distance = distance;
        }

        @Override
        public int compareTo(Vertex rhs) {
            return Integer.compare(this.distance, rhs.distance);//compare this distance to distance passed in
        }
    }

    /**
     * This method is called from the RoutingCache class. It fills a HashMap that contains all of the routes to all other
     * nodes in this format: <ip:port, [ip:port1, ip:port2, ...]>. The weights contains all of the edge weights for all edges
     * in the overlay. The ipPort is an identifier for the current node, and allConnections is essentially msgRoutes.keySet().
     * This will first create an adjacency matrix of node size x node size and fill all values with the weights associated with
     * that link. The diagonal will be 0s since this is an undirected graph. It will then call dijkstra.
     */
    public void createSP(HashMap<String, String[]> msgRoutes, LinkWeights weights, String ipPort, ArrayList<String> allConnections) {
        int numNodes = allConnections.size();
        adjMatrix = new int[numNodes][numNodes];

        for (String connection: weights.getLinks()) {
            String[] splitLink = connection.split("\\s+");//Source: 0, Dest: 1, Weight: 2
            adjMatrix[allConnections.indexOf(splitLink[0])][allConnections.indexOf(splitLink[1])] = Integer.parseInt(splitLink[2]);
            adjMatrix[allConnections.indexOf(splitLink[1])][allConnections.indexOf(splitLink[0])] = Integer.parseInt(splitLink[2]);
        }

        //constructing Dijkstra
        dijkstra(msgRoutes, adjMatrix, allConnections, ipPort);
    }

    /**
     * This method follows the standard algorithm for computing the shortest paths using Dijkstra on a adjacency matrix.
     * The priority queue maintains the link weights in sorted order and greedly builds the shortest path. shortestPath is a
     * reference to the HashMap and this method modifies it by adding the shortest path to each peer.
     */
    private void dijkstra(HashMap<String, String[]> shortestPaths, int[][] adjMatrix, ArrayList<String> allNodes, String startNode) {
        int V = adjMatrix.length;
        int[] dist = new int[V];
        int[] pred = new int[V];
        PriorityQueue<Vertex> pq = new PriorityQueue<>();//create a priority queue

        Arrays.fill(dist, Integer.MAX_VALUE);//set all values to inf
        Arrays.fill(pred, -1);//set all values to -1
        dist[allNodes.indexOf(startNode)] = 0;
        pq.add(new Vertex(startNode, 0));

        while (!pq.isEmpty()) {
            Vertex current = pq.poll();
            int u = allNodes.indexOf(current.node);

            for (int i = 0; i < V; i++) {
                if (adjMatrix[u][i] != 0 && dist[u] != Integer.MAX_VALUE) {
                    int newDist = dist[u] + adjMatrix[u][i];
                    if (newDist < dist[i]) {
                        dist[i] = newDist;
                        pred[i] = u;
                        pq.add(new Vertex(allNodes.get(i), dist[i]));
                    }
                }
            }
        }

        //add to shortestPaths
        for (int i = 0; i < V; i++) {
            ArrayList<Integer> path = new ArrayList<>();
            ArrayList<String> strPath = new ArrayList<>();
            String curr = allNodes.get(i);
            if (!curr.equals(startNode)) {
                joinPath(i, pred, path);
                
                for (Integer j: path) {
                    if (allNodes.get(j).equals(startNode)) continue;
                    strPath.add(allNodes.get(j));
                }

                shortestPaths.put(curr, strPath.toArray(new String[strPath.size()]));
                path.clear();
            }
        }
    }

    /**
     * This method will construct the path from the starting node to the sink node
     * recursively by adding the node to the path list if the value at pred is not -1.
     * At each value of pred, the value indicates the index of the predecessor.
     */
    private void joinPath(int curr, int[] pred, ArrayList<Integer> path) {
        if (curr == -1) return;
        joinPath(pred[curr], pred, path);
        path.add(curr);
    }
}