123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144openCommonopenOcollectionopenOsetopenOassoc(* open Ograph *)openOassocbopenOsetb(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* An imperative directed polymorphic graph.
*
* What is the difference with ograph_extended? With ograph_extended we
* dont force the user to have a key; we generate those keys as he
* adds nodes. Here we assume the user already has an idea of what kind
* of key he wants to use (a string, a filename, a, int, whatever).
* This removes the need to remember some 'node -> nodeindex' mapping.
* It's very easy to add edge between entities with ograph_simple.
*)(*****************************************************************************)(* Type *)(*****************************************************************************)class['key,'a,'b]ograph_mutable=letbuild_assoc()=newoassocb[]inletbuild_set()=newosetbSet_.emptyinobject(o)valmutablesucc=build_assoc()valmutablepred=build_assoc()valmutablenods=(build_assoc():('key,'a)Oassocb.oassocb)methodadd_nodei(e:'a)=nods<-nods#add(i,e);pred<-pred#add(i,build_set());succ<-succ#add(i,build_set());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;methoddel_leaf_node_and_its_edges(i)=letsucc=o#successorsiinifnot(succ#null)thenfailwith"del_leaf_node_and_its_edges: have some successors"elsebeginletpred=o#predecessorsiinpred#tolist|>List.iter(fun(k,edge)->o#del_arc(k,i)edge;);o#del_nodeiendmethodleaf_nodes()=let(set:'keyOset.oset)=build_set()ino#nodes#tolist|>List.fold_left(funacc(k,v)->if(o#successorsk)#nullthenacc#addkelseacc)setmethodreplace_nodei(e:'a)=assert(nods#haskeyi);nods<-nods#replkey(i,e);methodadd_node_if_not_presenti(e:'a)=ifnods#haskeyithen()elseo#add_nodeiemethodadd_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(* detect if no loop ? *)methodancestorsk=letempty_set=build_set()inletrecauxaccx=ifacc#memxthen(* bugfix: have_loop := true; ? not, not necessarally.
* if you got a diamon, seeing a second time the same
* x does not mean we are in a loop
*)accelseletacc=acc#addxinletprefs=o#predecessorsxinletprefs=prefs#tolist|>List.mapfstinprefs|>List.fold_left(funaccx->auxaccx)accinletset=auxempty_setkinletset=set#delkinsetend(*****************************************************************************)(* Debugging *)(*****************************************************************************)letprint_ograph_generic~str_of_key~str_of_nodefilenameg=Common.with_open_outfilefilename(fun(pr,_)->pr"digraph misc {\n";pr"size = \"10,10\";\n";letnodes=g#nodesinnodes#iter(fun(k,node)->pr(spf"%s [label=\"%s\"];\n"(str_of_keyk)(str_of_nodeknode)));nodes#iter(fun(k,node)->letsucc=g#successorskinsucc#iter(fun(j,edge)->pr(spf"%s -> %s;\n"(str_of_keyk)(str_of_keyj));););pr"}\n";);Ograph_extended.launch_gv_cmdfilename;()