/** * This method is recursive. * * @param currentShortestPathToEnd Can be null. * @return A shortest path to end as a prolonging of rootPath, strictly shorter than the specified * shortest path to end, else the specified shortest path to end, possibly null. */ private static List<InterfaceVertex> tryFindAShorterPathToEndFromIncluded( List<InterfaceVertex> prevPath, InterfaceVertex currentVertex, // TreeSet<InterfaceVertex> endVertexSet, TreeMap<InterfaceVertex, Integer> shortestLengthByReached, // List<InterfaceVertex> currentShortestPathToEnd) { // Creating the path by prolonging to current vertex. final ArrayList<InterfaceVertex> currentPath = new ArrayList<InterfaceVertex>(prevPath.size() + 1); currentPath.addAll(prevPath); currentPath.add(currentVertex); // If we reached end set, that's the best we could do from current vertex. if (endVertexSet.contains(currentVertex)) { // Replacing only if better than current. if ((currentShortestPathToEnd == null) || (currentPath.size() < currentShortestPathToEnd.size())) { currentShortestPathToEnd = currentPath; } } else { // Looking further. final int currentLengthToSucc = currentPath.size(); for (InterfaceVertex successor : currentVertex.successors()) { // If already reached that vertex with a path of same length or shorter, // prolonging our path with it here is useless. final Integer length = shortestLengthByReached.get(successor); if ((length != null) && (length.intValue() <= currentLengthToSucc)) { continue; } // Replaces null, or a longer length. shortestLengthByReached.put(successor, currentLengthToSucc); // Did not reach end set yet: looking further. currentShortestPathToEnd = tryFindAShorterPathToEndFromIncluded( currentPath, successor, // endVertexSet, shortestLengthByReached, // currentShortestPathToEnd); } } return currentShortestPathToEnd; }
/** * @param maxSize Must be != 0. * @return True if must stop, false otherwise. */ private static boolean computeShortestCycles_onScc( ArrayList<InterfaceVertex> scc, int maxSize, InterfaceVertexCollProcessor processor) { if (maxSize == 0) { throw new AssertionError(); } /* * Dealing with SCCs of size 1 (and with cycles of size 1 along with that), * early, before creating a work graph, because it's the common case for * graphs with no or few cycles. */ for (InterfaceVertex v : scc) { // Might have vertices outside the SCC as successors, // so need to use contains(...). if (v.successors().contains(v)) { // Has itself as successor. if (processOneBackingVertexCycle(v, processor)) { return true; } } } if ((scc.size() == 1) || (maxSize == 1)) { // No need to go further here. return false; } /* * Using a work graph, not so much to have predecessors (which could * easily be computed by iterating on successors of each vertex), than * not to have edges dangling outside the SCC, and to be able to remove * cycles of size 1, to make things simpler. */ final ArrayList<WorkVertex> workScc = WorkGraphUtilz.newWorkGraphAsArrayList(scc); // Removing elementary circuits (already processed if any). for (WorkVertex v : workScc) { WorkGraphUtilz.removeEdge(v, v); } /* * */ final ArrayList<WorkVertex> bfsPredCleanupList = new ArrayList<WorkVertex>(); // Sets initially never empty, since we are in an SCC of size > 1, // and mapping is removed when set gets empty, to help memory. final Map<WorkVertex, Set<WorkVertex>> predToVisitSetByVertex = new HashMap<WorkVertex, Set<WorkVertex>>(); for (WorkVertex v : workScc) { predToVisitSetByVertex.put(v, new HashSet<WorkVertex>(v.predecessors())); } if (MUST_SORT_VERTICES) { SortUtils.sort(workScc, WORK_VERTEX_COMPARATOR); } /* * name_in_paper/name_in_code: * * parent: predecessor (parent/child more suited to tree-like graphs) * x: x (vertex) * A: predToVisitSet (set of vertices) * Q: queue (FIFO of vertices) * p: v (vertex) * y: succ (Vertex) * c: cycle * B: v.successors() (set of vertices) */ for (WorkVertex x : workScc) { final Set<WorkVertex> predToVisitSet = predToVisitSetByVertex.get(x); if (predToVisitSet == null) { // Corresponding cycles already taken care of. continue; } resetAndClear(bfsPredCleanupList); setBfsPredWithCleanup(x, TOMBSTONE, bfsPredCleanupList); final LinkedList<WorkVertex> queue = new LinkedList<WorkVertex>(); push(queue, x); while (predToVisitSet.size() != 0) { // Queue must not be empty, as long as we work on a SCC. final WorkVertex v = pop(queue); for (WorkVertex succ : v.successors()) { /* * Modif/paper: simpler conditional code. */ if (getBfsPred(succ) == null) { // Not yet "visited" nor enqueued. setBfsPredWithCleanup(succ, v, bfsPredCleanupList); push(queue, succ); } if (removeAndCleanupIfEmpty( predToVisitSet, succ, // predToVisitSetByVertex, x)) { /* * Found a new cycle. */ final ArrayList<WorkVertex> cycle = newReversedCycle(workScc.size(), succ); final boolean smallEnoughForProcess = (maxSize < 0) || (cycle.size() <= maxSize); if (smallEnoughForProcess) { dereverse(cycle); if (processCycle(cycle, processor)) { return true; } /* * Modif/paper: removal of already covered edges. */ removeCycleEdges(predToVisitSetByVertex, cycle); } } } } } return false; }