123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105open!Import(* These functions implement a search for the first (resp. last) element
satisfying a predicate, assuming that the predicate is increasing on
the container, meaning that, if the container is [u1...un], there exists a
k such that p(u1)=....=p(uk) = false and p(uk+1)=....=p(un)= true.
If this k = 1 (resp n), find_last_not_satisfying (resp find_first_satisfying)
will return None. *)letreclinear_search_first_satisfyingt~get~lo~hi~pred=iflo>hithenNoneelseifpred(gettlo)thenSomeloelselinear_search_first_satisfyingt~get~lo:(lo+1)~hi~pred;;(* Takes a container [t], a predicate [pred] and two indices [lo < hi], such that
[pred] is increasing on [t] between [lo] and [hi].
return a range (lo, hi) where:
- lo and hi are close enough together for a linear search
- If [pred] is not constantly [false] on [t] between [lo] and [hi], the first element
on which [pred] is [true] is between [lo] and [hi]. *)(* Invariant: the first element satisfying [pred], if it exists is between [lo] and [hi] *)letrecfind_range_near_first_satisfyingt~get~lo~hi~pred=(* Warning: this function will not terminate if the constant (currently 8) is
set <= 1 *)ifhi-lo<=8thenlo,hielse(letmid=lo+((hi-lo)/2)inifpred(gettmid)(* INVARIANT check: it means the first satisfying element is between [lo] and [mid] *)thenfind_range_near_first_satisfyingt~get~lo~hi:mid~pred(* INVARIANT check: it means the first satisfying element, if it exists,
is between [mid+1] and [hi] *)elsefind_range_near_first_satisfyingt~get~lo:(mid+1)~hi~pred);;letfind_first_satisfying?pos?lent~get~length~pred=letpos,len=Ordered_collection_common.get_pos_len_exn()?pos?len~total_length:(lengtht)inletlo=posinlethi=pos+len-1inletlo,hi=find_range_near_first_satisfyingt~get~lo~hi~predinlinear_search_first_satisfyingt~get~lo~hi~pred;;(* Takes an array with shape [true,...true,false,...false] (i.e., the _reverse_ of what
is described above) and returns the index of the last true or None if there are no
true*)letfind_last_satisfying?pos?lent~pred~get~length=letpos,len=Ordered_collection_common.get_pos_len_exn()?pos?len~total_length:(lengtht)iniflen=0thenNoneelse((* The last satisfying is the one just before the first not satisfying *)matchfind_first_satisfying~pos~lent~get~length~pred:(Fn.nonpred)with|None->Some(pos+len-1)(* This means that all elements satisfy pred.
There is at least an element as (len > 0) *)|Someiwheni=pos->None(* no element satisfies pred *)|Somei->Some(i-1));;letbinary_search?pos?lent~length~get~comparehowv=matchhowwith|`Last_strictly_less_than->find_last_satisfying?pos?lent~get~length~pred:(funx->comparexv<0)|`Last_less_than_or_equal_to->find_last_satisfying?pos?lent~get~length~pred:(funx->comparexv<=0)|`First_equal_to->(matchfind_first_satisfying?pos?lent~get~length~pred:(funx->comparexv>=0)with|Somexwhencompare(gettx)v=0->Somex|None|Some_->None)|`Last_equal_to->(matchfind_last_satisfying?pos?lent~get~length~pred:(funx->comparexv<=0)with|Somexwhencompare(gettx)v=0->Somex|None|Some_->None)|`First_greater_than_or_equal_to->find_first_satisfying?pos?lent~get~length~pred:(funx->comparexv>=0)|`First_strictly_greater_than->find_first_satisfying?pos?lent~get~length~pred:(funx->comparexv>0);;letbinary_search_segmented?pos?lent~length~get~segment_ofhow=letis_leftx=matchsegment_ofxwith|`Left->true|`Right->falseinletis_rightx=not(is_leftx)inmatchhowwith|`Last_on_left->find_last_satisfying?pos?lent~length~get~pred:is_left|`First_on_right->find_first_satisfying?pos?lent~length~get~pred:is_right;;