/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * ImmutableDirectedGraph.java * Created: Oct 12, 2007 * By: Joseph Wong */ package org.openquark.cal.samples.directedgraph; import java.util.List; import org.openquark.cal.runtime.CALExecutorException; import org.openquark.cal.runtime.CalValue; import org.openquark.cal.runtime.ExecutionContext; import org.openquark.util.Pair; /** * This class represents an immutable directed graph. It is intended to showcase the * use of CAL modules via standalone library JARs. The functionality of this class is * provided by the underlying CAL module {@code Cal.Utilities.DirectedGraph}, is * exposed via the API module {@code Cal.Samples.DirectedGraphLibrary}. * * @see org.openquark.cal.samples.directedgraph.DirectedGraphLibrary * * @author Andrew Casey * @author Joseph Wong */ public final class ImmutableDirectedGraph<T> { /** * The underlying CalValue of the graph. */ private final CalValue graph; /** * The execution context associated with the graph. */ private final ExecutionContext executionContext; /** * Constructs an instance with the graph represented as a CalValue. * @param graph the underlying graph. * @param executionContext the execution context associated with the graph. */ private ImmutableDirectedGraph(final CalValue graph, final ExecutionContext executionContext) { if (graph == null || executionContext == null) { throw new NullPointerException(); } this.graph = graph; this.executionContext = executionContext; } /** * @return the underlying CalValue of the graph. */ public CalValue getCalValue() { return graph; } /** * @return the execution context associated with the graph. */ public ExecutionContext getExecutionContext() { return executionContext; } /** * Factory method. * @param graph the underlying graph. * @return a new instance with the given graph and the existing execution context. */ private <V> ImmutableDirectedGraph<V> makeUpdated(final CalValue graph) { return new ImmutableDirectedGraph<V>(graph, executionContext); } /** * Factory method. * @param graph the underlying graph. * @param executionContext the execution context associated with the graph. * @return a new instance with the given graph and the existing execution context. */ private static <V> ImmutableDirectedGraph<V> make(final CalValue graph, final ExecutionContext executionContext) { return new ImmutableDirectedGraph<V>(graph, executionContext); } /** * Checks that the two graphs are constructed using the same execution context. * @param graph1 the first graph. * @param graph2 the second graph. */ private static void checkExecutionContexts(final ImmutableDirectedGraph<?> graph1, final ImmutableDirectedGraph<?> graph2) { if (graph1.executionContext != graph2.executionContext) { throw new IllegalArgumentException("The graphs are constructed with different execution contexts"); } } /** * Adds a new edge to a graph. If the specified edge is already in the graph, * then the resulting graph will be the same as the original. * <p> * <strong>Note</strong>: If the one or both vertices are not already in the graph, then * they will be added - the first before the second. * * @param newEdge * the edge to be added. * @return * a graph containing the same vertices and edges as the original, with * the possible addition of the specified edge and its endpoints. */ public ImmutableDirectedGraph<T> addEdge(final Pair<T, T> newEdge) throws CALExecutorException { return makeUpdated(DirectedGraphLibrary.addEdge(graph, newEdge, executionContext)); } /** * For each pair of vertices <code>v1</code> and <code>v2</code>, add an edge <code>(v1, v2)</code> * to the graph if and only if <code>existsEdgeFn v1 v2</code> returns <code>True</code>. * @param existsEdgeFn * a predicate function indicating, for each ordered-pair of * vertices in the graph, whether an edge should be added from one to the other. * @return * a new graph containing all of the vertices and edges in the original * graph, plus the edges induced by the specified predicate function. */ public ImmutableDirectedGraph<T> addEdges(final Predicate<Pair<T, T>> existsEdgeFn) throws CALExecutorException { return makeUpdated(DirectedGraphLibrary.addEdges(graph, existsEdgeFn, executionContext)); } /** * Adds a new vertex to a graph. If the specified vertex is already in the graph, * then the resulting graph will be the same as the original (including the * insertion order of the vertices). * @param vertex * the vertex to be added. * @return * a graph containing the same vertices and edges as the original, with * the possible addition of the specified vertex. */ public ImmutableDirectedGraph<T> addVertex(final T vertex) throws CALExecutorException { return makeUpdated(DirectedGraphLibrary.addVertex(graph, vertex, executionContext)); } /** * <code>containsEdge graph (vertex1, vertex2)</code> returns <code>True</code> if * <code>vertex1</code> and <code>vertex2</code> are vertices of <code>graph</code> and * <code>(vertex1, vertex2)</code> is an edge of <code>graph</code>. * @param edge * the value to be tested for membership in the graph. * @return * <code>True</code> if <code>edge</code> is an edge of <code>graph</code>; * <code>False</code> otherwise. */ public boolean containsEdge(final Pair<T, T> edge) throws CALExecutorException { return DirectedGraphLibrary.containsEdge(graph, edge, executionContext); } /** * <code>containsVertex graph vertex</code> returns <code>True</code> if <code>vertex</code> * is a vertex of <code>graph</code>. * @param vertex * the value to be tested for membership in the graph. * @return * <code>True</code> if <code>vertex</code> is a vertex of <code>graph</code>; * <code>False</code> otherwise. */ public boolean containsVertex(final T vertex) throws CALExecutorException { return DirectedGraphLibrary.containsVertex(graph, vertex, executionContext); } /** * Constructs a graph containing the specified vertices and no edges. * <p> * <strong>Note</strong>: The order of the vertices is preserved. * * @param vertices * the vertices of the graph to be constructed. * @return * a graph containing the specified vertices and no edges. */ public static <V> ImmutableDirectedGraph<V> edgelessGraph(final List<V> vertices, final ExecutionContext executionContext) throws CALExecutorException { return make(DirectedGraphLibrary.edgelessGraph(vertices, executionContext), executionContext); } /** * Constructs an empty graph. * @return (CAL type: {@code Cal.Utilities.DirectedGraph.DirectedGraph Vertex}) * an empty graph. */ public static <V> ImmutableDirectedGraph<V> emptyGraph(final ExecutionContext executionContext) throws CALExecutorException { return make(DirectedGraphLibrary.emptyGraph(executionContext), executionContext); } /** * Determines whether or not two graphs have the same vertices and whether each * vertex has the same neighbours in both graphs. * @param graph1 * the first graph. * @param graph2 * the second graph. * @return * <code>True</code> if the graphs have the same vertices with the same * neighbours; <code>False</code> otherwise. */ public static <V> boolean equalsDirectedGraphIgnoreInsertionOrder(final ImmutableDirectedGraph<V> graph1, final ImmutableDirectedGraph<V> graph2) throws CALExecutorException { checkExecutionContexts(graph1, graph2); return DirectedGraphLibrary.equalsDirectedGraphIgnoreInsertionOrder(graph1.graph, graph2.graph, graph1.executionContext); } /** * Determines whether or not two graphs have the same vertices, whether each vertex * has the same neighbours in both graphs, and whether both graphs have the same * vertex insertion order. * @param graph1 * the first graph. * @param graph2 * the second graph. * @return * <code>True</code> if the graphs have the same vertices with the same * neighbours and inserted in the same order; <code>False</code> otherwise. */ public static <V> boolean equalsDirectedGraphWithInsertionOrder(final ImmutableDirectedGraph<V> graph1, final ImmutableDirectedGraph<V> graph2) throws CALExecutorException { checkExecutionContexts(graph1, graph2); return DirectedGraphLibrary.equalsDirectedGraphWithInsertionOrder(graph1.graph, graph2.graph, graph1.executionContext); } /** * Determines whether or not the specified graph contains a cycle. * * @see #findCycle * * @return * <code>True</code> if the graph contains a cycle; <code>False</code> otherwise. */ public boolean existsCycle() throws CALExecutorException { return DirectedGraphLibrary.existsCycle(graph, executionContext); } /** * Determines whether the graph contains a path from path from <code>startVertex</code> * to <code>endVertex</code>. * <p> * <strong>Note</strong>: each vertex is considered to have a trivial path from itself to itself. * * * @see #findPath * * @param startVertex * the vertex from which to search. * @param endVertex * the vertex to seek. * @return * <code>True</code> if the graph contains a path from <code>startVertex</code> * to <code>endVertex</code>; <code>False</code> otherwise. */ public boolean existsPath(final T startVertex, final T endVertex) throws CALExecutorException { return DirectedGraphLibrary.existsPath(graph, startVertex, endVertex, executionContext); } /** * Determines whether or not the specified graph contains a cycle reachable from * the specified vertex. * * @see #findReachableCycle * * @param startVertex * the vertex from which to seek a cycle. * @return * <code>True</code> if the graph contains a reachable cycle; <code>False</code> otherwise. */ public boolean existsReachableCycle(final T startVertex) throws CALExecutorException { return DirectedGraphLibrary.existsReachableCycle(graph, startVertex, executionContext); } /** * Eliminates from the graph all elements for which <code>filterFn</code> returns * <code>False</code>. * * @see #partition * * @param filterFn * a function which returns <code>True</code> if a vertex belongs * in the subgraph and <code>False</code> otherwise. * @return * the subgraph induced by the filter function. */ public ImmutableDirectedGraph<T> filter(final Predicate<T> filterFn) throws CALExecutorException { return makeUpdated(DirectedGraphLibrary.filter(filterFn, graph, executionContext)); } /** * Returns a list of vertices forming a cycle (first list element is duplicated * in the last position), if one exists. * <p> * <strong>Note</strong>: if the graph contains multiple cycles, then any one may be returned. * * * @see #findReachableCycle * @see #existsCycle * * @return * the cycle if one exists, or <code>null</code> otherwise. */ @SuppressWarnings("unchecked") public List<T> findCycle() throws CALExecutorException { return DirectedGraphLibrary.findCycle(graph, executionContext); } /** * Returns a list of vertices forming a path from <code>startVertex</code> to * <code>endVertex</code> (inclusive), if one exists. * <p> * <strong>Note</strong>: if the graph contains multiple such paths, then any one may be returned. * <strong>Note</strong>: each vertex is considered to have a trivial path from itself to itself. * * * @see #existsPath * * @param startVertex * the vertex from which to search. * @param endVertex * the vertex to seek. * @return * the path if one exists, or <code>null</code> otherwise. */ @SuppressWarnings("unchecked") public List<T> findPath(final T startVertex, final T endVertex) throws CALExecutorException { return DirectedGraphLibrary.findPath(graph, startVertex, endVertex, executionContext); } /** * Returns a list of vertices forming a cycle reachable from the specified * vertex (first list element is duplicated in the last position), if one exists. * <p> * <strong>Note</strong>: if the reachable subgraph contains multiple cycles, then any one * may be returned. * * * @see #findCycle * @see #existsReachableCycle * * @param startVertex * the vertex from which to seek a cycle. * @return * the cycle if one exists, or <code>null</code> otherwise. */ @SuppressWarnings("unchecked") public List<T> findReachableCycle(final T startVertex) throws CALExecutorException { return DirectedGraphLibrary.findReachableCycle(graph, startVertex, executionContext); } /** * Returns a copy of the graph that contains no cycles - within each strongly- * connected component, all edges are removed and replaced with new edges * enforcing the insertion order. * <p> * e.g. <code>cycle{B, A} -> F -> cycle{C, E, D}</code> becomes * <code>A -> B -> F -> C -> D -> E</code> (assuming the vertices were created in * alphabetical order). * * @return * a copy of the specified graph with all cycles flattened. */ public ImmutableDirectedGraph<T> flattenComponents() throws CALExecutorException { return makeUpdated(DirectedGraphLibrary.flattenComponents(graph, executionContext)); } /** * Fold across the entire graph in depth-first search order. * <p> * <strong>Note</strong>: this is simply an alias for {@link #foldInDepthFirstSearchOrder foldInDepthFirstSearchOrder}. * * * @see #foldInDepthFirstSearchOrder * @see #foldReachableDFS * @see #foldReachableInDepthFirstSearchOrder * * @param startVertexFn * called when a vertex is visited for the first time. * Guaranteed to be called exactly once per vertex. * @param finishVertexFn * called when a vertex is finished (all children are finished). * Guaranteed to be called exactly once per vertex. * @param init * the initial accumulator value (returned directly if the graph is empty). * @return * The accumulated value after the final vertex is finished. */ @SuppressWarnings("unchecked") public <A> A foldDFS(final BinaryFunction<A, T, A> startVertexFn, final BinaryFunction<A, T, A> finishVertexFn, final A init) throws CALExecutorException { return (A)DirectedGraphLibrary.foldDFS(startVertexFn, finishVertexFn, init, graph, executionContext); } /** * Fold across the entire graph in depth-first search order. * <p> * <strong>Note</strong>: for convenience, you may wish to call this function as {@link #foldDFS foldDFS}. * * * @see #foldDFS * @see #foldReachableDFS * @see #foldReachableInDepthFirstSearchOrder * * @param startVertexFn * called when a vertex is visited for the first time. * Guaranteed to be called exactly once per vertex. * @param finishVertexFn * called when a vertex is finished (all children are finished). * Guaranteed to be called exactly once per vertex. * @param init * the initial accumulator value (returned directly if the graph is empty). * @return * The accumulated value after the final vertex is finished. */ @SuppressWarnings("unchecked") public <A> A foldInDepthFirstSearchOrder(final BinaryFunction<A, T, A> startVertexFn, final BinaryFunction<A, T, A> finishVertexFn, final A init) throws CALExecutorException { return (A)DirectedGraphLibrary.foldInDepthFirstSearchOrder(startVertexFn, finishVertexFn, init, graph, executionContext); } /** * Fold across graph vertices reachable from the specified root in depth-first * search order. * <p> * <strong>Note</strong>: this is simply an alias for {@link #foldReachableInDepthFirstSearchOrder foldReachableInDepthFirstSearchOrder}. * * * @see #foldDFS * @see #foldInDepthFirstSearchOrder * @see #foldReachableInDepthFirstSearchOrder * * @param startVertex * the vertex at which to begin the traversal. * @param startVertexFn * called when a vertex is visited for the first time. * Guaranteed to be called exactly once for each reachable vertex. * @param finishVertexFn * called when a vertex is finished (all children are finished). * Guaranteed to be called exactly once for each reachable vertex. * @param init * the initial accumulator value (returned directly if the graph is empty). * @return * The accumulated value after the final vertex is finished. */ @SuppressWarnings("unchecked") public <A> A foldReachableDFS(final T startVertex, final BinaryFunction<A, T, A> startVertexFn, final BinaryFunction<A, T, A> finishVertexFn, final A init) throws CALExecutorException { return (A)DirectedGraphLibrary.foldReachableDFS(startVertex, startVertexFn, finishVertexFn, init, graph, executionContext); } /** * Fold across graph vertices reachable from the specified root in depth-first * search order. * <p> * <strong>Note</strong>: for convenience, you may wish to call this function as {@link #foldReachableDFS foldReachableDFS}. * * * @see #foldDFS * @see #foldInDepthFirstSearchOrder * @see #foldReachableDFS * * @param startVertex * the vertex at which to begin the traversal. * @param startVertexFn * called when a vertex is visited for the first time. * Guaranteed to be called exactly once for each reachable vertex. * @param finishVertexFn * called when a vertex is finished (all children are finished). * Guaranteed to be called exactly once for each reachable vertex. * @param init * the initial accumulator value (returned directly if the graph is empty). * @return * The accumulated value after the final vertex is finished. */ @SuppressWarnings("unchecked") public <A> A foldReachableInDepthFirstSearchOrder(final T startVertex, final BinaryFunction<A, T, A> startVertexFn, final BinaryFunction<A, T, A> finishVertexFn, final A init) throws CALExecutorException { return (A)DirectedGraphLibrary.foldReachableInDepthFirstSearchOrder(startVertex, startVertexFn, finishVertexFn, init, graph, executionContext); } /** * Returns the number of edges in the specified graph. * @return * the number of edges in the graph. */ public int getEdgeCount() throws CALExecutorException { return DirectedGraphLibrary.getEdgeCount(graph, executionContext); } /** * Returns the list of out-neighbours of the specified vertex. No particular * order is guaranteed. * @param vertex * the vertex whose neighbours are sought. * @return * a list of out-neighbours of the specified vertex. */ @SuppressWarnings("unchecked") public List<T> getNeighbours(final T vertex) throws CALExecutorException { return DirectedGraphLibrary.getNeighbours(graph, vertex, executionContext); } /** * Returns the number of vertices in the specified graph. * @return * the number of vertices in the graph. */ public int getVertexCount() throws CALExecutorException { return DirectedGraphLibrary.getVertexCount(graph, executionContext); } /** * Returns a list of the vertices in the specified graph. No particular order * is guaranteed. * @return * a list of vertices in the graph. */ @SuppressWarnings("unchecked") public List<T> getVertices() throws CALExecutorException { return DirectedGraphLibrary.getVertices(graph, executionContext); } /** * Returns a list of the vertices in the specified graph. The vertices will * be ordered by insertion time. * @return * a list of vertices in the graph. */ @SuppressWarnings("unchecked") public List<T> getVerticesInInsertionOrder() throws CALExecutorException { return DirectedGraphLibrary.getVerticesInInsertionOrder(graph, executionContext); } /** * Returns whether the specified graph is the empty graph (i.e. contains no * vertices). * @return * <code>True</code> if the graph is empty; <code>False</code> otherwise. */ public boolean isEmpty() throws CALExecutorException { return DirectedGraphLibrary.isEmpty(graph, executionContext); } /** * Constructs a graph containing the specified vertices and edges. * <p> * <strong>Note</strong>: If an edge <code>(v1, v2)</code> is specified and if either * <code>v1</code> or <code>v2</code> is not in the list of vertices, then it will be * added to the graph anyway. * <p> * <strong>Note</strong>: The insertion order will be determined by the order in which * such vertices are encountered while adding edges to the graph. For example, * <code>getVerticesInInsertionOrder (makeGraph [v1, v2] [(v1, v3), (v4, v2)])</code> * will return <code>[v1, v2, v3, v4]</code>. * * @param vertices * the vertices of the graph to be constructed. * @param edges * the edges of the graph to be constructed. * @return * a graph containing the specified vertices and edges. */ public static <V> ImmutableDirectedGraph<V> makeGraph(final List<V> vertices, final List<Pair<V, V>> edges, final ExecutionContext executionContext) throws CALExecutorException { return make(DirectedGraphLibrary.makeGraph(vertices, edges, executionContext), executionContext); } /** * Constructs a graph containing the specified vertices. For each pair of vertices * <code>v1</code> and <code>v2</code>, the graph will contain contain an edge <code>(v1, v2)</code> * if and only if <code>existsEdgeFn v1 v2</code> returns <code>True</code>. * <p> * <strong>Note</strong>: The order of the vertices is preserved. * * * @see #addEdges * * @param vertices * the vertices of the graph to be constructed. * @param existsEdgeFn * a predicate function indicating, for each ordered-pair of * vertices in the graph, whether an edge exists from one to the other. * @return * a graph containing the specified vertices and the edges induced by * the specified predicate function. */ public static <V> ImmutableDirectedGraph<V> makePredicateGraph(final List<V> vertices, final Predicate<Pair<V, V>> existsEdgeFn, final ExecutionContext executionContext) throws CALExecutorException { return make(DirectedGraphLibrary.makePredicateGraph(vertices, existsEdgeFn, executionContext), executionContext); } /** * Applies the specified function to each vertex in the specified graph. * <p> * <strong>Note</strong>: If two vertices have the same image under the specified function, * then they will be merged (retaining self-loops created during the merge). * * * @see #mergeVertices * * @param mapFn the mapping function. * @return * the graph that results when the specified function is applied to each * vertex in the specified graph. */ public <V> ImmutableDirectedGraph<V> map(final UnaryFunction<T, V> mapFn) throws CALExecutorException { return makeUpdated(DirectedGraphLibrary.map(mapFn, graph, executionContext)); } /** * Merges two vertices of a graph. * <code>mergeVertices graph retainLoops vertex1 vertex2 mergedVertex</code> * results in a graph satisfying: * <ul> * <li> * <code>vertex1</code> is removed from the graph * </li> * <li> * <code>vertex2</code> is removed from the graph * </li> * <li> * <code>mergedVertex</code> is added to the graph * </li> * <li> * Edges are transformed (modulo <code>retainLoops</code>): * <ul> * <li> * <code>(A, ?)</code> -> <code>(C, ?)</code> * </li> * <li> * <code>(?, A)</code> -> <code>(?, C)</code> * </li> * <li> * <code>(B, ?)</code> -> <code>(C, ?)</code> * </li> * <li> * <code>(?, B)</code> -> <code>(?, C)</code> * </li> * </ul> * </li> * </ul> * <p> * * <p> * <strong>Side effect</strong>: If the merged vertex is already contained in the graph, then * its insertion order will not change. Otherwise, the merged vertex will * acquire the insertion order of the first vertex argument (note: argument 1, * not vertex with earlier insertion time). * <p> * <strong>Note</strong>: Throws an <code>error</code> if either <code>vertex1</code> or * <code>vertex2</code> is not in the graph. * * @param retainLoops * if vertices A and B are merged to C and the graph contains * (A, B) or (B, A), then (C, C) will be added to the new graph if retainLoops * is true. Note that if the graph contains (A, A) or (B, B), then the new * graph will contain (C, C) regardless of the value of retainLoops. * @param vertex1 * the first vertex to be merged (error if invalid). * @param vertex2 * the second vertex to be merged (error if invalid). * @param mergedVertex * the resulting vertex. * @return * a new graph with the two vertices merged. */ public ImmutableDirectedGraph<T> mergeVertices(final boolean retainLoops, final T vertex1, final T vertex2, final T mergedVertex) throws CALExecutorException { return makeUpdated(DirectedGraphLibrary.mergeVertices(graph, retainLoops, vertex1, vertex2, mergedVertex, executionContext)); } /** * Partitions the vertices into two sets and then removes all edges from one set * to the other. * * @see #filter * * @param partitionFn * a function which returns <code>True</code> if a vertex belongs * in the first subgraph and <code>False</code> if a vertex belongs in the second * subgraph. * @return * the two subgraphs induced by the partition function. */ @SuppressWarnings("unchecked") public Pair<ImmutableDirectedGraph<T>, ImmutableDirectedGraph<T>> partition(final Predicate<T> partitionFn) throws CALExecutorException { return DirectedGraphLibrary.partition(partitionFn, graph, executionContext); } /** * Removes an edge from a graph. If the specified edge is already absent from * the graph (perhaps because one of the endpoints is absent from the graph), * then the resulting graph will be the same as the original. * @param edge * the edge to be removed. * @return * a graph containing the same vertices and edges as the original, with * the possible exception of the specified edge. */ public ImmutableDirectedGraph<T> removeEdge(final Pair<T, T> edge) throws CALExecutorException { return makeUpdated(DirectedGraphLibrary.removeEdge(graph, edge, executionContext)); } /** * Removes a new vertex from a graph. If the specified vertex is already absent * from the graph, then the resulting graph will be the same as the original. * @param vertex * the vertex to be removed. * @return * a graph containing the same vertices and edges as the original, with * the possible exception of the specified vertex. */ public ImmutableDirectedGraph<T> removeVertex(final T vertex) throws CALExecutorException { return makeUpdated(DirectedGraphLibrary.removeVertex(graph, vertex, executionContext)); } /** * Reverses all of the edges in a graph (sometimes referred to as the transpose * graph). * @return * the graph with all edges reversed. */ public ImmutableDirectedGraph<T> reverse() throws CALExecutorException { return makeUpdated(DirectedGraphLibrary.reverse(graph, executionContext)); } /** * Returns a string representation of the graph. The graph is traversed in depth-first * order and each vertex is displayed with a list of its children. Any vertex * that has previously been encountered will be shown in angle brackets and * not expanded (i.e. its children will not be shown). For example: * <p> * * <pre> vertex1 { * child_1 { * grandchild_1_1 { * } * grandchild_1_2 { * } * child_2 { * grandchild_2_1 { * } * <grandchild_1_1> <-- Details omitted since repeated * } * } * vertex2 { * ... * } * </pre> * * * @return * a string representation of the graph. */ public java.lang.String showDirectedGraph() throws CALExecutorException { return DirectedGraphLibrary.showDirectedGraph(graph, executionContext); } /** * Returns the source vertex of the given edge. * @param edge * the edge. * @return * the source vertex of the given edge. */ public static <V> V sourceVertex(final Pair<V, V> edge) { return edge.fst(); } /** * Returns the vertices in topological order if the graph is acyclic and in an * unspecified order otherwise. If the relative order of two vertices is not * specified by the graph, then their insertion order will be used. * <p> * If the graph contains cycles, then calling {@link #flattenComponents flattenComponents} before * {@link #stableTopologicalSort stableTopologicalSort} will produce the desired stable order. * <p> * <strong>Note</strong>: this may be quite a bit slower than a normal topological sort. * <p> * Algorithm adapted from Exercise 22.4-5 on p. 552 of * "Introduction to Algorithms 2E" * by Cormen, Leiserson, Rivest, and Stein (2002). * * @return * an ordered list of vertices. */ @SuppressWarnings("unchecked") public List<T> stableTopologicalSort() throws CALExecutorException { return DirectedGraphLibrary.stableTopologicalSort(graph, executionContext); } /** * Returns a topologically sorted list of strongly-connected components of a * specified graph (i.e. if A and B are SCCs and A precedes B in the returned * list, then the graph contains no edges from vertices in A to vertices in B). * <p> * Algorithm based on STRONGLY-CONNECTED-COMPONENTS on p. 554 of * "Introduction to Algorithms 2E" * by Cormen, Leiserson, Rivest, and Stein (2002). * * @return * a topologically sorted list of strongly-connected components of the * specified graph. */ @SuppressWarnings("unchecked") public List<List<T>> stronglyConnectedComponents() throws CALExecutorException { return DirectedGraphLibrary.stronglyConnectedComponents(graph, executionContext); } /** * Returns the target vertex of the given edge. * @param edge * the edge. * @return * the target vertex of the given edge. */ public static <V> V targetVertex(final Pair<V, V> edge) { return edge.snd(); } /** * Returns the vertices of a graph in topological order if the graph is acyclic * and in an unspecified order otherwise. * <p> * Algorithm based on TOPOLOGICAL-SORT on p. 550 of * "Introduction to Algorithms 2E" * by Cormen, Leiserson, Rivest, and Stein (2002). * * @return * an ordered list of vertices. */ @SuppressWarnings("unchecked") public List<T> topologicalSort() throws CALExecutorException { return DirectedGraphLibrary.topologicalSort(graph, executionContext); } /** * Since most clients of foldDFS will not want to use all of the function arguments, * this is provided as a convenient default (simply passes the accumulated value * through to the next handler). */ public static <A, V> BinaryFunction<A, V, A> noChange() { return new BinaryFunction<A, V, A>() { public A apply(final A arg1, final V arg2) { return arg1; } }; } /** * {@inheritDoc} */ @Override public String toString() { try { return showDirectedGraph(); } catch (CALExecutorException e) { throw new IllegalStateException(e); } } }