package iamrescue.routing.dijkstra;
import iamrescue.routing.IRoutingAlgorithm;
import iamrescue.routing.dijkstra.SimpleGraph.Node;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javolution.util.FastMap;
import org.jgrapht.util.FibonacciHeap;
import org.jgrapht.util.FibonacciHeapNode;
import cern.colt.Arrays;
public class DijkstrasShortestPath implements IRoutingAlgorithm {
// private boolean[] expanded;
private double[] costs;
private int[] predecessors;
private FibonacciHeapNode<Integer>[] heapNodes;
private boolean[] terminalNodes;
private SimpleGraph graph;
private FibonacciHeap<Integer> heap;
public DijkstrasShortestPath(SimpleGraph graph, int source) {
this(graph, Collections.singletonList(source), Collections
.singletonList(0.0));
}
@SuppressWarnings("unchecked")
public DijkstrasShortestPath(SimpleGraph graph,
List<Integer> potentialSources, List<Double> initialCosts) {
if (potentialSources.size() != initialCosts.size()) {
throw new IllegalArgumentException("Sources and costs "
+ "must have the same number of elements.");
}
// Initialise
// expanded = new boolean[graph.getNodes().size()];
costs = new double[graph.getNodes().size()];
predecessors = new int[graph.getNodes().size()];
heapNodes = new FibonacciHeapNode[graph.getNodes().size()];
terminalNodes = new boolean[graph.getNodes().size()];
for (int i = 0; i < costs.length; i++) {
// expanded[i] = false;
costs[i] = Double.POSITIVE_INFINITY;
predecessors[i] = -1;
terminalNodes[i] = false;
}
this.graph = graph;
heap = new FibonacciHeap<Integer>();
for (int i = 0; i < potentialSources.size(); i++) {
int source = potentialSources.get(i);
double cost = initialCosts.get(i);
FibonacciHeapNode<Integer> firstNode = new FibonacciHeapNode<Integer>(
source, cost);
heapNodes[source] = firstNode;
costs[source] = cost;
heap.insert(firstNode, cost);
}
}
public PathSolution getShortestPath(int destination) {
return getShortestPath(Collections.singletonList(destination),
new ArrayList<Double>());
}
public void forceFullCompute() {
calculateShortestPath(new ArrayList<Integer>(), new ArrayList<Double>());
}
public double getCost(int index) {
return costs[index];
}
public int getPredecessor(int index) {
return predecessors[index];
}
private int calculateShortestPath(List<Integer> targetSet,
List<Double> targetCosts) {
Map<Integer, Double> targetExtras = new FastMap<Integer, Double>();
for (int i = 0; i < targetSet.size(); i++) {
int target = targetSet.get(i);
terminalNodes[target] = true;
if (targetCosts.size() > 0) {
double cost = targetCosts.get(i);
if (cost > 0) {
targetExtras.put(target, cost);
}
}
}
int bestSolution = -1;
double bestCost = Double.POSITIVE_INFINITY;
while (heap.size() > 0) {
FibonacciHeapNode<Integer> min = heap.removeMin();
int index = min.getData();
double cost = min.getKey();
if (cost >= bestCost) {
break;
}
//
if (terminalNodes[index]) {
if (targetExtras.size() == 0) {
bestSolution = index;
// System.out.println("found");
break;
} else {
Double extraCost = targetExtras.get(index);
if (extraCost == null || cost + extraCost < bestCost) {
if (extraCost == null) {
bestCost = cost;
} else {
bestCost = cost + extraCost;
}
bestSolution = index;
}
}
}
Node graphNode = graph.getNodes().get(index);
heapNodes[index] = null;
// For all neighbours
for (int i = 0; i < graphNode.getNeighbours().size(); i++) {
Node neighbour = graphNode.getNeighbours().get(i);
int neighbourIndex = neighbour.getID();
// Still in heap or unvisited?
if (costs[neighbourIndex] == Double.POSITIVE_INFINITY
|| heapNodes[neighbourIndex] != null) {
double costToNeighbour = cost + graphNode.getCosts().get(i);
// Better cost?
if (costToNeighbour < costs[neighbourIndex]) {
costs[neighbourIndex] = costToNeighbour;
predecessors[neighbourIndex] = index;
FibonacciHeapNode<Integer> heapNode = heapNodes[neighbourIndex];
// Check if we have previously inserted this
if (heapNode == null) {
heapNode = new FibonacciHeapNode<Integer>(
neighbourIndex, costToNeighbour);
heapNodes[neighbourIndex] = heapNode;
heap.insert(heapNode, costToNeighbour);
} else {
heap.decreaseKey(heapNode, costToNeighbour);
}
}
}
}
}
for (int target : targetSet) {
terminalNodes[target] = false;
}
return bestSolution;
}
/**
* @param targetSet
* @return
*/
public PathSolution getShortestPath(List<Integer> targetSet,
List<Double> targetCosts) {
int bestSolution = -1;
double bestCost = Double.POSITIVE_INFINITY;
if (targetCosts.size() == 0) {
for (int target : targetSet) {
if (costs[target] < bestCost) {
bestCost = costs[target];
bestSolution = target;
}
}
} else {
for (int i = 0; i < targetSet.size(); i++) {
int target = targetSet.get(i);
double extra = targetCosts.get(i);
double total = costs[target] + extra;
if (total < bestCost) {
bestCost = total;
bestSolution = target;
}
}
}
if (bestSolution == -1
|| (heap.size() > 0 && bestCost > heap.min().getKey())) {
bestSolution = calculateShortestPath(targetSet, targetCosts);
}
if (bestSolution == -1) {
return new PathSolution(Double.POSITIVE_INFINITY, new int[0]);
}
List<Integer> path = new LinkedList<Integer>();
int pointer = bestSolution;
while (pointer != -1) {
path.add(pointer);
pointer = predecessors[pointer];
}
int[] pathIDs = new int[path.size()];
int counter = path.size() - 1;
for (int id : path) {
pathIDs[counter] = id;
counter--;
}
double cost = costs[bestSolution];
if (targetCosts.size() > 0) {
for (int i = 0; i < targetSet.size(); i++) {
if (targetSet.get(i) == bestSolution) {
cost += targetCosts.get(i);
break;
}
}
}
// System.out.println(Arrays.toString(pathIDs));
return new PathSolution(cost, pathIDs);
}
/*
* (non-Javadoc)
*
* @see newrouting.IRoutingAlgorithm#getShortestPath(java.util.List,
* java.util.List)
*/
// @Override
/*
* public PathSolution getShortestPath(List<Integer> targetSet, List<Double>
* targetCosts) { for (Double d : targetCosts) { if (d != 0) { throw new
* IllegalArgumentException( "Still need to implement arbitrary costs for "
* + "normal Dijkstra. Use Bidirectional instead."); } } Set<Integer>
* targets = new FastSet<Integer>(); targets.addAll(targetSet); return
* getShortestPath(targetSet); }
*/
}