package com.plectix.simulator.staticanalysis.graphs; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.Stack; public final class Graph { private final LinkedList<Edge> edges = new LinkedList<Edge>(); private final ArrayList<Vertex> vertices = new ArrayList<Vertex>(); public final ArrayList<Vertex> getVertices() { return vertices; } /** * Notice! Source and target of edge will be changed * * @return */ public final List<Edge> getAllEdgesInDirectedCycles() { this.clearTags(); LinkedList<Edge> list = new LinkedList<Edge>(); Stack<Vertex> stack = new Stack<Vertex>(); vertices.get(0).setParentVertex(null); vertices.get(0).setTag(true); vertices.get(0).setMark2(true); stack.push(vertices.get(0)); int temp = 0; while (temp != -1) { if (!stack.empty()) { doStep(stack, list); } else { temp = needExploreMore(); if (temp != -1) { Vertex v = vertices.get(temp); stack.push(v); v.setTag(true); v.setMark2(true); } } } return list; } /** * we move the path. isTag = true when path include this vertex doesn't use * neighbor * * @param stack * @param list */ private final void doStep(Stack<Vertex> stack, LinkedList<Edge> list) { Vertex from = stack.peek(); Vertex to = from.next(); if (to == from) { addEdgeToAnswer(list, from, to); return; } if (to != null) { if (!to.isTag()) { to.setParentVertex(from); to.setMark2(true); to.setTag(true); stack.push(to); } else { extractCycle(from, to, list, stack); } } else { stack.pop().setTag(false); } } private final int needExploreMore() { for (int i = 0; i < vertices.size(); i++) { if (!vertices.get(i).isMark2()) { return i; } } return -1; } // redraw all egdes from vertices of cycle form one Big vertex private final void extractCycle(Vertex startVertex, Vertex lastVertex, LinkedList<Edge> edges, Stack<Vertex> verticesStack) { addEdgeToAnswer(edges, startVertex, lastVertex); while (startVertex != lastVertex) { addEdgeToAnswer(edges, startVertex.getParentVertex(), startVertex); mergeVertices(lastVertex, startVertex); startVertex = startVertex.getParentVertex(); verticesStack.pop(); } } private final void addEdgeToAnswer(LinkedList<Edge> answer, Vertex startVertex, Vertex endVertex) { Edge edge = getEdge(startVertex, endVertex); answer.add(edge); startVertex.removeEdge(edge); endVertex.removeEdge(edge); endVertex.setExplored(0); startVertex.setExplored(0); edges.remove(edge); } private final void mergeVertices(Vertex mergedVertex, Vertex vertex) { for (Edge eTemp : vertex.getEdges()) { if (eTemp.getTarget() == vertex) { eTemp.setTarget(mergedVertex); mergedVertex.addEdge(eTemp); } if (eTemp.getSource() == vertex) { eTemp.setSource(mergedVertex); mergedVertex.addEdge(eTemp); } } vertex.clearEdges(); } public final ArrayList<ArrayList<Vertex>> getAllWeakClosureComponent() { this.closeGraph(); this.sortVerticesByOutDegree(); this.clearTags(); ArrayList<ArrayList<Vertex>> result = new ArrayList<ArrayList<Vertex>>(); int vertexIndex = 0; while (vertexIndex < vertices.size()) { if (vertices.get(vertexIndex).isTag()) { vertexIndex++; } else { ArrayList<Vertex> anotherSet = new ArrayList<Vertex>(); vertices.get(vertexIndex).setTag(true); anotherSet.add(vertices.get(vertexIndex)); for (Edge e : vertices.get(vertexIndex).getEdges()) { if (e.getSource() == vertices.get(vertexIndex)) { anotherSet.add(e.getTarget()); e.getTarget().setTag(true); } } result.add(anotherSet); } } return result; } private final void sortVerticesByOutDegree() { for (int i = 0; i < vertices.size(); i++) { outDegree(vertices.get(i)); } // may be optimize Vertex temp = new Vertex(); for (int i = 0; i < vertices.size() - 1; i++) { for (int j = i + 1; j < vertices.size(); j++) { if (vertices.get(i).getNeighbourVertices().size() < vertices.get(j).getNeighbourVertices().size()) { temp = vertices.get(i); vertices.set(i, vertices.get(j)); vertices.set(j, temp); } } } } // change neighbors private final void outDegree(Vertex v) { for (Edge e : v.getEdges()) { if (e.getTarget() == v) v.removeNeighbour(e.getSource()); } } private final void clearTags() { for (int i = 0; i < vertices.size(); i++) { vertices.get(i).setTag(false); } } /** * Computes the transitive closure of the given graph. * * @param graph * - Graph to compute transitive closure for. */ final void closeGraph() { Set<Vertex> newEdgeTargets = new LinkedHashSet<Vertex>(); // At every iteration of the outer loop, we add a path of length 1 // between nodes that originally had a path of length 2. In the worst // case, we need to make floor(log |V|) + 1 iterations. We stop earlier // if there is no change to the output graph. int bound = computeBinaryLog(vertices.size()); boolean done = false; for (int i = 0; !done && (i < bound); ++i) { done = true; for (Vertex v1 : vertices) { newEdgeTargets.clear(); for (Edge v1OutEdge : v1.getEdges()) { Vertex v2 = v1OutEdge.getTarget(); if (v2 != v1) { for (Edge v2OutEdge : v2.getEdges()) { Vertex v3 = v2OutEdge.getTarget(); if (v3 != v2) { if (v1.equals(v3)) { // Its a simple graph, so no self loops. continue; } if (getEdge(v1, v3) != null) { // There is already an edge from v1 ---> v3, // skip; continue; } newEdgeTargets.add(v3); done = false; } } } } for (Vertex v3 : newEdgeTargets) { addEdge(v1, v3); } } } } public final void addVertex(Vertex vertex) { vertices.add(vertex); } private final Edge getEdge(Vertex startVertex, Vertex endVertex) { for (Edge edge : startVertex.getEdges()) { if (edge.getTarget() == endVertex) return edge; } return null; } private final void addEdge(Vertex startVertex, Vertex endVertex) { Edge newEdge = new Edge(startVertex, endVertex); edges.add(newEdge); startVertex.addEdge(newEdge); endVertex.addEdge(newEdge); startVertex.addNeighbourVertex(endVertex); endVertex.addNeighbourVertex(startVertex); } /** * Computes floor(log_2(n)) + 1 */ private final int computeBinaryLog(int n) { assert n >= 0; int result = 0; while (n > 0) { n >>= 1; ++result; } return result; } public final void addEdge(Edge edge) { if (getEdge(edge.getSource(), edge.getTarget()) == null) { edges.add(edge); edge.getSource().addEdge(edge); edge.getSource().addNeighbourVertex(edge.getTarget()); edge.getTarget().addEdge(edge); edge.getTarget().addNeighbourVertex(edge.getSource()); } } }