123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523open!ImportopenStd_internaltype'at={(* [arr] is a cyclic buffer *)mutablearr:'aOption_array.t;(* [front_index] and [back_index] are the positions in which new elements may be
enqueued. This makes the active part of [arr] the range from [front_index+1] to
[back_index-1] (modulo the length of [arr] and wrapping around if necessary). Note
that this means the active range is maximized when [front_index = back_index], which
occurs when there are [Array.length arr - 1] active elements. *)mutablefront_index:int;mutableback_index:int;(* apparent_front_index is what is exposed as the front index externally. It has no
real relation to the array -- every enqueue to the front decrements it and every
dequeue from the front increments it. *)mutableapparent_front_index:int;mutablelength:int;(* We keep arr_length here as a speed hack. Calling Array.length on arr is actually
meaningfully slower. *)mutablearr_length:int;never_shrink:bool}letcreate?initial_length?never_shrink()=letnever_shrink=matchnever_shrinkwith|None->Option.is_someinitial_length|Someb->binletinitial_length=Option.value~default:7initial_lengthinifinitial_length<0theninvalid_argf"passed negative initial_length to Deque.create: %i"initial_length();(* Make the initial array length be [initial_length + 1] so we can fit [initial_length]
elements without growing. We never quite use the whole array. *)letarr_length=initial_length+1in{arr=Option_array.create~len:arr_length;front_index=0;back_index=1;apparent_front_index=0;length=0;arr_length;never_shrink};;letlengtht=t.lengthletis_emptyt=lengtht=0(* We keep track of the length in a mutable field for speed, but this calculation should
be correct by construction, and can be used for testing. *)let_invariant_lengtht=letconstructed_length=ift.front_index<t.back_indexthent.back_index-t.front_index-1elset.back_index-t.front_index-1+t.arr_lengthinassert(lengtht=constructed_length);;(* The various "when_not_empty" functions return misleading numbers when the dequeue is
empty. They are safe to call if it is known that the dequeue is non-empty. *)letapparent_front_index_when_not_emptyt=t.apparent_front_indexletapparent_back_index_when_not_emptyt=t.apparent_front_index+lengtht-1letactual_front_index_when_not_emptyt=ift.front_index=t.arr_length-1then0elset.front_index+1;;letactual_back_index_when_not_emptyt=ift.back_index=0thent.arr_length-1elset.back_index-1;;letcheckedtf=ifis_emptytthenNoneelseSome(ft)letapparent_front_indext=checkedtapparent_front_index_when_not_emptyletapparent_back_indext=checkedtapparent_back_index_when_not_emptyletfoldi'tdir~init~f=ifis_emptyttheninitelse(letapparent_front=apparent_front_index_when_not_emptytinletapparent_back=apparent_back_index_when_not_emptytinletactual_front=actual_front_index_when_not_emptytinletactual_back=actual_back_index_when_not_emptytinletrecloopacc~apparent_i~real_i~stop_pos~step=ifreal_i=stop_posthenacc,apparent_ielseloop(fapparent_iacc(Option_array.get_some_exnt.arrreal_i))~apparent_i:(apparent_i+step)~real_i:(real_i+step)~stop_pos~stepin(* We want to iterate from actual_front to actual_back (or vice versa), but we may
need to wrap around the array to do so. Thus we do the following:
1. If the active range is contiguous (i.e. actual_front <= actual_back), then loop
starting at the appropriate end of the active range until we reach the first
element outside of it.
2. If it is not contiguous (actual_front > actual_back), then first loop from the
appropriate end of the active range to the end of the array. Then, loop from
the opposite end of the array to the opposite end of the active range.
*)matchdirwith|`front_to_back->ifactual_front<=actual_backthen(letacc,_=loopinit~apparent_i:apparent_front~real_i:actual_front~stop_pos:(actual_back+1)~step:1inacc)else(letacc,apparent_i=loopinit~apparent_i:apparent_front~real_i:actual_front~stop_pos:t.arr_length~step:1inletacc,_=loopacc~apparent_i~real_i:0~stop_pos:(actual_back+1)~step:1inacc)|`back_to_front->ifactual_front<=actual_backthen(letacc,_=loopinit~apparent_i:apparent_back~real_i:actual_back~stop_pos:(actual_front-1)~step:(-1)inacc)else(letacc,apparent_i=loopinit~apparent_i:apparent_back~real_i:actual_back~stop_pos:(-1)~step:(-1)inletacc,_=loopacc~apparent_i~real_i:(t.arr_length-1)~stop_pos:(actual_front-1)~step:(-1)inacc));;letfold'tdir~init~f=foldi'tdir~init~f:(fun_accv->faccv)letiteri'tdir~f=foldi'tdir~init:()~f:(funi()v->fiv)letiter'tdir~f=foldi'tdir~init:()~f:(fun_()v->fv)letfoldt~init~f=fold't`front_to_back~init~fletfoldit~init~f=foldi't`front_to_back~init~fletiterit~f=iteri't`front_to_back~fletiteri_internalt~f=ifnot(is_emptyt)then(letactual_front=actual_front_index_when_not_emptytinletactual_back=actual_back_index_when_not_emptytinletrecloop~real_i~stop_pos=ifreal_i<stop_posthen(ft.arrreal_i;loop~real_i:(real_i+1)~stop_pos)inifactual_front<=actual_backthenloop~real_i:actual_front~stop_pos:(actual_back+1)else(loop~real_i:actual_front~stop_pos:t.arr_length;loop~real_i:0~stop_pos:(actual_back+1)));;letitert~f=iteri_internalt~f:(funarri->Option_array.get_some_exnarri|>f)letcleart=ift.never_shrinkthen(* clear the array to allow elements to be garbage collected *)iteri_internalt~f:Option_array.unsafe_set_noneelset.arr<-Option_array.create~len:8;t.front_index<-0;t.back_index<-1;t.length<-0;t.arr_length<-Option_array.lengtht.arr;;(* We have to be careful here, importing all of Container.Make would change the runtime of
some functions ([length] minimally) silently without changing the semantics. We get
around that by importing things explicitly. *)moduleC=Container.Make(structtypenonrec'at='atletfold=foldletiter=`Customiterletlength=`Customlengthend)letcount=C.countletsum=C.sumletexists=C.existsletmem=C.memletfor_all=C.for_allletfind_map=C.find_mapletfind=C.findletto_list=C.to_listletmin_elt=C.min_eltletmax_elt=C.max_eltletfold_result=C.fold_resultletfold_until=C.fold_untilletblitnew_arrt=assert(not(is_emptyt));letactual_front=actual_front_index_when_not_emptytinletactual_back=actual_back_index_when_not_emptytinletold_arr=t.arrinifactual_front<=actual_backthenOption_array.blit~src:old_arr~dst:new_arr~src_pos:actual_front~dst_pos:0~len:(lengtht)else(letbreak_pos=Option_array.lengthold_arr-actual_frontinOption_array.blit~src:old_arr~dst:new_arr~src_pos:actual_front~dst_pos:0~len:break_pos;Option_array.blit~src:old_arr~dst:new_arr~src_pos:0~dst_pos:break_pos~len:(actual_back+1));(* length depends on t.arr and t.front_index, so this needs to be first *)t.back_index<-lengtht;t.arr<-new_arr;t.arr_length<-Option_array.lengthnew_arr;t.front_index<-Option_array.lengthnew_arr-1;(* Since t.front_index = Option_array.length new_arr - 1, this is asserting that t.back_index
is a valid index in the array and that the array can support at least one more
element -- recall, if t.front_index = t.back_index then the array is full.
Note that this is true if and only if Option_array.length new_arr > length t + 1.
*)assert(t.front_index>t.back_index);;letmaybe_shrink_underlyingt=if(nott.never_shrink)&&t.arr_length>10&&t.arr_length/3>lengthtthen(letnew_arr=Option_array.create~len:(t.arr_length/2)inblitnew_arrt);;letgrow_underlyingt=letnew_arr=Option_array.create~len:(t.arr_length*2)inblitnew_arrt;;letenqueue_backtv=ift.front_index=t.back_indexthengrow_underlyingt;Option_array.set_somet.arrt.back_indexv;t.back_index<-(ift.back_index=t.arr_length-1then0elset.back_index+1);t.length<-t.length+1;;letenqueue_fronttv=ift.front_index=t.back_indexthengrow_underlyingt;Option_array.set_somet.arrt.front_indexv;t.front_index<-(ift.front_index=0thent.arr_length-1elset.front_index-1);t.apparent_front_index<-t.apparent_front_index-1;t.length<-t.length+1;;letenqueuetback_or_frontv=matchback_or_frontwith|`back->enqueue_backtv|`front->enqueue_fronttv;;letpeek_front_nonemptyt=Option_array.get_some_exnt.arr(actual_front_index_when_not_emptyt);;letpeek_front_exnt=ifis_emptytthenfailwith"Deque.peek_front_exn passed an empty queue"elsepeek_front_nonemptyt;;letpeek_frontt=ifis_emptytthenNoneelseSome(peek_front_nonemptyt)letpeek_back_nonemptyt=Option_array.get_some_exnt.arr(actual_back_index_when_not_emptyt);;letpeek_back_exnt=ifis_emptytthenfailwith"Deque.peek_back_exn passed an empty queue"elsepeek_back_nonemptyt;;letpeek_backt=ifis_emptytthenNoneelseSome(peek_back_nonemptyt)letpeektback_or_front=matchback_or_frontwith|`back->peek_backt|`front->peek_frontt;;letdequeue_front_nonemptyt=leti=actual_front_index_when_not_emptytinletres=Option_array.get_some_exnt.arriinOption_array.set_nonet.arri;t.front_index<-i;t.apparent_front_index<-t.apparent_front_index+1;t.length<-t.length-1;maybe_shrink_underlyingt;res;;letdequeue_front_exnt=ifis_emptytthenfailwith"Deque.dequeue_front_exn passed an empty queue"elsedequeue_front_nonemptyt;;letdequeue_frontt=ifis_emptytthenNoneelseSome(dequeue_front_nonemptyt)letdequeue_back_nonemptyt=leti=actual_back_index_when_not_emptytinletres=Option_array.get_some_exnt.arriinOption_array.set_nonet.arri;t.back_index<-i;t.length<-t.length-1;maybe_shrink_underlyingt;res;;letdequeue_back_exnt=ifis_emptytthenfailwith"Deque.dequeue_back_exn passed an empty queue"elsedequeue_back_nonemptyt;;letdequeue_backt=ifis_emptytthenNoneelseSome(dequeue_back_nonemptyt)letdequeue_exntback_or_front=matchback_or_frontwith|`front->dequeue_front_exnt|`back->dequeue_back_exnt;;letdequeuetback_or_front=matchback_or_frontwith|`front->dequeue_frontt|`back->dequeue_backt;;letdrop_gen?(n=1)~dequeuet=ifn<0theninvalid_argf"Deque.drop: negative input (%d)"n();letrecloopn=ifn>0then(matchdequeuetwith|None->()|Some_->loop(n-1))inloopn;;letdrop_front?nt=drop_gen?n~dequeue:dequeue_fronttletdrop_back?nt=drop_gen?n~dequeue:dequeue_backtletdrop?ntback_or_front=matchback_or_frontwith|`back->drop_back?nt|`front->drop_front?nt;;letassert_not_emptytname=ifis_emptytthenfailwithf"%s: Deque.t is empty"name()lettrue_index_exnti=leti_from_zero=i-t.apparent_front_indexinifi_from_zero<0||lengtht<=i_from_zerothen(assert_not_emptyt"Deque.true_index_exn";letapparent_front=apparent_front_index_when_not_emptytinletapparent_back=apparent_back_index_when_not_emptytininvalid_argf"invalid index: %i for array with indices (%i,%i)"iapparent_frontapparent_back());lettrue_i=t.front_index+1+i_from_zeroiniftrue_i>=t.arr_lengththentrue_i-t.arr_lengthelsetrue_i;;letgetti=Option_array.get_some_exnt.arr(true_index_exnti)letget_optti=trySome(getti)with|_->None;;letset_exntiv=Option_array.set_somet.arr(true_index_exnti)vletto_arrayt=matchpeek_fronttwith|None->[||]|Somefront->letarr=Array.create~len:(lengtht)frontinignore(foldt~init:0~f:(funiv->arr.(i)<-v;i+1):int);arr;;letof_arrayarr=lett=create~initial_length:(Array.lengtharr+1)()inArray.iterarr~f:(funv->enqueue_backtv);t;;includeBin_prot.Utils.Make_iterable_binable1(structtypenonrec'at='attype'ael='a[@@derivingbin_io]letcaller_identity=Bin_prot.Shape.Uuid.of_string"34c1e9ca-4992-11e6-a686-8b4bd4f87796";;letmodule_name=Some"Core_kernel.Deque"letlength=lengthletitert~f=itert~fletinit~len~next=lett=create~initial_length:len()infor_i=0tolen-1doletx=next()inenqueue_backtxdone;t;;end)lett_of_sexpfsexp=of_array(Array.t_of_sexpfsexp)letsexp_of_tft=Array.sexp_of_tf(to_arrayt)(* re-expose these here under a different name to avoid internal confusion *)letback_index=apparent_back_indexletfront_index=apparent_front_indexletback_index_exnt=assert_not_emptyt"Deque.back_index_exn";apparent_back_index_when_not_emptyt;;letfront_index_exnt=assert_not_emptyt"Deque.front_index_exn";apparent_front_index_when_not_emptyt;;moduleBinary_searchable=Test_binary_searchable.Make1_and_test(structtypenonrec'at='atletgetti=gett(front_index_exnt+i)letlength=lengthmoduleFor_test=structletof_array=of_arrayendend)(* The "stable" indices used in this module make the application of the
[Binary_searchable] functor awkward. We need to be sure to translate incoming
positions from stable space to the expected 0 -> length - 1 space and then we need to
translate them back on return. *)letbinary_search?pos?lent~comparehowv=letpos=matchposwith|None->None|Somepos->Some(pos-t.apparent_front_index)inmatchBinary_searchable.binary_search?pos?lent~comparehowvwith|None->None|Someuntranslated_i->Some(t.apparent_front_index+untranslated_i);;letbinary_search_segmented?pos?lent~segment_ofhow=letpos=matchposwith|None->None|Somepos->Some(pos-t.apparent_front_index)inmatchBinary_searchable.binary_search_segmented?pos?lent~segment_ofhowwith|None->None|Someuntranslated_i->Some(t.apparent_front_index+untranslated_i);;