123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311(* A representation of a tree with maybe infinite branching. Children
are evaluated lazily. Children aims to encode smaller values than
the root. There is no order, this is ensured by the user of the
module. See [shrink] and [bind] to see how it is used. *)type'at={root:'a;children:'atSeq.t;merge:'atSeq.t->'atSeq.t->'atSeq.t(* This component say how children should be merged using [bind]. *)}type'atree='at(* By default, children are merged using [Seq.append]. This is the
natural way to merge children. *)letdefault_merge=Seq.appendletroot{root;_}=rootletreturn:'a->'at=funvalue->{root=value;children=Seq.empty;merge=default_merge}letrecmakemake_childrenroot={root;children=Seq.map(makemake_children)(make_childrenroot);merge=default_merge}letmakerootmake_children=makemake_childrenrootletrecwith_merge~mergetree={root=tree.root;children=Seq.map(funtree->with_merge~mergetree)tree.children;merge}letget_merge{merge;_}=merge(* This simple function will not be used in practice. It is a
particular case of the one defined in the module [Forest]. *)letrecbind:'at->('a->'bt)->'bt=(* We are making an arbitrary choice by using [default_merge] here:
We could concatenate the right sequence first or we could even
give the opportunity to the user to chose the ordering. From
experiments, it seems that using [Seq.append] works pretty-well.
To define more complex behaviors, the user may redefine how merge
is done. This enalbes new possibilities for shrinking. *)funaf->letseq_left=Seq.map(funtree->bindtreef)a.childreninletb=fa.rootinletseq_right=b.childreninletchildren=b.mergeseq_leftseq_rightin{root=b.root;children;merge=b.merge}moduleSyntax=structlet(let*)=bindletreturn=returnendletrecmapfx={root=fx.root;children=Seq.map(mapf)x.children;merge=Seq.append}moduleSeq=struct(* [Seq] is a monad. *)includeSeqletbindxf=Seq.mapfx|>Seq.concatletsnocxseq=default_mergeseq(Seq.returnx)endletcrunch:int->'at->'at=fundepthtree->letdepths=Seq.ints0|>Seq.takedepthinletfoldtree_=letchildren=Seq.flat_map(funtree->Seq.snoc(returntree.root)tree.children)tree.childrenin{treewithchildren}inSeq.fold_leftfoldtreedepthsmoduleForest=structtype'at='atreeSeq.t(* Invariant: forall rs t, Seq.length (t rs) > 0
This invariant is ensured by the function of this module. We
could let the user ensuring this invariant or ensuring it via the
type system. For simplicity, efficiency, and because the module
is short, it seems better to guarantee it this way. *)letreturnv=Seq.return(returnv)letlifttree=Seq.returntreeletmakerootmake_children=lettree=makerootmake_childreninSeq.returntree(* This is just a mere generalisation of the [bind] function defined
previously. It is somehow surprising that this function cannot be
defined in term of the other one. *)letbind:'at->('a->'bt)->'bt=funaf->letrecbindaf=letseq_left=Seq.binda.children(funtree->bindtreef)inletbs=fa.rootinSeq.map(funb->letseq_right=b.childreninletchildren=b.mergeseq_leftseq_rightin{root=b.root;children;merge=b.merge})bsinSeq.binda(funtree->bindtreef)(* This function takes two arguments to ensure the sequence is not empty. *)letsequencegenseq=Seq.consgenseq|>Seq.concatletmap_tree=Seq.mapletmapf=Seq.map(mapf)letunconsseq=matchSeq.unconsseqwith|None->(* This invariant is ensured by the module itself. *)assertfalse|Some(x,seq)->(x,seq)letcrunchiseq=Seq.map(crunchi)seqmoduleSyntax=structlet(let*)=bindletreturn=returnendendletof_seqseqroot={root;children=Seq.map(funroot->{root;children=Seq.empty;merge=default_merge})seq;merge=default_merge}letlinear_search~initial~origin()=Seq.ints(Z.to_intorigin)|>Seq.mapZ.of_int|>Seq.take_while(funx->x<initial)|>Fun.flipof_seqinitial(* There is probably a better way to write this function. One cannot
simply using [make] because we need the two bounds of the
interval of the binary search. *)letrecpositive_binary_search~initial~origin()=letopenZinletopenZ.Compareinifinitial<=originthen{root=origin;children=Seq.empty;merge=default_merge}elseletinitials=Seq.ints0|>Seq.map(funpower->Z.onelslpower)|>Seq.map(funpower->origin+power)|>Seq.take_while(funx->x>=Z.zero&&x<initial-Z.one)|>Seq.snoc(initial-Z.one)|>Seq.consorigininletmins=Seq.consorigin(initials|>Seq.map(funx->x+Z.one))inletchildren=Seq.zipinitialsmins|>Seq.filter_map(fun(initial,origin)->iforigin>initialthenNoneelsepositive_binary_search~initial~origin()|>Option.some)in{root=initial;children;merge=default_merge}letbinary_search:initial:Z.t->origin:Z.t->unit->Z.tt=fun~initial~origin()->letopenZ.Compareinletshift=origininletinitial=Z.subinitialshiftin(* Invariant: origin is 0 *)letinversion=ifinitial<Z.zerothenZ.negelseFun.idinletinitial=inversioninitialin(* Invariant: initial >= 0 *)positive_binary_search~initial~origin:Z.zero()|>map(funx->Z.add(inversionx)shift)letfractional_search~exhaustive_search_digits~precision_digits~initial~origin()=letrecpoweri=ifi=0then1elseifi=1then10else10*power(i-1)in(* Reverting digits allows to get first floats closer to [origin]. *)letdigits=Seq.ints0|>Seq.take10|>Seq.map(funi->9-i)in(* This function truncates the digits of a fractional number. Only
[bit] digit are kept. *)letsimplifyfbitdigit=letfactor=powerbit|>float_of_intinletnumber=Float.modf(f*.factor)|>snd|>int_of_floatinletlast_digit=Int.remnumber10inifdigit>last_digit&&last_digit<>0thenNoneelseletx=Float.of_int(max0(number-digit))/.factorinifx>=origin&&x<=initialthenSomexelseNoneinletchildren=letleft=(* This sequence will compute all floating number whose string
representation contains at most [bits] digits and are in the
interval [origin;initial]. The sequence is ordered so that
float number which are closer to origin and whose string
representation has fewer digits appear first. *)letbits=Seq.ints1|>Seq.takeexhaustive_search_digitsinletfoldaccbit=acc|>Seq.map(funinitial->digits|>Seq.filter_map(fundigit->simplifyinitialbitdigit))|>Seq.concatinSeq.scanfold(Seq.returninitial)bits|>Seq.fold_leftSeq.appendSeq.empty|>Seq.filter(funx->x<>origin&&x<>initial)|>Seq.consorigin|>Seq.mapreturninletstart=exhaustive_search_digits+1inletrecrightinitialbitprecision=Seq.intsbit|>Seq.takeprecision|>Seq.map(funbit->digits|>Seq.filter_map(fundigit->simplifyinitialbitdigit))|>Seq.concat|>Seq.filter(funx->x<>origin&&x<>initial)|>funseq->(* This sequence will generate number whose digital
representation is changing one digit at a time the [initial]
number to get closer to [origin]. *)Seq.append(seq|>Seq.mapreturn)(* This complicated sequence is useful only with
[Tree.crunch]. It enables to get more clues. This is very
similar to an exhaustive search. *)@@(Seq.map(funroot->ifbit<precisionthen{root;children=rightrootstart(bit+1);merge=default_merge}else{root;children=Seq.empty;merge=default_merge})seq|>Seq.map(fun{children;_}->children)|>Seq.concat|>funseq->{root=origin;children=seq;merge=default_merge}|>Seq.return)inSeq.appendleft(rightinitialstartprecision_digits)in{root=initial;children;merge=default_merge}letfractional_search?(exhaustive_search_digits=0)?(precision_digits=20)~initial~origin()=letshift=origininletinitial=initial-.shiftinletinversion=ifinitial<0.thenFloat.negelseFun.idinletinitial=inversioninitialinletprecision_digits=maxexhaustive_search_digitsprecision_digitsinfractional_search~exhaustive_search_digits~precision_digits~initial~origin:0.()|>map(funx->inversionx+.shift)letrecshrink:('a->('ok,'err)Result.t)->'at->'a=funftree->(* Correctness of this function assumes that the function [f] fails
when it is called on the root of the tree. This function is
looking for "smaller" values than the current root tree for which
the function [f] fails. Children of a tree aims to be smaller
values than the root. *)letfind_best_candidatetree=(* If we found a smaller value on which the function fails,
we recursively shrink to find an even better one. *)(* This is not tail-recursive, we can limit the depth of the
search tree to overcome this issue. *)matchftree.rootwith|Error_->shrinkftree|>Option.some|Ok_->NoneinSeq.find_mapfind_best_candidatetree.children|>Option.value~default:tree.root(* This one is a particular case of [dfs_with_depth]. *)letrecdfs:'at->'aSeq.t=funtree->Seq.constree.root(Seq.flat_mapdfstree.children)letdfs_with_depth:'at->(int*'a)Seq.t=funtree->letrecloopdepthtree=Seq.cons(depth,tree.root)(Seq.flat_map(loop(depth+1))tree.children)inloop0treeletrecrow:int->'at->'aSeq.t=fundepthtree->ifdepth<=0thenSeq.returntree.rootelseSeq.flat_map(funtree->row(depth-1)tree)tree.children