1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207(*
* BatArray - additional and modified functions for arrays.
* Copyright (C) 2005 Richard W.M. Jones (rich @ annexia.org)
* 2009 David Rajchenbach-Teller, LIFO, Universite d'Orleans
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version,
* with the special exception on linking described in file LICENSE.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)includeArray##V>=5##modulePervasives=Stdlib(*$inject
##V>=5##module Pervasives = Stdlib
*)type'at='aarraytype'aenumerable='attype'amappable='at##V<4.2##letcreate_floatn=maken0.##V<4.2##letmake_float=create_float##V=4.2##externalmake_float:int->floatarray="caml_make_float_vect"##V=4.2##externalcreate_float:int->floatarray="caml_make_float_vect"##V>=5.3##externalcreate_float:int->floatarray="caml_array_create_float"##V>=4.3####V<5.3##externalcreate_float:int->floatarray="caml_make_float_vect"##V>=4.3##letmake_float=create_floatletsingletonx=[|x|](*$Q singleton
Q.int (fun x -> let s= singleton x in s.(0) = x && length s = 1)
*)letmodifyfa=fori=0tolengtha-1dounsafe_setai(f(unsafe_getai))doneletmodifyifa=fori=0tolengtha-1dounsafe_setai(fi(unsafe_getai))done(*$T modify
let a = [|3;2;1|]in modify (fun x -> x + 1) a; a = [|4;3;2|]
*)(*$Tmodifyi
let a =[|3;2;1|] in modifyi (funi x-> i * x) a; a= [|0;2;2|]
*)letfold=fold_leftletfold_left_mapfinita=letn=lengthainifn=0then(init,[||])elseletacc=refinitinletf'x=letacc',y=f!accxinacc:=acc';yinletres=mapf'ain(!acc,res)(*$T fold_left_map
fold_left_map(fun acc x -> (acc + x, x)) 0 [|0;1;2;3|] = (6, [|0;1;2;3|])
fold_left_map (fun acc x -> (acc + x, x)) 0 [||] = (0, [||])
*)letfold_leftifxa=letr=refxinfori=0tolengtha-1dor:=f!ri(unsafe_getai)done;!r(*$T fold_lefti
fold_lefti (fun a i x -> a+ i * x) 1 [|2;4;5|] = 1 + 0 + 4 + 10
fold_lefti (fun a i x -> a + i * x) 1 [||] = 1
*)letfold_rightifax=letr=refxinfori=lengtha-1downto0dor:=fi(unsafe_get ai)!rdone;!r(*$T fold_righti
fold_righti (fun i x a -> a + i * x) [|2;4;5|] 1 = 1 + 0 + 4 + 10
fold_righti (fun i x a -> a + i * x) [||] 1 = 1
*)letrev_in_placexs=letn=lengthxsinletj=ref(n-1)infori=0ton/2-1do(* let c = xs.(i) in *)letc=unsafe_getxsiin(* xs.(i) <- xs.(!j); *)unsafe_setxsi(unsafe_getxs!j);(* xs.(!j) <- c; *)unsafe_setxs!jc;decrjdone(*$T rev_in_place
let a = [|1;2;3;4|] in rev_in_place a; a = [|4;3;2;1|]
let a = [|1;2;3|] in rev_in_place a; a = [|3;2;1|]
let a = [||] inrev_in_placea; a=[||]
*)letrevxs=letys=copyxsinrev_in_place ys;ys(*$Q rev
(Q.array Q.int) ~count:5 (fun l -> rev l |> rev = l)
*)letfor_allpxs=letn=lengthxsinletrecloopi=ifi=nthentrueelseifp(unsafe_getxsi)thenloop(succi)elsefalseinloop0(*$T for_all
for_all (funx -> x mod 2 = 0) [|2;4;6|]
for_all (fun x ->x mod 2 = 0) [|2;3;6|] = false
for_all (fun _ -> false) [||]
*)letexistspxs=letn=lengthxsinletrecloopi=ifi=nthenfalseelseifp(unsafe_getxsi)thentrueelseloop(succi)inloop0(*$Texists
exists (fun x ->x mod 2 = 0) [|1;4;5|]
exists (fun x -> x mod2 = 0) [|1;3;5|] = false
exists (fun _ -> false) [||] = false
*)letmemaxs=letn=lengthxsinletrecloopi=ifi=nthenfalseelseifa=unsafe_getxsithentrueelseloop(succi)inloop0(*$T mem
mem 2 [|1;2;3|]
mem 2 [||] = false
mem (ref 3) [|ref 1; ref 2; ref 3|]
*)letmemqaxs=letn=lengthxsinletrecloopi=ifi=nthenfalseelseifa==unsafe_getxsithentrueelseloop(succi)inloop0(*$T memq
memq 2 [|1;2;3|]
memq 2 [||] = false
memq (ref 3) [|ref 1; ref 2; ref 3|] = false
*)letfindipxs=letn=lengthxsinletrecloopi=ifi=nthenraiseNot_foundelseifp(unsafe_getxsi)thenielseloop(succi)inloop0(*$Q findi
(Q.pair (Q.array Q.small_int) (Q.fun1 Q.Observable.int Q.bool)) (fun (a, Q.Fun(_,f)) -> \
try let index = findi f a in \
let i = ref (-1) in \
for_all (funelt -> incr i; \
if !i < index then not (f elt) \
else if !i = index then f elt else true)\
a \
with Not_found -> for_all (fun elt -> not (f elt)) a)
*)letfindpxs=xs.(findipxs)(*$Q find
(Q.pair (Q.array Q.small_int) (Q.fun1 Q.Observable.int Q.bool)) (fun (a, Q.Fun(_,f)) -> \
let a = map (fun x -> `a x) a in \
let f (`a x) = f x in\
try let elt = find f a in \
let past=ref falsein \
for_all (fun x ->if x == eltthen (past := true; f x) \
else!past || not (fx)) \
a \
with Not_found-> for_all (fun elt -> not (f elt)) a)
*)letfind_optpa=letn=lengthainletrecloopi=ifi=nthenNoneelseletx=unsafe_getaiinifpxthenSomexelseloop(succi)inloop0(*$T find_opt
find_opt (fun x -> x < 0) [||] = None
find_opt (fun x -> x < 0) [|0;1;2;3|] = None
find_opt (fun x -> x >= 3) [|0;1;2;3|] = Some 3
*)letfind_mapfa=letn=lengthainletrecloopi=ifi=nthenNoneelsematchf(unsafe_getai)with|None->loop(succi)|Some_asr->rinloop0(*$T find_map
find_map (fun x -> if x < 0 then Some x else None) [||] = None
find_map (fun x-> if x < 0 then Some x else None) [|0;-1;2|] = (Some (-1))
find_map (fun x -> if x < 0 then Some x else None) [|0;1;-2|] = (Some (-2))
*)(* Use of BitSet suggested by Brian Hurt. *)letfilterpxs=letn=lengthxsin(* Use a bitset to store which elements will be in the final array. *)letbs=BatBitSet.createninfori=0ton-1doifp(unsafe_getxsi)thenBatBitSet.setbsidone;(*Allocate the final array and copy elements into it. *)letn'=BatBitSet.countbsinletj=ref0ininitn'(fun_->matchBatBitSet.next_set_bitbs!jwith|Somei->j:=i+1;unsafe_getxsi|None->(* not enough 1 bits - incorrect count? *)assertfalse)(*$Q filter
(Q.pair (Q.array Q.small_int) (Q.fun1 Q.Observable.int Q.bool))(fun (a, Q.Fun(_,f)) -> \ let b = to_list (filter f a) in \
let b' = List.filter f (to_list a) in \
List.for_all (fun (x,y) ->x =y) (List.combine bb')\ )*)exceptionEndofintletfold_whilepfinitxs=letacc=refinitintryletn=lengthxsinfori=0ton-1doletx=unsafe_getxsiinifp!accxthenacc:=f!accxelseraise(Endi)done;(!acc,n)withEndi->(!acc,i)(*$T fold_while
fold_while (fun _ x -> x mod 2 = 0) (+) 0 [|1;2|] = (0, 0)
fold_while (fun _ x -> x mod 2 = 1)(+) 0 [|1;2|]= (1, 1)
fold_while (fun _ x -> x < 4) (+) 0 [|1;2;3;4|] = (6, 3)
fold_while (fun _ x -> x < 4) (+) 0[|1;2;3|] = (6, 3)
fold_while (fun _ x -> x < 4) (+) 0 [||] = (0, 0)
*)letcount_matchingpxs=letn=lengthxsinletcount=ref0infori=0ton-1doifp(unsafe_getxsi)thenincrcountdone;!count(*$T count_matching
count_matching (fun _ -> true) [||] = 0
count_matching (fun x -> x =-1) [|-1|] = 1
count_matching (fun x -> x = -1) [|-1;0;-1|] = 2
*)letfilteripxs=letn=lengthxsin(* Use a bitset to store which elements will be in the final array. *)letbs=BatBitSet.createninfori=0ton-1doifpi(unsafe_getxsi)thenBatBitSet.setbsidone;(*Allocate the final array and copy elements into it. *)letn'=BatBitSet.countbsinletj=ref0ininitn'(fun_->matchBatBitSet.next_set_bitbs!jwith|Somei->j:=i+1;unsafe_getxsi|None->(* not enough 1 bits -incorrect count? *)assertfalse)(*$T filteri
filteri (fun i x -> (i+x) mod 2 = 0) [|1;2;3;4;0;1;2;3|] = [|0;1;2;3|]
*)letfind_all=filter(* <=> List.partition*)letpartitionpa=letn=lengthainifn=0then ([||],[||])elseletok_count=ref0inletmask=initn(funi->let pi=p(unsafe_getai)inifpithenincrok_count;pi)inletko_count=n-!ok_countinletinit=unsafe_geta0inletok=make!ok_countinitinletko=makeko_countinitinletj=ref0inletk=ref0infori=0ton-1doletx=unsafe_getaiinletpx=unsafe_getmaskiinifpxthen(unsafe_setok!jx;incrj)else(unsafe_setko!kx;incrk)done;(ok,ko)(*$Q partition
(Q.pair (Q.array Q.small_int) (Q.fun1 Q.Observable.int Q.bool)) (fun (a, Q.Fun(_,f)) -> \
let b1, b2 = partition f a in \
let b1, b2 = to_list b1, to_list b2 in \
let b1', b2' = List.partition f (to_list a) in \
List.for_all (fun (x,y) -> x = y) (List.combine b1 b1') && \
List.for_all (fun (x,y) -> x = y) (List.combine b2b2') \
)
*)letenumxs=letrecmakestartxs=letn=lengthxsin(* inside the loop, as [make] may later be called with anotherarray *)BatEnum.make~next:(fun()->if!start <nthenunsafe_get xs(BatRef.post_incrstart)elseraiseBatEnum.No_more_elements)~count:(fun()->n-!start)~clone:(fun()->make(BatRef.copystart)xs)inmake(ref0)xs(*$Q enum
(Q.array Q.small_int) (fun a -> \
let e = enum a in \
for i = 0 to length a / 2 - 1 do\
assert (a.(i) = BatEnum.get_exn e)\
done; \
let e' = BatEnum.clone e in \
assert (BatEnum.count e = BatEnum.count e'); \
fori = length a / 2 to length a - 1 do \
assert (a.(i) = BatEnum.get_exn e && a.(i) = BatEnum.get_exn e') \
done; \
BatEnum.is_empty e && BatEnum.is_empty e' \
)
*)letbackwardsxs=letrecmakestartxs=BatEnum.make~next:(fun()->if!start>0thenunsafe_getxs (BatRef.pre_decrstart)elseraiseBatEnum.No_more_elements)~count:(fun()->!start)~clone:(fun()->make(BatRef.copystart)xs)inmake(ref(lengthxs))xs(*$Q backwards
(Q.array Q.small_int) (fun a -> \
let e = backwards a in \
let n = length a in \
for i = 0 to length a / 2 - 1 do\
assert (a.(n - 1 - i) = BatEnum.get_exn e)\
done; \
let e' = BatEnum.clone e in \
assert (BatEnum.count e = BatEnum.count e'); \
for i = length a / 2 tolength a - 1 do \ assert (a.(n - 1 - i) = BatEnum.get_exn e && \
a.(n - 1 - i) = BatEnum.get_exn e') \
done; \
BatEnum.is_empty e && BatEnum.is_empty e' \
)
*)letof_enume=letn=BatEnum.countein(* This assumes, reasonably, that init traverses the array in order. *)initn(fun_i->matchBatEnum.getewith|Somex->x|None->assertfalse)letof_backwardse=of_list(BatList.of_backwardse)letrangexs=BatEnum.(--^)0(lengthxs)(*$Q range
(Q.array Q.small_int) (fun a -> \
BatEnum.equal (=) (range a) \
(enum (init (length a) (fun i -> i))))
*)letfilter_mappxs=of_enum(BatEnum.filter_mapp(enumxs))(*$Q filter_map
(Q.pair (Q.array Q.small_int) (Q.fun1 Q.Observable.int(Q.option Q.int))) \
(fun (a, Q.Fun (_,f)) -> \
let a' = filter (fun elt -> f elt <> None)a in \
let a' = map (f %> BatOption.get) a' in \
let a = filter_map f a in \
a = a' \
)
*)letiter2fa1a2=iflengtha1<>lengtha2theninvalid_arg"Array.iter2";fori=0tolengtha1-1do(* f a1.(i) a2.(i) *)f(unsafe_geta1i)(unsafe_geta2i)done(*$Q iter2
(Q.array Q.small_int) (fun a -> \
let a' = map (fun a -> a + 1) a in \
let i = ref (-1) in \
let b = make (length a) (max_int, max_int) in \
let f x1 x2 = incr i; b.(!i) <- (x1, x2) in \
let b' = map (fun a -> (a, a + 1)) a in \
iter2 f a a'; \
b= b' \
)
*)(*$T iter2 try iter2 (fun _ _ -> ()) [|1|] [|1;2;3|]; false \
with Invalid_argument _ -> true
try iter2 (fun _ _ -> ()) [|1|] [||]; false \
with Invalid_argument _ -> true
*)letiter2ifa1a2=iflengtha1<>lengtha2theninvalid_arg"Array.iter2i";fori=0tolengtha1-1do(* f i a1.(i) a2.(i) *)fi(unsafe_geta1i)(unsafe_geta2i)done(*$Q iter2i
(Q.array Q.small_int) (fun a -> \
let a' = map (fun a -> a + 1) a in \
let i = ref (-1) in \
let b = make (length a) (max_int, max_int) in \
let f idx x1 x2 = incr i; assert (!i = idx); b.(!i) <- (x1, x2) in \
let b' = map (fun a -> (a, a + 1)) a in \
iter2i f a a'; \
b = b' \
)
*)(*$T iter2i
try iter2i (fun _ _ _ -> ()) [|1|] [|1;2;3|]; false \
with Invalid_argument _ -> true
try iter2i (fun _ _ _ -> ()) [|1|] [||]; false \
with Invalid_argument _ -> true
*)##V>=4.11##letfor_all2=Array.for_all2##V<4.11##letfor_all2pxsys=##V<4.11##letn=lengthxsin##V<4.11##iflengthys<>ntheninvalid_arg"Array.for_all2";##V<4.11##letrecloopi=##V<4.11##ifi=nthentrue##V<4.11##elseifp(unsafe_getxsi)(unsafe_getysi)thenloop(succi)##V<4.11##elsefalse##V<4.11##in##V<4.11##loop0(*$T for_all2
for_all2 (=) [|1;2;3|] [|3;2;1|] = false
for_all2 (=) [|1;2;3|] [|1;2;3|]
for_all2 (<>) [|1;2;3|] [|3;2;1|] = false
try ignore (for_all2 (=) [|1;2;3|] [|1;2;3;4|]); false \
with Invalid_argument _ -> true
try ignore (for_all2 (=) [|1;2|] [||]); false \
with Invalid_argument _ -> true
*)##V>=4.11##letexists2=Array.exists2##V<4.11##letexists2pxsys=##V<4.11##letn=lengthxsin##V<4.11##iflengthys <>ntheninvalid_arg"Array.exists2";##V<4.11##letrecloopi=##V<4.11##ifi=nthenfalse##V<4.11##elseifp(unsafe_getxsi)(unsafe_getysi)thentrue##V<4.11##elseloop(succi)##V<4.11##in##V<4.11##loop0(*$T exists2
exists2 (=) [|1;2;3|] [|3;2;1|]
exists2 (<>) [|1;2;3|] [|1;2;3|] = false
try ignore (exists2 (=) [|1;2|] [|3|]); false \
with Invalid_argument _->true
*)letmap2fxsys=letn=lengthxsiniflengthys<>ntheninvalid_arg"Array.map2";init n(funi->f(unsafe_getxsi)(unsafe_getysi))(*$T map2
map2 (-) [|1;2;3|] [|6;3;1|] = [|-5;-1;2|]
map2 (-) [|2;4;6|] [|1;2;3|] = [|1;2;3|]
try ignore (map2 (-) [|2;4|] [|1;2;3|]); false \
with Invalid_argument _ -> true
try ignore (map2 (-) [|2;4|] [|3|]); false \
with Invalid_argument _ -> true
*)letcartesian_productab=letna=lengthainletnb=lengthbininit(na*nb)(funj->leti=j/nbin(unsafe_getai,unsafe_getb(j-i*nb)))(*$T cartesian_product
let a =cartesian_product [|1;2|][|"a";"b"|]in\
sort Legacy.compare a; \
a = [|1,"a";1,"b"; 2,"a"; 2,"b" |]
*)(*$Q cartesian_product
(Q.pair (Q.list Q.small_int) (Q.list Q.small_int)) (fun(la,lb) ->\
let a = of_list (List.take 5 la)and b = of_list (List.take 4 lb) in \
length (cartesian_product a b) = length a * length b)
*)letcomparecmpab=letlength_a=lengthainletlength_b=lengthbinletlength=BatInt.minlength_alength_binletrecauxi=ifi<lengththenletresult=cmp(unsafe_getai)(unsafe_getbi)inifresult=0thenaux(i+1)elseresultelseiflength_a=length_bthen0elseiflength_a<length_bthen-1else1inaux0(*$T compare
compare Legacy.compare [|1;2;3|] [|1;2|] =1
compare Legacy.compare [|1;2|] [|1;2;4|] = -1
compare Legacy.compare [|1|] [||] = 1
compare Legacy.compare [||] [||] = 0
compare Legacy.compare [|1;2|] [|1;2|] = 0
compare (fun x y -> -(Legacy.compare x y)) [|2;1|] [|1;2|] = -1
*)letprint?(first="[|")?(last="|]")?(sep="; ")print_aoutt=matchlengthtwith|0->BatInnerIO.nwriteoutfirst;BatInnerIO.nwriteoutlast|n->BatInnerIO.nwriteoutfirst;print_aout(unsafe_gett0);fori=1ton-1doBatInnerIO.nwriteoutsep;print_aout(unsafe_getti);done;BatInnerIO.nwriteoutlast(*$T
BatIO.to_string (print ~sep:"," ~first:"[" ~last:"]" BatInt.print) \
[|2;4;66|] = "[2,4,66]"
BatIO.to_string (print ~sep:"," ~first:"[" ~last:"]" BatInt.print) \
[|2|] = "[2]"
BatIO.to_string (print~sep:"," ~first:"[" ~last:"]" BatInt.print) \
[||] = "[]"*)letreducefa=iflengtha=0theninvalid_arg"Array.reduce: empty array"elseletacc=ref(unsafe_geta0)infori=1tolengtha-1doacc:=f!acc(unsafe_getai)done;!acc(*$T reduce
reduce (+)[|1;2;3|] = 6
reduce (fun _ -> assert false) [|1|] = 1
tryreduce (fun _ _ -> ()) [||];false\ with Invalid_argument _ -> true
*)letmina=reducePervasives.minalet maxa=reducePervasives.maxa(*$T min
min [|1;2;3|] = 1
min [|2;3;1|] = 1
*)(*$T max
max [|1;2;3|] = 3
max [|2;3;1|] = 3
*)letmin_maxa=letn=lengthainifn=0theninvalid_arg"Array.min_max: empty array"elseletmini=ref(unsafe_geta0)inletmaxi=ref(unsafe_geta0)infori=1ton-1doif(unsafe_getai)>!maxithenmaxi:=(unsafe_getai);if(unsafe_getai)<!minithenmini:=(unsafe_getai)done;(!mini,!maxi)(*$T min_max
min_max [|1|] = (1, 1)min_max [|1;-2;10;3|] =(-2, 10)
try ignore (min_max [||]); false with Invalid_argument _ -> true
*)letsum=fold_left(+)0letfsum=fold_left(+.)0.(*$T sum
sum [|1;2;3|] = 6
sum [|0|] = 0
sum [||] = 0
*)(*$T fsum
fsum [|1.0;2.0;3.0|]= 6.0
fsum [|0.0|] = 0.0
*)letkahan_sumarr=letsum=ref0.inleterr=ref0.infori=0tolengtharr-1doletx=(unsafe_getarri)-.!errinletnew_sum=!sum+.xinerr:=(new_sum-.!sum)-.x;sum:=new_sum+.0.;(* thissuspicious +. 0. is added tohelp
the hand of the somewhat flaky unboxing optimizer;
it hopefully won't be necessary anymore
in a few OCaml versions *)done;!sum+.0.(*$T kahan_sum
kahan_sum [| |] = 0.
kahan_sum [| 1.; 2. |] = 3.
let n, x = 1_000, 1.1 in \
Float.approx_equal (float n *. x) \
(kahan_sum (make n x))
*)letflengtha=float_of_int(lengtha)letavga=(float_of_int(suma))/.(flengtha)letfavga=(fsuma)/.(flengtha);;(*$T avg
avg [|1; 2; 3|] = 2.
avg [|0|] = 0.
*)(*$T favg favg [|1.0; 2.0; 3.0|] = 2.0
favg [|0.0|] = 0.0
*)(*meant for tests, don't care about side effects being repeated
or not failing early *)letis_sorted_byfxs=letok=reftrueinfori=0tolengthxs-2dook:=!ok&&(f(unsafe_getxsi))<=(f(unsafe_getxs(i+1)))done;!ok(* TODO: Investigate whether a second array is better than pairs *)letdecorate_stable_sortfxs=letdecorated=map(funx->(fx,x))xsinlet()=stable_sort(fun(i,_)(j,_)->Pervasives.compareij)decoratedinmap(fun(_,x)->x)decorated(*$T decorate_stable_sort
decorate_stable_sort fst [|(1,2);(1,3);(0,2);(1,4)|] \
= [|(0,2);(1,2);(1,3);(1,4)|]
*)(*$Q decorate_stable_sort
(Q.pair (Q.array Q.small_int) (Q.fun1 Q.Observable.int (Q.option Q.int))) \
(fun (a, Q.Fun(_,f)) -> is_sorted_by f (decorate_stable_sort f a))
*)letdecorate_fast_sortfxs=letdecorated=map(funx->(fx,x))xsinlet()=fast_sort(fun(i,_)(j,_)->Pervasives.compareij)decoratedinmap(fun(_,x)->x)decorated(*$Q decorate_fast_sort
(Q.pair (Q.arrayQ.small_int) (Q.fun1 Q.Observable.int (Q.option Q.int))) \
(fun (a, Q.Fun(_,f)) -> is_sorted_by f (decorate_fast_sort fa))
*)letbsearchcmparrx=letrecbsearchij=ifi>jthen`Just_afterjelseletmiddle=i+(j-i)/2in(* avoid overflow *)matchcmpx(unsafe_getarrmiddle)with|BatOrd.Eq->`Atmiddle|BatOrd.Lt->bsearchi(middle-1)|BatOrd.Gt->bsearch(middle+1)jiniflengtharr=0then`Emptyelsematch(cmp(unsafe_getarr0)x,cmp(unsafe_getarr(lengtharr-1))x)with|BatOrd.Gt,_->`All_bigger|_,BatOrd.Lt->`All_lower|_->bsearch 0(lengtharr-1)(*$T bsearch
bsearch BatInt.ord [|1; 2; 2; 3; 4; 10|] 3 = `At3 bsearch BatInt.ord [|1; 2; 2; 3; 4; 10|] 5 = `Just_after4
bsearch BatInt.ord [|1; 2; 5; 5; 11; 12|] 1 = `At 0
bsearch BatInt.ord [|1; 2; 5; 5; 11; 12|] 12 = `At 5
bsearch BatInt.ord [|1; 2; 2; 3; 4; 9|] 10 = `All_lower
bsearch BatInt.ord [|1; 2; 2; 3; 4; 9|] 0 = `All_bigger
bsearch BatInt.ord [| |] 3 = `Empty
*)letpivot_splitcmparrx=letopenBatOrdinletn=lengtharrin(*find left edge between i and j *)letrecsearch_leftij=ifi>jthenielseletmiddle=i+(j-i)/2inmatchcmp(unsafe_getarrmiddle)xwith|Lt->search_left(middle+1)j|Gt->search_lefti(middle-1)|Eq->(* check whether [middle] is the edge, ie the leftmost index where arr.(_) = x *)let neighbor=middle-1inifneighbor<0||cmp(unsafe_getarrneighbor)x=Ltthenmiddle(* found!*)elsesearch_leftineighbor(* go further on left *)(* find right edge, between i and j *)andsearch_rightij=ifi>jthenielseletmiddle=i+(j-i)/2inmatchcmp(unsafe_getarrmiddle)xwith|Lt->search_right(middle+1)j|Gt->search_righti(middle-1)|Eq->letneighbor=middle+1inifneighbor=n||cmp(unsafe_getarrneighbor)x=Gtthenmiddle+1(* found! *)elsesearch_rightneighborj(*go further on right *)in(search_left0(n-1),search_right0(n-1))(*$T pivot_split
pivot_split BatInt.ord [||] 1 = (0, 0)
pivot_split BatInt.ord [|1;2;2;3;3;4;5|] 3 = (3,5)pivot_split BatInt.ord [|1;1;1;2;3;3;4;5|] 1 = (0,3)
pivot_split BatInt.ord [|1;2;2;3;3;4;5|] 10 = (7,7)
pivot_split BatInt.ord [|1;2;2;3;3;4;5|] 0 = (0,0)
pivot_split BatInt.ord [|2;2;2|] 2 = (0,3)
pivot_split BatInt.ord [|1;2;2;4;5|] 3 = (3,3)
*)letinsertxsxi=letlen=lengthxsinifi<0||i>lentheninvalid_arg"Array.insert: offsetout of range";init(len+1)(funj->ifj<ithenunsafe_getxsjelse ifj>ithenunsafe_getxs(j-1)elsex)(*$T insert
insert[|1;2;3|] 4 0 = [|4;1;2;3|]
insert [|1;2;3|] 4 3 = [|1;2;3;4|]
insert [|1;2;3|] 4 2 = [|1;2;4;3|]
try ignore (insert [|1;2;3|] 4 100); false \
with Invalid_argument _ -> true
try ignore (insert [|1;2;3|] 4 (-40)); false \
with Invalid_argument _ -> true
*)letremove_atisrc=letx=src.(i)in(* keep the bound check in there *)letn=lengthsrcinletdst=make(n-1)xinblitsrc0dst0i;blitsrc(i+1)dsti(n-i-1);dst(*$T remove_at
try remove_at 0 [||] = [|1|] \
with Invalid_argument _ -> true
remove_at 0[|1;2;3|] = [|2;3|]
remove_at 1 [|1;2;3|] = [|1;3|]
remove_at 2 [|1;2;3|] = [|1;2|]
try remove_at 3 [|1;2;3|] = [|1|]\
with Invalid_argument _ -> true
*)(* helper function; only works for arrays of equal length *)leteq_elementseq_elta1a2=for_all2eq_elta1a2(* helper function to compare arrays *)letrecord_auxeq_eltia1a2=letopenBatOrdinifi>=lengtha1thenEqelsematcheq_elt(unsafe_geta1i)(unsafe_geta2i)with|(Lt|Gt)asres->res|Eq->ord_aux eq_elt(i+1)a1a2letord_elementseq_elta1a2=ord_auxeq_elt0a1a2letequaleqa1a2=BatOrd.bin_eqBatInt.equal(lengtha1)(lengtha2)(eq_elementseq)a1a2(*$T equal
equal (=) [|1;2;3|] [|1;2;3|]
not (equal (=) [|1;2;3|] [|1;2;3;4|])
not (equal (=) [|1;2;3;4|] [|1;2;3|])
equal (=) [||] [||]
equal (<>) [|1;2;3|] [|2;3;4|]
not (equal (<>) [|1;2;3|] [|3;2;1|])
*)letordord_elta1a2=BatOrd.bin_ordBatInt.ord(lengtha1)(lengtha2)(ord_elementsord_elt)a1a2(*$T ord
ord BatInt.ord [|2|] [|1;2|] = BatOrd.Lt
ord BatInt.ord [|1;1|] [|2|] = BatOrd.Gt
ord BatInt.ord [|1;1;1|] [|1;1;2|] = BatOrd.Lt
ord BatInt.ord [|1;1;1|] [|1;1;1|] = BatOrd.Eq
*)letshuffle?statea=BatInnerShuffle.array_shuffle?statea(*$T shuffle
let s = Random.State.make [|11|] in \
let a = [|1;2;3;4;5;6;7;8;9|] in \
shuffle ~state:s a; \
let ocaml_version= int_of_string (String.make 1Sys.ocaml_version.[0]) in \ a = if ocaml_version < 5 then \
[|7; 2; 9; 5; 3; 6; 4; 1; 8|] else \
[|1; 7; 4; 9; 5; 2; 8; 6; 3|]
let b = [||] in \
shuffleb;\
b = [||]
*)(* equivalent of List.split *)letsplita=letn=length ainifn=0then([||],[||])elseletl,r=unsafe_geta0inletleft=makenlinletright=makenrinfori=1ton-1doletl,r=unsafe_getaiinunsafe_setleftil;unsafe_setrightirdone;(left,right)(*$T split
split [||] = ([||], [||])
split [|(1,2);(3,4);(5,6)|] = ([|1;3;5|], [|2;4;6|])
*)letcombineab=letm=lengthainletn=lengthbinifm<>nthen invalid_arg"Array.combine";map2(funxy->(x,y))ab(*$T combine
combine [||] [||]= [||]
try combine [|1;2;3|] [||] = [||] withInvalid_argument _ -> true combine [|1;2;3|] [|4;5;6|] = [|(1,4);(2,5);(3,6)|]
*)moduleIncubator=structmoduleEq(T:BatOrd.Eq)=structtypet=T.tarrayleteq=equalT.eqendmoduleOrd(T:BatOrd.Ord)=structtypet=T.tarrayletord=ordT.ordendendletleftalen=iflen>=lengthathenaelsesuba0lenletrightalen=letalen=lengthainiflen>=alenthenaelsesuba(alen-len)lenletheadapos=leftaposlettailapos=letalen=lengthainifpos>=alenthen[||]elsesubapos(alen-pos)(*$= left & ~printer:(IO.to_string (print Int.print))
(left [|1;2;3|] 1) [|1|]
(left [|1;2|] 3) [|1;2|]
(left [|1;2;3|] 3) [|1;2;3|]
(left [|1;2;3|] 10)[|1;2;3|]
(left [|1;2;3|] 0) [||]
*)(*$= right & ~printer:(IO.to_string (print Int.print))
(right [|1;2;3|] 1) [|3|]
(right [|1;2|] 3) [|1;2|]
(right [|1;2;3|] 3) [|1;2;3|]
(right [|1;2;3|] 10) [|1;2;3|]
(right [|1;2;3|] 0) [||]
*)(*$= tail & ~printer:(IO.to_string (print Int.print))
(tail [|1;2;3|] 1) [|2;3|]
[||] (tail [|1;2;3|] 10)
(tail [|1;2;3|] 0) [|1;2;3|]
*)(*$= head & ~printer:(IO.to_string (print Int.print))
(head [|1;2;3|] 1) [|1|]
(head [|1;2;3|] 10) [|1;2;3|]
(head [|1;2;3|] 0) [||]
*)moduleCap=struct(** Implementation note: in [('a, 'b) t], ['b] serves only as
a phantom type, to mark which operations are only legitimate on
readable arrays or writeable arrays.*)type('a,'b)t='aarrayconstraint 'b=[<`Read|`Write]externalof_array:'aarray->('a,_)t="%identity"externalto_array:('a,[`Read|`Write])t->'aarray="%identity"externalread_only:('a,[>`Read])t->('a,[`Read])t="%identity"externalwrite_only :('a,[>`Write])t->('a,[`Write])t="%identity"externallength:('a,[>])t->int ="%array_length"externalget:('a,[>`Read])t->int->'a="%array_safe_get"externalset:('a,[>`Write])t->int->'a->unit="%array_safe_set"##V>=5.3##externalmake:int->'a->('a,_)t="caml_array_make"##V<5.3##externalmake:int->'a->('a,_)t="caml_make_vect"##V>=5.3##externalcreate:int->'a->('a,_)t="caml_array_make"##V<5.3##externalcreate:int->'a->('a,_)t="caml_make_vect"##V>=5.3##externalmake_float:int->(float,_)t="caml_array_create_float"##V>=4.2####V<5.3##externalmake_float:int->(float,_)t="caml_make_float_vect"##V<4.2##letmake_floatn=maken0.letinit=initletmake_matrix=make_matrixletcreate_matrix=make_matrixletiter=iterletmap=mapletfilter=filterletfilter_map=filter_mapletcount_matching=count_matchingletiteri=iteriletmapi=mapiletmodify=modifyletmodifyi=modifyiletfold_left=fold_leftletfold=fold_leftletfold_left_map=fold_left_mapletfold_right=fold_rightletfold_while=fold_whileletiter2 =iter2letiter2i=iter2iletfor_all=for_allletexists=existsletfind=findletfind_opt=find_optletfind_map=find_mapletmem=memletmemq=memqletfindi=findiletfind_all=find_allletpartition=partitionletrev=revletrev_in_place=rev_in_placeletappend=appendletconcat =concatletsub=subletcopy=copyletfill=fillletblit=blitlet enum=enumletof_enum=of_enumletbackwards=backwardsletof_backwards=of_backwardsletto_list=to_listletsplit=splitlet combine=combineletpivot_split=pivot_splitletof_list=of_listlet sort=sortletstable_sort =stable_sortletfast_sort=fast_sortletcompare=compareletprint=printletord=ordletequal=equalexternalunsafe_get:('a,[>`Read])t->int->'a="%array_unsafe_get"externalunsafe_set:('a,[>`Write])t->int->'a->unit="%array_unsafe_set"moduleLabels=structletiniti~f=initifletcreate len~init=createleninitletmake=createletmake_matrix~dimx~dimyx=make_matrixdimxdimyxletcreate_matrix=make_matrixletsuba~pos~len=subaposlenletfilla~pos~lenx=fillaposlenxletblit~src~src_pos~dst~dst_pos~len=blitsrcsrc_posdstdst_poslenletiter~fa=iterfaletmap~fa=mapfaletiteri~fa=iterifaletmapi~fa=mapifaletmodify~fa=modifyfaletmodifyi~fa=modifyifaletfold_left~f~inita=fold_leftfinitaletfold_left_map~f~inita=fold_left_mapfinitaletfold=fold_leftletfold_right~fa~init=fold_rightfainitletfold_while~p~f~inita=fold_whilepfinitaletsort~cmpa=sortcmpaletstable_sort~cmpa=stable_sortcmpaletfast_sort~cmpa=fast_sortcmpaletiter2~fab=iter2fabletexists~fa=existsfaletfor_all~fa=for_allfaletiter2i~fab=iter2ifabletfind~fa=findfaletfind_opt~fa=find_optfaletfind_map~fa=find_mapfaletfilter~fa=filterfaletfilter_map~fa=filter_mapfaletcount_matching~fa=count_matchingfaendmoduleExceptionless=structletfindfe=trySome(findfe)withNot_found->Noneletfindi fe=trySome(findife)withNot_found->NoneendendmoduleExceptionless=structletfindfe=trySome(findfe)withNot_found->Noneletfindife=trySome(findife)with Not_found->NoneendmoduleLabels=structletiniti~f=initifletcreatelen~init=makeleninitletmake=createletmake_matrix~dimx~dimyx=make_matrixdimxdimyxletcreate_matrix=make_matrixletsuba~pos~len=subaposlenletfilla~pos~lenx=fillaposlenxletblit~src~src_pos~dst~dst_pos~len=blitsrcsrc_posdstdst_poslenletiter~fa=iterfaletmap~fa=mapfaletiteri~fa=iterifaletmapi~fa=mapifaletmodify~fa=modifyfaletmodifyi~fa=modifyifaletfold_left~f~inita=fold_leftfinitaletfold_left_map~f~inita=fold_left_mapfinitaletfold=fold_leftletfold_right~fa~init=fold_rightfainitletfold_while~p~f~inita=fold_whilepfinitaletsort~cmpa=sortcmpaletstable_sort~cmpa=stable_sortcmpaletfast_sort~cmpa=fast_sortcmpaletiter2~fab=iter2fabletexists~fa=existsfaletfor_all~fa=for_allfaletiter2i~fab=iter2ifabletfind~fa=findfaletfind_opt~fa=find_optfaletfind_map~fa=find_mapfaletfindi~fe=findifeletfilter~fa=filterfaletfilter_map~fa=filter_mapfaletcount_matching~fa=count_matchingfamoduleLExceptionless=structincludeExceptionlessletfind~fe=findfeletfindi~fe=findifeendend