19#ifndef HEDGEHOG_CYCLE_TEST_H_ 
   20#define HEDGEHOG_CYCLE_TEST_H_ 
   36template<tool::HedgehogDynamicGraphForStaticAnalysis GraphType>
 
   37class CycleTest : 
public hh_cx::AbstractTest<GraphType> {
 
   40  class SimpleSubGraph {
 
   42    using pointer_const_abstract_node = hh_cx::behavior::AbstractNode 
const *;
 
   44    std::vector<pointer_const_abstract_node>
 
   47    std::vector<std::vector<bool>>
 
   52    constexpr SimpleSubGraph() = 
default;
 
   57    constexpr SimpleSubGraph(hh_cx::Graph<GraphType> 
const *graph, 
size_t minNodeId) {
 
   58      for (
size_t nodeIdSrc = minNodeId; nodeIdSrc < graph->numberNodesRegistered(); ++nodeIdSrc) {
 
   59        for (
size_t nodeIdDest = minNodeId; nodeIdDest < graph->numberNodesRegistered(); ++nodeIdDest) {
 
   60          if (graph->isLinked(nodeIdSrc, nodeIdDest)) {
 
   61            this->addEdge(graph->node(nodeIdSrc), graph->node(nodeIdDest));
 
   68    virtual ~SimpleSubGraph() = 
default;
 
   72    [[nodiscard]] 
constexpr std::vector<pointer_const_abstract_node> 
const ®isteredNodes()
 const {
 
   73      return registeredNodes_;
 
   78    [[nodiscard]] 
constexpr size_t numberNodes()
 const { 
return registeredNodes_.size(); }
 
   83    [[nodiscard]] 
constexpr std::vector<pointer_const_abstract_node>
 
   84    adjacentNodes(pointer_const_abstract_node origin)
 const {
 
   85      std::vector<pointer_const_abstract_node> adjacentNodes{};
 
   86      auto senderId = nodeId(origin);
 
   87      for (
size_t receiverId = 0; receiverId < numberNodes(); ++receiverId) {
 
   88        if (isLinked(senderId, receiverId)) {
 
   89          adjacentNodes.push_back(node(receiverId));
 
   97    [[nodiscard]] 
constexpr size_t numberNodesRegistered()
 const { 
return registeredNodes_.size(); }
 
  103    [[nodiscard]] 
constexpr bool isLinked(pointer_const_abstract_node sender,
 
  104                                          pointer_const_abstract_node receiver)
 const {
 
  106          idSender = nodeId(sender),
 
  107          idReceiver = nodeId(receiver);
 
  109      if (idSender == numberNodes() || idReceiver == numberNodes()) { 
return false; }
 
  110      return adjacencyMatrix_[idSender][idReceiver];
 
  118    [[nodiscard]] 
constexpr bool isLinked(
size_t idSender, 
size_t idReceiver)
 const {
 
  119      if (idSender >= numberNodes() || idReceiver >= numberNodes()) {
 
  120        throw (std::runtime_error(
"The nodes you are trying to test do not exist in the graph."));
 
  122      return adjacencyMatrix_[idSender][idReceiver];
 
  128    [[nodiscard]]  
constexpr size_t nodeId(pointer_const_abstract_node node)
 const {
 
  129      if (!isNodeRegistered(node)) {
 
  130        throw (std::runtime_error(
"The node you are trying to get does not exist in the graph."));
 
  133      for (
auto registeredNode : registeredNodes_) {
 
  134        if (registeredNode == node) { 
return nodeId; }
 
  143    [[nodiscard]] 
constexpr pointer_const_abstract_node node(
size_t id)
 const {
 
  144      if (
id >= registeredNodes_.size()) {
 
  145        throw (std::runtime_error(
"The node you are requesting does not exist."));
 
  147        return registeredNodes_.at(
id);
 
  154    constexpr void addEdge(pointer_const_abstract_node sender, pointer_const_abstract_node receiver) {
 
  155      registerNode(sender);
 
  156      registerNode(receiver);
 
  157      adjacencyMatrix_.at(nodeId(sender)).at(nodeId(receiver)) = 
true;
 
  163    constexpr bool isNodeRegistered(pointer_const_abstract_node node)
 const {
 
  164      return std::find(registeredNodes_.cbegin(), registeredNodes_.cend(), node) != registeredNodes_.cend();
 
  169    constexpr void registerNode(pointer_const_abstract_node node) {
 
  170      if (!isNodeRegistered(node)) { registeredNodes_.push_back(node); }
 
  171      for (
auto &line : adjacencyMatrix_) {
 
  172        line.push_back(
false);
 
  174      adjacencyMatrix_.emplace_back(adjacencyMatrix_.size() + 1, 
false);
 
  179      tarjanNodeNumbers_{}, 
 
  184  johnsonBlockedSetArray_{}; 
 
  186  std::vector<hh_cx::behavior::AbstractNode const *>
 
  190  std::vector<std::vector<hh_cx::behavior::AbstractNode const *>>
 
  191      tarjanConnectedComponent_{}, 
 
  193  johnsonBlockedMap_{}; 
 
  199  constexpr explicit CycleTest() : AbstractTest<GraphType>(
"Cycle test") {}
 
  202  constexpr ~CycleTest() 
override = 
default;
 
  206  constexpr void test(hh_cx::Graph<GraphType> 
const *graph)
 override {
 
  207    numberOfNodes_ = graph->numberNodesRegistered();
 
  208    findAllCycles(graph);
 
  209    removeCyclesWhereNodesRedefineCanTerminate();
 
  210    if (johnsonCycles_.empty()) {
 
  211      this->graphValid(
true);
 
  213      this->graphValid(
false);
 
  214      this->appendErrorMessage(
"Cycles found, the canTerminate() method needs to be defined for each of these cycles.");
 
  215      for (
const auto &cycle : johnsonCycles_) {
 
  216        this->appendErrorMessage(
"\n\t");
 
  217        auto origin = cycle.at(0);
 
  218        for (
const auto &node : cycle) {
 
  219          this->appendErrorMessage(node->name());
 
  220          this->appendErrorMessage(
" -> ");
 
  222        this->appendErrorMessage(origin->name());
 
  230  constexpr void findAllCycles(hh_cx::Graph<GraphType> 
const *graph) {
 
  231    size_t startIndex = 0;
 
  232    while (startIndex < graph->numberNodesRegistered()) {
 
  234      SimpleSubGraph subGraph(graph, startIndex);
 
  235      tarjanAllStrongConnect(subGraph);
 
  237      auto [leastIndex, minSubGraph] = leastIndexStrongConnectedComponents(subGraph);
 
  239      if (leastIndex == numberOfNodes_) {
 
  243        auto node = subGraph.node(leastIndex);
 
  244        findCyclesInSCC(node, node, minSubGraph);
 
  251  constexpr void initTarjan() {
 
  252    tarjanNodeNumbers_ = std::vector<size_t>(numberOfNodes_);
 
  253    tarjanLowLink_ = std::vector<size_t>(numberOfNodes_);
 
  254    tarjanIsOnStack_ = std::vector<bool>(numberOfNodes_, 
false);
 
  255    tarjanStack_.clear();
 
  256    tarjanConnectedComponent_.clear();
 
  260  constexpr void initJohnson() {
 
  261    johnsonStack_.clear();
 
  262    johnsonBlockedSetArray_ = std::vector<bool>(numberOfNodes_, 
false);
 
  263    for (
size_t nodeId = 0; nodeId < numberOfNodes_; ++nodeId) {
 
  264      johnsonBlockedMap_.emplace_back();
 
  270  constexpr void tarjanAllStrongConnect(SimpleSubGraph 
const &subGraph) {
 
  272    for (
size_t nodeId = 0; nodeId < subGraph.numberNodesRegistered(); ++nodeId) {
 
  273      if (tarjanNodeNumbers_[nodeId] == 0) { tarjanStrongConnect(num, nodeId, subGraph); }
 
  281  constexpr void tarjanStrongConnect(
size_t &num, 
size_t &nodeId, SimpleSubGraph 
const &subGraph) {
 
  282    auto node = subGraph.node(nodeId);
 
  283    tarjanNodeNumbers_[nodeId] = num;
 
  284    tarjanLowLink_[nodeId] = num;
 
  285    tarjanIsOnStack_[nodeId] = 
true;
 
  286    tarjanStack_.push_back(node);
 
  289    for (
auto &neighbor : subGraph.adjacentNodes(node)) {
 
  290      size_t neighborId = subGraph.nodeId(neighbor);
 
  291      if (tarjanNodeNumbers_[neighborId] == 0) {
 
  292        tarjanStrongConnect(num, neighborId, subGraph);
 
  293        tarjanLowLink_[nodeId] = std::min(tarjanLowLink_[nodeId], tarjanLowLink_[neighborId]);
 
  294      } 
else if (tarjanIsOnStack_[neighborId]) {
 
  295        tarjanLowLink_[nodeId] = std::min(tarjanLowLink_[nodeId], tarjanNodeNumbers_[neighborId]);
 
  299    if (tarjanLowLink_[nodeId] == tarjanNodeNumbers_[nodeId]) {
 
  300      std::vector<hh_cx::behavior::AbstractNode const *> component{};
 
  301      hh_cx::behavior::AbstractNode 
const *nodeStack = 
nullptr;
 
  303        nodeStack = tarjanStack_.back();
 
  304        tarjanStack_.pop_back();
 
  305        tarjanIsOnStack_[subGraph.nodeId(nodeStack)] = 
false;
 
  306        component.push_back(nodeStack);
 
  307      } 
while (node != nodeStack);
 
  308      tarjanConnectedComponent_.push_back(component);
 
  315  constexpr std::pair<size_t, SimpleSubGraph> leastIndexStrongConnectedComponents(SimpleSubGraph 
const &subGraph) {
 
  316    size_t min = numberOfNodes_;
 
  317    std::vector<hh_cx::behavior::AbstractNode const *> minSCC(numberOfNodes_, 
nullptr);
 
  319    for (
auto connectedComponent : tarjanConnectedComponent_) {
 
  320      if (connectedComponent.size() == 1) {
 
  321        auto node = connectedComponent.at(0);
 
  322        if (subGraph.isLinked(node, node)) {
 
  323          johnsonCycles_.push_back({node});
 
  326        for (
auto node : connectedComponent) {
 
  327          if (subGraph.nodeId(node) < min) {
 
  328            min = subGraph.nodeId(node);
 
  329            minSCC = connectedComponent;
 
  335    if (min == numberOfNodes_) { 
return {numberOfNodes_, SimpleSubGraph{}}; }
 
  337      SimpleSubGraph minSubGraph{};
 
  339      std::vector<size_t> minIndexes{};
 
  340      for (
auto &sender : subGraph.registeredNodes()) {
 
  341        for (
auto &receiver : subGraph.registeredNodes()) {
 
  342          if (subGraph.isLinked(sender, receiver)) {
 
  343            if (std::find(minSCC.cbegin(), minSCC.cend(), sender) != minSCC.cend()
 
  344                && std::find(minSCC.cbegin(), minSCC.cend(), receiver) != minSCC.cend()) {
 
  345              minSubGraph.addEdge(sender, receiver);
 
  346              minIndexes.push_back(subGraph.nodeId(sender));
 
  347              minIndexes.push_back(subGraph.nodeId(receiver));
 
  352      return {*std::min_element(minIndexes.cbegin(), minIndexes.cend()), minSubGraph};
 
  361  constexpr bool findCyclesInSCC(
 
  362      hh_cx::behavior::AbstractNode 
const *startNode,
 
  363      hh_cx::behavior::AbstractNode 
const *currentNode,
 
  364      SimpleSubGraph 
const &subGraph) {
 
  365    bool cycleFound = 
false;
 
  366    johnsonStack_.push_back(currentNode);
 
  367    johnsonBlockedSetArray_.at(subGraph.nodeId(currentNode)) = 
true;
 
  369    for (
auto neighbor : subGraph.adjacentNodes(currentNode)) {
 
  370      if (neighbor == startNode) {
 
  371        std::vector<hh_cx::behavior::AbstractNode const *> cycle{};
 
  372        auto tempStack(johnsonStack_);
 
  373        while (!tempStack.empty()) {
 
  374          cycle.push_back(tempStack.back());
 
  375          tempStack.pop_back();
 
  377        std::reverse(cycle.begin(), cycle.end());
 
  379        bool johnsonCycleFound = 
false;
 
  380        for (
auto const &johnsonCycle : johnsonCycles_) {
 
  381          if (johnsonCycle.size() == cycle.size()) {
 
  382            bool cycleSame = 
true;
 
  383            for (
size_t nodeId = 0; nodeId < johnsonCycle.size(); ++nodeId) {
 
  384              cycleSame &= (johnsonCycle.at(nodeId) == cycle.at(nodeId));
 
  386            johnsonCycleFound |= cycleSame;
 
  390        if (!johnsonCycleFound) {
 
  391          johnsonCycles_.push_back(cycle);
 
  394      } 
else if (!johnsonBlockedSetArray_.at(subGraph.nodeId(neighbor))) {
 
  395        cycleFound |= findCyclesInSCC(startNode, neighbor, subGraph);
 
  399    if (cycleFound) { unblock(currentNode, subGraph); }
 
  401      for (
auto neighbors : subGraph.adjacentNodes(currentNode)) {
 
  402        auto nodes = johnsonBlockedMap_.at(subGraph.nodeId(neighbors));
 
  403        if (std::find(nodes.cbegin(), nodes.cend(), currentNode) == nodes.cend()) {
 
  404          nodes.push_back(currentNode);
 
  405          johnsonBlockedMap_.at(subGraph.nodeId(neighbors)) = nodes;
 
  410    johnsonStack_.pop_back();
 
  415  constexpr void removeCyclesWhereNodesRedefineCanTerminate() {
 
  416    std::vector<std::vector<hh_cx::behavior::AbstractNode const *>> temp{};
 
  417    for (
const auto &cycle : johnsonCycles_) {
 
  418      if (!std::any_of(cycle.cbegin(), cycle.cend(),
 
  419                       [](
auto const &node) { return node->isCanTerminateOverloaded(); })) {
 
  420        temp.push_back(cycle);
 
  423    johnsonCycles_ = temp;
 
  429  constexpr void unblock(hh_cx::behavior::AbstractNode 
const *node, SimpleSubGraph 
const &subGraph) {
 
  430    johnsonBlockedSetArray_.at(subGraph.nodeId(node)) = 
false;
 
  431    for (
auto nodeBlocked : johnsonBlockedMap_.at(subGraph.nodeId(node))) {
 
  432      if (johnsonBlockedSetArray_.at(subGraph.nodeId(nodeBlocked))) {
 
  433        unblock(nodeBlocked, subGraph);
 
  436    johnsonBlockedMap_.at(subGraph.nodeId(node)) = {};