From 24031a87b60ab08fd3aed74e552aeda2c2e64c4c Mon Sep 17 00:00:00 2001
From: khansadaoudi <hk_daoudi@esi.dz>
Date: Fri, 3 Jan 2025 15:00:41 +0100
Subject: [PATCH] fix detect cycle algorithm based on dfs algorithm

---
 app/trees/controller.py |  2 +-
 app/trees/service.py    | 44 ++++++++++++++++++++++++++++++++---------
 2 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/app/trees/controller.py b/app/trees/controller.py
index fda6f9b..2f0909f 100644
--- a/app/trees/controller.py
+++ b/app/trees/controller.py
@@ -202,7 +202,7 @@ def post(self, project_name: str):
         else: #for the moment if sud we check only the cycles
             cycle_nodes = TreeService.check_cycle(data)
             if cycle_nodes: 
-                error_message = 'Non tree structure, tokens: ' + ', '.join([str(tuple_nodes) for tuple_nodes in cycle_nodes]) + ' form a cycle'
+                error_message = 'Non tree structure, tokens: ' + ', '.join([str(list_nodes) for list_nodes in cycle_nodes]) + ' form a cycle'
                 return { "message": error_message, "passed": False }
             else:
                 return { "message": '', "passed": True }               
diff --git a/app/trees/service.py b/app/trees/service.py
index 90fa0e0..fc3d509 100644
--- a/app/trees/service.py
+++ b/app/trees/service.py
@@ -13,6 +13,7 @@
 
 class TreeService:
     """this class contains all methods that deal with trees"""
+
     @staticmethod
     def check_cycle(conll): 
         """check if there is a cycle in the graph
@@ -21,27 +22,52 @@ def check_cycle(conll):
             conll (str): user tree
 
         Returns:
-            cycle_nodes (List[Tuple(node1, node2)])
+            cycle_nodes (List[List[str]]): list of cycles nodes
         """
         sentence_json = sentenceConllToJson(conll)
         nodes_json = sentence_json['treeJson']['nodesJson']
         
         nodes_children_list = {}
         for index in nodes_json:
+            if index not in nodes_children_list.keys():
+                nodes_children_list[index] = []
             token_head = str(nodes_json[index]['HEAD'])
             if token_head not in nodes_children_list.keys():
                 nodes_children_list[token_head] = [index]
             else:
                 nodes_children_list[token_head].append(index)
+        print(nodes_children_list)
+
+        # check if there is a cycle in the tree using recursive dfs
+        def dfs_recursive(first_node, visited, stack, path):
+            visited.add(first_node)
+            stack.add(first_node)
+            path.append(first_node)
+            
+            for child in nodes_children_list[first_node]:
+                if child not in visited:
+                    if dfs_recursive(child, visited, stack, path):
+                        return True
+                elif child in stack:
+                    path.append(child)
+                    return True
+            stack.remove(first_node)
+            path.pop()
+            return False
         
-        cycle_nodes = []        
-        for node, list_children in nodes_children_list.items():
-            for child in list_children:
-                if child in nodes_children_list.keys() and node in nodes_children_list[child]:
-                   cycle_nodes.append((child, node))
-        
-        return list(set(tuple(sorted(nodes_tuple)) for nodes_tuple in cycle_nodes))
-                       
+        visited = set()
+        stack = set()
+        cycle_nodes = []
+
+        for node in nodes_children_list:
+            if node not in visited:
+                path = []
+                if dfs_recursive(node, visited, stack, path):
+                    cycle_start_index = cycle_nodes.index(path[-1]) if path[-1] in cycle_nodes else 0
+                    cycle_nodes.append(path[cycle_start_index:])
+
+        return cycle_nodes
+                     
     @staticmethod
     def samples_to_trees(sample_trees, sample_name):
         """