Commit | Line | Data |
---|---|---|
34e49164 C |
1 | open Common |
2 | ||
3 | open Ocollection | |
4 | open Oset | |
5 | open Oassoc | |
6 | (* open Ograph *) | |
7 | ||
8 | open Oassocb | |
9 | open Osetb | |
10 | ||
ae4735db C |
11 | (* |
12 | * graph structure: | |
34e49164 C |
13 | * - node: index -> nodevalue |
14 | * - arc: (index * index) * edgevalue | |
ae4735db C |
15 | * |
16 | * invariant: key in pred is also in succ (completness) and value in | |
34e49164 | 17 | * either assoc is a key also. |
ae4735db | 18 | * |
34e49164 | 19 | * How ? matrix ? but no growing array :( |
ae4735db | 20 | * |
34e49164 C |
21 | * When need index ? Must have an index when can't just use nodevalue |
22 | * as a key, cos sometimes may have 2 times the same key, but it must | |
23 | * be 2 different nodes. For instance in program f(); f(); we want 2 | |
24 | * nodes, one per f(); hence the index. If each node is different, | |
25 | * then no problem, can omit index. | |
ae4735db | 26 | * |
34e49164 | 27 | * todo?: prend en parametre le type de finitemap et set a prendre |
ae4735db | 28 | * todo?: add_arc doit ramer, car del la key, puis add => better to |
34e49164 | 29 | * have a ref to a set. |
ae4735db C |
30 | * |
31 | * opti: graph with pointers and a tag visited => need keep global value | |
34e49164 | 32 | * visited_counter. check(that node is in, ...), display. |
ae4735db C |
33 | * opti: when the graph structure is stable, have a method compact, that |
34 | * transforms that in a matrix (assert that all number between 0 and | |
34e49164 | 35 | * free_index are used, or do some defrag-like-move/renaming). |
ae4735db | 36 | * |
34e49164 C |
37 | *) |
38 | ||
39 | type nodei = int | |
40 | ||
41 | class ['a,'b] ograph_extended = | |
42 | let build_assoc () = new oassocb [] in (* opti?: = oassoch *) | |
43 | let build_set () = new osetb Setb.empty in | |
44 | ||
45 | object(o) | |
46 | (* inherit ['a] ograph *) | |
ae4735db | 47 | |
34e49164 C |
48 | val free_index = 0 |
49 | ||
50 | val succ = build_assoc() | |
51 | val pred = build_assoc() | |
52 | val nods = build_assoc() | |
53 | ||
ae4735db | 54 | method add_node (e: 'a) = |
34e49164 | 55 | let i = free_index in |
ae4735db C |
56 | ({< |
57 | nods = nods#add (i, e); | |
34e49164 C |
58 | pred = pred#add (i, build_set() ); |
59 | succ = succ#add (i, build_set() ); | |
60 | free_index = i + 1; | |
61 | >}, i) | |
62 | ||
ae4735db C |
63 | method add_nodei i (e: 'a) = |
64 | ({< | |
65 | nods = nods#add (i, e); | |
34e49164 C |
66 | pred = pred#add (i, build_set() ); |
67 | succ = succ#add (i, build_set() ); | |
68 | free_index = (max free_index i) + 1; | |
69 | >}, i) | |
70 | ||
71 | ||
ae4735db | 72 | method del_node (i) = |
34e49164 | 73 | {< |
ae4735db | 74 | (* check: e is effectively the index associated with e, |
34e49164 C |
75 | and check that already in *) |
76 | ||
77 | (* todo: assert that have no pred and succ, otherwise | |
ae4735db | 78 | * will have some dangling pointers |
34e49164 | 79 | *) |
ae4735db | 80 | nods = nods#delkey i; |
34e49164 C |
81 | pred = pred#delkey i; |
82 | succ = succ#delkey i; | |
83 | >} | |
84 | ||
ae4735db | 85 | method replace_node (i, (e: 'a)) = |
34e49164 C |
86 | assert (nods#haskey i); |
87 | {< | |
88 | nods = nods#replkey (i, e); | |
89 | >} | |
90 | ||
ae4735db C |
91 | method add_arc ((a,b),(v: 'b)) = |
92 | {< | |
34e49164 C |
93 | succ = succ#replkey (a, (succ#find a)#add (b, v)); |
94 | pred = pred#replkey (b, (pred#find b)#add (a, v)); | |
95 | >} | |
96 | method del_arc ((a,b),v) = | |
ae4735db | 97 | {< |
34e49164 C |
98 | succ = succ#replkey (a, (succ#find a)#del (b,v)); |
99 | pred = pred#replkey (b, (pred#find b)#del (a,v)); | |
100 | >} | |
101 | ||
102 | method successors e = succ#find e | |
103 | method predecessors e = pred#find e | |
104 | ||
105 | method nodes = nods | |
106 | method allsuccessors = succ | |
107 | ||
108 | (* | |
ae4735db C |
109 | method ancestors xs = |
110 | let rec aux xs acc = | |
34e49164 C |
111 | match xs#view with (* could be done with an iter *) |
112 | | Empty -> acc | |
ae4735db | 113 | | Cons(x, xs) -> (acc#add x) |
34e49164 C |
114 | +> (fun newacc -> aux (o#predecessors x) newacc) |
115 | +> (fun newacc -> aux xs newacc) | |
116 | in aux xs (f2()) (* (new osetb []) *) | |
117 | ||
ae4735db C |
118 | method children xs = |
119 | let rec aux xs acc = | |
34e49164 C |
120 | match xs#view with (* could be done with an iter *) |
121 | | Empty -> acc | |
ae4735db | 122 | | Cons(x, xs) -> (acc#add x) |
34e49164 C |
123 | +> (fun newacc -> aux (o#successors x) newacc) |
124 | +> (fun newacc -> aux xs newacc) | |
125 | in aux xs (f2()) (* (new osetb []) *) | |
126 | ||
ae4735db | 127 | method brothers x = |
34e49164 C |
128 | let parents = o#predecessors x in |
129 | (parents#fold (fun acc e -> acc $++$ o#successors e) (f2()))#del x | |
130 | ||
131 | *) | |
132 | ||
ae4735db | 133 | end |
34e49164 C |
134 | |
135 | ||
136 | ||
137 | ||
138 | class ['a,'b] ograph_mutable = | |
139 | let build_assoc () = new oassocb [] in | |
140 | let build_set () = new osetb Setb.empty in | |
141 | ||
142 | object(o) | |
ae4735db | 143 | |
34e49164 C |
144 | val mutable free_index = 0 |
145 | ||
146 | val mutable succ = build_assoc() | |
147 | val mutable pred = build_assoc() | |
148 | val mutable nods = build_assoc() | |
149 | ||
ae4735db | 150 | method add_node (e: 'a) = |
34e49164 | 151 | let i = free_index in |
ae4735db | 152 | nods <- nods#add (i, e); |
34e49164 C |
153 | pred <- pred#add (i, build_set() ); |
154 | succ <- succ#add (i, build_set() ); | |
155 | free_index <- i + 1; | |
156 | i | |
157 | ||
ae4735db C |
158 | method add_nodei i (e: 'a) = |
159 | nods <- nods#add (i, e); | |
34e49164 C |
160 | pred <- pred#add (i, build_set() ); |
161 | succ <- succ#add (i, build_set() ); | |
162 | free_index <- (max free_index i) + 1; | |
163 | ||
164 | ||
ae4735db C |
165 | method del_node (i) = |
166 | (* check: e is effectively the index associated with e, | |
34e49164 C |
167 | and check that already in *) |
168 | ||
169 | (* todo: assert that have no pred and succ, otherwise | |
ae4735db | 170 | * will have some dangling pointers |
34e49164 | 171 | *) |
ae4735db | 172 | nods <- nods#delkey i; |
34e49164 C |
173 | pred <- pred#delkey i; |
174 | succ <- succ#delkey i; | |
175 | ||
ae4735db | 176 | method replace_node (i, (e: 'a)) = |
34e49164 C |
177 | assert (nods#haskey i); |
178 | nods <- nods#replkey (i, e); | |
ae4735db C |
179 | |
180 | method add_arc ((a,b),(v: 'b)) = | |
34e49164 C |
181 | succ <- succ#replkey (a, (succ#find a)#add (b, v)); |
182 | pred <- pred#replkey (b, (pred#find b)#add (a, v)); | |
183 | method del_arc ((a,b),v) = | |
184 | succ <- succ#replkey (a, (succ#find a)#del (b,v)); | |
185 | pred <- pred#replkey (b, (pred#find b)#del (a,v)); | |
186 | ||
187 | method successors e = succ#find e | |
188 | method predecessors e = pred#find e | |
189 | ||
190 | method nodes = nods | |
191 | method allsuccessors = succ | |
192 | ||
ae4735db | 193 | end |
34e49164 C |
194 | |
195 | ||
196 | (* depth first search *) | |
197 | let dfs_iter xi f g = | |
198 | let already = Hashtbl.create 101 in | |
ae4735db C |
199 | let rec aux_dfs xs = |
200 | xs +> List.iter (fun xi -> | |
34e49164 C |
201 | if Hashtbl.mem already xi then () |
202 | else begin | |
203 | Hashtbl.add already xi true; | |
204 | f xi; | |
205 | let succ = g#successors xi in | |
206 | aux_dfs (succ#tolist +> List.map fst); | |
207 | end | |
208 | ) in | |
209 | aux_dfs [xi] | |
210 | ||
211 | ||
ae4735db | 212 | let dfs_iter_with_path xi f g = |
34e49164 | 213 | let already = Hashtbl.create 101 in |
ae4735db | 214 | let rec aux_dfs path xi = |
34e49164 C |
215 | if Hashtbl.mem already xi then () |
216 | else begin | |
217 | Hashtbl.add already xi true; | |
218 | f xi path; | |
219 | let succ = g#successors xi in | |
220 | let succ' = succ#tolist +> List.map fst in | |
ae4735db | 221 | succ' +> List.iter (fun yi -> |
34e49164 C |
222 | aux_dfs (xi::path) yi |
223 | ); | |
224 | end | |
225 | in | |
226 | aux_dfs [] xi | |
ae4735db C |
227 | |
228 | ||
34e49164 | 229 | |
485bce71 | 230 | let generate_ograph_generic g label fnode filename = |
978fd7e5 | 231 | Common.with_open_outfile filename (fun (pr,_) -> |
485bce71 C |
232 | pr "digraph misc {\n" ; |
233 | pr "size = \"10,10\";\n" ; | |
234 | (match label with | |
235 | None -> () | |
236 | | Some x -> pr (Printf.sprintf "label = \"%s\";\n" x)); | |
237 | ||
238 | let nodes = g#nodes in | |
ae4735db | 239 | nodes#iter (fun (k,node) -> |
485bce71 C |
240 | let (str,border_color,inner_color) = fnode (k, node) in |
241 | let color = | |
242 | match inner_color with | |
243 | None -> | |
244 | (match border_color with | |
245 | None -> "" | |
246 | | Some x -> Printf.sprintf ", style=\"setlinewidth(3)\", color = %s" x) | |
247 | | Some x -> | |
248 | (match border_color with | |
ae4735db | 249 | None -> Printf.sprintf ", style=\"setlinewidth(3),filled\", fillcolor = %s" x |
485bce71 | 250 | | Some x' -> Printf.sprintf ", style=\"setlinewidth(3),filled\", fillcolor = %s, color = %s" x x') in |
ae4735db | 251 | (* so can see if nodes without arcs were created *) |
485bce71 C |
252 | pr (sprintf "%d [label=\"%s [%d]\"%s];\n" k str k color) |
253 | ); | |
254 | ||
ae4735db | 255 | nodes#iter (fun (k,node) -> |
485bce71 C |
256 | let succ = g#successors k in |
257 | succ#iter (fun (j,edge) -> | |
258 | pr (sprintf "%d -> %d;\n" k j); | |
259 | ); | |
260 | ); | |
261 | pr "}\n" ; | |
262 | ); | |
263 | () | |
264 | ||
34e49164 C |
265 | |
266 | let generate_ograph_xxx g filename = | |
267 | with_open_outfile filename (fun (pr,_) -> | |
268 | pr "digraph misc {\n" ; | |
269 | pr "size = \"10,10\";\n" ; | |
270 | ||
271 | let nodes = g#nodes in | |
ae4735db C |
272 | nodes#iter (fun (k,(node, s)) -> |
273 | (* so can see if nodes without arcs were created *) | |
485bce71 | 274 | pr (sprintf "%d [label=\"%s [%d]\"];\n" k s k) |
34e49164 C |
275 | ); |
276 | ||
ae4735db | 277 | nodes#iter (fun (k,node) -> |
34e49164 C |
278 | let succ = g#successors k in |
279 | succ#iter (fun (j,edge) -> | |
280 | pr (sprintf "%d -> %d;\n" k j); | |
281 | ); | |
282 | ); | |
283 | pr "}\n" ; | |
284 | ); | |
34e49164 C |
285 | () |
286 | ||
485bce71 C |
287 | |
288 | let launch_gv_cmd filename = | |
ae4735db | 289 | let _status = |
485bce71 | 290 | Unix.system ("dot " ^ filename ^ " -Tps -o " ^ filename ^ ".ps;") in |
34e49164 C |
291 | let _status = Unix.system ("gv " ^ filename ^ ".ps &") |
292 | in | |
293 | (* zarb: I need this when I launch the program via eshell, otherwise gv | |
294 | do not get the chance to be launched *) | |
ae4735db | 295 | Unix.sleep 1; |
34e49164 C |
296 | () |
297 | ||
ae4735db | 298 | let print_ograph_extended g filename launchgv = |
34e49164 | 299 | generate_ograph_xxx g filename; |
485bce71 | 300 | if launchgv then launch_gv_cmd filename |
34e49164 | 301 | |
ae4735db | 302 | let print_ograph_mutable g filename launchgv = |
34e49164 | 303 | generate_ograph_xxx g filename; |
485bce71 C |
304 | if launchgv then launch_gv_cmd filename |
305 | ||
ae4735db | 306 | let print_ograph_mutable_generic g label fnode ~output_file ~launch_gv = |
485bce71 C |
307 | generate_ograph_generic g label fnode output_file; |
308 | if launch_gv then launch_gv_cmd output_file |