12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265moduleZ=structopenCoreincludeZarith.Zletz_ten=of_int10letpow_10=(* When converting bignum to decimal string, we need to compute the value [10**(log2
denominator)]. Meanwhile, [log2 (max finite float)] is approximately 1024, so
this seems like a reasonable guess for the upper bound for computations where
performance may matter. Add 17% tip, and you end up with 1200. On the other
hand, 1200 words sounds like a sane enough amount of memory for a library to
preallocate statically. If this table fills up, it will take 0.3 MB, which is
also not crazy for something actually being used. *)letmax_memoized_pow=1200inlettbl=Array.create~len:Int.(max_memoized_pow+1)Noneinletpow_10n=powz_tenninfunn->ifn>max_memoized_powthenpow_10nelse(matchtbl.(n)with|Somex->x|None->letx=pow_10nintbl.(n)<-Some(pow_10n);x);;endmoduleQ=structopenCoreincludeZarith.Qopen(Int:Interfaces.Infix_comparatorswithtypet:=int)lett_sexp_grammar:tSexplib.Sexp_grammar.t=letplus_character:Sexplib.Sexp_grammar.grammar=Variant{case_sensitivity=Case_insensitive;clauses=[No_tag{name="+";clause_kind=Atom_clause}]}in{untyped=Union[Float;plus_character;List(Cons(Union[Float;plus_character],Cons(plus_character,Cons(String,Empty))))]};;letof_float_dyadic=of_floatletof_float=`dont_use_itlet_=of_floatlethash(t:t)=Hashtbl.hashtlethash_fold_tstatet=hash_fold_intstate(Hashtbl.hasht)letnumt=of_bigintt.numletdent=of_bigintt.denlethalf=of_ints12letone=of_int1letten=of_int10lethundred=of_int100letthousand=of_int1_000letmillion=of_int1_000_000letbillion=of_int1_000_000_000lettrillion=million*millionlettenth=one/tenlethundredth=one/hundredletthousandth=one/thousandletmillionth=one/millionletbillionth=one/billionlettrillionth=one/trillionletnan=zero/zeroletinfinity=one/zeroletneg_infinity=minus_one/zeroletto_rational_string=to_stringletof_rational_string=of_stringletto_string=`renamed_to_rational_stringlet_=to_stringletof_string=`renamed_of_rational_stringlet_=of_stringletto_string_decimal_truncate~max_decimal_digits:shift_lent=letdecimal_mover=of_bigint(Z.pow_10shift_len)inlet(-)=Int.(-)inlet(+)=Int.(+)inletneg=lttzeroinletshifted=mul(abst)decimal_moverinletnum,den=shifted.num,shifted.deninlets=Z.to_string(Z.divnumden)inletrecdec_end_posposcount=ifpos<0||count=shift_lenthenNoneelseifChar.(=)s.[pos]'0'thendec_end_pos(pos-1)(count+1)elseSomeposinletlen=String.lengthsinletint_part,dec_part=matchdec_end_pos(String.lengths-1)0with|None->letint_part=iflen>shift_lenthenString.subs~pos:0~len:(len-shift_len)else""inint_part,""|Someend_pos->letint_len=iflen>shift_lenthenlen-shift_lenelse0inletint_part=ifint_len>0thenString.subs~pos:0~len:int_lenelse""inletdec_pad=iflen>=shift_lenthen""elseString.make(shift_len-len)'0'inletdec_part=dec_pad^String.subs~pos:int_len~len:(end_pos-int_len+1)inint_part,dec_partinmatchneg,int_part,dec_partwith|_,"",""->"0"|true,"",_->"-0."^dec_part|false,"",_->"0."^dec_part|true,_,""->"-"^int_part|false,_,""->int_part|true,_,_->"-"^int_part^"."^dec_part|false,_,_->int_part^"."^dec_part;;letto_string_when_den_is_zero~num=matchOrdering.of_int(Z.comparenumZ.zero)with|Greater->"inf"|Less->"-inf"|Equal->"nan";;moduleOf_string_internal:sigvalof_string_internal:string->tend=structletfails=failwithf"unable to parse %S as Bignum.t"s()letrecall_zeroess~pos~len=iflen<=0thentrueelseChar.equals.[pos]'0'&&all_zeroess~pos:Int.(pos+1)~len:Int.(len-1);;(* parse the substring of s between starting and finishing (both
included), knowing the position of the dot.
The decimal and frac parts can be empty strings, with semantics
zero (as in .5 or 1.). If both are empty strings, we raise an
error though.
*)letof_float_substrings~starting~dot~finishing:t=let(-)=Int.(-)inlet(+)=Int.(+)inletdecimal_len=Int.max0(dot-starting)inletfrac_len=Int.max0(1+finishing-(dot+1))inifdecimal_len=0&&frac_len=0thenfails;letdecimal=ifdecimal_len=0thenZ.zeroelseZ.of_substrings~pos:starting~len:decimal_leniniffrac_len=0||all_zeroess~pos:(dot+1)~len:frac_lenthenof_bigintdecimalelse(letfrac=Z.of_substrings~pos:(dot+1)~len:frac_leninletden=Z.pow_10frac_leninletint_part=Z.(decimal*den)inletnum=Z.addint_partfracinmakenumden);;letof_scientific_string_components~coefficient~power=letpower=Int.of_stringpowerinletpower'=Z.pow_10(Int.abspower)inletpower'=ifInt.(>)power0thenmakepower'Z.oneelsemakeZ.onepower'inmulcoefficientpower';;(* There are six possible cases to parse:
- the string is in rational notation: it is of the form `a / b`
- the string is in scientific notation: it is of the form `a E b` (a.b E c )
- the stringis in floating point notation: it is of the form `a.b`
- the string is in decimal notation: it is of the form `i` where i is an int
- the string is nan, +nan, -nan, inf, +inf, -inf
- the string is invalid
*)(* Use a bitset to implement the state *)lethas_dot=1lethas_slash=2lethas_exp=4(* perform a case analysis on the state, the position of the various tokens
('.','e','/') and constructs the bignum that was parsed. *)letmakes~length~state~dot~exp~slash=letopenIntinletis_negative,skip_sign=matchs.[0]with|'+'->false,1|'-'->true,1|_->false,0inifstate=0thenof_bigint(Z.of_strings)else(lett=ifstatelandhas_exp<>0then(letpower=String.subs~pos:(exp+1)~len:(length-exp-1)inletcoefficient=ifstatelandhas_dot<>0thenifdot<expthenof_float_substrings~starting:skip_sign~dot~finishing:(exp-1)elsefailselseifexp<=skip_signthenfails(* e1, -e1 are not valid *)elseof_bigint(Z.of_substrings~pos:skip_sign~len:(exp-skip_sign))inof_scientific_string_components~coefficient~power)elseifstatelandhas_dot<>0thenof_float_substrings~starting:skip_sign~dot~finishing:(length-1)elseifstatelandhas_slash<>0then(letnum=Z.of_substrings~pos:skip_sign~len:(slash-skip_sign)inletden=Z.of_substrings~pos:(slash+1)~len:(length-slash-1)inmakenumden)elsefailsinifis_negativethenZarith.Q.negtelset);;letrecdecomposes~lengthi~state~dot~exp~slash=ifi<lengththen(matchs.[i]with|'0'..'9'->decomposes~length(succi)~state~dot~exp~slash|'.'->ifstatelandhas_dot<>0thenfailselsedecomposes~length(succi)~state:(statelorhas_dot)~dot:i~exp~slash|'/'->ifstatelandhas_slash<>0thenfailselsedecomposes~length(succi)~state:(statelorhas_slash)~dot~exp~slash:i|'e'|'E'->ifstatelandhas_exp<>0thenfailselsedecomposes~length(succi)~state:(statelorhas_exp)~dot~exp:i~slash|'+'|'-'->(* the only place where signs are allowed is at the very beginning, or after
an exp sign (in scientific notation). *)ifi=0||predi=expthendecomposes~length(succi)~state~dot~exp~slashelsefails|_->(matchString.lowercaseswith|"nan"|"+nan"|"-nan"->nan|"inf"|"+inf"->infinity|"-inf"->neg_infinity|_->fails))elsemakes~length~state~dot~exp~slash;;letstrip_underscores_if_anys=letunderscores=ref0inletlength=String.lengthsinfori=0toInt.predlengthdomatchs.[i]with|'_'->incrunderscores|_->()done;if!underscores>0then(letunderscores_seen=ref0inString.initInt.(length-!underscores)~f:(funi->whileChar.equals.[Int.(+)i!underscores_seen]'_'doincrunderscores_seendone;s.[Int.(+)i!underscores_seen]))elses;;letof_string_internals:t=lets=strip_underscores_if_anysinletlength=String.lengthsiniflength=0thenfails;decomposes~length0~state:0~dot:(-1)~exp:(-1)~slash:(-1);;endletof_string_internal=Of_string_internal.of_string_internalmoduleKind=structtypet=|Den_equals_zero|Rational_not_decimal|Decimalof{max_decimal_digits:int}[@@derivingsexp_of]endletkindt=ifZ.equalt.denZ.zerothenKind.Den_equals_zeroelse(letmax_decimal_digits=Z.log2t.denin(* There exist k and n such that [t.den = 2**k * 5**n]
iff
There exists m such that [10**m % t.den = 0]
But it's sufficient to check for [m = floor (log2 t.den)], since
[log2 t.den >= k + n], assuming k and n exist, and of course
[10**(k + n + l) % (2**k * 5**n) = 0] for any [l >= 0]. *)ifZ.equal(Z.rem(Z.pow_10max_decimal_digits)t.den)Z.zerothenKind.Decimal{max_decimal_digits}elseRational_not_decimal);;moduleSerialized_parts=structtypet=|Atomofstring|Listofstring*string*stringletcreatet:t=ifZ.equalt.denZ.one(* Special case motivated by performance speedup. *)thenAtom(Z.to_stringt.num)else(matchkindtwith|Den_equals_zero->Atom(to_string_when_den_is_zero~num:t.num)|Decimal{max_decimal_digits}->Atom(to_string_decimal_truncate~max_decimal_digitst)|Rational_not_decimal->letmain=to_string_decimal_truncate~max_decimal_digits:9tinletmain_t=of_string_internalmaininletremaining=subtmain_tinList(main,"+",to_rational_stringremaining));;endendmoduleStable=structopen!Core.Core_stableopen!Core.Int.Replace_polymorphic_comparemoduleV1=structmoduleBin_rep_conversion=structtypet=Q.ttypetarget=stringletto_binable=Q.to_rational_stringletof_binable=Q.of_rational_stringendtypet=Q.t[@@derivingcompare,equal,hash,sexp_grammar]letsexp_of_tt=letopenCoreinmatchQ.Serialized_parts.createtwith|Atomatom->Sexp.Atomatom|List(a,b,c)->Sexp.List[Atoma;Atomb;Atomc]|exceptione->Exn.reraisee"Bignum.sexp_of_t";;lett_of_sexps=letopenCoreinmatchswith|Sexp.Atoms->Q.of_string_internals|Sexp.List[Sexp.Atomfloat_part;Sexp.Atom"+";Sexp.Atomrational_part]->lett1=Q.of_string_internalfloat_partinlett2=Q.of_rational_stringrational_partinQ.addt1t2|Sexp.List_->of_sexp_error{|expected Atom or List [float; "+"; remainder]|}s;;includeBinable.Of_binable.V1[@alert"-legacy"](String.V1)(Bin_rep_conversion)letstable_witness=let(_bin_io:tStable_witness.t)=(* [Binable.Of_binable.V1] *)Stable_witness.of_serializableString.V1.stable_witnessBin_rep_conversion.of_binableBin_rep_conversion.to_binableinlet(_sexp:tStable_witness.t)=(* Defined directly above *)Stable_witness.assert_stableinStable_witness.assert_stable;;moduleFor_testing=Bin_rep_conversionendmoduleV2=struct(* The V2 serialized representation makes use of special case to
achieve better compression AND less overhead when serialising /
deserialising.
It is written to go via an intermediate type. However to gain
additional speed during deserialisation, we provide a handexpanded
read function that avoids the unnecessary allocation of the
intermediate type. To do so the two types below must be kept in
sync (including order of constructors) -- this is enforced by a
unit test in test_bignum.ml. *)moduleTag=structtypet=|Zero|Int|Over_10|Over_100|Over_1_000|Over_10_000|Over_100_000|Over_1_000_000|Over_10_000_000|Over_100_000_000|Over_int|Other[@@derivingbin_io,variants]endmoduleBin_rep=struct(* Before [Bignum] was compatible with javascript, we were using [Int.t]. In order
to get JavaScript compatibility, we switched to [Int63.t] which behaves the
same as [Int] on 64bit architectures. However, because we wanted to not change
the [bin_shape], we had to lie a little and add the following trick *)moduleInt63=structincludeInt63.V1letbin_shape_t=Int.V1.bin_shape_tendtypet=|Zero|IntofInt63.t|Over_10ofInt63.t|Over_100ofInt63.t|Over_1_000ofInt63.t|Over_10_000ofInt63.t|Over_100_000ofInt63.t|Over_1_000_000ofInt63.t|Over_10_000_000ofInt63.t|Over_100_000_000ofInt63.t|Over_intofInt63.t*Int63.t|OtherofV1.t[@@derivingbin_io,stable_witness,variants]endletz_of_int63=matchSys.word_sizewith|64->funx->Z.of_int(Core.Int63.to_int_exnx)|32->funx->Z.of_int64(Core.Int63.to_int64x)|_->assertfalse;;moduleBin_rep_conversion=structopen!Coretypet=Q.ttypetarget=Bin_rep.tletequal=Q.equal(* For testing *)lettag_variants=Tag.Variants.descriptionsletbin_rep_variants=Bin_rep.Variants.descriptions(* To prevent a silent overflow that would result in a wrong result,
we only optimise after having checked that the numerator will still fit in an int
after having been multiplied by (i / d).*)(* pre condition: i > 0, d > 0 and d divides i *)letcheck_overflowf~n~di=(* Let p = i / d. p is an integer (cf pre condition). We have i = p.d.
n <= Max / i * d = Max / p.d * d
-> n * p <= Max / p.d * d.p, by multiplying by p on both sides.
-> n * p <= Max, because (Max / pd) * pd = pd q,
where Max = pd q + r, with 0 <= r < pd
So if n is positive, n <= Max / i * d, implies n * (i / d) <= Max.
If n is negative, n >= - Max / i * d , implies -n <= Max / i * d
which implies -n * p <= Max, see above.
-n * p <= Max implies n * p >= -Max > Min.
*)letmax_n=Int.(max_value/i*d)inifInt.(n>max_n||n<-max_n)thenBin_rep.Over_int(Int63.of_intn,Int63.of_intd)elsef(Int63.of_int(n*(i/d)));;(* Context: This code logic use to rely on [Int.t] instead of [Int63.t].
We could be more aggressive and check for overflows based on [Int63.max_value]
but we want to be conservative with existing 32bits users who might not be able
to read large ints. *)letto_binablet=ifequaltQ.zerothenBin_rep.Zeroelse(letnum=t.numinletden=t.deninifnot(Z.fits_intnum&&Z.fits_intden)thenBin_rep.Othertelse((* Both num and den fits in an int each *)letn=Z.to_intnumin(* Z.fits_int num *)letd=Z.to_intdenin(* Z.fits_int den *)let(=)=Core.Int.(=)inlet(mod)=Stdlib.(mod)inifd=0thenBin_rep.Othertelseifd=1thenBin_rep.Int(Int63.of_intn)elseif10_000modd=0thenif100modd=0thenif10modd=0thencheck_overflowBin_rep.over_10~n~d10elsecheck_overflowBin_rep.over_100~n~d100elseif1_000modd=0thencheck_overflowBin_rep.over_1_000~n~d1_000elsecheck_overflowBin_rep.over_10_000~n~d10_000elseif100_000_000modd=0thenif1_000_000modd=0thenif100_000modd=0thencheck_overflowBin_rep.over_100_000~n~d100_000elsecheck_overflowBin_rep.over_1_000_000~n~d1_000_000elseif10_000_000modd=0thencheck_overflowBin_rep.over_10_000_000~n~d10_000_000elsecheck_overflowBin_rep.over_100_000_000~n~d100_000_000elseBin_rep.Over_int(Int63.of_intn,Int63.of_intd)));;letof_binable=letopenQinfunction|Bin_rep.Zero->zero|Bin_rep.Inti->of_bigint(z_of_int63i)|Bin_rep.Over_int(n,d)->make(z_of_int63n)(z_of_int63d)|Bin_rep.Over_10n->make(z_of_int63n)(Z.of_int10)|Bin_rep.Over_100n->make(z_of_int63n)(Z.of_int100)|Bin_rep.Over_1_000n->make(z_of_int63n)(Z.of_int1_000)|Bin_rep.Over_10_000n->make(z_of_int63n)(Z.of_int10_000)|Bin_rep.Over_100_000n->make(z_of_int63n)(Z.of_int100_000)|Bin_rep.Over_1_000_000n->make(z_of_int63n)(Z.of_int1_000_000)|Bin_rep.Over_10_000_000n->make(z_of_int63n)(Z.of_int10_000_000)|Bin_rep.Over_100_000_000n->make(z_of_int63n)(Z.of_int100_000_000)|Bin_rep.Othero->o;;endtypet=Q.t[@@derivingcompare,equal,hash]includeBinable.Of_binable.V1[@alert"-legacy"](Bin_rep)(Bin_rep_conversion)moduleFor_testing=Bin_rep_conversionlett_of_sexp=V1.t_of_sexpletsexp_of_t=V1.sexp_of_tlett_sexp_grammar=V1.t_sexp_grammarletbin_read_tbuf~pos_ref=letbin_read_z_as_int63buf~pos_ref=z_of_int63(Core.Int63.bin_read_tbuf~pos_ref)inmatchTag.bin_read_tbuf~pos_refwith|Tag.Zero->Q.zero|Tag.Int->Q.of_bigint(bin_read_z_as_int63buf~pos_ref)|Tag.Over_int->letn=bin_read_z_as_int63buf~pos_refinletd=bin_read_z_as_int63buf~pos_refinQ.makend|Tag.Over_10->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int10)|Tag.Over_100->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int100)|Tag.Over_1_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int1_000)|Tag.Over_10_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int10_000)|Tag.Over_100_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int100_000)|Tag.Over_1_000_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int1_000_000)|Tag.Over_10_000_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int10_000_000)|Tag.Over_100_000_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int100_000_000)|Tag.Other->V1.bin_read_tbuf~pos_ref;;letbin_reader_t={bin_reader_twithBin_prot.Type_class.read=bin_read_t}letstable_witness=let(_bin_io:tStable_witness.t)=(* [Binable.Of_binable.V1] *)Stable_witness.of_serializableBin_rep.stable_witnessBin_rep_conversion.of_binableBin_rep_conversion.to_binableinlet(_sexp:tStable_witness.t)=(* Aliased to V1 *)V1.stable_witnessinStable_witness.assert_stable;;endmoduleV3=struct(* The V3 serialization is heavily based on V2.
The V3 serialized representation makes use of special case to
achieve better compression AND less overhead when serialising /
deserialising.
It is written to go via an intermediate type. However to gain
additional speed during deserialisation, we provide a handexpanded
read function that avoids the unnecessary allocation of the
intermediate type. To do so the two types below must be kept in
sync (including order of constructors) -- this is enforced by a
unit test in test_bignum.ml. *)moduleTag=structtypet=V2.Tag.t=|Zero|Int|Over_10|Over_100|Over_1_000|Over_10_000|Over_100_000|Over_1_000_000|Over_10_000_000|Over_100_000_000|Over_int|Other[@@derivingbin_io,variants]endmoduleBin_rep=structtypet=|Zero|IntofInt63.V1.t|Over_10ofInt63.V1.t|Over_100ofInt63.V1.t|Over_1_000ofInt63.V1.t|Over_10_000ofInt63.V1.t|Over_100_000ofInt63.V1.t|Over_1_000_000ofInt63.V1.t|Over_10_000_000ofInt63.V1.t|Over_100_000_000ofInt63.V1.t|Over_intofInt63.V1.t*Int63.V1.t|Otherof{num:Bigint.Stable.V2.t;den:Bigint.Stable.V2.t}[@@derivingbin_io,stable_witness,variants]endmoduleBin_rep_conversion=structopen!Coretypet=Q.ttypetarget=Bin_rep.tletequal=Q.equalletz_of_int63=matchSys.word_size_in_bitswith|64->funx->Z.of_int(Core.Int63.to_int_exnx)|32->funx->Z.of_int64(Core.Int63.to_int64x)|_->assertfalse;;letint63_of_z=matchSys.word_size_in_bitswith|64->funx->Core.Int63.of_int(Z.to_intx)|32->funx->Core.Int63.of_int64_exn(Z.to_int64x)|_->assertfalse;;(* For testing *)lettag_variants=Tag.Variants.descriptionsletbin_rep_variants=Bin_rep.Variants.descriptions(* To prevent a silent overflow that would result in a wrong result,
we only optimise after having checked that the numerator will still fit in an int
after having been multiplied by (i / d).*)(* pre condition: i > 0, d > 0 and d divides i *)letcheck_overflowf~n~di=(* Let p = i / d. p is an integer (cf pre condition). We have i = p.d.
n <= Max / i * d = Max / p.d * d
-> n * p <= Max / p.d * d.p, by multiplying by p on both sides.
-> n * p <= Max, because (Max / pd) * pd = pd q,
where Max = pd q + r, with 0 <= r < pd
So if n is positive, n <= Max / i * d, implies n * (i / d) <= Max.
If n is negative, n >= - Max / i * d , implies -n <= Max / i * d
which implies -n * p <= Max, see above.
-n * p <= Max implies n * p >= -Max > Min.
*)letmax_n=Int63.(max_value/i*d)inifInt63.(n>max_n||n<-max_n)thenBin_rep.Over_int(n,d)elsefInt63.O.(n*(i/d));;letint63_min_value=z_of_int63Int63.min_valueletint63_max_value=z_of_int63Int63.max_valueletfits_int63x=Z.leqint63_min_valuex&&Z.leqxint63_max_valueletto_binablet=ifequaltQ.zerothenBin_rep.Zeroelse(letnum=t.numinletden=t.deninifnot(fits_int63num&&fits_int63den)thenBin_rep.Other{num=Bigint.of_zarith_bigintnum;den=Bigint.of_zarith_bigintden}else((* Both num and den fits in an int each *)letn=int63_of_znumin(* fits_int63 num *)letd=int63_of_zdenin(* fits_int63 den *)let(=)=Core.Int63.(=)inlet(mod)=(* We only use [mod] below for positive arguments, so [Int63.rem] is
equivalent to [Int63.(%)] for these purposes. We prefer [rem] because it
is based on a builtin, and should be faster. *)Core.Int63.reminifd=Int63.zerothenBin_rep.Other{num=Bigint.of_zarith_bigintnum;den=Bigint.of_zarith_bigintden}elseifd=Int63.onethenBin_rep.IntnelseifInt63.of_int10_000modd=Int63.zerothenifInt63.of_int100modd=Int63.zerothenifInt63.of_int10modd=Int63.zerothencheck_overflowBin_rep.over_10~n~d(Int63.of_int10)elsecheck_overflowBin_rep.over_100~n~d(Int63.of_int100)elseifInt63.of_int1_000modd=Int63.zerothencheck_overflowBin_rep.over_1_000~n~d(Int63.of_int1_000)elsecheck_overflowBin_rep.over_10_000~n~d(Int63.of_int10_000)elseifInt63.of_int100_000_000modd=Int63.zerothenifInt63.of_int1_000_000modd=Int63.zerothenifInt63.of_int100_000modd=Int63.zerothencheck_overflowBin_rep.over_100_000~n~d(Int63.of_int100_000)elsecheck_overflowBin_rep.over_1_000_000~n~d(Int63.of_int1_000_000)elseifInt63.of_int10_000_000modd=Int63.zerothencheck_overflowBin_rep.over_10_000_000~n~d(Int63.of_int10_000_000)elsecheck_overflowBin_rep.over_100_000_000~n~d(Int63.of_int100_000_000)elseBin_rep.Over_int(n,d)));;letof_binable=letopenQinfunction|Bin_rep.Zero->zero|Bin_rep.Inti->of_bigint(z_of_int63i)|Bin_rep.Over_int(n,d)->make(z_of_int63n)(z_of_int63d)|Bin_rep.Over_10n->make(z_of_int63n)(Z.of_int10)|Bin_rep.Over_100n->make(z_of_int63n)(Z.of_int100)|Bin_rep.Over_1_000n->make(z_of_int63n)(Z.of_int1_000)|Bin_rep.Over_10_000n->make(z_of_int63n)(Z.of_int10_000)|Bin_rep.Over_100_000n->make(z_of_int63n)(Z.of_int100_000)|Bin_rep.Over_1_000_000n->make(z_of_int63n)(Z.of_int1_000_000)|Bin_rep.Over_10_000_000n->make(z_of_int63n)(Z.of_int10_000_000)|Bin_rep.Over_100_000_000n->make(z_of_int63n)(Z.of_int100_000_000)|Bin_rep.Other{num;den}->make(Bigint.to_zarith_bigintnum)(Bigint.to_zarith_bigintden);;endtypet=Q.t[@@derivingcompare,equal,hash]includeBinable.Of_binable.V1[@alert"-legacy"](Bin_rep)(Bin_rep_conversion)moduleFor_testing=Bin_rep_conversionlett_of_sexp=V1.t_of_sexpletsexp_of_t=V1.sexp_of_tlett_sexp_grammar=V1.t_sexp_grammarletbin_read_tbuf~pos_ref=letbin_read_z_as_int63buf~pos_ref=Bin_rep_conversion.z_of_int63(Core.Int63.bin_read_tbuf~pos_ref)inmatchTag.bin_read_tbuf~pos_refwith|Tag.Zero->Q.zero|Tag.Int->Q.of_bigint(bin_read_z_as_int63buf~pos_ref)|Tag.Over_int->letn=bin_read_z_as_int63buf~pos_refinletd=bin_read_z_as_int63buf~pos_refinQ.makend|Tag.Over_10->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int10)|Tag.Over_100->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int100)|Tag.Over_1_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int1_000)|Tag.Over_10_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int10_000)|Tag.Over_100_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int100_000)|Tag.Over_1_000_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int1_000_000)|Tag.Over_10_000_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int10_000_000)|Tag.Over_100_000_000->letn=bin_read_z_as_int63buf~pos_refinQ.maken(Z.of_int100_000_000)|Tag.Other->letnum=Bigint.Stable.V2.bin_read_tbuf~pos_refinletden=Bigint.Stable.V2.bin_read_tbuf~pos_refinQ.make(Bigint.to_zarith_bigintnum)(Bigint.to_zarith_bigintden);;letbin_reader_t={bin_reader_twithBin_prot.Type_class.read=bin_read_t}letstable_witness=let(_bin_io:tStable_witness.t)=(* [Binable.Of_binable.V1] *)Stable_witness.of_serializableBin_rep.stable_witnessBin_rep_conversion.of_binableBin_rep_conversion.to_binableinlet(_sexp:tStable_witness.t)=(* Aliased to V1 *)V1.stable_witnessinStable_witness.assert_stable;;end(* Note V1, V2 and V3 are the same type in ocaml. The only thing
that changes is the binprot representation. This is safe (imho)
as people declaring a stable type will have to explicitely referred
to V1, V2 or V3. At a later point we can hide that V1 or V2 is equal to
the regular type and thereby force people to switch to V3 or explicity
call a of/to v1/v2 function (which would be the identity) *)moduleCurrent=V3endopen!CoremoduleUnstable=Stable.CurrentincludeQincludeComparable.Make_binable(Unstable)lett_of_sexp=Unstable.t_of_sexpletsexp_of_t=Unstable.sexp_of_tletis_representable_as_decimalt=matchkindtwith|Den_equals_zero|Rational_not_decimal->false|Decimal{max_decimal_digits=_}->true;;letis_nant=Z.equalt.denZ.zero&&Z.equalt.numZ.zeroletis_integert=Z.equalt.denZ.oneletis_infinitet=Z.equalt.denZ.zero&¬(Z.equalt.numZ.zero)letis_positive_infinityt=equaltinfinityletis_negative_infinityt=equaltneg_infinityletto_bigint_optt=ifis_integertthenSome(Bigint.of_zarith_bigintt.num)elseNoneletround_to_nearest_z_half_to_event=lett=t+halfinifZ.equalZ.onet.denthen(letnum=t.numinifZ.is_evennumthennumelseZ.prednum)else((* Since t is not a natural number, t' <> t. Thus, t < 0 => t' > t. *)lett'=Q.to_biginttinifInt.equal(Z.signt.num)(-1)thenZ.predt'elset');;letround_decimal_to_nearest_half_to_even~digitst=letshift_left=Z.pow_10digitsinletshifted=t*Q.of_bigintshift_leftinifZ.equalZ.oneshifted.denthentelseQ.make(round_to_nearest_z_half_to_evenshifted)shift_left;;letto_string_accuratet=matchSerialized_parts.createtwith|Atomatom->atom|List(a,b,c)->String.concat_array[|"(";a;" ";b;" ";c;")"|];;letof_stringstr=if(not(String.is_emptystr))&&Char.equalstr.[0]'('thent_of_sexp(Sexp.of_stringstr)elseof_string_internalstr;;letto_string_decimal_accurate_exn=letnot_representablet=raise_s[%message"Not representable as decimal"~_:(t:t)]infunt->matchkindtwith|Den_equals_zero|Rational_not_decimal->not_representablet|Decimal{max_decimal_digits}->to_string_decimal_truncate~max_decimal_digitst;;letto_string_decimal_accuratet=Or_error.try_with(fun()->to_string_decimal_accurate_exnt);;letof_zarith_bigint=of_bigintletto_zarith_bigint=to_bigintletof_bigintbig=of_zarith_bigint(Bigint.to_zarith_bigintbig)letnum_as_bigintt=Bigint.of_zarith_bigintt.numletden_as_bigintt=Bigint.of_zarith_bigintt.denletto_int_exn=to_intletto_intt=Option.try_with(fun()->to_int_exnt)letsumxs=List.foldxs~init:zero~f:(+)letis_zero(x:t)=x=zeroletsignx=ifx<zerothen-1elseifx>zerothen1else0letsign_or_nant:Sign_or_nan.t=ifis_nantthenNanelseift>zerothenPoselseift<zerothenNegelseZero;;letsign_exnt:Sign.t=matchsign_or_nantwith|Pos->Pos|Neg->Neg|Zero->Zero|Nan->raise_s[%message"Bignum.sign_exn of NaN"~_:(t:t)];;letinverset=divonet(* Exponentiation by repeated squaring, to calculate t^n in O(log n) multiplications. *)let(**)tpow=(* Invariant: [result * (squares ** n) = t ** pow].
Termination: Reduces number of binary digits of [n] each iteration, so eventually
[n = 0], at which point [result = result * (squares ** n) = t ** pow]. *)letrecloopresultsquaresn=ifInt.equaln0thenresultelseifInt.equal(n%2)0thenloopresult(squares*squares)(Int.(/)n2)elseloop(result*squares)(squares*squares)Int.((n-1)/2)in(* Int.abs Int.min_value < 0, so have to handle it separately.
Although raising anything other than one to that power would probably eat your entire
RAM pretty quickly.
*)ifInt.equalpowInt.min_valuetheninverse(loopttInt.max_value)elseifInt.(<)pow0theninverse(looponet(Int.abspow))elselooponetpow;;lettruncatet=of_zarith_bigint(to_zarith_bigintt)letfloort=lett'=truncatetinift'>tthent'-oneelset';;(* This is quite a common case, and substantially faster than faffing around with
[to_multiple_of] *)letround_integer?(dir=`Nearest)t=matchdirwith|`Zero->truncatet|`Down->floort|`Up->neg(floor(negt))|`Nearest->floor(t+half);;letround?dir?to_multiple_oft=matchto_multiple_ofwith|None->round_integer?dirt|Someto_multiple_of->ifis_zeroto_multiple_ofthenfailwith"Bignum.round: to_multiple_of may not be zero";to_multiple_of*round_integer?dir(t/to_multiple_of);;letiround?dir?to_multiple_oft=matchto_multiple_ofwith|None->to_int(round_integer?dirt)|Someto_multiple_of->ifInt.equal0to_multiple_ofthenNoneelseto_int(round?dir~to_multiple_of:(of_intto_multiple_of)t);;letiround_exn?dir?to_multiple_oft=matchto_multiple_ofwith|None->to_int_exn(round_integer?dirt)|Someto_multiple_of->to_int_exn(round?dir~to_multiple_of:(of_intto_multiple_of)t);;letround_as_bigint_exn?dir?to_multiple_oft=Bigint.of_zarith_bigint(matchto_multiple_ofwith|None->to_zarith_bigint(round_integer?dirt)|Someto_multiple_of->to_zarith_bigint(round?dir~to_multiple_of:(of_bigintto_multiple_of)t));;letround_as_bigint?dir?to_multiple_oft=Option.try_with(fun()->round_as_bigint_exn?dir?to_multiple_oft);;letround_decimal?dir~digitst=ifInt.equal0digitsthenround_integer?dirtelseround?dir~to_multiple_of:(tenth**digits)t;;letto_string_hum?delimiter?(decimals=9)?(strip_zero=true)t=ifZ.equalt.denZ.zerothento_string_when_den_is_zero~num:t.numelse(lets=ifZ.equalt.denZ.onethenZ.to_stringt.numelseto_string_decimal_truncate~max_decimal_digits:decimals(round_decimal_to_nearest_half_to_even~digits:decimalst)inifOption.is_nonedelimiter&&strip_zerothenselse(letleft,right=matchString.rsplit2s~on:'.'with|None->s,""|Some(left,right)->left,rightinletleft=matchdelimiterwith|None->left|Somedelimiter->Int_conversions.insert_delimiterleft~delimiterinletright=ifstrip_zerothenrightelseright^String.make(Int.max0(Int.(-)decimals(String.lengthright)))'0'inifstrip_zero&&String.is_emptyrightthenleftelseleft^"."^right));;letpp_humppft=Format.fprintfppf"%s"(to_string_humt)letpp_accurateppft=Format.fprintfppf"%s"(to_string_accuratet)include(Hashable.Make_binable(Unstable):Hashable.S_binablewithtypet:=t)letof_float_decimalf=of_string(Float.to_stringf)letarg_type=Command.Arg_type.createof_stringmoduleO=structlet(+)=(+)let(-)=(-)let(/)=(/)let(//)=(//)let(*)=(*)let(**)=(**)include(Replace_polymorphic_compare:Core.Comparisons.Infixwithtypet:=t)letabs=absletneg=negletzero=zeroletone=oneletten=tenlethundred=hundredletthousand=thousandletmillion=millionletbillion=billionlettrillion=trillionlettenth=tenthlethundredth=hundredthletthousandth=thousandthletmillionth=millionthletbillionth=billionthlettrillionth=trillionthletof_int=of_intletof_float_dyadic=of_float_dyadicletof_float_decimal=of_float_decimalletof_float=of_float_dyadicendmoduleFor_quickcheck=structmoduleGenerator=Quickcheck.GeneratoropenGenerator.Let_syntaxletsplit_weighted_in_favor_of_right_sidesize=let%mapfirst_half=Int.gen_log_uniform_incl0sizeinletother_half=Int.(-)sizefirst_halfinfirst_half,other_half;;letbigint_power_of_tenexpt=Bigint.pow(Bigint.of_int10)(Bigint.of_intexpt)letexponential~size=let%mapexponent=Int.gen_uniform_incl0(Int.(*)size3)inof_bigint(bigint_power_of_tenexponent);;letbigint_gcdxy=Bigint.of_zarith_bigint(Z.gcd(Bigint.to_zarith_bigintx)(Bigint.to_zarith_biginty));;letbigint_lcmxy=Bigint.of_zarith_bigint(Z.lcm(Bigint.to_zarith_bigintx)(Bigint.to_zarith_biginty));;letpositive_abs_num_as_bigintx=num_as_bigintx|>Bigint.abs|>Bigint.maxBigint.one;;letfractional_partt=t-roundt~dir:`Zeroletgen_uniform_excllower_boundupper_bound=iflower_bound>=upper_boundthenraise_s[%message"Bignum.gen_uniform_excl: bounds are crossed"(lower_bound:t)(upper_bound:t)];(* figure out the fractional units implied by the bounds *)letgcd=letlo=fractional_partlower_boundinlethi=fractional_partupper_boundinletnum=bigint_gcd(positive_abs_num_as_bigintlo)(positive_abs_num_as_biginthi)inletden=bigint_lcm(den_as_bigintlo)(den_as_biginthi)inof_bigintnum/of_bigintdeninlet%bindsize=Generator.sizein(* Pick a precision beyond just [gcd], based on [size]. We want to add some digits of
precision, and also a potentially non-decimal factor. *)let%binddecimal_size,fractional_size=split_weighted_in_favor_of_right_sidesizeinlet%binddecimal_divisor=exponential~size:decimal_sizeinletfractional_divisor=of_int(Int.succfractional_size)in(* We have to divide the range into at least 2 parts (otherwise the only candidate
numbers are the bounds themselves). [fractional_divisor] and [decimal_divisor] can
both be 1, so we multiply by an arbitrary small number to guarantee that [divisor >
1]. *)letdivisor=fractional_divisor*decimal_divisor*tenin(* choose values in units of the chosen precision. *)letincrement=gcd/divisorinletcount=num_as_bigint((upper_bound-lower_bound)/increment)inlet%mapindex=Bigint.gen_uniform_inclBigint.one(Bigint.predcount)inlower_bound+(of_bigintindex*increment);;letgen_incllower_boundupper_bound=Generator.weighted_union[0.05,returnlower_bound;0.05,returnupper_bound;0.9,gen_uniform_excllower_boundupper_bound];;letgen_finite=let%bindsize=Generator.sizeinlet%bindorder_of_magnitude,precision=split_weighted_in_favor_of_right_sidesizeinlet%bindmagnitude=exponential~size:order_of_magnitudeinlet%bindhi=if%mapBool.quickcheck_generatorthenmagnitudeelseone/magnitudeinletlo=neghiinGenerator.with_size~size:precision(gen_incllohi);;letquickcheck_generator=Generator.weighted_union[0.05,returninfinity;0.05,returnneg_infinity;0.05,returnnan;0.85,gen_finite];;letquickcheck_observer=Quickcheck.Observer.create(funt~size:_~hash->hash_fold_thasht);;letquickcheck_shrinker=Quickcheck.Shrinker.empty()endletquickcheck_observer=For_quickcheck.quickcheck_observerletquickcheck_generator=For_quickcheck.quickcheck_generatorletgen_finite=For_quickcheck.gen_finiteletgen_incl=For_quickcheck.gen_inclletgen_uniform_excl=For_quickcheck.gen_uniform_exclletquickcheck_shrinker=For_quickcheck.quickcheck_shrinkermodule_:sigend=structincludePretty_printer.Register(structincludeUnstableletmodule_name="Bignum"letto_stringt=Sexp.to_string(sexp_of_tt)end)endletof_float=of_float_dyadicletto_stringt=ifZ.equalt.denZ.zerothento_string_when_den_is_zero~num:t.numelseto_string_decimal_truncate~max_decimal_digits:9t;;letppppft=Format.fprintfppf"%s"(to_stringt)moduleFor_testing=structletof_string_internal=of_string_internalletof_float_dyadic=of_float_dyadicletto_string_decimal_truncate=to_string_decimal_truncateletof_int64=of_int64letof_zarith_bignumt=tletto_zarith_bignumt=tend(* bin_io functions at toplevel are deprecated but we need to export them anyway *)include(Unstable:Binable.Swithtypet:=t)