123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123(* This file is part of asak.
*
* Copyright (C) 2019 IRIF / OCaml Software Foundation.
*
* asak is distributed under the terms of the MIT license. See the
* included LICENSE file for details. *)openWtreemoduleDistance=structtypet=Regularofint|Infinityletcomparexy=matchxwith|Infinity->1|Regularx'->matchywith|Infinity->-1|Regulary'->comparex'y'let(<)xy=comparexy=-1letmaxxy=ifcomparexy<=0thenyelsexletminxy=ifcomparexy<=0thenxelseyendletrecsymmetric_differencexy=matchx,ywith|[],z|z,[]->false,z|xx::xs,yy::ys->ifxx<yythenletb,ndiff=symmetric_differencexsyinb,xx::ndiffelseifxx>yythenletb,ndiff=symmetric_differencexysinb,yy::ndiffelselet_,ndiff=symmetric_differencexsysintrue,ndiffletsum_of_fst=List.fold_left(funacc(a,_)->acc+a)0letdistxy=letopenDistanceinletrecauxxy=matchx,ywith|Leaf(x,_),Leaf(y,_)->letb,diff=symmetric_differencexyinifbthenRegular(sum_of_fstdiff)elseInfinity|Node(_,u,v),l|l,Node(_,u,v)->max(auxul)(auxvl)inauxxyletget_min_distxs=letchoose_optionde=letopenDistanceinfunction|None->(d,e)|Some(old_d,old_e)->ifd<old_dthen(d,e)else(old_d,old_e)inletmin=refNoneinList.iter(funx->List.iter(funy->ifx!=ythenmin:=Some(choose_option(distxy)(x,y)!min))xs)xs;match!minwith|None->failwith"get_min_dist"|Somex->xletmergepuvxs=letxs=List.filter(funx->x!=u&&x!=v)xsin(Node(p,u,v))::xs(* Add x in a cluster, identified by its hash list xs *)letadd_in_clusterxxs=letrecgo=function|[]->[(xs,[x])]|((us,ys)ase)::zs->ifus=xsthen(us,x::ys)::zselsee::gozsingoletremove_fst_in_treet=fold_tree(funpuv->Node(p,u,v))(fun(_,x)->Leafx)tletcluster(m:('a*(int*string)list)list):('alist)wtreelist=letrecaux=function|[]->[]|[x]->[x]|lst->let(p,(u,v))=get_min_distlstinmatchpwith|Infinity->lst|Regularp->aux(mergepuvlst)inletstart=List.map(funx->Leafx)@@List.fold_left(funacc(x,xs)->add_in_clusterx(List.sortcomparexs)acc)[]minList.sort(funxy->-compare(size_of_treeList.lengthx)(size_of_treeList.lengthy))@@List.mapremove_fst_in_tree@@auxstart