1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
type 'a sort_result =
| Sorted of 'a list
| ErrorCycle of 'a list
let find_isolated_nodes hash =
let aux id deps acc =
match deps with
| [] -> id :: acc
| _ -> acc
in Hashtbl.fold aux hash []
let remove_nodes nodes hash =
List.iter (Hashtbl.remove hash) nodes
let remove_dependency hash dep =
let aux dep hash id =
let deps = Hashtbl.find hash id in
let deps =
if List.exists ((=) dep) deps then
CCList.remove ~eq:(=) ~key:dep deps
else deps
in
begin
Hashtbl.remove hash id;
Hashtbl.add hash id deps
end
in
let ids = CCHashtbl.keys_list hash in
List.iter (aux dep hash) ids
let find_nonexistent_nodes nodes =
let keys = List.fold_left (fun acc (k, _) -> k :: acc) [] nodes in
let rec find_aux ns nonexistent =
match ns with
| n :: ns ->
if List.exists ((=) n) keys then find_aux ns nonexistent
else find_aux ns (n :: nonexistent)
| [] -> nonexistent |> CCList.uniq ~eq:(=)
in
List.fold_left (fun acc (v, vs) ->
let ns = find_aux vs [] in
match ns with
| [] -> acc
| _ -> (v, ns) :: acc
) [] nodes
let add_missing_nodes graph_l graph =
let missing =
List.fold_left (fun acc (_, vl) ->
List.fold_left (fun acc v ->
if not (Hashtbl.mem graph v) then
(v, []) :: acc
else
acc
) acc vl
) [] graph_l
|> List.rev
in
List.iter (fun (v, vl) -> Hashtbl.replace graph v vl) missing;
graph_l @ missing
let sort nodes =
let rec sorting_loop deps hash acc =
match deps with
| [] -> acc
| dep :: deps ->
let () = remove_dependency hash dep in
let isolated_nodes = find_isolated_nodes hash in
let () = remove_nodes isolated_nodes hash in
sorting_loop
(List.append deps isolated_nodes) hash (List.append acc isolated_nodes)
in
let nodes_hash = CCHashtbl.of_list nodes in
let _nodes = add_missing_nodes nodes nodes_hash in
let base_nodes = find_isolated_nodes nodes_hash in
let () = remove_nodes base_nodes nodes_hash in
let sorted_node_ids = sorting_loop base_nodes nodes_hash [] in
let sorted_node_ids = List.append base_nodes sorted_node_ids in
let remaining_ids = CCHashtbl.keys_list nodes_hash in
match remaining_ids with
| [] -> Sorted sorted_node_ids
| _ -> ErrorCycle remaining_ids
module Graph = struct
type ('a, 'b) t = ('a, 'b list) Hashtbl.t
let create l : (_, _) t =
let tbl = Hashtbl.create 100 in
List.iter (fun (k, v) -> Hashtbl.replace tbl k v) l;
tbl
let transpose tbl =
let tbl2 = Hashtbl.create 100 in
let init v =
if not (Hashtbl.mem tbl2 v) then
Hashtbl.add tbl2 v []
in
Hashtbl.iter (fun u vl ->
init u;
List.iter (fun v ->
let ul =
try Hashtbl.find tbl2 v
with Not_found -> []
in
Hashtbl.replace tbl2 v (u :: ul)
) vl
) tbl;
tbl2
let _to_list tbl =
Hashtbl.fold (fun u vl acc -> (u, vl) :: acc) tbl []
end
let sort_partition graph_l clusters =
let priority = Hashtbl.create 100 in
List.iteri (fun i (v, _) -> Hashtbl.replace priority v i) graph_l;
let prio v =
try Hashtbl.find priority v
with Not_found -> assert false
in
let list_prio vl =
match vl with
| [] -> assert false
| x :: _ -> prio x
in
let cmp u v = compare (prio u) (prio v) in
let cmp_list ul vl = compare (list_prio ul) (list_prio vl) in
List.map (fun l -> List.sort cmp l) clusters
|> List.sort cmp_list
let partition graph_l =
let graph = Graph.create graph_l in
let graph_l = add_missing_nodes graph_l graph in
let tr_graph = Graph.transpose graph in
let visits = Hashtbl.create 100 in
let is_visited v = Hashtbl.mem visits v in
let mark_visited v = Hashtbl.replace visits v () in
let get_out_neighbors v =
try Hashtbl.find graph v
with Not_found -> assert false
in
let get_in_neighbors v =
try Hashtbl.find tr_graph v
with Not_found -> assert false
in
let rec visit acc v =
if not (is_visited v) then (
mark_visited v;
let out_neighbors = get_out_neighbors v in
let acc =
List.fold_left (fun acc u -> visit acc u) acc out_neighbors in
v :: acc
)
else
acc
in
let l =
List.fold_left (fun acc (v, _vl) ->
visit acc v
) [] graph_l
in
let assignments = Hashtbl.create 100 in
let is_assigned v = Hashtbl.mem assignments v in
let rec assign v root =
if not (is_assigned v) then (
Hashtbl.replace assignments v root;
let in_neighbors = get_in_neighbors v in
List.iter (fun u ->
assign u root
) in_neighbors
)
in
List.iter (fun v ->
assign v v
) l;
let clusters = Hashtbl.create 100 in
Hashtbl.iter (fun v id ->
let members =
try Hashtbl.find clusters id
with Not_found -> []
in
Hashtbl.replace clusters id (v :: members)
) assignments;
let partition =
Hashtbl.fold (fun _id members acc -> members :: acc) clusters []
in
graph_l, sort_partition graph_l partition
let find_strongly_connected_components graph_l =
let _completed_graph_l, components = partition graph_l in
components
let sort_strongly_connected_components graph_l =
let graph_l, components = partition graph_l in
let index = Hashtbl.create 100 in
let rev_index = Hashtbl.create 100 in
List.iteri (fun id comp ->
List.iter (fun v ->
Hashtbl.add index v id;
Hashtbl.add rev_index id comp
) comp
) components;
let get_comp_id v =
try Hashtbl.find index v
with Not_found -> assert false
in
let get_comp_members id =
try Hashtbl.find rev_index id
with Not_found -> assert false
in
let condensation =
let tbl = Hashtbl.create 100 in
List.iter (fun (u, vl) ->
let id = get_comp_id u in
let idl0 =
try Hashtbl.find tbl id
with Not_found -> []
in
let idl = List.map get_comp_id vl @ idl0 in
Hashtbl.replace tbl id idl
) graph_l;
Hashtbl.fold (fun id idl acc ->
let filtered = List.filter ((<>) id) idl in
(id, filtered) :: acc
) tbl []
in
let sorted_components =
match sort condensation with
| Sorted comp_ids -> List.map get_comp_members comp_ids
| ErrorCycle _ -> assert false
in
sorted_components