// GraphTea Project: http://github.com/graphtheorysoftware/GraphTea // Copyright (C) 2012 Graph Theory Software Foundation: http://GraphTheorySoftware.com // Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology // Distributed under the terms of the GNU General Public License (GPL): http://www.gnu.org/licenses/ package graphtea.plugins.main.core; import graphtea.graph.graph.GPoint; import graphtea.graph.graph.GRect; import graphtea.graph.graph.Vertex; import graphtea.library.BaseEdge; import graphtea.library.BaseGraph; import graphtea.library.BaseVertex; import graphtea.library.Path; import graphtea.library.algorithms.util.LibraryUtils; import java.util.*; /** * Just some methods helping you to write Graph Algorithms easier, * * @see graphtea.library.algorithms.util.LibraryUtils */ public class AlgorithmUtils { public final static int Max_Int = 2100000000; /** * sets all vertex colors to 0. */ public static void resetVertexColors(BaseGraph<BaseVertex, BaseEdge<BaseVertex>> g) { for (BaseVertex v : g) { v.setColor(0); } } /** * sets all vertex marks to false */ public static void resetVertexMarks(BaseGraph<BaseVertex, BaseEdge<BaseVertex>> g) { for (BaseVertex v : g) { v.setMark(false); } } /** * determines wether g is connected or not */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> boolean isConnected(BaseGraph<VertexType, EdgeType> g) { ArrayList vs = new ArrayList(); int[] parent = new int[g.getVerticesCount()]; for(int i=0;i < g.getVerticesCount();i++) parent[i] = -1; dfs(g, 0, vs, parent); return Arrays.stream(vs.toArray()).distinct().toArray().length == g.getVerticesCount(); } /** * determines wether g is complete or not */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> boolean isCompleteGraph(BaseGraph<VertexType, EdgeType> g) { int size = g.getVerticesCount(); for (int i : getDegreesList(g)) if (i != size - 1) return false; return true; } /** * returns the adjacency list of g. * * @deprecated use BaseGraph.getEdgeArray instead */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> ArrayList<ArrayList<Integer>> getAdjList(BaseGraph<VertexType, EdgeType> g) { double[][] mat = g.getAdjacencyMatrix().getArray(); ArrayList<ArrayList<Integer>> alist = new ArrayList<>(); int vCount = mat.length; for (int i = 0; i < vCount; i++) { ArrayList<Integer> adjacencyList = new ArrayList<>(); for (int j = 0; j < vCount; j++) if (mat[i][j] == 1) adjacencyList.add(j); alist.add(adjacencyList); } return alist; } /** * returns the degree of the node with the id * @deprecated * @see graphtea.library.BaseGraph#getDegree(graphtea.library.BaseVertex) */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> int getDegree(BaseGraph<VertexType, EdgeType> bg, int node) { int result = 0; double[][] mat = bg.getAdjacencyMatrix().getArray(); int vCount = mat.length; if (node > vCount) return -1; for (int i = 0; i < vCount; i++) if (mat[node][i] == 1) result++; return result; } /** * returns all neighbors of the given vertex * @deprecated * @see graphtea.library.BaseGraph#getNeighbors(graphtea.library.BaseVertex) */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> ArrayList<VertexType> getNeighbors(BaseGraph<VertexType, EdgeType> g, VertexType source) { ArrayList<VertexType> ret = new ArrayList<>(); Iterator<EdgeType> ie = g.edgeIterator(source); while (ie.hasNext()) { EdgeType e = ie.next(); if (e.target == source && !ret.contains(e.source)) ret.add(e.source); if (e.source == source && !ret.contains(e.target)) ret.add(e.target); } return ret; } /** * returns all neighbors of the given vertex * @deprecated * @see graphtea.library.BaseGraph#getNeighbors(graphtea.library.BaseVertex) */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> ArrayList<VertexType> getNeighbors2(BaseGraph<VertexType, EdgeType> g, VertexType source) { ArrayList<VertexType> ret = new ArrayList<>(); Iterator<EdgeType> ie = g.edgeIterator(source); while (ie.hasNext()) { EdgeType e = ie.next(); // if (e.target == source && !ret.contains(e.source)) // ret.add(e.source); if (e.source == source && !ret.contains(e.target)) ret.add(e.target); } return ret; } /** * returns a path from source to target * path.get(0) = dest */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> Path<VertexType> getPath(BaseGraph<VertexType, EdgeType> g, VertexType source, VertexType dest) { boolean vertexMarksBackup[] = LibraryUtils.getVertexMarks(g); clearVertexMarks(g); Vector<VertexType> q = new Vector<>(); q.add(source); source.setMark(true); BaseVertex[] parents = new BaseVertex[g.getVerticesCount()]; boolean found = false; while (!q.isEmpty() && !found) { VertexType v = q.remove(0); for (VertexType neigh : getNeighbors(g, v)) { if (neigh == dest) { found = true; // break; } if (!neigh.getMark()) { q.add(neigh); neigh.setMark(true); parents[neigh.getId()] = v; } } } if (!found) { return null; } //extract the path Path<VertexType> ret = new Path<>(); int did = dest.getId(); ret.insert(dest); while (did != source.getId()) { ret.insert((VertexType) parents[did]); if (parents[did] == null) return null; did = parents[did].getId(); } LibraryUtils.setVertexMarks(g, vertexMarksBackup); return ret; } /** * returns the parent of v, if ve DFS on parent */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> VertexType getParent(BaseGraph<VertexType, EdgeType> g, VertexType treeRoot, VertexType v) { return getPath(g, treeRoot, v).get(1); } /** * clears all vertex marks */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> void clearVertexMarks(BaseGraph<VertexType, EdgeType> g) { for (VertexType type : g) { type.setMark(false); } } /** * returns the subtree rooted by subTreeRoot in the rooted tree tree with the root treeRoot * the vertices are ordered by their distances to subTreeRoot * the exact distance is placed in v.getProp().obj as an Integer, starting distance is 0 which is subTreeRoot */ public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>> ArrayList<Vertex> getSubTree(BaseGraph<Vertex, Edge> tree, Vertex treeRoot, Vertex subTreeRoot) { boolean vertexMarksBackup[] = LibraryUtils.getVertexMarks(tree); Path<Vertex> pathToRoot = getPath(tree, treeRoot, subTreeRoot); clearVertexMarks(tree); //close the path to tree root for (Vertex vertex : pathToRoot) { vertex.setMark(true); } ArrayList<Vertex> ret = BFS(tree, subTreeRoot, null); LibraryUtils.setVertexMarks(tree, vertexMarksBackup); return ret; } /** * gets the vertices in the order of AlgorithmUtils.getSubTree() */ public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>> ArrayList<Vertex> BFSOrder(BaseGraph<Vertex, Edge> unRootedTree, Vertex treeRoot) { boolean vertexMarksBackup[] = LibraryUtils.getVertexMarks(unRootedTree); clearVertexMarks(unRootedTree); ArrayList<Vertex> ret = BFS(unRootedTree, treeRoot, null); LibraryUtils.setVertexMarks(unRootedTree, vertexMarksBackup); return ret; } /** * runs a BFS on graph, starting the given vertex as the root */ public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>> void BFSrun(BaseGraph<Vertex, Edge> unRootedTree, Vertex treeRoot, BFSListener<Vertex> listener) { boolean vertexMarksBackup[] = LibraryUtils.getVertexMarks(unRootedTree); clearVertexMarks(unRootedTree); BFS(unRootedTree, treeRoot, listener); LibraryUtils.setVertexMarks(unRootedTree, vertexMarksBackup); } /** * performs a full BFS on graph, it selects the vertices with minimum degrees as the * roots of the resulting forest * * @param unRootedTree An unrooted tree * @param listener The listener */ public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>> void BFS(BaseGraph<Vertex, Edge> unRootedTree, BFSListener<Vertex> listener) { boolean vertexMarksBackup[] = LibraryUtils.getVertexMarks(unRootedTree); clearVertexMarks(unRootedTree); for (Vertex v : unRootedTree) { if (!v.getMark()) { BFS(unRootedTree, v, listener); } } LibraryUtils.setVertexMarks(unRootedTree, vertexMarksBackup); } /** * performs a bfs on the given root, * this method changes vertex marks, and also marked vertices will not be traversed * * @param unRootedTree An unrooted tree * @param treeRoot The tree root * @param listener The listener * @return The results of the BFS algorithm */ public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>> ArrayList<Vertex> BFS(BaseGraph<Vertex, Edge> unRootedTree, Vertex treeRoot, BFSListener<Vertex> listener) { //do a bfs on the subTreeRoot ArrayList<Vertex> q = new ArrayList<>(); ArrayList<Vertex> ret = new ArrayList<>(); q.add(treeRoot); ret.add(treeRoot); treeRoot.setMark(true); treeRoot.getProp().obj = 0; while (!q.isEmpty()) { Vertex v = q.remove(0); for (Vertex vertex : unRootedTree.getNeighbors(v)) { if (!vertex.getMark()) { q.add(vertex); ret.add(vertex); vertex.setMark(true); vertex.getProp().obj = ((Integer) v.getProp().obj) + 1; //set the distance if (listener != null) listener.visit(vertex, v); } } } return ret; } /** * runs a dfs and fills visit and parent, visit is the visiting order of vertices and parent[i] is the id of i'th vertex parent * the parent array should be initialized by -1 */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> void dfs(BaseGraph<VertexType, EdgeType> g, int node, ArrayList visit, int parent[]) { visit.add(node); ArrayList e = getAdjList(g); ArrayList neighbors = (ArrayList) e.get(node); for (Object neighbor1 : neighbors) { int neighbor = (Integer) neighbor1; if (parent[neighbor] == -1) { parent[neighbor] = node; dfs(g, neighbor, visit, parent); } } } /** * retunrs the degree of vertex (indegree + outdegree) */ public static int getTotalDegree(BaseGraph g, BaseVertex v) { return g.getOutDegree(v) + g.getInDegree(v); } /** * returns the root which is assigned to each vertex * it is the minimum id vertex in the corresponding component of vertex */ public static <Vertex extends BaseVertex, Edge extends BaseEdge<Vertex>> Vertex getRoot(BaseGraph<Vertex, Edge> g, Vertex v) { ArrayList<Vertex> componentVertices = BFSOrder(g, v); Vertex rootCandidate = v; for (Vertex vertex : componentVertices) { if (vertex.getId() < v.getId()) { rootCandidate = vertex; } } return rootCandidate; } /** * returns the angle between 3 vertices in graphical world! */ public static double getAngle(Vertex root, Vertex v1, Vertex v2) { GPoint rootp = root.getLocation(); GPoint v1p = v1.getLocation(); GPoint v2p = v2.getLocation(); return getAngle(rootp, v1p, v2p); } /** * returns the angle between 3 points */ public static double getAngle(GPoint rootp, GPoint v1p, GPoint v2p) { double px = v1p.x - rootp.x; double py = v1p.y - rootp.y; double qx = v2p.x - rootp.x; double qy = v2p.y - rootp.y; double pDOTq = px * qx + py * qy; double plength = getLength(px, py); double qlength = getLength(qx, qy); double cartesianProd = py * qx - px * qy; if (plength == 0 || qlength == 0) return 0; else { double alfacos = pDOTq / (plength * qlength); double alfa = Math.acos(alfacos); // return (Math.signum(cartesianProd) < 0 ? 2 * Math.PI - Math.acos(alfacos) : Math.acos(alfacos)); return alfa; } } /** * returns the length of the given vector */ public static double getLength(double dx, double dy) { return GPoint.distance(0,0,dx,dy); } /** * moves the vertex relative to its current position */ public static void move(Vertex v, double dx, double dy) { GPoint loc = v.getLocation(); v.setLocation(new GPoint(loc.x + dx, loc.y + dy)); } /** * returns the distance between two vertices in pixels, (in graphics not the path length between them) */ public static double getDistance(Vertex v1, Vertex v2) { return GPoint.distance(v1.getLocation().x, v1.getLocation().y, v2.getLocation().x, v2.getLocation().y); } /** * returns the distance between two points */ public static double getDistance(GPoint p1, GPoint p2) { return GPoint.distance(p1.x, p1.y, p2.x, p2.y); } /** * @return the angle between vector p2-p1 and X-Axis */ public static double getAngle(GPoint p1, GPoint p2) { double angle = Math.atan2(p1.y - p2.y, p1.x - p2.x); if (angle < 0) { // atan2 returns getAngle in phase -pi to pi, which means // we have to convert the answer into 0 to 2pi range. angle += 2 * Math.PI; } return angle; } /** * locations v in a r-teta cordination */ public static void setLocation(Vertex v, GPoint center, double radius, double ang) { v.setLocation(new GPoint(center.x + radius * Math.cos(ang), center.y + radius * Math.sin(ang))); } /** * @return the bounding rectangle arround vertices */ public static GRect getBoundingRegion(Collection<Vertex> vertices) { GRect ret = new GRect(); boolean first = true; for (Vertex v : vertices) { GPoint p = v.getLocation(); if (first) { ret = new GRect(p.x, p.y, 0, 0); first = false; } ret.add(p); } return ret; } public static GPoint getCenter(Collection<Vertex> V) { GPoint center = new GPoint(0, 0); for (Vertex v : V) { GPoint loc = v.getLocation(); center.x += loc.x; center.y += loc.y; } center.x = center.x / V.size(); center.y = center.y / V.size(); return center; } /** * returns the vertex degrees as a list, sorted by vertex ids */ public static <VertexType extends BaseVertex, EdgeType extends BaseEdge<VertexType>> ArrayList<Integer> getDegreesList(BaseGraph<VertexType, EdgeType> g) { ArrayList<Integer> result = new ArrayList<>(); int vCount = g.getVertexArray().length; for (int i = 0; i < vCount; i++) result.add(getDegree(g, i)); return result; } public interface BFSListener<Vertex extends BaseVertex> { void visit(Vertex v, Vertex parent); } /** * @param p1 The first point * @param p2 The second point * @return a point whose x and y are average of the given graph points. */ public static GPoint getMiddlePoint(GPoint p1, GPoint p2) { return new GPoint((p1.x + p2.x) / 2, (p1.y + p2.y) / 2); } public static GPoint normalize(GPoint vector) { double size = Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2)); GPoint ret = new GPoint(vector); if (size != 0) ret.multiply(1 / size); return ret; } }