1 module dkh.graph.scc;
2 
3 import dkh.graph.primitive;
4 import dkh.container.stackpayload, dkh.container.deque;
5 
6 /// 強連結成分の情報
7 struct SCCInfo {
8     int[] id; /// 頂点id -> 強連結成分id
9     int[][] groups; /// 強連結成分id -> その連結成分の頂点idたち
10     /// iと同じgroupの頂点を返す
11     const(int[]) group(size_t i) const {
12         return groups[id[i]];
13     }    
14     this(size_t n) {
15         id = new int[n];
16     }
17 }
18 
19 /// 強連結成分分解
20 SCCInfo scc(T)(T g) {
21     import std.range;
22     import std.algorithm : each, map, min, reverse;
23     import std.conv : to;
24     auto n = g.length;
25     auto sccInfo = SCCInfo(n);
26     with (sccInfo) {
27         bool[] inS = new bool[n];
28         int[] low = new int[n], ord = new int[n]; ord[] = -1;
29         int time = 0;
30         Deque!int st;
31         int bufC = 0;
32         StackPayload!int buf; buf.reserve(n);
33         StackPayload!(int[]) gBuf;
34         void dfs(int v) {
35             low[v] = ord[v] = time++;
36             st.insertBack(v);
37             inS[v] = true;
38             foreach (e; g[v]) {
39                 if (ord[e.to] == -1) {
40                     dfs(e.to);
41                     low[v] = min(low[v], low[e.to]);
42                 } else if (inS[e.to]) {
43                     low[v] = min(low[v], ord[e.to]);
44                 }
45             }
46             if (low[v] == ord[v]) {
47                 while (true) {
48                     int u = st.back; st.removeBack;
49                     buf ~= u;
50                     if (u == v) break;
51                 }
52                 auto gr = buf.data[bufC..$];
53                 bufC = buf.length.to!int;
54                 gr.each!(x => inS[x] = false);
55                 gBuf ~= gr;
56             }
57         }
58         foreach (i; 0..n) {
59             if (ord[i] == -1) dfs(i.to!int);
60         }
61         groups = gBuf.data;
62         reverse(groups);
63         groups.each!((i, v) => v.each!(x => id[x] = i.to!int));
64     }
65     return sccInfo;
66 }
67 
68 ///
69 unittest {
70     import std.algorithm, std.typecons;
71     alias E = Tuple!(int, "to");
72     E[][] g = new E[][5];
73     g[0] ~= E(1);
74     g[1] ~= E(2);
75     g[2] ~= E(0); g[2] ~= E(3);
76     g[3] ~= E(4);
77     g[4] ~= E(3);
78 
79     auto info = scc(g);
80 
81     assert(info.id[0] == info.id[1] && info.id[1] == info.id[2]);
82     assert(info.id[3] == info.id[4]);
83     assert(info.id[0] < info.id[3]); //idはトポロジカル順
84     assert(equal(info.group(0).dup.sort!"a<b", [0, 1, 2]));
85     assert(equal(info.group(3).dup.sort!"a<b", [3, 4]));
86 }
87 
88 unittest {
89     import std.algorithm, std.conv, std.stdio, std.range;
90     import std.random;
91     import std.typecons;
92 
93     struct E {int to;}
94 
95     void f() {
96         int n = uniform(1, 50);
97         int p = uniform(1, 50);
98         E[][] g = new E[][n];
99         bool[][] naive = new bool[][](n, n);
100         foreach (i; 0..n) {
101             foreach (j; 0..n) {
102                 if (i == j) continue;
103                 if (uniform(0, 100) < p) {
104                     g[i] ~= E(j);
105                     naive[i][j] = true;
106                 }
107             }
108         }
109 
110         auto sccInfo = scc(g);
111 
112         foreach (k; 0..n) {
113             foreach (i; 0..n) {
114                 foreach (j; 0..n) {
115                     naive[i][j] |= naive[i][k] && naive[k][j];
116                 }
117             }
118         }
119 
120         foreach (i; 0..n) {
121             int iid = sccInfo.id[i];
122             assert(sccInfo.groups[iid].find(i).empty == false);
123             foreach (j; i+1..n) {
124                 bool same = sccInfo.id[i] == sccInfo.id[j];
125                 if (same) {
126                     assert(naive[i][j] && naive[j][i]);
127                 } else {
128                     assert(!naive[i][j] || !naive[j][i]);                    
129                     if (sccInfo.id[i] < sccInfo.id[j]) {
130                         assert(!naive[j][i]);
131                     } else {
132                         assert(!naive[i][j]);
133                     }
134                 }
135             }
136         }
137     }
138     import dkh.stopwatch;
139     auto ti = benchmark!(f)(1000);
140     writeln("SCC Random1000: ", ti[0].toMsecs);
141 }