123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324openCommonopenOcollectionopenOsetopenOassoc(* open Ograph *)openOassocbopenOsetb(*****************************************************************************)(* Prelude *)(*****************************************************************************)(*
* An imperative directed polymorphic graph.
*
* todo?: prendre en parametre le type de finitemap et set?
* todo?: add_arc doit ramer, car del la key, puis add. Better to
* have a ref to a set?
*
* opti: graph with pointers and a tag visited => need keep global value
* visited_counter. check(that node is in, ...), display.
* opti: when the graph structure is stable, have a method compact, that
* transforms that in a matrix (assert that all number between 0 and
* free_index are used, or do some defrag-like-move/renaming).
*)(*****************************************************************************)(* Types *)(*****************************************************************************)typenodei=int(*****************************************************************************)(* Pure version *)(*****************************************************************************)class['a,'b]ograph_extended=letbuild_assoc()=newoassocb[]in(* opti?: = oassoch *)letbuild_set()=newosetbSet_.emptyinobject(o)(* inherit ['a] ograph *)valfree_index=0valsucc=build_assoc()valpred=build_assoc()valnods=build_assoc()methodadd_node(e:'a)=leti=free_indexin({<nods=nods#add(i,e);pred=pred#add(i,build_set());succ=succ#add(i,build_set());free_index=i+1;>},i)methodadd_nodeii(e:'a)=({<nods=nods#add(i,e);pred=pred#add(i,build_set());succ=succ#add(i,build_set());free_index=(maxfree_indexi)+1;>},i)methoddel_node(i)={<(* check: e is effectively the index associated with e,
and check that already in *)(* todo: assert that have no pred and succ, otherwise
* will have some dangling pointers
*)nods=nods#delkeyi;pred=pred#delkeyi;succ=succ#delkeyi;>}methodreplace_node(i,(e:'a))=assert(nods#haskeyi);{<nods=nods#replkey(i,e);>}methodadd_arc((a,b),(v:'b))={<succ=succ#replkey(a,(succ#finda)#add(b,v));pred=pred#replkey(b,(pred#findb)#add(a,v));>}methoddel_arc((a,b),v)={<succ=succ#replkey(a,(succ#finda)#del(b,v));pred=pred#replkey(b,(pred#findb)#del(a,v));>}methodsuccessorse=succ#findemethodpredecessorse=pred#findemethodnodes=nodsmethodallsuccessors=succ(*
method ancestors xs =
let rec aux xs acc =
match xs#view with (* could be done with an iter *)
| Empty -> acc
| Cons(x, xs) -> (acc#add x)
+> (fun newacc -> aux (o#predecessors x) newacc)
+> (fun newacc -> aux xs newacc)
in aux xs (f2()) (* (new osetb []) *)
method children xs =
let rec aux xs acc =
match xs#view with (* could be done with an iter *)
| Empty -> acc
| Cons(x, xs) -> (acc#add x)
+> (fun newacc -> aux (o#successors x) newacc)
+> (fun newacc -> aux xs newacc)
in aux xs (f2()) (* (new osetb []) *)
method brothers x =
let parents = o#predecessors x in
(parents#fold (fun acc e -> acc $++$ o#successors e) (f2()))#del x
*)end(*****************************************************************************)(* Mutable version *)(*****************************************************************************)class['a,'b]ograph_mutable=letbuild_assoc()=newoassocb[]inletbuild_set()=newosetbSet_.emptyinobject(o)valmutablefree_index=0valmutablesucc=build_assoc()valmutablepred=build_assoc()valmutablenods=build_assoc()methodadd_node(e:'a)=leti=free_indexinnods<-nods#add(i,e);pred<-pred#add(i,build_set());succ<-succ#add(i,build_set());free_index<-i+1;imethodadd_nodeii(e:'a)=nods<-nods#add(i,e);pred<-pred#add(i,build_set());succ<-succ#add(i,build_set());free_index<-(maxfree_indexi)+1;methoddel_node(i)=(* check: e is effectively the index associated with e,
and check that already in *)(* todo: assert that have no pred and succ, otherwise
* will have some dangling pointers
*)nods<-nods#delkeyi;pred<-pred#delkeyi;succ<-succ#delkeyi;methodreplace_node(i,(e:'a))=assert(nods#haskeyi);nods<-nods#replkey(i,e);methodadd_arc((a,b),(v:'b))=succ<-succ#replkey(a,(succ#finda)#add(b,v));pred<-pred#replkey(b,(pred#findb)#add(a,v));methoddel_arc((a,b),v)=succ<-succ#replkey(a,(succ#finda)#del(b,v));pred<-pred#replkey(b,(pred#findb)#del(a,v));methodsuccessorse=succ#findemethodpredecessorse=pred#findemethodnodes=nodsmethodallsuccessors=succmethodnb_nodes=nods#lengthmethodnb_edges=nods#fold(funacc(i,e)->letchildren=o#successorsiinacc+children#cardinal)0end(*****************************************************************************)(* API *)(*****************************************************************************)(* depth first search *)letdfs_iterxifg=letalready=Hashtbl.create101inletrecaux_dfsxs=xs|>List.iter(funxi->ifHashtbl.memalreadyxithen()elsebeginHashtbl.addalreadyxitrue;fxi;letsucc=g#successorsxiinaux_dfs(succ#tolist|>List.mapfst);end)inaux_dfs[xi]letdfs_iter_with_pathxifg=letalready=Hashtbl.create101inletrecaux_dfspathxi=ifHashtbl.memalreadyxithen()elsebeginHashtbl.addalreadyxitrue;fxipath;letsucc=g#successorsxiinletsucc'=succ#tolist|>List.mapfstinsucc'|>List.iter(funyi->aux_dfs(xi::path)yi);endinaux_dfs[]xiletgenerate_ograph_genericglabelfnodefilename=Common.with_open_outfilefilename(fun(pr,_)->pr"digraph misc {\n";pr"size = \"10,10\";\n";(matchlabelwithNone->()|Somex->pr(Printf.sprintf"label = \"%s\";\n"x));letnodes=g#nodesinnodes#iter(fun(k,node)->let(str,border_color,inner_color)=fnode(k,node)inletcolor=matchinner_colorwithNone->(matchborder_colorwithNone->""|Somex->Printf.sprintf", style=\"setlinewidth(3)\", color = %s"x)|Somex->(matchborder_colorwithNone->Printf.sprintf", style=\"setlinewidth(3),filled\", fillcolor = %s"x|Somex'->Printf.sprintf", style=\"setlinewidth(3),filled\", fillcolor = %s, color = %s"xx')in(* so can see if nodes without arcs were created *)pr(spf"%d [label=\"%s [%d]\"%s];\n"kstrkcolor));nodes#iter(fun(k,node)->letsucc=g#successorskinsucc#iter(fun(j,edge)->pr(spf"%d -> %d;\n"kj);););pr"}\n";);()letgenerate_ograph_xxxgfilename=with_open_outfilefilename(fun(pr,_)->pr"digraph misc {\n";pr"size = \"10,10\";\n";letnodes=g#nodesinnodes#iter(fun(k,(node,s))->(* so can see if nodes without arcs were created *)pr(spf"%d [label=\"%s [%d]\"];\n"ksk));nodes#iter(fun(k,node)->letsucc=g#successorskinsucc#iter(fun(j,edge)->pr(spf"%d -> %d;\n"kj);););pr"}\n";);()letlaunch_gv_cmdfilename=let_status=Unix.system("dot "^filename^" -Tps -o "^filename^".ps;")inlet_status=Unix.system("gv "^filename^".ps")in(* zarb: I needed this when I launch the program with '&' via eshell,
* otherwise gv did not get the chance to be launched
* Unix.sleep 1;
*)()letprint_ograph_extendedgfilenamelaunchgv=generate_ograph_xxxgfilename;iflaunchgvthenlaunch_gv_cmdfilenameletprint_ograph_mutablegfilenamelaunchgv=generate_ograph_xxxgfilename;iflaunchgvthenlaunch_gv_cmdfilenameletprint_ograph_mutable_generic?(title=None)?(launch_gv=true)?(output_file="/tmp/ograph.dot")~s_of_nodeg=generate_ograph_genericgtitles_of_nodeoutput_file;iflaunch_gvthenlaunch_gv_cmdoutput_file