123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247(**************************************************************************)(* *)(* Copyright (C) Jean-Christophe Filliatre *)(* *)(* 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. *)(* *)(* File modified by CEA (Commissariat à l'énergie atomique et aux *)(* énergies alternatives). *)(* *)(**************************************************************************)(*s A trie is a tree-like structure to implement dictionaries over
keys which have list-like structures. The idea is that each node
branches on an element of the list and stores the value associated
to the path from the root, if any. Therefore, a trie can be
defined as soon as a map over the elements of the list is
given. *)moduletypeS=sigtypekeytype+'atvalempty:'atvalis_empty:'at->boolvaladd:key->'a->'at->'atvalfind:key->'at->'avalfind_opt:key->'at->'aoptionvalremove:key->'at->'atvalmerge:(key->'aoption->'boption->'coption)->'at->'bt->'ctvalunion:(key->'a->'a->'aoption)->'at->'at->'atvalmem:key->'at->boolvaliter:(key->'a->unit)->'at->unitvalmap:('a->'b)->'at->'btvalmapi:(key->'a->'b)->'at->'btvalfold:(key->'a->'b->'b)->'at->'b->'bvalcompare:('a->'a->int)->'at->'at->intvalequal:('a->'a->bool)->'at->'at->boolvalexists:(key->'a->bool)->'at->boolvalto_seq:'at->(key*'a)Seq.tendmoduleMake(M:S)=struct(*s Then a trie is just a tree-like structure, where a possible
information is stored at the node (['a option]) and where the sons
are given by a map from type [key] to sub-tries, so of type
['a t M.t]. The empty trie is just the empty map. *)typekey=M.keylisttype'at=Nodeof'aoption*'atM.t(*
open Unmarshal
let help dkey =
let key' = t_list dkey in
let tmp = [| Abstract; Abstract |] in
let t = Structure (Sum [| [| t_tuple tmp |] |]) in
tmp.(0) <- t_option key';
tmp.(1) <- M.descr t;
t
*)letempty=Node(None,M.empty)(*s To find a mapping in a trie is easy: when all the elements of the
key have been read, we just inspect the optional info at the
current node; otherwise, we descend in the appropriate sub-trie
using [M.find]. *)letrecfindlt=match(l,t)with|[],Node(None,_)->raiseNot_found|[],Node(Somev,_)->v|x::r,Node(_,m)->findr(M.findxm)letrecfind_optlt=match(l,t)with|[],Node(None,_)->None|[],Node(Somev,_)->Somev|x::r,Node(_,m)->Option.bind(find_optr)(M.find_optxm)letrecmemlt=match(l,t)with|[],Node(None,_)->false|[],Node(Some_,_)->true|x::r,Node(_,m)->trymemr(M.findxm)withNot_found->false(*s Insertion is more subtle. When the final node is reached, we just
put the information ([Some v]). Otherwise, we have to insert the
binding in the appropriate sub-trie [t']. But it may not exists,
and in that case [t'] is bound to an empty trie. Then we get a new
sub-trie [t''] by a recursive insertion and we modify the
branching, so that it now points to [t''], with [M.add]. *)letaddlvt=letrecins=function|[],Node(_,m)->Node(Somev,m)|x::r,Node(v,m)->lett'=tryM.findxmwithNot_found->emptyinlett''=ins(r,t')inNode(v,M.addxt''m)inins(l,t)(*s When removing a binding, we take care of not leaving bindings to empty
sub-tries in the nodes. Therefore, we test wether the result [t'] of
the recursive call is the empty trie [empty]: if so, we just remove
the branching with [M.remove]; otherwise, we modify it with [M.add]. *)letrecremovelt=match(l,t)with|[],Node(_,m)->Node(None,m)|x::r,Node(v,m)->trylett'=remover(M.findxm)inNode(v,ift'=emptythenM.removexmelseM.addxt'm)withNot_found->t(*s The iterators [map], [mapi], [iter] and [fold] are implemented in
a straigthforward way using the corresponding iterators [M.map],
[M.mapi], [M.iter] and [M.fold]. For the last three of them,
we have to remember the path from the root, as an extra argument
[revp]. Since elements are pushed in reverse order in [revp],
we have to reverse it with [List.rev] when the actual binding
has to be passed to function [f]. *)letrecmapf=function|Node(None,m)->Node(None,M.map(mapf)m)|Node(Somev,m)->Node(Some(fv),M.map(mapf)m)letmapift=letrecmaprecrevp=function|Node(None,m)->Node(None,M.mapi(funx->maprec(x::revp))m)|Node(Somev,m)->Node(Some(frevpv),M.mapi(funx->maprec(x::revp))m)inmaprec[]tletiterft=letrectraverserevp=function|Node(None,m)->M.iter(funx->traverse(x::revp))m|Node(Somev,m)->frevpv;M.iter(funxt->traverse(x::revp)t)mintraverse[]tletfoldftacc=letrectraverserevptacc=matchtwith|Node(None,m)->M.fold(funx->traverse(x::revp))macc|Node(Somev,m)->frevpv(M.fold(funx->traverse(x::revp))macc)intraverse[]taccletexistsft=letrectraverserevpt=matchtwith|Node(None,m)->M.exists(funx->traverse(x::revp))m|Node(Somev,m)->frevpv||M.exists(funx->traverse(x::revp))mintraverse[]tletcomparecmpab=letreccompab=matcha,bwith|Node(Some_,_),Node(None,_)->1|Node(None,_),Node(Some_,_)->-1|Node(None,m1),Node(None,m2)->M.comparecompm1m2|Node(Somea,m1),Node(Someb,m2)->letc=cmpabinifc<>0thencelseM.comparecompm1m2incompabletequaleqab=letreccompab=matcha,bwith|Node(None,m1),Node(None,m2)->M.equalcompm1m2|Node(Somea,m1),Node(Someb,m2)->eqab&&M.equalcompm1m2|_->falseincompab(* The base case is rather stupid, but constructable *)letis_empty=function|Node(None,m1)->M.is_emptym1|Node(Some_,_)->falseletmergeft1t2=letrecauxrevpt1t2=letv1,m1=matcht1with|None->None,M.empty|Some(Node(v1,m1))->v1,m1andv2,m2=matcht2with|None->None,M.empty|Some(Node(v2,m2))->v2,m2inletv=frevpv1v2andm=M.merge(funxt1t2->Some(aux(x::revp)t1t2))m1m2inNode(v,m)inaux[](Somet1)(Somet2)letunionft1t2=letrecauxrevpt1t2=letNode(v1,m1)=t1andNode(v2,m2)=t2inletv=matchv1,v2with|None,None->None|(Some_asv),None|None,(Some_asv)->v|Somev1,Somev2->frevpv1v2andm=M.union(funxt1t2->Some(aux(x::revp)t1t2))m1m2inNode(v,m)inaux[]t1t2letto_seqt=letrecauxrevpt=letNode(v,m)=tinSeq.append(Seq.map(funv->revp,v)(Option.to_seqv))(Seq.flat_map(fun(x,t)->aux(x::revp)t)(M.to_seqm))inaux[]tletadd_prefixkm=Node(None,M.addkmM.empty)letselect_prefixkt=letNode(_,m)=tinM.findkmletprefixes_seq(Node(_,map))=M.to_seqmapend