123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098openGraph(* open Sexplib.Conv *)openCore_kernelmoduletypeVERTEX=sigtypet[@@derivingsexp]valcompare:t->t->intvalto_string:t->stringvalto_dot:t->stringvalto_mininet:t->stringvalparse_dot:Graph.Dot_ast.node_id->Graph.Dot_ast.attrlist->tvalparse_gml:Graph.Gml.value_list->tendmoduletypeEDGE=sigtypet[@@derivingsexp]valcompare:t->t->intvalto_string:t->stringvalto_dot:t->stringvalparse_dot:Graph.Dot_ast.attrlist->tvalparse_gml:Graph.Gml.value_list->tvaldefault:tendmoduletypeWEIGHT=sigtypet[@@derivingsexp]typeedge[@@derivingsexp]valweight:edge->tvalcompare:t->t->intvaladd:t->t->tvalzero:tendmoduletypeNETWORK=sigmoduleTopology:sigtypettypevertex[@@derivingsexp]typeedge[@@derivingsexp]typeport=int32[@@derivingsexp]moduleVertex:VERTEXmoduleEdge:EDGEmoduleUnitWeight:WEIGHTwithtypet=intandtypeedge=Edge.tmoduleEdgeSet:Set.SwithtypeElt.t=edgemoduleVertexSet:Set.SwithtypeElt.t=vertexmoduleVertexHash:Hashtbl.Swithtypekey=vertexmodulePortSet:Set.SwithtypeElt.t=port(* Constructors *)valcopy:t->tvalempty:unit->tvaladd_vertex:t->Vertex.t->(t*vertex)valadd_port:t->vertex->port->tvaladd_edge:t->vertex->port->Edge.t->vertex->port->(t*edge)(* Special Accessors *)valnum_vertexes:t->intvalnum_edges:t->intvalvertexes:t->VertexSet.tvaledges:t->EdgeSet.tvalneighbors:t->vertex->VertexSet.tvalfind_edge:t->vertex->vertex->edgevalfind_all_edges:t->vertex->vertex->EdgeSet.tvalvertex_to_ports:t->vertex->PortSet.tvalnext_hop:t->vertex->port->edgeoptionvaledge_src:edge->(vertex*port)valedge_dst:edge->(vertex*port)valinverse_edge:t->edge->edgeoption(* Label Accessors *)valvertex_to_string:t->vertex->stringvalvertex_to_label:t->vertex->Vertex.tvalvertex_of_label:t->Vertex.t->vertexvaledge_to_string:t->edge->stringvaledge_to_label:t->edge->Edge.t(* Iterators *)valiter_succ:(edge->unit)->t->vertex->unitvaliter_vertexes:(vertex->unit)->t->unitvaliter_edges:(edge->unit)->t->unitvalfold_vertexes:(vertex->'a->'a)->t->'a->'avalfold_edges:(edge->'a->'a)->t->'a->'a(* Mutators *)valremove_vertex:t->vertex->tvalremove_port:t->vertex->port->tvalremove_edge:t->edge->tvalremove_endpoint:t->(vertex*port)->tend(* Traversals *)moduleTraverse:sigvalbfs:(Topology.vertex->unit)->Topology.t->unitvaldfs:(Topology.vertex->unit)->Topology.t->unitendvalspanningtree_from:(Topology.vertex->'alist->'a)->Topology.t->Topology.vertex->'a(* Paths *)moduletypePATH=sigtypeweighttypet=Topology.edgelistexceptionNegativeCycleoftvalshortest_path:Topology.t->Topology.vertex->Topology.vertex->toptionvalall_shortest_paths:Topology.t->Topology.vertex->Topology.vertexTopology.VertexHash.tvalall_pairs_shortest_paths:topo:Topology.t->f:(Topology.vertex->Topology.vertex->bool)->(weight*Topology.vertex*Topology.vertex*Topology.edgelist)listendmodulePath(Weight:WEIGHTwithtypeedge=Topology.Edge.t):PATHwithtypeweight=Weight.tmoduleUnitPath:PATHwithtypeweight=int(* Parsing *)moduleParse:sigvalfrom_dotfile:string->Topology.tvalfrom_gmlfile:string->Topology.tend(* Pretty Printing *)modulePretty:sigvalto_string:Topology.t->stringvalto_dot:Topology.t->stringvalto_mininet:?prologue_file:string->?epilogue_file:string->Topology.t->stringendendmoduletypeMAKE=functor(Vertex:VERTEX)->functor(Edge:EDGE)->NETWORKwithmoduleTopology.Vertex=VertexandmoduleTopology.Edge=EdgemoduleMake:MAKE=functor(Vertex:VERTEX)->functor(Edge:EDGE)->struct(* FIXME: temporary hack to avoid Jane Street's annoying warnings. *)[@@@warning"-3"]moduleTopology=structtypeport=int32[@@derivingsexp]modulePortSet=Set.Make(Int32)modulePortMap=Map.Make(Int32)moduleVertex=VertexmoduleEdge=EdgemoduleVL=structtypet={id:int;label:Vertex.t}[@@derivingsexp]letcomparen1n2=Int.comparen1.idn2.idlethashn1=Hashtbl.hashn1.idletequaln1n2=n1.id=n2.idletto_stringn=string_of_intn.id(*
let sexp_of_t v = Sexp.List [Sexp.Atom "id"; sexp_of_int v.id ]
let t_of_sexp s = { id = int_of_sexp (Sexp.Atom "1"); label = vertex_t_of_sexp s }
*)endmoduleVertexSet=Set.Make(VL)moduleVertexMap=Map.Make(Vertex)moduleVertexHash=Hashtbl.Make(VL)moduleEL=structtypet={id:int;label:Edge.t;src:port;dst:port}[@@derivingsexp]letcomparee1e2=Int.comparee1.ide2.idlethashe1=Hashtbl.hashe1.idletequale1e2=e1.id=e2.idletto_stringe=string_of_inte.idletdefault={id=0;label=Edge.default;src=0l;dst=0l}endmoduleUnitWeight=structtypeedge=Edge.t[@@derivingsexp]typet=int[@@derivingsexp,compare]typelabel=EL.tletweight_=1letadd=(+)letzero=0endtypevertex=VL.t[@@derivingsexp]typeedge=vertex*EL.t*vertex[@@derivingsexp]moduleEdgeSet=Set.Make(structtypet=VL.t*EL.t*VL.t[@@derivingsexp]letcompare(e1:t)(e2:t):int=let(_,l1,_)=e1inlet(_,l2,_)=e2inEL.comparel1l2end)moduleP=Graph.Persistent.Digraph.ConcreteBidirectionalLabeled(VL)(EL)typet={graph:P.t;node_info:(vertex*PortSet.t)VertexMap.t;next_node:int;next_edge:int}(* Constructors *)letcopy(t:t):t=tletempty():t={graph=P.empty;node_info=VertexMap.empty;next_node=0;next_edge=0}let_node_vertex(t:t)(l:Vertex.t):vertex=fst(VertexMap.find_exnt.node_infol)let_node_ports(t:t)(l:Vertex.t):PortSet.t=snd(VertexMap.find_exnt.node_infol)letadd_vertex(t:t)(l:Vertex.t):t*vertex=letopenVLintry(t,_node_vertextl)withNot_found|Not_found_s_->letid=t.next_node+1inletv={id=id;label=l}inletg=P.add_vertext.graphvinletnl=VertexMap.sett.node_infol(v,PortSet.empty)in({twithgraph=g;node_info=nl;next_node=id},v)letadd_port(t:t)(v:vertex)(p:port):t=letl=v.VL.labelinletv,ps=VertexMap.find_exnt.node_infolinletnode_info=VertexMap.sett.node_infol(v,PortSet.addpsp)in{twithnode_info}letadd_edge(t:t)(v1:vertex)(p1:port)(l:Edge.t)(v2:vertex)(p2:port):t*edge=letopenELinletauxt=letid=t.next_edge+1inletl={id=id;label=l;src=p1;dst=p2}inlete=(v1,l,v2)inlett=add_porttv1p1inlett=add_porttv2p2in({twithgraph=P.add_edge_et.graphe;next_edge=id},e)intryletes=P.find_all_edgest.graphv1v2inletes'=List.filteres(fun(s,l,d)->Poly.(l.src=p1&&l.dst=p2))inmatches'with|[]->auxt|es->letgraph'=List.fold_leftes~init:t.graph~f:(funacce->P.remove_edge_eacce)inlett'={twithgraph=graph'}inauxt'withNot_found|Not_found_s_->auxt(* Special Accessors *)letnum_vertexes(t:t):int=P.nb_vertext.graphletnum_edges(t:t):int=P.nb_edgest.graphletedges(t:t):EdgeSet.t=P.fold_edges_e(funeacc->EdgeSet.addacce)t.graphEdgeSet.emptyletvertexes(t:t):VertexSet.t=P.fold_vertex(funvacc->VertexSet.addaccv)t.graphVertexSet.emptyletneighbors(t:t)(v:vertex):VertexSet.t=P.fold_succ(funvacc->VertexSet.addaccv)t.graphvVertexSet.emptyletfind_edge(t:t)(src:vertex)(dst:vertex):edge=P.find_edget.graphsrcdstletfind_all_edges(t:t)(src:vertex)(dst:vertex):EdgeSet.t=List.fold_left(P.find_all_edgest.graphsrcdst)~init:EdgeSet.empty~f:EdgeSet.addletvertex_to_string(t:t)(v:vertex):string=VL.to_stringvletvertex_to_label(t:t)(v:vertex):Vertex.t=v.VL.labelletvertex_of_label(t:t)(l:Vertex.t):vertex=_node_vertextlletedge_to_label(t:t)(e:edge):Edge.t=let(_,l,_)=einl.EL.labelletedge_to_string(t:t)(e:edge):string=let(_,e,_)=einEL.to_stringeletedge_src(e:edge):(vertex*port)=let(v1,l,_)=ein(v1,l.EL.src)letedge_dst(e:edge):(vertex*port)=let(_,l,v2)=ein(v2,l.EL.dst)letinverse_edge(t:t)(e:edge):edgeoption=letsrc_vertex,src_port=edge_srceinletdst_vertex,dst_port=edge_dsteintryletinv_e=find_edgetdst_vertexsrc_vertexinifPoly.(dst_port=snd(edge_srcinv_e)&&src_port=snd(edge_dstinv_e))thenSome(inv_e)elseNonewith_->Noneletnext_hop(t:t)(v1:vertex)(p:port):edgeoption=letrecloopes=matcheswith|[]->None|((_,l,v2)ase)::es'->ifPoly.(l.EL.src=p)thenSomeeelse(loopes')inloop(P.succ_et.graphv1)letvertex_to_ports(t:t)(v1:vertex):PortSet.t=_node_portstv1.VL.label(* Iterators *)letfold_vertexes(f:vertex->'a->'a)(t:t)(init:'a):'a=P.fold_vertexft.graphinitletfold_edges(f:edge->'a->'a)(t:t)(init:'a):'a=P.fold_edges_eft.graphinitletiter_vertexes(f:vertex->unit)(t:t):unit=P.iter_vertexft.graphletiter_edges(f:edge->unit)(t:t):unit=P.iter_edges_eft.graphletiter_succ(f:edge->unit)(t:t)(v:vertex):unit=P.iter_succ_eft.graphv(* Mutators *)letremove_vertex(t:t)(v:vertex):t=letgraph=P.remove_vertext.graphvinletnode_info=VertexMap.removet.node_infov.VL.labelin{twithgraph;node_info}letremove_port(t:t)(v:vertex)(p:port):t=letv,ps=VertexMap.find_exnt.node_infov.VL.labelinletps=PortSet.removepspinletnode_info=VertexMap.sett.node_infov.VL.label(v,ps)in{twithnode_info}letremove_edge(t:t)(e:edge):t={twithgraph=P.remove_edge_et.graphe}letremove_endpoint(t:t)(ep:vertex*port):t=lett=fold_edges(funeacc->ifPoly.(edge_srce=ep||edge_dste=ep)thenremove_edgeacceelseacc)ttinletv,p=epinletv,ps=VertexMap.find_exnt.node_infov.VL.labelinletps=PortSet.removepspinletnode_info=VertexMap.sett.node_infov.VL.label(v,ps)in{twithnode_info}letremove_port(t:t)(v:vertex)(p:port)=remove_endpointt(v,p)end(* Traversals *)moduleTraverse=structopenTopologymoduleBfs=Graph.Traverse.Bfs(P)moduleDfs=Graph.Traverse.Dfs(P)letbfs(f:vertex->unit)(t:t)=Bfs.iterft.graphletdfs(f:vertex->unit)(t:t)=Dfs.prefixft.graphendmodulePrim=Graph.Prim.Make(Topology.P)(structtypeedge=Topology.P.edgetypet=int[@@derivingcompare]typelabel=Topology.EL.tletweight_=1letadd=(+)letzero=0end)letspanningtree_fromfgraphvertex=letopenTopology.Pinletedges=Prim.spanningtree_fromgraph.Topology.graphvertexinlettree=List.fold_leftedges~init:empty~f:add_edge_einletrecloopvx=fvx(List.map(succtreevx)~f:loop)inloopvertex(* Paths *)moduletypePATH=sigtypeweighttypet=Topology.edgelistexceptionNegativeCycleoftvalshortest_path:Topology.t->Topology.vertex->Topology.vertex->toptionvalall_shortest_paths:Topology.t->Topology.vertex->Topology.vertexTopology.VertexHash.tvalall_pairs_shortest_paths:topo:Topology.t->f:(Topology.vertex->Topology.vertex->bool)->(weight*Topology.vertex*Topology.vertex*Topology.edgelist)listendmodulePath=functor(Weight:WEIGHTwithtypeedge=Topology.Edge.t)->structopenTopologymoduleWL=structtypet=Weight.ttypeedge=P.E.tletweighte=Weight.weight((P.E.labele).EL.label)letcompare=Weight.compareletadd=Weight.addletzero=Weight.zeroendmoduleDijkstra=Graph.Path.Dijkstra(P)(WL)typeweight=Weight.ttypet=edgelistletshortest_path(t:Topology.t)(v1:vertex)(v2:vertex):toption=tryletpth,_=Dijkstra.shortest_patht.graphv1v2inSomepthwithNot_found|Not_found_s_->NoneexceptionNegativeCycleofedgelist(* Implementation of Bellman-Ford algorithm, based on that in ocamlgraph's
Path library. Returns a hashtable mapping each node to its predecessor in
the path *)letall_shortest_paths(t:Topology.t)(src:vertex):(vertexVertexHash.t)=letsize=P.nb_vertext.graphinletdist=VertexHash.create()~size:sizeinletprev=VertexHash.create()~size:sizeinletadmissible=VertexHash.create()~size:sizeinVertexHash.setdistsrcWeight.zero;letbuild_cycle_fromx0=letrectraverse_parentxret=lete=VertexHash.find_exnadmissiblexinlets,_=edge_srceinifPoly.(s=x0)thene::retelsetraverse_parents(e::ret)intraverse_parentx0[]inletfind_cyclex0=letrecvisitxvisited=ifVertexSet.memvisitedxthenbuild_cycle_fromxelsebeginlete=VertexHash.find_exnadmissiblexinlets,_=edge_srceinvisits(VertexSet.addvisitedx)endinvisitx0(VertexSet.empty)inletrecrelax(i:int)=letupdate=P.fold_edges_e(funex->letev1,_=edge_srceinletev2,_=edge_dsteintrybeginletdev1=VertexHash.find_exndistev1inletdev2=Weight.adddev1(Weight.weight(Topology.edge_to_labelte))inletimprovement=tryWeight.comparedev2(VertexHash.find_exndistev2)<0withNot_found|Not_found_s_->trueinifimprovementthenbeginVertexHash.setprevev2ev1;VertexHash.setdistev2dev2;VertexHash.setadmissibleev2e;Someev2endelsexendwithNot_found|Not_found_s_->x)t.graphNoneinmatchupdatewith|Somex->if(phys_equali(P.nb_vertext.graph))thenraise(NegativeCycle(find_cyclex))elserelax(i+1)|None->previnletr=relax0inrletall_pairs_shortest_paths~(topo:Topology.t)~(f:Topology.vertex->Topology.vertex->bool):(Weight.t*vertex*vertex*edgelist)list=(* Because Weight does not provide infinity, we lift Weight.t
using an option: None corresponds to infinity, and Some w
corresponds to a finite weight. *)letadd_opto1o2=matcho1,o2with|Somew1,Somew2->Some(Weight.addw1w2)|_->Noneinletlt_opto1o2=matcho1,o2with|Somew1,Somew2->Weight.comparew1w2<0|Some_,None->true|None,Some_->false|None,None->falseinletmake_matrix(g:Topology.t)=letn=P.nb_vertexg.graphinletvs=vertexesginletnodes=Array.create~len:n(VertexSet.choose_exnvs)inlet_=VertexSet.foldvs~init:0~f:(funiv->Array.setnodesiv;i+1)in(Array.initn(funi->Array.initn(funj->ifi=jthen(SomeWeight.zero,lazy[])elsetrylete=find_edgegnodes.(i)nodes.(j)inletw=Weight.weight(Topology.edge_to_labelge)in(Somew,lazy[e])withNot_found|Not_found_s_->(None,lazy[]))),nodes)inletmatrix,vxs=make_matrixtopoinletn=P.nb_vertextopo.graphinletdistij=fst(matrix.(i).(j))in(* let path i j = Lazy.force (snd (matrix.(i).(j))) in *)(* assumes that !(start = mid && stop = mid) *)letpath(start:int)(mid:int)(stop:int)=ifstart=midthenlazy(find_edgetopovxs.(start)vxs.(stop)::Lazy.force(snd(matrix.(mid).(stop))))elseifstop=midthenlazy(Lazy.force(snd(matrix.(start).(mid)))@[find_edgetopovxs.(start)vxs.(stop)])elselazy(Lazy.force(sndmatrix.(start).(mid))@Lazy.force(sndmatrix.(mid).(stop)))infork=0ton-1dofori=0ton-1doforj=0ton-1doletdist_ikj=add_opt(distik)(distkj)iniflt_optdist_ikj(distij)thenmatrix.(i).(j)<-(dist_ikj,pathikj)donedonedone;letpaths=ref[]inArray.iterimatrix~f:(funiarray->Array.iteriarray~f:(funjelt->matcheltwith|Somew,pwhenf(vxs.(i))(vxs.(j))->paths:=(w,vxs.(i),vxs.(j),Lazy.forcep)::!paths|_->()));!pathsendmoduleUnitPath=Path(Topology.UnitWeight)(* Parsing *)moduleParse=structopenTopology(* TODO(jnf): this could be refactored into a functor that wraps a
G.t in an arbitrary type and lifts all other G operations over
that type. *)moduleBuild=structmoduleG=structmoduleV=P.VmoduleE=P.Etypevertex=V.ttypeedge=E.ttypet=Topology.tletempty()=empty()letremove_vertextv={twithgraph=P.remove_vertext.graphv}letremove_edgetv1v2={twithgraph=P.remove_edget.graphv1v2}letremove_edge_ete={twithgraph=P.remove_edge_et.graphe}letadd_vertextv={twithgraph=P.add_vertext.graphv;node_info=VertexMap.sett.node_infov.Topology.VL.label(v,PortSet.empty);next_node=v.Topology.VL.id+1}letadd_edgetv1v2={twithgraph=P.add_edget.graphv1v2;next_edge=t.next_edge+1}letadd_edge_ete=let(_,l,_)=ein{twithgraph=P.add_edge_et.graphe;next_edge=l.Topology.EL.id+1}letfold_pred_efti=P.fold_pred_eft.graphiletiter_pred_eft=P.iter_pred_eft.graphletfold_succ_efti=P.fold_succ_eft.graphiletiter_succftv=P.iter_succft.graphvletiter_succ_eftv=P.iter_succ_eft.graphvletiter_edgesft=P.iter_edgesft.graphletfold_predftvi=P.fold_predft.graphviletfold_succftvi=P.fold_succft.graphviletiter_predftv=P.iter_predft.graphvletmap_vertexft={twithgraph=P.map_vertexft.graph}letfold_edges_efti=P.fold_edges_eft.graphiletiter_edges_eft=P.iter_edges_eft.graphletfold_vertexfti=P.fold_vertexft.graphiletfold_edgesfti=P.fold_edgesft.graphiletiter_vertexft=P.iter_vertexft.graphletpred_etv=P.pred_et.graphvletsucc_etv=P.succ_et.graphvletpredtv=P.predt.graphvletsucctv=P.succt.graphvletfind_all_edgestv1v2=P.find_all_edgest.graphv1v2letfind_edgetv1v2=P.find_edget.graphv1v2letmem_edge_ete=P.mem_edge_et.grapheletmem_edgetv1v2=P.mem_edget.graphv1v2letmem_vertextv=P.mem_vertext.graphvletin_degreetv=P.in_degreet.graphvletout_degreetv=P.out_degreet.graphvletnb_edgest=P.nb_edgest.graphletnb_vertext=P.nb_vertext.graphletis_emptyt=P.is_emptyt.graphletis_directed=P.is_directedendletempty=G.emptyletremove_vertex=G.remove_vertexletremove_edge=G.remove_edgeletremove_edge_e=G.remove_edge_eletadd_vertex=G.add_vertexletadd_edge=G.add_edgeletadd_edge_e=G.add_edge_eletcopyt=tendmoduleDot=Graph.Dot.Parse(Build)(structletget_porto=matchowith|Some(s)->beginmatchswith|Graph.Dot_ast.Number(i)->Scanf.sscanfi"%lu"(funi->i)|_->failwith"Requires number"end|None->failwith"Requires value"letnext_node=letr=ref0infun_->incrr;!rletnext_edge=letr=ref0infun_->incrr;!rletnodeidattrs=letopenVLin{id=next_node();label=Vertex.parse_dotidattrs}letedgeattrs=(* This is a bit of a hack because we only look at the first list of attrs *)letats=List.hd_exnattrsinletsrc,dst,rest=List.fold_leftats~init:(0l,0l,[])~f:(fun(src,dst,acc)(k,v)->matchkwith|Graph.Dot_ast.Ident("src_port")->(get_portv,dst,acc)|Graph.Dot_ast.Ident("dst_port")->(src,get_portv,acc)|_->(src,dst,(k,v)::acc))inletattrs'=rest::(List.tl_exnattrs)inletopenELin{id=next_edge();label=Edge.parse_dotattrs';src=src;dst=dst}end)moduleGml=Graph.Gml.Parse(Build)(structletnext_node=letr=ref0infun_->incrr;!rletnext_edge=letr=ref0infun_->incrr;!rletnodevs=letopenVLin{id=next_node();label=Vertex.parse_gmlvs}letedgevs=letopenELin{id=next_edge();label=Edge.parse_gmlvs;src=0l;dst=0l}end)letfrom_dotfile=Dot.parseletfrom_gmlfile=Gml.parseend(* Pretty Printing *)modulePretty=structopenTopologyletload_filefn=In_channel.(with_filefn~f:input_all)letto_dot(t:t)=letes=(EdgeSet.fold(edgest)~init:""~f:(funacc(s,l,d)->let_,src_port=edge_src(s,l,d)inlet_,dst_port=edge_dst(s,l,d)inPrintf.sprintf"%s%s%s -> %s {src_port=%lu; dst_port=%lu; %s};"acc(ifPoly.(acc="")then""else"\n")(Vertex.to_strings.VL.label)(Vertex.to_stringd.VL.label)src_portdst_port(Edge.to_dotl.EL.label)))inletvs=(VertexSet.fold(vertexest)~init:""~f:(funaccv->Printf.sprintf"%s%s\n%s;"acc(ifPoly.(acc="")then""else"\n")(Vertex.to_dotv.VL.label)))inPrintf.sprintf"digraph G {\n%s\n%s\n}\n"vsesletto_string(t:t):string=to_dott(* Produce a Mininet script that implements the given topology *)letto_mininet?(prologue_file="static/mn_prologue.txt")?(epilogue_file="static/mn_epilogue.txt")(t:t):string=(* Load static strings (maybe there's a better way to do this?) *)letprologue=load_fileprologue_fileinletepilogue=load_fileepilogue_filein(* Check if an edge or its reverse has been added already *)letseen=refEdgeSet.emptyinletnot_printablee=let(src,edge,dst)=einletinverse=matchinverse_edgetewith|None->false|Somee->EdgeSet.mem!seeneinPoly.(src=dst)||EdgeSet.mem!seene||inversein(* Add the hosts and switches *)letadd_hosts=fold_vertexes(funvacc->letlabel=vertex_to_labeltvinletadd=Vertex.to_mininetlabelinacc^" "^add)t""in(* Add links between them *)letlinks=fold_edges(funeacc->letadd=if(not_printablee)then""(* Mininet links are bidirectional *)elseletsrc_vertex,src_port=edge_srceinletdst_vertex,dst_port=edge_dsteinletsrc_label=vertex_to_labeltsrc_vertexinletdst_label=vertex_to_labeltdst_vertexinletsrc=Str.global_replace(Str.regexp"[ ,]")""(Vertex.to_stringsrc_label)inletdst=Str.global_replace(Str.regexp"[ ,]")""(Vertex.to_stringdst_label)inPrintf.sprintf" net.addLink(%s, %s, %ld, %ld)\n"srcdstsrc_portdst_portinseen:=EdgeSet.add!seene;acc^add)t""inprologue^add_hosts^links^epilogueendend(* Utility functions *)letparse_rate(r:string):Int64.t=leta=Str.search_forward(Str.regexp"\\([0-9]+\\)")r0inletamt=Str.matched_group0rinlet_=Str.search_forward(Str.regexp"\\([A-Za-z]+\\)")rainletrate=Str.matched_group0rinletn=Int64.of_stringamtinletm=matchratewith|"bps"->1L|"Bps"->8L|"kbps"->1024L|"kBps"->8192L|"Mbps"->1048576L|"MBps"->8388608L|"Gbps"->1073741824L|"GBps"->8589934592L|_->failwith"Invalid rate specifier"inInt64.(n*m)letmaybeo=matchowith|Some(s)->s|None->failwith"Requires value"(* Convert the generic id type to more specialized types *)letstring_of_idid=matchidwith|Dot_ast.Ident(s)->s|Dot_ast.Number(s)->"n"^s|Dot_ast.String(s)->s|Dot_ast.Html(s)->sletint32_of_idvo=matchmaybevowith|Dot_ast.Number(n)->Int32.of_stringn|_->failwith"Need a number to get int32\n"letint64_of_idvo=matchmaybevowith|Dot_ast.Number(n)->Int64.of_stringn|_->failwith"Need a number to get id\n"letcapacity_of_idvo=matchmaybevowith|Dot_ast.String(s)->parse_rates|_->failwith"Need a string to get capacity\n"moduleNode=structtypedevice=Switch|Host|Middlebox[@@derivingsexp,compare]typet={dev_type:device;dev_id:int64;ip:int32;mac:int64;name:string}[@@derivingsexp,compare]typepartial_t={partial_dev_type:deviceoption;partial_dev_id:int64option;partial_ip:int32option;partial_mac:int64option;partial_name:stringoption}letdefault={dev_type=Host;dev_id=0L;name="";ip=0l;mac=0L}letpartial_default={partial_dev_type=None;partial_dev_id=None;partial_ip=None;partial_mac=None;partial_name=None}letcreate(n:string)(i:int64)(d:device)(ip:int32)(mac:int64):t={dev_type=d;name=n;ip=ip;mac=mac;dev_id=i}letname(n:t):string=n.nameletid(n:t):int64=n.dev_idletdevice(n:t):device=n.dev_typeletmac(n:t):int64=n.macletip(n:t):int32=n.ipletto_stringn=n.nameletto_dotn=letdevstr=matchn.dev_typewith|Switch->"switch"|Host->"host"|Middlebox->"middlebox"inPrintf.sprintf"%s [type=%s, ip=\"%s\", mac=\"%s\", id=%Ld]"n.namedevstr(Packet.string_of_ipn.ip)(Packet.string_of_macn.mac)(n.dev_id)letto_mininetn=matchn.dev_typewith|Host->(* Mininet doesn't like underscores in host names *)letmnname=Str.global_replace(Str.regexp"_")""n.nameinPrintf.sprintf"%s = net.addHost(\'%s\', mac=\'%s\', ip=\'%s\')\n"n.namemnname(Packet.string_of_macn.mac)(Packet.string_of_ipn.ip)|_->Printf.sprintf"%s = net.addSwitch(\'s%Ld\')\n"n.namen.dev_id(* Update the record for a node *)letupdate_dot_attrn(k,vo)=letdev_type_ofvo=matchstring_of_id(maybevo)with|"host"->Host|"switch"->Switch|"middlebox"->Middlebox|s->failwith(Printf.sprintf"Unknown node type: %s\n"s)inletip_ofvo=matchmaybevowith|Dot_ast.String(s)->Packet.ip_of_strings|_->failwith"IPs must be represented as a string (in quotes)\n"inletmac_ofvo=matchmaybevowith|Dot_ast.String(s)->Packet.mac_of_strings|_->failwith"MAC must be represented as a string (in quotes)\n"inmatchkwith|Dot_ast.Ident("type")->{nwithpartial_dev_type=Some(dev_type_ofvo)}|Dot_ast.Ident("id")->{nwithpartial_dev_id=Some(int64_of_idvo)}|Dot_ast.Ident("ip")->{nwithpartial_ip=Some(ip_ofvo)}|Dot_ast.Ident("mac")->{nwithpartial_mac=Some(mac_ofvo)}|_->failwith"Unknown node attribute\n"(* Take the partial node record and remove the option types, or
raise an error if it is not fully filled *)letunbox(p:partial_t):t=letunbox_host(p:partial_t)=leti=matchp.partial_ipwith|Somei->i|None->failwith"Host must have an IP address"inletm=matchp.partial_macwith|Somem->m|None->failwith"Host must have a MAC address"inletn=matchp.partial_namewith|Somen->n|None->failwith"Host must have a name"inletid=matchp.partial_dev_idwith|Somei->i|None->min{dev_type=Host;dev_id=id;ip=i;mac=m;name=n}inletunbox_switch(p:partial_t)=letid=matchp.partial_dev_idwith|Somei->i|None->failwith"Switches must have a unique id"inletn=matchp.partial_namewith|Somen->n|None->failwith"Switch must have a name"inletm=matchp.partial_macwith|Somem->m|None->0Linleti=matchp.partial_ipwith|Somei->i|None->0lin{dev_type=Switch;dev_id=id;ip=i;mac=m;name=n}inmatchp.partial_dev_typewith|SomeHost->unbox_hostp|SomeSwitch->unbox_switchp|SomeMiddlebox->unbox_switchp|_->failwith"Must provide valid devide type for all nodes"letparse_dot(i:Dot_ast.node_id)(ats:Dot_ast.attrlist):t=let(id,popt)=iinletname=string_of_ididinletat=List.hd_exnatsinletpartial=List.fold_leftat~init:{partial_defaultwithpartial_name=Somename}~f:update_dot_attrinunboxpartialletint64_of_valuev=matchvwith|Gml.Int(i)->Int64.of_inti|_->failwith"Id requires int value\n"letstring_of_valuev=matchvwith|Gml.String(s)->s|_->failwith"Label requires int value\n"letupdate_gml_attrn(key,value)=matchkeywith|"id"->{nwithdev_id=int64_of_valuevalue}|"label"->{nwithname=string_of_valuevalue}|"mac"->{nwithmac=Packet.mac_of_string(string_of_valuevalue)}|"ip"->{nwithip=Packet.ip_of_string(string_of_valuevalue)}|_->nletparse_gml(vs:Gml.value_list):t=List.fold_leftvs~init:default~f:update_gml_attrendmoduleLink=structtypet={cost:int64;capacity:int64;mutableweight:float}[@@derivingsexp,compare]letdefault={cost=1L;capacity=Int64.of_int640x7FFFFFFFFFFFFFFFL;weight=1.}letcreate(cost:int64)(cap:int64):t={defaultwithcost=cost;capacity=cap}letcost(l:t)=l.costletcapacity(l:t)=l.capacityletweight(l:t)=l.weightletset_weight(l:t)(w:float)=l.weight<-wletto_string(l:t):string=Printf.sprintf" cost = %s; capacity = %s; "(Int64.to_stringl.cost)(Int64.to_stringl.capacity)letto_dot=to_stringletupdate_dot_attredge(key,valopt)=matchkeywith|Dot_ast.Ident("cost")->{edgewithcost=int64_of_idvalopt}|Dot_ast.Ident("capacity")->{edgewithcapacity=capacity_of_idvalopt}|Dot_ast.Ident(s)->edge|_->failwith("Unknown edge attribute\n")letupdate_gml_attredge(key,value)=matchkeywith|_->edgeletparse_dot(ats:Dot_ast.attrlist):t=letat=List.hd_exnatsinletlink=List.fold_leftat~init:default~f:update_dot_attrinlinkletparse_gml(vs:Gml.value_list):t=letlink=List.fold_leftvs~init:default~f:update_gml_attrinlinkendmoduleWeight=structtypeedge=Link.t[@@derivingsexp]typet=float[@@derivingsexp]letweightl=letopenLinkinl.weightletcompare=Poly.compareletadd=(+.)letzero=0.endmoduleNet=Make(Node)(Link)moduleNetPath=Net.Path(Weight)