123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408(**************************************************************************)(* *)(* Ocamlgraph: a generic graph library for OCaml *)(* Copyright (C) 2004-2010 *)(* Sylvain Conchon, Jean-Christophe Filliatre and Julien Signoles *)(* *)(* This software is free software; you can redistribute it and/or *)(* modify it under the terms of the GNU Library General Public *)(* License version 2.1, with the special exception on linking *)(* described in file LICENSE. *)(* *)(* This software is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *)(* *)(**************************************************************************)(* Graph traversal *)moduletypeG=sigvalis_directed:booltypetmoduleV:Sig.COMPARABLEvaliter_vertex:(V.t->unit)->t->unitvalfold_vertex:(V.t->'a->'a)->t->'a->'avaliter_succ:(V.t->unit)->t->V.t->unitvalfold_succ:(V.t->'a->'a)->t->V.t->'a->'aend(* depth-first search *)moduleDfs(G:G)=structmoduleH=Hashtbl.Make(G.V)letfoldfaccg=leth=H.create97inlets=Stack.create()inletrecloopacc=ifnot(Stack.is_emptys)thenletv=Stack.popsinifnot(H.memhv)thenbeginH.addhv();letacc=fvaccinG.iter_succ(funw->Stack.pushws)gv;loopaccendelseloopaccelseaccinG.fold_vertex(funvacc->Stack.pushvs;loopacc)gaccletiter?(pre=fun_->())?(post=fun_->())g=leth=H.create97inletrecvisitv=ifnot(H.memhv)thenbeginH.addhv();prev;G.iter_succvisitgv;postvendinG.iter_vertexvisitgletpostfixpostg=iter~postgletfold_componentfaccgv0=leth=H.create97inlets=Stack.create()inStack.pushv0s;letrecloopacc=ifnot(Stack.is_emptys)thenletv=Stack.popsinifnot(H.memhv)thenbeginH.addhv();letacc=fvaccinG.iter_succ(funw->Stack.pushws)gv;loopaccendelseloopaccelseaccinloopaccletiter_component?(pre=fun_->())?(post=fun_->())gv=leth=H.create97inletrecvisitv=H.addhv();prev;G.iter_succ(funw->ifnot(H.memhw)thenvisitw)gv;postvinvisitvletpostfix_componentpostg=iter_component~postgmoduleTail=structlethas_cycleg=leth=H.create97inletstack=Stack.create()inletloop()=whilenot(Stack.is_emptystack)doletv=Stack.topstackinifH.memhvthenbegin(* we are now done with node v *)(* assert (H.find h v = true); *)H.replacehvfalse;ignore(Stack.popstack)endelsebegin(* we start DFS from node v *)H.addhvtrue;G.iter_succ(funw->tryifH.findhwthenraiseExitwithNot_found->Stack.pushwstack)gv;enddoneintryG.iter_vertex(funv->ifnot(H.memhv)thenbeginStack.pushvstack;loop()end)g;falsewithExit->truelethas_cycle_undirectedg=leth=H.create97inletfather=H.create97inletis_fatheruv=(* u is the father of v in the DFS descent *)tryG.V.equal(H.findfatherv)uwithNot_found->falseinletstack=Stack.create()inletloop()=whilenot(Stack.is_emptystack)doletv=Stack.topstackinifH.memhvthenbegin(* we are now done with node v *)(* assert (H.find h v = true); *)H.removefatherv;H.replacehvfalse;ignore(Stack.popstack)endelsebegin(* we start DFS from node v *)H.addhvtrue;G.iter_succ(funw->tryifH.findhw&¬(is_fatherwv)thenraiseExitwithNot_found->H.addfatherwv;Stack.pushwstack)gv;enddoneintryG.iter_vertex(funv->ifnot(H.memhv)thenbeginStack.pushvstack;loop()end)g;falsewithExit->truelethas_cycleg=ifG.is_directedthenhas_cyclegelsehas_cycle_undirectedgletiterfg=leth=H.create97inletstack=Stack.create()inletloop()=whilenot(Stack.is_emptystack)doletv=Stack.popstackinifnot(H.memhv)thenbeginH.addhv();fv;G.iter_succ(funw->ifnot(H.memhw)thenStack.pushwstack)gvenddoneinG.iter_vertex(funv->ifnot(H.memhv)thenbeginStack.pushvstack;loop()end)gletiter_componentfgv0=leth=H.create97inletstack=Stack.create()inStack.pushv0stack;whilenot(Stack.is_emptystack)doletv=Stack.popstackinifnot(H.memhv)thenbeginH.addhv();fv;G.iter_succ(funw->ifnot(H.memhw)thenStack.pushwstack)gvenddoneendletprefix=Tail.iterlethas_cycle=Tail.has_cycleletprefix_component=Tail.iter_component(* step-by-step iterator *)moduleS=Set.Make(G.V)typeiterator=S.t*G.V.tlist*G.t(** (h, st, g) where h is the set of marked vertices and st the stack
invariant: the first element of st is not in h i.e. to be visited *)letstartg=letst=G.fold_vertex(funvst->v::st)g[]inS.empty,st,gletget(_,st,_)=matchstwith|[]->raiseExit|v::_->vletstep(s,st,g)=matchstwith|[]->raiseExit|v::st->lets'=S.addvsinletst'=G.fold_succ(funwst->w::st)gvstinletrecclean=function|w::stwhenS.memws'->cleanst|st->stin(s',cleanst',g)end(* breadth-first search *)moduleBfs(G:G)=structmoduleH=Hashtbl.Make(G.V)letfoldfi(g:G.t)=leth=H.create97inletq=Queue.create()in(* invariant: [h] contains exactly the vertices which have been pushed *)letpushv=ifnot(H.memhv)thenbeginH.addhv();Queue.addvqendinletrecloops=ifnot(Queue.is_emptyq)thenletv=Queue.popqinletns=fvsin(* Sticking to OCamlGraph's fold conv *)G.iter_succpushgv;loopnselsesinG.fold_vertex(funvs->pushv;loops)giletiterf=fold(funv()->fv)()letfold_componentfigv0=leth=H.create97inletq=Queue.create()in(* invariant: [h] contains exactly the vertices which have been pushed *)letpushv=ifnot(H.memhv)thenbeginH.addhv();Queue.addvqendinpushv0;letrecloops=ifnot(Queue.is_emptyq)thenletv=Queue.popqinletns=fvsinG.iter_succpushgv;loopnselsesinloopiletiter_componentf=fold_component(funv()->fv)()(* with distance from the source
instead of using a queue, we use two bags
(`todo` with vertices at distance `d`
and `next` with vertices at distance `d+1`*)letfold_component_distfaccgv0=leth=H.create97in(* invariant: [h] contains exactly the vertices
which have been pushed *)letpushvnext=ifH.memhvthennextelse(H.addhv();v::next)inletrecloopaccdnext=function|[]->ifnext=[]thenaccelseloopacc(d+1)[]next|v::todo->letacc=fvdaccinletnext=G.fold_succpushgvnextinloopaccdnexttodoinloopacc0[](pushv0[])letiter_component_distf=fold_component_dist(funvd()->fvd)()(* step-by-step iterator *)(* simple, yet O(1)-amortized, persistent queues *)moduleQ=structtype'at='alist*'alistexceptionEmptyletempty=[],[]letis_empty=function[],[]->true|_->falseletpushx(i,o)=(x::i,o)letpop=function|i,y::o->y,(i,o)|[],[]->raiseEmpty|i,[]->matchList.reviwith|x::o->x,([],o)|[]->assertfalseletpeekq=fst(popq)endmoduleS=Set.Make(G.V)(* state is [(s,q,g)] : [s] contains elements never been pushed in [q] *)typeiterator=S.t*G.V.tQ.t*G.tletstartg=lets=G.fold_vertexS.addgS.emptyins,Q.empty,gletget(s,q,_)=ifQ.is_emptyqthenifS.is_emptysthenraiseExitelseS.chooseselseQ.peekqletstep(s,q,g)=letpushv(s,qasacc)=ifS.memvsthenS.removevs,Q.pushvqelseaccinletv,s',q'=ifQ.is_emptyqthenbeginifS.is_emptysthenraiseExit;letv=S.choosesinv,S.removevs,qendelseletv,q'=Q.popqinv,s,q'inlets'',q''=G.fold_succpushgv(s',q')in(s'',q'',g)end(* Graph traversal with marking. *)moduletypeGM=sigtypetmoduleV:sigtypetendvaliter_vertex:(V.t->unit)->t->unitvaliter_succ:(V.t->unit)->t->V.t->unitmoduleMark:sigvalclear:t->unitvalget:V.t->intvalset:V.t->int->unitendendmoduleMark(G:GM)=structletdfsg=G.Mark.clearg;letn=ref0inletrecvisitv=ifG.Mark.getv=0thenbeginincrn;G.Mark.setv!n;G.iter_succvisitgvendinG.iter_vertexvisitg(* invariant: [h v = 0] means not visited at all; [h v = 1] means
already visited in the current component; [h v = 2] means
already visited in another tree *)lethas_cycleg=G.Mark.clearg;letrecvisitv=G.Mark.setv1;G.iter_succ(funw->letm=G.Mark.getwinifm=1thenraiseExit;ifm=0thenvisitw)gv;G.Mark.setv2intryG.iter_vertex(funv->ifG.Mark.getv=0thenvisitv)g;falsewithExit->trueend