1+ // This class will help us to detect if any new edge can lead to cycle or not
2+ // Thus we can alert user to avoid that edge thus resolving cycle issue on UI
3+
4+ // Directed Acyclic Graph
5+ export class DAG {
6+ #launcher_list;
7+ #adjacency_list;
8+ #visited;
9+ #stack;
10+ #has_cycle;
11+
12+ constructor ( ) {
13+ this . #launcher_list = [ ] ;
14+ this . #adjacency_list = { } ;
15+ }
16+
17+ addEdge ( fromId , toId ) {
18+ if ( ! this . #launcher_list. includes ( fromId ) ) {
19+ this . #launcher_list. push ( fromId ) ;
20+ }
21+
22+ if ( ! this . #launcher_list. includes ( toId ) ) {
23+ this . #launcher_list. push ( toId ) ;
24+ }
25+
26+ if ( ! this . #adjacency_list[ fromId ] ) {
27+ this . #adjacency_list[ fromId ] = [ ] ;
28+ }
29+ this . #adjacency_list[ fromId ] . push ( toId ) ;
30+
31+ this . #has_cycle = false ;
32+ this . #visited = new Set ( ) ;
33+ this . #stack = new Set ( ) ;
34+ this . #launcher_list. forEach ( l => this . #detectCycle( l ) ) ;
35+
36+ // Remove the added edges form the adjacency list
37+ if ( this . #has_cycle === true ) {
38+ this . #adjacency_list[ fromId ] . pop ( ) ;
39+ }
40+ }
41+
42+ hasCycle ( ) {
43+ return this . #has_cycle;
44+ }
45+
46+ // Basic dfs on graph to find a cycle
47+ #detectCycle( id ) {
48+ if ( this . #stack. has ( id ) ) {
49+ this . #has_cycle = true ;
50+ return ;
51+ }
52+ if ( this . #visited. has ( id ) ) {
53+ return ;
54+ }
55+
56+ this . #stack. add ( id ) ;
57+ this . #visited. add ( id ) ;
58+ for ( const l of this . #adjacency_list[ id ] || [ ] ) {
59+ this . #detectCycle( l ) ;
60+ }
61+
62+ this . #stack. delete ( id ) ;
63+ }
64+ }
0 commit comments