/*******************************************************************************
* Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2019)
*
* contact.vitam@culture.gouv.fr
*
* This software is a computer program whose purpose is to implement a digital archiving back-office system managing
* high volumetry securely and efficiently.
*
* This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free
* software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as
* circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
* users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the
* successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or
* developing or reproducing the software by the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it is reserved for developers and
* experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling the security of their systems and/or data
* to be ensured and, more generally, to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you
* accept its terms.
*******************************************************************************/
package fr.gouv.vitam.common.graph;
import java.util.Stack;
import fr.gouv.vitam.common.exception.CycleFoundException;
/**
* DirectedCycle Class
*/
public class DirectedCycle {
// has vertex v been marked
private final boolean[] marked;
// previous vertex on path to v
private final int[] edgeTo;
// is vertex on the stack?
private final boolean[] onStack;
// directed cycle (or null if no such cycle)
private Stack<Integer> cycle;
private boolean isCyclic;
/**
* DirectedCycle a constructor :fired when a cycle is found.
*
* @param graph the DirectedCycle
* @throws CycleFoundException
*/
public DirectedCycle(DirectedGraph graph) {
marked = new boolean[graph.getVertices()];
onStack = new boolean[graph.getVertices()];
edgeTo = new int[graph.getVertices()];
// FIXME P1 n² or even worth => while merging Graph and DIrectedGraph, you can have the "real" roots (from
// Graph)
// so using it
for (int v = 0; v < graph.getVertices(); v++) {
if (!marked[v] && cycle == null) {
depthFirstSearch(graph, v);
}
}
}
/**
*
* depthFirstSearch is a method for traversing or searching graph data structures. One starts at the root and
* explores as far as possible along each branch.
*
* @param graph
* @param root
* @throws CycleFoundException
*/
private void depthFirstSearch(DirectedGraph graph, int root) {
// TODO P1 : the case of graphs which are not strongly connected must be managed
onStack[root] = true;
marked[root] = true;
for (final int w : graph.adj(root)) {
// short circuit if directed cycle found
if (cycle != null) {
return;
} else if (!marked[w]) {
edgeTo[w] = root;
depthFirstSearch(graph, w);
} else if (onStack[w]) {
// trace back directed cycle
// FIXME P1 you reallocate memory (stack) how many times ??? Clean such as GC is not under pressure
// FIXME P1 If I understand correctly, once here, we have a cycle (cycle != null), so why doing the next
// computation ?
cycle = new Stack<>();
for (int x = root; x != w; x = edgeTo[x]) {
cycle.push(x);
}
cycle.push(w);
cycle.push(root);
if (check()) {
isCyclic = true;
return;
}
}
}
onStack[root] = false;
}
/**
* Does the DirectedCycle have a directed cycle
*
* @return <tt>true</tt> if the Graph has a directed cycle, <tt>false</tt> otherwise
*/
public boolean hasCycle() {
return cycle != null;
}
/**
* Returns a cycle if the Graph has a directed cycle, and <tt>null</tt> otherwise.
*
* @return a cycle (as an iterable) if the DirectedCycle has a cycle, and <tt>null</tt> otherwise
*/
private Iterable<Integer> cycle() {
return cycle;
}
/**
* certify that Graph has a cycle if it reports one
*
* @return boolean
*/
private boolean check() {
if (hasCycle()) {
// verify cycle
int first = -1;
int last = -1;
for (final int v : cycle()) {
if (first == -1) {
first = v;
}
last = v;
}
// graph don't have a directed cycle
if (first != last) {
return false;
}
return true;
}
return false;
}
/**
* isCyclic know of a graph is cyclic or not
*
* @return boolean
*/
public boolean isCyclic() {
return isCyclic;
}
}