All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.teavm.common.GraphUtils Maven / Gradle / Ivy

There is a newer version: 0.2.8
Show newest version
/*
 *  Copyright 2011 Alexey Andreev.
 *
 *  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 org.teavm.common;

import com.carrotsearch.hppc.IntStack;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.IntPredicate;
import java.util.stream.Collectors;

public final class GraphUtils {
    static final byte NONE = 0;
    static final byte VISITING = 1;
    static final byte VISITED = 2;

    private GraphUtils() {
    }

    public static int[] findBackEdges(Graph graph) {
        int sz = graph.size();
        int[] stack = new int[sz * 2];
        int stackSize = 0;
        byte[] state = new byte[sz];
        for (int i = 0; i < sz; ++i) {
            if (graph.incomingEdgesCount(i) == 0) {
                stack[stackSize++] = i;
            }
        }
        IntegerArray result = new IntegerArray(2);
        while (stackSize > 0) {
            int node = stack[--stackSize];
            switch (state[node]) {
                case NONE:
                    state[node] = VISITING;
                    stack[stackSize++] = node;
                    for (int next : graph.outgoingEdges(node)) {
                        switch (state[next]) {
                            case NONE:
                                stack[stackSize++] = next;
                                break;
                            case VISITING:
                                result.add(node);
                                result.add(next);
                                break;
                        }
                    }
                    break;
                case VISITING:
                    state[node] = VISITED;
                    break;
            }
        }
        return result.getAll();
    }

    public static Graph removeLoops(Graph graph) {
        int sz = graph.size();
        int[] stack = new int[sz * 2];
        int stackSize = 0;
        byte[] state = new byte[sz];
        for (int i = 0; i < sz; ++i) {
            if (graph.incomingEdgesCount(i) == 0) {
                stack[stackSize++] = i;
            }
        }
        GraphBuilder builder = new GraphBuilder(graph.size());
        while (stackSize > 0) {
            int node = stack[--stackSize];
            switch (state[node]) {
                case NONE:
                    state[node] = VISITING;
                    stack[stackSize++] = node;
                    for (int next : graph.outgoingEdges(node)) {
                        switch (state[next]) {
                            case NONE:
                                stack[stackSize++] = next;
                                builder.addEdge(node, next);
                                break;
                            case VISITED:
                                builder.addEdge(node, next);
                                break;
                        }
                    }
                    break;
                case VISITING:
                    state[node] = VISITED;
                    break;
            }
        }
        return builder.build();
    }

    public static boolean isIrreducible(Graph graph) {
        DominatorTree dom = buildDominatorTree(graph);
        int[] backEdges = findBackEdges(graph);
        for (int i = 0; i < backEdges.length; i += 2) {
            if (!dom.dominates(backEdges[i + 1], backEdges[i])) {
                return true;
            }
        }
        return false;
    }

    public static Graph subgraph(Graph graph, IntPredicate filter) {
        if (graph instanceof FilteredGraph) {
            FilteredGraph filteredGraph = (FilteredGraph) graph;
            IntPredicate oldFilter = filteredGraph.filter;
            IntPredicate newFilter = v -> oldFilter.test(v) && filter.test(v);
            return new FilteredGraph(filteredGraph.innerGraph, newFilter);
        } else {
            return new FilteredGraph(graph, filter);
        }
    }

    static class FilteredGraph implements Graph {
        final Graph innerGraph;
        final IntPredicate filter;

        FilteredGraph(Graph innerGraph, IntPredicate filter) {
            this.innerGraph = innerGraph;
            this.filter = filter;
        }

        @Override
        public int size() {
            return innerGraph.size();
        }

        @Override
        public int[] incomingEdges(int node) {
            if (!filter.test(node)) {
                return new int[0];
            }
            return filterNodes(innerGraph.incomingEdges(node));
        }

        private int[] filterNodes(int[] nodes) {
            int j = 0;
            for (int v : nodes) {
                if (filter.test(v)) {
                    nodes[j++] = v;
                }
            }
            if (j < nodes.length) {
                nodes = Arrays.copyOf(nodes, j);
            }
            return nodes;
        }

        @Override
        public int copyIncomingEdges(int node, int[] target) {
            if (!filter.test(node)) {
                return 0;
            }
            int[] result = incomingEdges(node);
            System.arraycopy(result, 0, target, 0, result.length);
            return result.length;
        }

        @Override
        public int[] outgoingEdges(int node) {
            if (!filter.test(node)) {
                return new int[0];
            }
            return filterNodes(innerGraph.outgoingEdges(node));
        }

        @Override
        public int copyOutgoingEdges(int node, int[] target) {
            if (!filter.test(node)) {
                return 0;
            }
            int[] result = outgoingEdges(node);
            System.arraycopy(result, 0, target, 0, result.length);
            return result.length;
        }

        @Override
        public int incomingEdgesCount(int node) {
            return incomingEdges(node).length;
        }

        @Override
        public int outgoingEdgesCount(int node) {
            return outgoingEdges(node).length;
        }

        @Override
        public String toString() {
            return GraphUtils.printToDot(this);
        }
    }

    /*
     * Tarjan's algorithm
     * See pseudocode at https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
     * This is a stackless version.
     */
    public static int[][] findStronglyConnectedComponents(Graph graph) {
        List components = new ArrayList<>();

        int index = 0;
        IntStack procStack = new IntStack();
        IntStack stack = new IntStack();
        int[] nodeIndex = new int[graph.size()];
        int[] nodeLowLink = new int[graph.size()];
        boolean[] nodeOnStack = new boolean[graph.size()];

        Arrays.fill(nodeIndex, -1);
        Arrays.fill(nodeLowLink, -2);

        for (int i = 0; i < graph.size(); ++i) {
            procStack.push(i);
            procStack.push(0);
        }

        while (!procStack.isEmpty()) {
            int state = procStack.pop();
            int v = procStack.pop();

            switch (state) {
                case 0: {
                    if (nodeIndex[v] >= 0) {
                        break;
                    }
                    nodeIndex[v] = index;
                    nodeLowLink[v] = index;
                    index++;
                    stack.push(v);
                    nodeOnStack[v] = true;

                    procStack.push(v);
                    procStack.push(3);

                    for (int w : graph.outgoingEdges(v)) {
                        procStack.push(w);
                        procStack.push(v);
                        procStack.push(1);
                    }
                    break;
                }

                case 1: {
                    int w = procStack.pop();

                    if (nodeIndex[w] < 0) {
                        procStack.push(w);
                        procStack.push(v);
                        procStack.push(2);

                        procStack.push(w);
                        procStack.push(0);
                    } else if (nodeOnStack[w]) {
                        nodeLowLink[v] = Math.min(nodeLowLink[v], nodeIndex[w]);
                    }

                    break;
                }

                case 2: {
                    int w = procStack.pop();
                    nodeLowLink[v] = Math.min(nodeLowLink[v], nodeLowLink[w]);
                    break;
                }

                case 3: {
                    if (nodeLowLink[v] == nodeIndex[v]) {
                        IntegerArray scc = new IntegerArray(4);
                        int w;
                        do {
                            w = stack.pop();
                            nodeOnStack[w] = false;
                            scc.add(w);
                        } while (w != v);

                        if (scc.size() > 1) {
                            components.add(scc.getAll());
                        } else {
                            for (int successor : graph.outgoingEdges(v)) {
                                if (successor == v) {
                                    components.add(scc.getAll());
                                    break;
                                }
                            }
                        }
                    }
                    break;
                }
            }
        }

        return components.toArray(new int[0][]);
    }

    public static DominatorTree buildDominatorTree(Graph graph) {
        return buildDominatorTree(graph, 0);
    }

    public static DominatorTree buildDominatorTree(Graph graph, int start) {
        DominatorTreeBuilder builder = new DominatorTreeBuilder(graph, start);
        builder.build();
        return new DefaultDominatorTree(builder.dominators, builder.vertices);
    }

    public static Graph buildDominatorGraph(DominatorTree domTree, int sz) {
        GraphBuilder graph = new GraphBuilder(sz);
        for (int i = 0; i < sz; ++i) {
            int idom = domTree.immediateDominatorOf(i);
            if (idom >= 0) {
                graph.addEdge(idom, i);
            }
        }
        return graph.build();
    }

    public static int[] dfs(Graph graph) {
        int[] result = new int[graph.size()];
        int[] state = new int[graph.size()];
        int[] stack = new int[graph.size() * 2];
        int top = 0;
        stack[top++] = 0;
        int index = graph.size();

        while (top > 0) {
            int node = stack[--top];
            switch (state[node]) {
                case 0:
                    state[node] = 1;
                    stack[top++] = node;
                    for (int successor : graph.outgoingEdges(node)) {
                        if (state[successor] == 0) {
                            stack[top++] = successor;
                        }
                    }
                    break;
                case 1:
                    result[node] = --index;
                    state[node] = 2;
                    break;
            }
        }

        return result;
    }

    public static void splitIrreducibleGraph(Graph graph, int[] weights, GraphSplittingBackend backend) {
        new IrreducibleGraphSplitter(backend, graph, weights).splitLoops();
    }

    public static int[][] findDominanceFrontiers(Graph cfg, DominatorTree domTree) {
        IntegerArray[] tmpFrontiers = new IntegerArray[cfg.size()];
        int[][] domFrontiers = new int[cfg.size()][];

        // For each node calculate the number of descendants in dominator tree
        int[] descCount = new int[cfg.size()];
        for (int i = 0; i < cfg.size(); ++i) {
            int idom = domTree.immediateDominatorOf(i);
            if (idom >= 0) {
                descCount[idom]++;
            }
        }

        // Push final nodes onto stack
        int[] stack = new int[cfg.size() * 2];
        int head = 0;
        for (int i = 0; i < cfg.size(); ++i) {
            if (descCount[i] == 0) {
                stack[head++] = i;
            }
        }

        // Process dominator tree in bottom-up order
        while (head > 0) {
            int node = stack[--head];
            IntegerArray frontier = tmpFrontiers[node];
            if (frontier == null) {
                frontier = new IntegerArray(1);
            }
            int idom = domTree.immediateDominatorOf(node);
            for (int successor : cfg.outgoingEdges(node)) {
                // If successor's immediate dominator is not the node,
                // then add successor to node's dominance frontiers
                if (domTree.immediateDominatorOf(successor) != node) {
                    frontier.add(successor);
                }
            }

            tmpFrontiers[node] = null;
            int[] frontierSet = makeSet(frontier);
            domFrontiers[node] = frontierSet;

            if (idom >= 0) {
                // Propagate current set to immediate dominator
                for (int element : frontierSet) {
                    if (domTree.immediateDominatorOf(element) != idom) {
                        IntegerArray idomFrontier = tmpFrontiers[idom];
                        if (idomFrontier == null) {
                            idomFrontier = new IntegerArray(1);
                            tmpFrontiers[idom] = idomFrontier;
                        }
                        idomFrontier.add(element);
                    }
                }

                // Schedule processing the immediate dominator if all of its ancestors
                // in dominator tree have been processed
                if (--descCount[idom] == 0) {
                    stack[head++] = idom;
                }
            }
        }

        return domFrontiers;
    }

    private static int[] makeSet(IntegerArray array) {
        int[] items = array.getAll();
        int[] set = new int[items.length];
        int sz = 0;
        int last = -1;
        for (int item : items) {
            if (item != last) {
                set[sz++] = item;
                last = item;
            }
        }
        if (sz != set.length) {
            set = Arrays.copyOf(set, sz);
        }
        return set;
    }

    public static String printToDot(Graph graph) {
        StringBuilder sb = new StringBuilder("digraph G {\n");
        for (int i = 0; i < graph.size(); ++i) {
            String successors = Arrays.stream(graph.outgoingEdges(i))
                    .mapToObj(String::valueOf)
                    .collect(Collectors.joining(", "));
            sb.append("  ").append(i).append(" -> {").append(successors).append("}\n");
        }
        sb.append("}");
        return sb.toString();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy