mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Add unit tests for irreducible graph splitter
This commit is contained in:
parent
04677d0103
commit
e2aababde8
|
@ -31,37 +31,8 @@ public final class GraphUtils {
|
||||||
private GraphUtils() {
|
private GraphUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Graph invert(Graph graph) {
|
public static int[] findBackEdges(Graph graph) {
|
||||||
int sz = graph.size();
|
int sz = graph.size();
|
||||||
GraphBuilder result = new GraphBuilder();
|
|
||||||
int[] sourceEdges = new int[sz];
|
|
||||||
for (int node = 0; node < sz; ++node) {
|
|
||||||
int sourceCount = graph.copyIncomingEdges(node, sourceEdges);
|
|
||||||
for (int i = 0; i < sourceCount; ++i) {
|
|
||||||
int source = sourceEdges[i];
|
|
||||||
result.addEdge(node, source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Graph close(Graph graph) {
|
|
||||||
GraphBuilder result = new GraphBuilder();
|
|
||||||
for (int node = 0; node < graph.size(); ++node) {
|
|
||||||
int[] next = graph.outgoingEdges(node);
|
|
||||||
for (int target : next) {
|
|
||||||
result.addEdge(node, target);
|
|
||||||
}
|
|
||||||
if (next.length == 0) {
|
|
||||||
result.addEdge(node, graph.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Graph removeLoops(Graph graph) {
|
|
||||||
int sz = graph.size();
|
|
||||||
GraphBuilder result = new GraphBuilder();
|
|
||||||
int[] stack = new int[sz * 2];
|
int[] stack = new int[sz * 2];
|
||||||
int stackSize = 0;
|
int stackSize = 0;
|
||||||
byte[] state = new byte[sz];
|
byte[] state = new byte[sz];
|
||||||
|
@ -70,6 +41,7 @@ public final class GraphUtils {
|
||||||
stack[stackSize++] = i;
|
stack[stackSize++] = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
IntegerArray result = new IntegerArray(2);
|
||||||
while (stackSize > 0) {
|
while (stackSize > 0) {
|
||||||
int node = stack[--stackSize];
|
int node = stack[--stackSize];
|
||||||
switch (state[node]) {
|
switch (state[node]) {
|
||||||
|
@ -79,11 +51,11 @@ public final class GraphUtils {
|
||||||
for (int next : graph.outgoingEdges(node)) {
|
for (int next : graph.outgoingEdges(node)) {
|
||||||
switch (state[next]) {
|
switch (state[next]) {
|
||||||
case NONE:
|
case NONE:
|
||||||
result.addEdge(node, next);
|
|
||||||
stack[stackSize++] = next;
|
stack[stackSize++] = next;
|
||||||
break;
|
break;
|
||||||
case VISITED:
|
case VISITING:
|
||||||
result.addEdge(node, next);
|
result.add(node);
|
||||||
|
result.add(next);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,22 +65,25 @@ public final class GraphUtils {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result.build();
|
return result.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int edgeCount(Graph graph) {
|
public static boolean isIrreducible(Graph graph) {
|
||||||
int cnt = 0;
|
DominatorTree dom = buildDominatorTree(graph);
|
||||||
int sz = graph.size();
|
int[] backEdges = findBackEdges(graph);
|
||||||
for (int node = 0; node < sz; ++node) {
|
for (int i = 0; i < backEdges.length; i += 2) {
|
||||||
cnt += graph.outgoingEdgesCount(node);
|
if (!dom.dominates(backEdges[i + 1], backEdges[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cnt;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tarjan's algorithm
|
* Tarjan's algorithm
|
||||||
*/
|
*/
|
||||||
public static int[][] findStronglyConnectedComponents(Graph graph, int[] start, GraphNodeFilter filter) {
|
public static int[][] findStronglyConnectedComponents(Graph graph, int[] start, GraphNodeFilter filter) {
|
||||||
|
// TODO: can show incorrect behaviour sometimes
|
||||||
List<int[]> components = new ArrayList<>();
|
List<int[]> components = new ArrayList<>();
|
||||||
int[] visitIndex = new int[graph.size()];
|
int[] visitIndex = new int[graph.size()];
|
||||||
int[] headerIndex = new int[graph.size()];
|
int[] headerIndex = new int[graph.size()];
|
||||||
|
|
|
@ -17,6 +17,8 @@ package org.teavm.common;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
import com.carrotsearch.hppc.IntOpenHashSet;
|
||||||
|
import com.carrotsearch.hppc.IntSet;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -86,6 +88,71 @@ public class GraphTest {
|
||||||
assertThat(sccs[0], is(new int[] { 1, 2, 3, 4, 5 }));
|
assertThat(sccs[0], is(new int[] { 1, 2, 3, 4, 5 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void irreducibleGraphSplit() {
|
||||||
|
GraphBuilder builder = new GraphBuilder();
|
||||||
|
builder.addEdge(0, 1);
|
||||||
|
builder.addEdge(0, 2);
|
||||||
|
builder.addEdge(0, 3);
|
||||||
|
builder.addEdge(1, 2);
|
||||||
|
builder.addEdge(2, 1);
|
||||||
|
builder.addEdge(3, 2);
|
||||||
|
builder.addEdge(2, 4);
|
||||||
|
builder.addEdge(4, 5);
|
||||||
|
builder.addEdge(4, 1);
|
||||||
|
builder.addEdge(5, 3);
|
||||||
|
|
||||||
|
Graph graph = builder.build();
|
||||||
|
DefaultGraphSplittingBackend backend = new DefaultGraphSplittingBackend(graph);
|
||||||
|
int[] weights = { 1, 4, 1, 10, 1, 1 };
|
||||||
|
GraphUtils.splitIrreducibleGraph(graph, weights, backend);
|
||||||
|
Graph result = backend.getGraph();
|
||||||
|
|
||||||
|
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
||||||
|
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
||||||
|
assertTrue("Should be equialent", isEquialent(backend, graph));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void irreducibleGraphSplit2() {
|
||||||
|
GraphBuilder builder = new GraphBuilder();
|
||||||
|
builder.addEdge(0, 1);
|
||||||
|
builder.addEdge(0, 2);
|
||||||
|
builder.addEdge(1, 2);
|
||||||
|
builder.addEdge(2, 1);
|
||||||
|
Graph graph = builder.build();
|
||||||
|
|
||||||
|
DefaultGraphSplittingBackend backend = new DefaultGraphSplittingBackend(graph);
|
||||||
|
int[] weights = new int[graph.size()];
|
||||||
|
Arrays.fill(weights, 1);
|
||||||
|
GraphUtils.splitIrreducibleGraph(graph, weights, backend);
|
||||||
|
Graph result = backend.getGraph();
|
||||||
|
|
||||||
|
assertTrue("Should be irreducible", GraphUtils.isIrreducible(graph));
|
||||||
|
assertFalse("Should be reducible", GraphUtils.isIrreducible(result));
|
||||||
|
assertTrue("Should be equialent", isEquialent(backend, graph));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEquialent(DefaultGraphSplittingBackend backend, Graph proto) {
|
||||||
|
Graph graph = backend.getGraph();
|
||||||
|
for (int node = 0; node < graph.size(); ++node) {
|
||||||
|
int nodeProto = backend.prototype(node);
|
||||||
|
IntSet succProto = new IntOpenHashSet();
|
||||||
|
for (int succ : graph.outgoingEdges(node)) {
|
||||||
|
succProto.add(backend.prototype(succ));
|
||||||
|
}
|
||||||
|
if (succProto.size() != proto.outgoingEdgesCount(nodeProto)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int succ : proto.outgoingEdges(nodeProto)) {
|
||||||
|
if (!succProto.contains(succ)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private GraphNodeFilter filter = new GraphNodeFilter() {
|
private GraphNodeFilter filter = new GraphNodeFilter() {
|
||||||
@Override public boolean match(int node) {
|
@Override public boolean match(int node) {
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user