/**
   * 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;
  }