123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269open!ImportmoduleArray=Array0moduleInt=Int0moduleChar=Char0(* Unfortunately, because the standard library does not expose
[Caml.Random.State.default], we have to construct our own. We then build the
[Caml.Random.int], [Caml.Random.bool] functions and friends using that default state in
exactly the same way as the standard library.
One other trickiness is that we need access to the unexposed [Caml.Random.State.assign]
function, which accesses the unexposed state representation. So, we copy the
[State.repr] type definition and [assign] function to here from the standard library,
and use [Obj.magic] to get access to the underlying implementation. *)(* Regression tests ought to be deterministic because that way anyone who breaks the test
knows that it's their code that broke the test. If tests are nondeterministic, a test
failure may instead happen because the test runner got unlucky and uncovered an
existing bug in the code supposedly being "protected" by the test in question. *)letforbid_nondeterminism_in_tests~allow_in_tests=ifam_testingthen(matchallow_in_testswith|Sometrue->()|None|Somefalse->failwith"initializing Random with a nondeterministic seed is forbidden in inline tests");;externalrandom_seed:unit->intarray="caml_sys_random_seed"letrandom_seed?allow_in_tests()=forbid_nondeterminism_in_tests~allow_in_tests;random_seed();;moduleState=struct(* We allow laziness only for the definition of [default], below, which may lazily call
[make_self_init]. For all other purposes, we create and use [t] eagerly. *)typet=Caml.Random.State.tLazy.tletbitst=Caml.Random.State.bits(Lazy.forcet)letboolt=Caml.Random.State.bool(Lazy.forcet)letinttx=Caml.Random.State.int(Lazy.forcet)xletint32tx=Caml.Random.State.int32(Lazy.forcet)xletint64tx=Caml.Random.State.int64(Lazy.forcet)xletnativeinttx=Caml.Random.State.nativeint(Lazy.forcet)xletmakeseed=Lazy.from_val(Caml.Random.State.makeseed)letcopyt=Lazy.from_val(Caml.Random.State.copy(Lazy.forcet))letchart=intt256|>Char.unsafe_of_intletasciit=intt128|>Char.unsafe_of_intletmake_self_init?allow_in_tests()=forbid_nondeterminism_in_tests~allow_in_tests;Lazy.from_val(Caml.Random.State.make_self_init());;moduleRepr=structtypet={st:intarray;mutableidx:int}letof_state:Caml.Random.State.t->t=Caml.Obj.magicendletassignt1t2=lett1=Repr.of_state(Lazy.forcet1)inlett2=Repr.of_state(Lazy.forcet2)inArray.blit~src:t2.st~src_pos:0~dst:t1.st~dst_pos:0~len:(Array.lengtht1.st);t1.idx<-t2.idx;;letfull_inittseed=assignt(makeseed)letdefault=ifam_testingthen((* We define Base's default random state as a copy of OCaml's default random state.
This means that programs that use Base.Random will see the same sequence of
random bits as if they had used Caml.Random. However, because [get_state] returns
a copy, Base.Random and OCaml.Random are not using the same state. If a program
used both, each of them would go through the same sequence of random bits. To
avoid that, we reset OCaml's random state to a different seed, giving it a
different sequence. *)lett=Caml.Random.get_state()inCaml.Random.init137;Lazy.from_valt)elselazy(* Outside of tests, we initialize random state nondeterministically and lazily.
We force the random initialization to be lazy so that we do not pay any cost
for it in programs that do not use randomness. *)(Lazy.force(make_self_init()));;letint_on_64bitstbound=ifbound<=0x3FFFFFFF(* (1 lsl 30) - 1 *)theninttboundelseCaml.Int64.to_int(int64t(Caml.Int64.of_intbound));;letint_on_32bitstbound=(* Not always true with the JavaScript backend. *)ifbound<=0x3FFFFFFF(* (1 lsl 30) - 1 *)theninttboundelseCaml.Int32.to_int(int32t(Caml.Int32.of_intbound));;letint=matchWord_size.word_sizewith|W64->int_on_64bits|W32->int_on_32bits;;letfull_range_int64=letopenCaml.Int64inletbitsstate=of_int(bitsstate)infunstate->logxor(bitsstate)(logxor(shift_left(bitsstate)30)(shift_left(bitsstate)60));;letfull_range_int32=letopenCaml.Int32inletbitsstate=of_int(bitsstate)infunstate->logxor(bitsstate)(shift_left(bitsstate)30);;letfull_range_int_on_64bitsstate=Caml.Int64.to_int(full_range_int64state)letfull_range_int_on_32bitsstate=Caml.Int32.to_int(full_range_int32state)letfull_range_int=matchWord_size.word_sizewith|W64->full_range_int_on_64bits|W32->full_range_int_on_32bits;;letfull_range_nativeint_on_64bitsstate=Caml.Int64.to_nativeint(full_range_int64state);;letfull_range_nativeint_on_32bitsstate=Caml.Nativeint.of_int32(full_range_int32state);;letfull_range_nativeint=matchWord_size.word_sizewith|W64->full_range_nativeint_on_64bits|W32->full_range_nativeint_on_32bits;;let[@cold]raise_crossed_boundsnamelower_boundupper_boundstring_of_bound=Printf.failwithf"Random.%s: crossed bounds [%s > %s]"name(string_of_boundlower_bound)(string_of_boundupper_bound)();;letint_incl=letrecin_rangestatelohi=letint=full_range_intstateinifint>=lo&&int<=hithenintelsein_rangestatelohiinfunstatelohi->iflo>hithenraise_crossed_bounds"int"lohiInt.to_string;letdiff=hi-loinifdiff=Int.max_valuethenlo+(full_range_intstatelandInt.max_value)elseifdiff>=0thenlo+intstate(Int.succdiff)elsein_rangestatelohi;;letint32_incl=letopenInt32_replace_polymorphic_compareinletrecin_rangestatelohi=letint=full_range_int32stateinifint>=lo&&int<=hithenintelsein_rangestatelohiinletopenCaml.Int32infunstatelohi->iflo>hithenraise_crossed_bounds"int32"lohito_string;letdiff=subhiloinifdiff=max_intthenaddlo(logand(full_range_int32state)max_int)elseifdiff>=0lthenaddlo(int32state(succdiff))elsein_rangestatelohi;;letnativeint_incl=letopenNativeint_replace_polymorphic_compareinletrecin_rangestatelohi=letint=full_range_nativeintstateinifint>=lo&&int<=hithenintelsein_rangestatelohiinletopenCaml.Nativeintinfunstatelohi->iflo>hithenraise_crossed_bounds"nativeint"lohito_string;letdiff=subhiloinifdiff=max_intthenaddlo(logand(full_range_nativeintstate)max_int)elseifdiff>=0nthenaddlo(nativeintstate(succdiff))elsein_rangestatelohi;;letint64_incl=letopenInt64_replace_polymorphic_compareinletrecin_rangestatelohi=letint=full_range_int64stateinifint>=lo&&int<=hithenintelsein_rangestatelohiinletopenCaml.Int64infunstatelohi->iflo>hithenraise_crossed_bounds"int64"lohito_string;letdiff=subhiloinifdiff=max_intthenaddlo(logand(full_range_int64state)max_int)elseifdiff>=0Lthenaddlo(int64state(succdiff))elsein_rangestatelohi;;(* Return a uniformly random float in [0, 1). *)letrecrawfloatstate=letopenFloat_replace_polymorphic_compareinletscale=0x1p-30in(* 2^-30 *)letr1=Caml.float_of_int(bitsstate)inletr2=Caml.float_of_int(bitsstate)inletresult=((r1*.scale)+.r2)*.scalein(* With very small probability, result can round up to 1.0, so in that case, we just
try again. *)ifresult<1.0thenresultelserawfloatstate;;letfloatstatehi=rawfloatstate*.hiletfloat_rangestatelohi=letopenFloat_replace_polymorphic_compareiniflo>hithenraise_crossed_bounds"float"lohiCaml.string_of_float;lo+.floatstate(hi-.lo);;endletdefault=State.defaultletbits()=State.bitsdefaultletintx=State.intdefaultxletint32x=State.int32defaultxletnativeintx=State.nativeintdefaultxletint64x=State.int64defaultxletfloatx=State.floatdefaultxletint_inclxy=State.int_incldefaultxyletint32_inclxy=State.int32_incldefaultxyletnativeint_inclxy=State.nativeint_incldefaultxyletint64_inclxy=State.int64_incldefaultxyletfloat_rangexy=State.float_rangedefaultxyletbool()=State.booldefaultletchar()=State.chardefaultletascii()=State.asciidefaultletfull_initseed=State.full_initdefaultseedletinitseed=full_init[|seed|]letself_init?allow_in_tests()=full_init(random_seed?allow_in_tests())letset_states=State.assigndefaults