229 lines
6.7 KiB
Java
229 lines
6.7 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2002-2010 IBM Corporation.
|
|
* 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
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*******************************************************************************/
|
|
package com.ibm.wala.util.graph.traverse;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
import com.ibm.wala.util.graph.NumberedGraph;
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
import com.ibm.wala.util.intset.IntSetAction;
|
|
import com.ibm.wala.util.intset.IntSetUtil;
|
|
import com.ibm.wala.util.intset.MutableIntSet;
|
|
|
|
/**
|
|
* Floyd-Warshall algorithm to compute all-pairs shortest path in graph with no negative cycles.
|
|
*
|
|
* TODO: this API should be cleaned up.
|
|
*
|
|
* @param <T> node type in the graph
|
|
*/
|
|
public class FloydWarshall<T> {
|
|
|
|
public interface GetPath<T> {
|
|
List<T> getPath(T from, T to);
|
|
}
|
|
|
|
public interface GetPaths<T> {
|
|
Set<List<T>> getPaths(T from, T to);
|
|
}
|
|
|
|
protected final NumberedGraph<T> G;
|
|
|
|
public FloydWarshall(NumberedGraph<T> g) {
|
|
G = g;
|
|
}
|
|
|
|
protected int edgeCost(int from, int to) {
|
|
return 1;
|
|
}
|
|
|
|
protected void pathCallback(int i, int j, int k) {
|
|
|
|
}
|
|
|
|
public int[][] allPairsShortestPaths() {
|
|
final int[][] result = new int[G.getNumberOfNodes()][G.getNumberOfNodes()];
|
|
|
|
for(int i = 0; i < result.length; i++) {
|
|
for(int j = 0; j < result[i].length; j++) {
|
|
result[i][j] = Integer.MAX_VALUE;
|
|
}
|
|
}
|
|
|
|
for(T from : G) {
|
|
final int fn = G.getNumber(from);
|
|
IntSet tos = G.getSuccNodeNumbers(from);
|
|
tos.foreach(new IntSetAction() {
|
|
@Override
|
|
public void act(int x) {
|
|
result[fn][x] = edgeCost(fn, x);
|
|
}
|
|
});
|
|
}
|
|
|
|
for(T kn : G) {
|
|
int k = G.getNumber(kn);
|
|
for(T in : G) {
|
|
int i = G.getNumber(in);
|
|
for(T jn : G) {
|
|
int j = G.getNumber(jn);
|
|
long newLen = (long)result[i][k] + (long)result[k][j];
|
|
if (newLen <= result[i][j]) {
|
|
pathCallback(i, j, k);
|
|
}
|
|
if (newLen < result[i][j]) {
|
|
result[i][j] = (int)newLen;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static <T> int[][] shortestPathLengths(NumberedGraph<T> G) {
|
|
return new FloydWarshall<>(G).allPairsShortestPaths();
|
|
}
|
|
|
|
public static <T> GetPath<T> allPairsShortestPath(final NumberedGraph<T> G) {
|
|
return new FloydWarshall<T>(G) {
|
|
int[][] next = new int[G.getNumberOfNodes()][G.getNumberOfNodes()];
|
|
|
|
@Override
|
|
protected void pathCallback(int i, int j, int k) {
|
|
next[i][j] = k;
|
|
}
|
|
|
|
private GetPath<T> doit() {
|
|
for(int i = 0; i < next.length; i++) {
|
|
for(int j = 0; j < next[i].length; j++) {
|
|
next[i][j] = -1;
|
|
}
|
|
}
|
|
|
|
final int[][] paths = allPairsShortestPaths();
|
|
return new GetPath<T>() {
|
|
|
|
@Override
|
|
public String toString() {
|
|
String s = "";
|
|
for(int i = 0; i <= G.getMaxNumber(); i++) {
|
|
for(int j = 0; j <= G.getMaxNumber(); j++) {
|
|
try {
|
|
s += getPath(G.getNode(i), G.getNode(j));
|
|
} catch (UnsupportedOperationException e) {
|
|
|
|
}
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
@Override
|
|
public List<T> getPath(T from, T to) {
|
|
int fn = G.getNumber(from);
|
|
int tn = G.getNumber(to);
|
|
if (paths[fn][tn] == Integer.MAX_VALUE) {
|
|
throw new UnsupportedOperationException("no path from " + from + " to " + to);
|
|
} else {
|
|
int intermediate = next[fn][tn];
|
|
if (intermediate == -1) {
|
|
return Collections.emptyList();
|
|
} else {
|
|
T in = G.getNode(intermediate);
|
|
List<T> result = new LinkedList<>(getPath(from, in));
|
|
result.add(in);
|
|
result.addAll(getPath(in, to));
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}.doit();
|
|
}
|
|
|
|
public static <T> GetPaths<T> allPairsShortestPaths(final NumberedGraph<T> G) {
|
|
return new FloydWarshall<T>(G) {
|
|
MutableIntSet[][] next = new MutableIntSet[G.getNumberOfNodes()][G.getNumberOfNodes()];
|
|
|
|
@Override
|
|
protected void pathCallback(int i, int j, int k) {
|
|
if (next[i][j] == null) {
|
|
next[i][j] = IntSetUtil.make();
|
|
}
|
|
next[i][j].add(k);
|
|
}
|
|
|
|
private GetPaths<T> doit() {
|
|
final int[][] paths = allPairsShortestPaths();
|
|
return new GetPaths<T>() {
|
|
|
|
@Override
|
|
public String toString() {
|
|
List<Set<List<T>>> x = new ArrayList<>();
|
|
for(int i = 0; i <= G.getMaxNumber(); i++) {
|
|
for(int j = 0; j <= G.getMaxNumber(); j++) {
|
|
try {
|
|
x.add(getPaths(G.getNode(i), G.getNode(j)));
|
|
} catch (UnsupportedOperationException e) {
|
|
|
|
}
|
|
}
|
|
}
|
|
return x.toString();
|
|
}
|
|
|
|
@Override
|
|
public Set<List<T>> getPaths(final T from, final T to) {
|
|
int fn = G.getNumber(from);
|
|
int tn = G.getNumber(to);
|
|
if (paths[fn][tn] == Integer.MAX_VALUE) {
|
|
throw new UnsupportedOperationException("no path from " + from + " to " + to);
|
|
} else {
|
|
MutableIntSet intermediate = next[fn][tn];
|
|
if (intermediate == null) {
|
|
List<T> none = Collections.emptyList();
|
|
return Collections.singleton(none);
|
|
} else {
|
|
final Set<List<T>> result = new HashSet<>();
|
|
|
|
intermediate.foreach(new IntSetAction() {
|
|
@Override
|
|
public void act(int x) {
|
|
T in = G.getNode(x);
|
|
for(List<T> pre : getPaths(from, in)) {
|
|
for(List<T> post : getPaths(in, to)) {
|
|
List<T> path = new LinkedList<>(pre);
|
|
path.add(in);
|
|
path.addAll(post);
|
|
result.add(path);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}.doit();
|
|
}
|
|
|
|
}
|