123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104type'aiter=('a->unit)->unitmoduletypeARG=sigtypettypenodevalchildren:t->node->nodeitermoduleNode_tbl:Hashtbl.Swithtypekey=nodeendmoduletypeS=sigmoduleA:ARGvalscc:A.t->A.nodelist->A.nodelistlistendmoduleMake(A:ARG)=structmoduleA=Atypestate={mutablemin_id:int;(* min ID of the vertex' scc *)id:int;(* ID of the vertex *)mutableon_stack:bool;vertex:A.node;}letmk_cellvn={min_id=n;id=n;on_stack=false;vertex=v}(* pop elements of [stack] until we reach node with given [id] *)letrecpop_down_to~idaccstack=assert(not(Stack.is_emptystack));letcell=Stack.popstackincell.on_stack<-false;ifcell.id=idthen(assert(cell.id=cell.min_id);cell.vertex::acc(* return SCC *))elsepop_down_to~id(cell.vertex::acc)stackletscc(graph:A.t)(nodes:A.nodelist):_listlist=letres=ref[]inlettbl=A.Node_tbl.create16in(* stack of nodes being explored, for the DFS *)letto_explore=Stack.create()in(* stack for Tarjan's algorithm itself *)letstack=Stack.create()in(* unique ID for new nodes *)letn=ref0in(* exploration starting from [v] *)letexplore_from(v:A.node):unit=Stack.push(`Enterv)to_explore;whilenot(Stack.is_emptyto_explore)domatchStack.popto_explorewith|`Enterv->ifnot(A.Node_tbl.memtblv)then((* remember unique ID for [v] *)letid=!ninincrn;letcell=mk_cellvidincell.on_stack<-true;A.Node_tbl.addtblvcell;Stack.pushcellstack;Stack.push(`Exit(v,cell))to_explore;(* explore children *)letchildren=A.childrengraphvinchildren(funv'->Stack.push(`Enterv')to_explore))|`Exit(v,cell)->(* update [min_id] *)assertcell.on_stack;letchildren=A.childrengraphvinchildren(fundest->(* must not fail, [dest] already explored *)letdest_cell=A.Node_tbl.findtbldestin(* same SCC? yes if [dest] points to [cell.v] *)ifdest_cell.on_stackthencell.min_id<-mincell.min_iddest_cell.min_id);(* pop from stack if SCC found *)ifcell.id=cell.min_idthen(letscc=pop_down_to~id:cell.id[]stackinres:=scc::!res)doneinList.iterexplore_fromnodes;assert(Stack.is_emptystack);!resendletscc(typegraphnode)~(tbl:(moduleHashtbl.Swithtypekey=node))~graph~children~nodes():_list=letmoduleS=Make(structtypet=graphtypenonrecnode=nodeletchildren=childrenmoduleNode_tbl=(valtbl)end)inS.sccgraphnodes