method to compute graph of strongly connected components moved to service

This commit is contained in:
nik
2011-06-08 16:17:09 +04:00
parent fc23689801
commit 310e37f50b
8 changed files with 192 additions and 58 deletions
@@ -27,12 +27,7 @@ import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.Pair;
import com.intellij.util.Chunk;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.graph.CachingSemiGraph;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.Graph;
import com.intellij.util.graph.GraphGenerator;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import com.intellij.util.graph.*;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -92,47 +87,7 @@ public final class ModuleCompilerUtil {
}
public static <Node> Graph<Chunk<Node>> toChunkGraph(final Graph<Node> graph) {
final DFSTBuilder<Node> builder = new DFSTBuilder<Node>(graph);
final TIntArrayList sccs = builder.getSCCs();
final List<Chunk<Node>> chunks = new ArrayList<Chunk<Node>>(sccs.size());
final Map<Node, Chunk<Node>> nodeToChunkMap = new LinkedHashMap<Node, Chunk<Node>>();
sccs.forEach(new TIntProcedure() {
int myTNumber = 0;
public boolean execute(int size) {
final Set<Node> chunkNodes = new LinkedHashSet<Node>();
final Chunk<Node> chunk = new Chunk<Node>(chunkNodes);
chunks.add(chunk);
for (int j = 0; j < size; j++) {
final Node node = builder.getNodeByTNumber(myTNumber + j);
chunkNodes.add(node);
nodeToChunkMap.put(node, chunk);
}
myTNumber += size;
return true;
}
});
return GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<Chunk<Node>>() {
public Collection<Chunk<Node>> getNodes() {
return chunks;
}
public Iterator<Chunk<Node>> getIn(Chunk<Node> chunk) {
final Set<Node> chunkNodes = chunk.getNodes();
final Set<Chunk<Node>> ins = new LinkedHashSet<Chunk<Node>>();
for (final Node node : chunkNodes) {
for (Iterator<Node> nodeIns = graph.getIn(node); nodeIns.hasNext();) {
final Node in = nodeIns.next();
if (!chunk.containsNode(in)) {
ins.add(nodeToChunkMap.get(in));
}
}
}
return ins.iterator();
}
}));
return GraphAlgorithms.getInstance().computeSCCGraph(graph);
}
public static void sortModules(final Project project, final List<Module> modules) {
@@ -17,6 +17,8 @@ package com.intellij.util.graph;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.util.Chunk;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -46,4 +48,8 @@ public abstract class GraphAlgorithms {
@NotNull
public abstract <Node> Graph<Node> invertEdgeDirections(@NotNull Graph<Node> graph);
public abstract <Node> Graph<Chunk<Node>> computeSCCGraph(final Graph<Node> graph);
public abstract <Node> Graph<Node> createGraphByInArcs(MultiMap<Node, Node> inArcs);
}
@@ -16,8 +16,11 @@
package com.intellij.util.graph.impl;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.util.graph.Graph;
import com.intellij.util.graph.GraphAlgorithms;
import com.intellij.util.Chunk;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.graph.*;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -63,6 +66,69 @@ public class GraphAlgorithmsImpl extends GraphAlgorithms {
};
}
@Override
public <Node> Graph<Chunk<Node>> computeSCCGraph(final Graph<Node> graph) {
final DFSTBuilder<Node> builder = new DFSTBuilder<Node>(graph);
final TIntArrayList sccs = builder.getSCCs();
final List<Chunk<Node>> chunks = new ArrayList<Chunk<Node>>(sccs.size());
final Map<Node, Chunk<Node>> nodeToChunkMap = new LinkedHashMap<Node, Chunk<Node>>();
sccs.forEach(new TIntProcedure() {
int myTNumber = 0;
public boolean execute(int size) {
final Set<Node> chunkNodes = new LinkedHashSet<Node>();
final Chunk<Node> chunk = new Chunk<Node>(chunkNodes);
chunks.add(chunk);
for (int j = 0; j < size; j++) {
final Node node = builder.getNodeByTNumber(myTNumber + j);
chunkNodes.add(node);
nodeToChunkMap.put(node, chunk);
}
myTNumber += size;
return true;
}
});
return GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<Chunk<Node>>() {
public Collection<Chunk<Node>> getNodes() {
return chunks;
}
public Iterator<Chunk<Node>> getIn(Chunk<Node> chunk) {
final Set<Node> chunkNodes = chunk.getNodes();
final Set<Chunk<Node>> ins = new LinkedHashSet<Chunk<Node>>();
for (final Node node : chunkNodes) {
for (Iterator<Node> nodeIns = graph.getIn(node); nodeIns.hasNext(); ) {
final Node in = nodeIns.next();
if (!chunk.containsNode(in)) {
ins.add(nodeToChunkMap.get(in));
}
}
}
return ins.iterator();
}
}));
}
@Override
public <Node> Graph<Node> createGraphByInArcs(final MultiMap<Node, Node> inArcs) {
final Set<Node> nodes = new LinkedHashSet<Node>();
nodes.addAll(inArcs.keySet());
nodes.addAll(inArcs.values());
return new GraphGenerator<Node>(new CachingSemiGraph<Node>(new GraphGenerator.SemiGraph<Node>() {
@Override
public Collection<Node> getNodes() {
return nodes;
}
@Override
public Iterator<Node> getIn(Node n) {
return inArcs.get(n).iterator();
}
}));
}
@NotNull
@Override
public <Node> List<List<Node>> removePathsWithCycles(@NotNull List<List<Node>> paths) {
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
* Use is subject to license terms.
*/
package com.intellij.util.graph;
import com.intellij.util.Chunk;
import java.util.*;
/**
* @author Eugene Zhuravlev
* Date: Sep 27, 2004
*/
@SuppressWarnings({"HardCodedStringLiteral"})
public class ChunkGraphTest extends GraphTestCase {
public void testGraph1() {
final Map<String, String> arcs = new HashMap<String, String>();
arcs.put("a", "b");
arcs.put("b", "c");
arcs.put("c", "bd");
arcs.put("d", "e");
arcs.put("e", "d");
final Graph<Chunk<String>> graph = getAlgorithmsInstance().computeSCCGraph(initGraph(arcs));
final List<Chunk<String>> expectedNodes = new ArrayList<Chunk<String>>();
Chunk<String> A = new Chunk<String>("a");
expectedNodes.add(A);
Chunk<String> BC = new Chunk<String>(toSet("b", "c"));
expectedNodes.add(BC);
Chunk<String> DE = new Chunk<String>(toSet("d", "e"));
expectedNodes.add(DE);
checkVertices(expectedNodes, graph.getNodes().iterator());
final Map<Chunk<String>, Set<Chunk<String>>> expectedArcs = new HashMap<Chunk<String>, Set<Chunk<String>>>();
expectedArcs.put(A, toSet());
expectedArcs.put(BC, toSet(A));
expectedArcs.put(DE, toSet(BC));
checkArcs(expectedArcs, graph);
}
public void testGraph2() {
final Map<String, String> arcs = new HashMap<String, String>();
arcs.put("a", "b");
arcs.put("b", "ac");
arcs.put("c", "ad");
arcs.put("d", "");
final Graph<Chunk<String>> graph = getAlgorithmsInstance().computeSCCGraph(initGraph(arcs));
final List<Chunk<String>> expectedNodes = new ArrayList<Chunk<String>>();
Chunk<String> ABC = new Chunk<String>(toSet("a", "b", "c"));
expectedNodes.add(ABC);
Chunk<String> D = new Chunk<String>("d");
expectedNodes.add(D);
checkVertices(expectedNodes, graph.getNodes().iterator());
final Map<Chunk<String>, Set<Chunk<String>>> expectedArcs = new HashMap<Chunk<String>, Set<Chunk<String>>>();
expectedArcs.put(ABC, toSet());
expectedArcs.put(D, toSet(ABC));
checkArcs(expectedArcs, graph);
}
private static void checkArcs(Map<Chunk<String>, Set<Chunk<String>>> expectedArcs, Graph<Chunk<String>> graph) {
for (Chunk<String> chunk : graph.getNodes()) {
final List<Chunk<String>> ins = new ArrayList<Chunk<String>>();
final Iterator<Chunk<String>> insIterator = graph.getIn(chunk);
while (insIterator.hasNext()) {
ins.add(insIterator.next());
}
final Set<Chunk<String>> expectedIns = expectedArcs.get(chunk);
assertTrue(expectedIns.size() == ins.size());
assertTrue(expectedIns.equals(new HashSet<Chunk<String>>(ins)));
}
}
private static <T> Set<T> toSet(T... strings) {
return new HashSet<T>(Arrays.asList(strings));
}
private static Set<Chunk<String>> toSet() {
return new HashSet<Chunk<String>>();
}
private static Set<Chunk<String>> toSet(Chunk<String> c) {
return Collections.singleton(c);
}
private static void checkVertices(List<Chunk<String>> expected, Iterator<Chunk<String>> nodes) {
List<Chunk<String>> realNodes = new ArrayList<Chunk<String>>();
while (nodes.hasNext()) {
realNodes.add(nodes.next());
}
assertTrue(expected.size() == realNodes.size());
assertTrue(new HashSet<Chunk<String>>(expected).equals(new HashSet<Chunk<String>>(realNodes)));
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.util.graph;
import com.intellij.util.graph.impl.GraphAlgorithmsImpl;
import junit.framework.TestCase;
import java.util.*;
@@ -23,6 +24,10 @@ import java.util.*;
* @author nik
*/
public abstract class GraphTestCase extends TestCase {
protected static GraphAlgorithmsImpl getAlgorithmsInstance() {
return new GraphAlgorithmsImpl();
}
protected static Graph<String> initGraph(final Map<String, String> graph) {
final Map<String, List<String>> out = new HashMap<String, List<String>>();
final Map<String, List<String>> in = new HashMap<String, List<String>>();
@@ -150,8 +150,7 @@ public class KShortestPathsFinderTest extends GraphTestCase {
private static void doTest(Map<String, String> graph, final String start, final String finish, final int k, String... expectedPaths) {
final Graph<String> generator = initGraph(graph);
final KShortestPathsFinder<String> finder = new KShortestPathsFinder<String>(generator, start, finish, new EmptyProgressIndicator());
final List<List<String>> paths = finder.findShortestPaths(k);
final List<List<String>> paths = getAlgorithmsInstance().findKShortestPaths(generator, start, finish, k, new EmptyProgressIndicator());
List<String> pathStrings = new ArrayList<String>();
Set<Integer> sizes = new HashSet<Integer>();
for (List<String> path : paths) {
@@ -16,11 +16,13 @@
package com.intellij.util.graph;
import com.intellij.util.graph.impl.CycleFinder;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.testFramework.UsefulTestCase;
import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* User: anna
@@ -81,8 +83,7 @@ public class SearchCyclesTest extends GraphTestCase {
}
private static void doTest(HashMap<String, String> graph, final String node, String... expected) {
Graph<String> stringGraph = initGraph(graph);
final Set<List<String>> nodeCycles = new CycleFinder<String>(stringGraph).getNodeCycles(node);
final Set<List<String>> nodeCycles = getAlgorithmsInstance().findCycles(initGraph(graph), node);
checkResult(expected, nodeCycles);
}
@@ -17,7 +17,6 @@
package com.intellij.util.graph;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.graph.impl.ShortestPathFinder;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
@@ -90,7 +89,6 @@ public class ShortestPathTest extends GraphTestCase {
@Nullable
private static List<String> getShortestPath(Map<String, String> graph, final String from, final String to) {
Graph<String> graphGenerator = initGraph(graph);
return new ShortestPathFinder<String>(graphGenerator).findPath(from, to);
return getAlgorithmsInstance().findShortestPath(initGraph(graph), from, to);
}
}