/* * Copyright 2003-2011 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.util; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Stack; /** * <code>int[][] graph;</code> defines an oriented graph of {graph.length} vertices, where * graph[i] is an array of edges from vertex i (Adjacency list) * <p/> * Evgeny Gryaznov, Jan 11, 2010 */ public class GraphUtil { /** * set[i] is transformed to the res[k] | res[k] -- k-th nonzero element in the set array */ public static int[] setToList(int[] set) { int size = 0; for (int i = 0; i < set.length; i++) { if (set[i] == 1) { size++; } } int[] res = new int[size]; int e = 0; for (int i = 0; i < set.length; i++) { if (set[i] == 1) { res[e++] = i; } } return res; } public static int[][] removeOrientation(int[][] graph) { return merge(transpose(graph), graph); } /** * sorts every adjacency list in the graph */ public static void sort(int[][] graph) { for(int[] edges : graph) { Arrays.sort(edges); } } /** * preserve the graph[k] arrays sorted (!) */ public static int[][] transpose(int[][] graph) { int vertexCount = graph.length; int[] size = new int[vertexCount]; for (int v = 0; v < vertexCount; v++) { for (int target : graph[v]) { size[target]++; } } int[][] result = new int[vertexCount][]; for (int v = 0; v < vertexCount; v++) { result[v] = new int[size[v]]; size[v] = 0; } for (int v = 0; v < vertexCount; v++) { for (int target : graph[v]) { result[target][size[target]++] = v; } } return result; } /** * here we work with the sorted arrays */ public static int[][] merge(int[][] graph1, int[][] graph2) { assert graph1.length == graph2.length; int count = graph1.length; int[] data = new int[count]; int[][] result = new int[count][]; for (int vertex = 0; vertex < count; vertex++) { int size = mergeSortedArrays(data, graph1[vertex], graph2[vertex]); result[vertex] = new int[size]; System.arraycopy(data, 0, result[vertex], 0, size); } return result; } /** * pure util method to merge two sorted arrays */ private static int mergeSortedArrays(int[] target, int[] source1, int[] source2) { int size = 0, i1 = 0, i2 = 0; while (i1 < source1.length && i2 < source2.length) { if (source1[i1] < source2[i2]) { target[size++] = source1[i1++]; } else if (source1[i1] > source2[i2]) { target[size++] = source2[i2++]; } else { target[size++] = source1[i1]; i1++; i2++; } } while (i1 < source1.length) { target[size++] = source1[i1++]; } while (i2 < source2.length) { target[size++] = source2[i2++]; } return size; } public static int[][] components(int[][] graph) { int count = graph.length; List<int[]> result = new ArrayList<int[]>(); if (count < 2) { if (count == 1) { result.add(new int[]{0}); // fixme AP: is zero vertex connected to itself? } return result.toArray(new int[result.size()][]); } int[] component = new int[count]; int[] state = new int[count]; Stack<Integer> stack = new Stack<Integer>(); int nextVertex = 0; while (nextVertex < count) { int componentSize = 0; stack.add(nextVertex); state[nextVertex] = 1; nextVertex++; while (!stack.isEmpty()) { int current = stack.pop(); component[componentSize++] = current; for (int target : graph[current]) { if (state[target] == 0) { state[target] = 1; stack.push(target); } } } int[] res = new int[componentSize]; System.arraycopy(component, 0, res, 0, componentSize); result.add(res); while (nextVertex < count && state[nextVertex] != 0) { nextVertex++; } } return result.toArray(new int[result.size()][]); } private static class Tarjan { List<int[]> result = new ArrayList<int[]>(); int[] stack; int[] index; int[] lowlink; int[][] graph; boolean[] onstack; int current = 0; int stackTop = 0; public Tarjan(int[][] graph) { index = new int[graph.length]; Arrays.fill(index, -1); lowlink = new int[graph.length]; onstack = new boolean[graph.length]; stack = new int[graph.length]; this.graph = graph; } private int[][] run() { if (graph.length < 2) { if (graph.length == 1) { result.add(new int[]{0}); } return result.toArray(new int[result.size()][]); } for (int v = 0; v < graph.length; v++) { if (index[v] == -1) { tarjan(v); } } return result.toArray(new int[result.size()][]); } private void tarjan(int v) { index[v] = current; lowlink[v] = current; current++; stack[stackTop++] = v; onstack[v] = true; for (int vt : graph[v]) { if (index[vt] == -1) { tarjan(vt); lowlink[v] = Math.min(lowlink[v], lowlink[vt]); } else if (onstack[vt]) { lowlink[v] = Math.min(lowlink[v], index[vt]); } } if (lowlink[v] == index[v]) { int stackSize = stackTop; do { stackTop--; onstack[stack[stackTop]] = false; } while (stack[stackTop] != v); int[] res = new int[stackSize - stackTop]; System.arraycopy(stack, stackTop, res, 0, stackSize - stackTop); result.add(res); } } } public static int[][] tarjan(int[][] graph) { return new Tarjan(graph).run(); } /** * Partitions vertices into several sets to satisfy given relations. * * @param graph represents >= relation between vertices * @param strict represents > relation between vertices * @return partition index for each vertex, or -1-index in case of a conflict (ex: b >= a, a > b) */ public static int[] partition(int[][] graph, int[][] strict) { int[][] merged = merge(graph, strict); int[][] scc = tarjan(merged); int[] layer = new int[graph.length]; boolean[] conflicting = new boolean[graph.length]; Arrays.fill(layer, -1); for (int[] c : scc) { int cl = 0; boolean hasConflict = false; for (int i : c) { for (int v : graph[i]) { if (layer[v] >= 0) { cl = Math.max(cl, conflicting[v] ? layer[v] + 1 : layer[v]); } } for (int v : strict[i]) { if (layer[v] >= 0) { cl = Math.max(cl, layer[v] + 1); } else { hasConflict = true; } } } for (int i : c) { layer[i] = cl; conflicting[i] = hasConflict; } } for (int i = 0; i < layer.length; i++) { assert layer[i] >= 0; if (conflicting[i]) { layer[i] = -1 - layer[i]; } } return layer; } public static void printGraph(int[][] graph, Object[] associated) { for (int v = 0; v < graph.length; v++) { System.out.println(v + " (" + (associated != null ? associated[v] : "?") + ")"); System.out.print(" "); for (int i : graph[v]) { System.out.print(" "); System.out.print(i); } System.out.println(); } } }