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
type 'a sort_result = Sorted of 'a list | ErrorNonexistent 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
in
let nonexistent = List.fold_left (fun acc (_, vs) -> List.append acc (find_aux vs [])) [] nodes in
CCList.uniq ~eq:(=) nonexistent
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 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
| _ ->
let nonexistent_nodes = find_nonexistent_nodes nodes in
begin
match nonexistent_nodes with
| [] -> ErrorCycle remaining_ids
| _ -> ErrorNonexistent nonexistent_nodes
end