/*
* Copyright © 2016 Inocybe Technologies and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.nic.of.renderer.algorithm;
import org.apache.commons.collections15.ListUtils;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.MapTransformer;
import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.util.EdgeType;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class SuurballeAlgorithm<V, E> {
private final DijkstraShortestPath<V, E> dijkstraAlgorithm;
private final Graph<V, E> graph;
private final Transformer<E, Number> defaultEdgeWeight = arg0 -> (Number)new Double(1);
Map<V, Number> initialCostMap = null;
public SuurballeAlgorithm(Graph<V,E> graph) {
this.graph = graph;
this.dijkstraAlgorithm = new DijkstraShortestPath<>(graph, defaultEdgeWeight, true);
}
public final List<List<E>> findShortestPath(final V initial, final V destination){
final List<E> shortestPath = dijkstraAlgorithm.getPath(initial, destination);
initialCostMap = dijkstraAlgorithm.getDistanceMap(initial);
final List<E> reversedShortestPath =
reverseUpdateEdgesWeight(graph, MapTransformer.getInstance(initialCostMap),
shortestPath, initial, destination);
discardCommonReversedEdges(graph, shortestPath, reversedShortestPath);
final List<E> unitedPaths = ListUtils.union(shortestPath, reversedShortestPath);
final List<E> resultPath1 = restorePaths(shortestPath, destination, unitedPaths);
final List<E> resultPath2 = restorePaths(reversedShortestPath, destination, unitedPaths);
List<List<E>> result = mergePaths(resultPath1, resultPath2);
if ((result == null) || (result.size() == 0)){
result = new ArrayList<>();
result.add(shortestPath);
}
return result;
}
private List<List<E>> mergePaths(final List<E> resultPath1, final List<E> resultPath2) {
final List<List<E>> result = new ArrayList<>();
final Number cost1 = calculatePathCost(resultPath1);
final Number cost2 = calculatePathCost(resultPath2);
if (cost1.doubleValue() > cost2.doubleValue()){
result.add(resultPath1);
result.add(resultPath2);
} else {
result.add(resultPath2);
result.add(resultPath1);
}
return result;
}
private void discardCommonReversedEdges(final Graph<V,E> graph, final List<E> path1, final List<E> path2) {
if (path1.size() == 0 || path2.size() == 0){
return;
} else {
final V source = graph.getSource(path1.get(0));
final V target = graph.getDest(path1.get(path1.size() - 1));
for(final E edge2 : path2){
for(final E edge1 : path1){
if (edge1.equals(edge2)){
if (graph.isSource(source, edge1) ||
graph.isSource(source, edge2) ||
graph.isDest(target, edge1) ||
graph.isDest(target, edge2)){
// Return only shortest path
path2.clear();
return;
}
path1.remove(edge1);
path2.remove(edge2);
break;
}
}
}
}
}
protected List<E> reverseUpdateEdgesWeight(final Graph<V, E> graph, final Transformer<V, Number> transformer,
final List<E> shortestPath, final V initial, final V destination) {
for(final E edge1 : shortestPath){
V src = graph.getSource(edge1);
V dst = graph.getDest(edge1);
graph.removeEdge(edge1);
graph.addEdge(edge1, dst, src, EdgeType.DIRECTED);
}
final List<E> edges = new ArrayList<>(graph.getEdges());
final Map<E, Number> map = new LinkedHashMap<>();
edges.forEach(edge -> {
final V source = graph.getSource(edge);
final V dest = graph.getDest(edge);
Number cost = calculateCost(transformer, edge, source, dest);
map.put(edge,cost);
});
final DijkstraShortestPath<V, E> reversedDijkstra =
new DijkstraShortestPath<>(graph, MapTransformer.getInstance(map));
DijkstraShortestPath<V, E> validatedShortestPath = checkPath(initial, destination, reversedDijkstra);
return validatedShortestPath != null ? reversedDijkstra.getPath(initial, destination) : new ArrayList<>();
}
private DijkstraShortestPath<V, E> checkPath(final V startPoint, final V endPoint,
final DijkstraShortestPath<V, E> reversedDijkstra) {
final Number reversedDistance = reversedDijkstra.getDistance(startPoint, endPoint);
if ((reversedDistance == null) || (reversedDistance.doubleValue() == Double.MAX_VALUE)){
return null;
}
return reversedDijkstra;
}
private Number calculateCost(final Transformer<V, Number> transformer, final E edge,
final V source, final V destination) {
double cost = 0;
if (transformer.transform(source) != null) {
double edgeWeight = defaultEdgeWeight.transform(edge).doubleValue();
double destinationWeight = (transformer.transform(destination).doubleValue());
double sourceWeight = transformer.transform(source).doubleValue();
cost = (edgeWeight - (destinationWeight + sourceWeight));
if (Math.abs(cost) < 0.000001) {
cost = 0;
} else {
cost = Double.POSITIVE_INFINITY;
}
}
return cost;
}
private List<E> restorePaths(final List<E> path, final V target, final List<E> partialMergedPaths) {
final List<E> resultPath = new ArrayList<>();
resultPath.add(path.get(0));
partialMergedPaths.remove(path.get(0));
if (!isDestination(target, resultPath)){
V currentDestination = graph.getDest(resultPath.get(resultPath.size() - 1));
for (E edge : partialMergedPaths){
if (graph.isSource(currentDestination, edge)) {
resultPath.add(edge);
partialMergedPaths.remove(edge);
break;
}
}
}
return resultPath;
}
private boolean isDestination(final V target, final List<E> resultPath) {
return graph.getDest(resultPath.get(resultPath.size() - 1)).equals(target);
}
private double calculatePathCost(final List<E> path){
double result = 0;
for(E edge : path) {
result += defaultEdgeWeight.transform(edge).doubleValue();
}
return result;
}
}