123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212(******************************************************************************)(* *)(* Feat *)(* *)(* François Pottier, Inria Paris *)(* *)(* Copyright Inria. All rights reserved. This file is distributed under the *)(* terms of the MIT license, as described in the file LICENSE. *)(******************************************************************************)(* This is an implementation of implicit finite sequences as objects, that is,
records of closures. This is the implementation style proposed in the Feat
paper by Duregard et al. *)(* In this implementation, the constructors have time complexity O(1),
under the assumption that the arithmetic operations provided by [Z]
cost O(1) as well. *)moduleMake(Z:BigIntSig.BASIC)=structtypeindex=Z.t(* A sequence stores its length (which is computed at construction time)
as well as [get] and [foreach] methods. *)(* The [foreach] method takes a Boolean sense as a parameter. This allows
us to easily implement the [foreach] method of a reversed sequence, as
produced by the [rev] combinator. *)(* For comments on [to_seq], see IFSeqSyn. *)type('a,'b)producer=('a->'bSeq.t->'bSeq.t)->'bSeq.t->'bSeq.ttype'aseq={length:index;get:index->'a;foreach:bool->('a->unit)->unit;to_seq:'b.bool->('a,'b)producer;}letis_emptys=Z.equals.lengthZ.zeroletout_of_bounds()=failwith"Index is out of bounds."letempty=letlength=Z.zeroandget_=out_of_bounds()andforeach_sense_k=()andto_seq_sense_consk=kin{length;get;foreach;to_seq}letzero=emptyletsingletonx=letlength=Z.oneandgeti=ifZ.equaliZ.zerothenxelseout_of_bounds()andforeach_sensek=kxandto_seq_senseconsk=consxkin{length;get;foreach;to_seq}letone=singletonletrevs=letlength=s.lengthandgeti=s.get(Z.sub(Z.preds.length)i)andforeachsensek=s.foreach(notsense)kandto_seqsenseconsk=s.to_seq(notsense)conskin{length;get;foreach;to_seq}letsums1s2=letlength=Z.adds1.lengths2.lengthandgeti=ifZ.ltis1.lengththens1.getielses2.get(Z.subis1.length)andforeachsensek=lets1,s2=ifsensethens1,s2elses2,s1ins1.foreachsensek;s2.foreachsensekandto_seqsenseconsk=lets1,s2=ifsensethens1,s2elses2,s1ins1.to_seqsensecons(fun()->s2.to_seqsenseconsk())in{length;get;foreach;to_seq}letsums1s2=(* To save space and reduce access time, we recognize an empty sequence as a
neutral element for concatenation. *)ifis_emptys1thens2elseifis_emptys2thens1elsesums1s2let(++)=sumletproducts1s2=letlength=Z.muls1.lengths2.lengthandgeti=letq,r=Z.div_remis2.lengthins1.getq,s2.getrandforeachsensek=s1.foreachsense(funx1->s2.foreachsense(funx2->k(x1,x2)))andto_seqsenseconsk=s1.to_seqsense(funx1k->s2.to_seqsense(funx2k->cons(x1,x2)k)k)kin{length;get;foreach;to_seq}letproducts1s2=(* To save space, we recognize an empty sequence as an absorbing element for
product. This also eliminates the risk of a division by zero in the above
code, which could arise if the user attempts an out-of-bounds access. *)ifis_emptys1||is_emptys2thenemptyelseproducts1s2let(**)=productletmapphis=letlength=s.lengthandgeti=phi(s.geti)andforeachsensek=s.foreachsense(funx->k(phix))andto_seqsenseconsk=s.to_seqsense(funxk->cons(phix)k)kin{length;get;foreach;to_seq}letmapphis=(* To save space, we recognize an empty sequence as a fixed point for map. *)ifis_emptysthenemptyelsemapphisletrecintervalsenseab:(int,'b)producer=funconsk->ifa<bthen(* Compute the first element [x] and the parameters [a] and [b]
of the recursive call. *)letx,a,b=ifsensethena,a+1,belseb-1,a,b-1in(* Produce [x] and delay the recursive call. *)consx(fun()->intervalsenseabconsk())elsekletupab=ifa<bthenletlength=Z.of_int(b-a)andgeti=matchZ.to_intiwith|exceptionZ.Overflow->out_of_bounds()|i->letx=a+iinifx<a||b<=xthenout_of_bounds()elsexandforeachsensek=ifsensethenforx=atob-1dokxdoneelseforx=b-1downtoadokxdoneandto_seqsenseconsk=intervalsenseabconskin{length;get;foreach;to_seq}elseemptyletlengths=s.lengthletgetsi=s.getiletforeachsk=s.foreachtruekletconsxxs=fun()->Seq.Cons(x,xs)letto_seqsk=s.to_seqtrueconskend