123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809open!ImportopenStd_internalopen!Int.Replace_polymorphic_comparemoduleStable=structmoduleV1=structmoduleParts=structtypet={sign:Sign.t;hr:int;min:int;sec:int;ms:int;us:int;ns:int}[@@derivingcompare,sexp,sexp_grammar]endmoduletypeLike_a_float=sigtypet[@@derivingbin_io,hash,quickcheck,typerep]includeComparable.S_commonwithtypet:=tincludeComparable.With_zerowithtypet:=tincludeFloatablewithtypet:=tval(+):t->t->tval(-):t->t->tvalzero:tvalrobust_comparison_tolerance:tvalabs:t->tvalneg:t->tvalscale:t->float->tendmoduleT:sigtypeunderlying=float[@@derivinghash]typet=privateunderlying[@@derivingbin_io,hash]includeLike_a_floatwithtypet:=tincludeRobustly_comparablewithtypet:=tmoduleConstant:sigvalnanoseconds_per_second:floatvalmicroseconds_per_second:floatvalmilliseconds_per_second:floatvalnanosecond:tvalmicrosecond:tvalmillisecond:tvalsecond:tvalminute:tvalhour:tvalday:tendvalto_parts:t->Parts.tvalnext:t->tvalprev:t->tend=structtypeunderlying=float[@@derivinghash]typet=underlying[@@derivinghash]letnextt=Float.one_ulp`Uptletprevt=Float.one_ulp`Downt(* IF THIS REPRESENTATION EVER CHANGES, ENSURE THAT EITHER
(1) all values serialize the same way in both representations, or
(2) you add a new Time.Span version to stable.ml *)include(structincludeFloatletsign=sign_exnend:Like_a_floatwithtypet:=t)(* due to precision limitations in float we can't expect better than microsecond
precision *)includeFloat.Robust_compare.Make(structletrobust_comparison_tolerance=1E-6end)(* this prevents any worry about having these very common names redefined below and
makes their usage within this module safer. Constant is included at the very
bottom to re-export these constants in a more convenient way *)moduleConstant=structletnanoseconds_per_second=1E9letmicroseconds_per_second=1E6letmilliseconds_per_second=1E3(* spans are stored as a float in seconds *)letnanosecond=of_float(1./.nanoseconds_per_second)letmicrosecond=of_float(1./.microseconds_per_second)letmillisecond=of_float(1./.milliseconds_per_second)letsecond=of_float1.letminute=of_float60.lethour=of_float(60.*.60.)letday=of_float(24.*.60.*.60.)endletto_partst:Parts.t=letsign=Float.sign_exntinlett=abstinletintegral=Float.round_downtinletfractional=t-.integralinletseconds=Float.iround_down_exnintegralinletnanoseconds=Float.iround_nearest_exn(fractional*.1E9)inletseconds,nanoseconds=ifInt.equalnanoseconds1_000_000_000thenInt.succseconds,0elseseconds,nanosecondsinletsec=secondsmod60inletminutes=seconds/60inletmin=minutesmod60inlethr=minutes/60inletns=nanosecondsmod1000inletmicroseconds=nanoseconds/1000inletus=microsecondsmod1000inletmilliseconds=microseconds/1000inletms=millisecondsin{sign;hr;min;sec;ms;us;ns};;endlet(/)tf=T.of_float((t:T.t:>float)/.f)let(//)(f:T.t)(t:T.t)=(f:>float)/.(t:>float)(* Multiplying by 1E3 is more accurate than division by 1E-3 *)letto_ns(x:T.t)=(x:>float)*.T.Constant.nanoseconds_per_secondletto_us(x:T.t)=(x:>float)*.T.Constant.microseconds_per_secondletto_ms(x:T.t)=(x:>float)*.T.Constant.milliseconds_per_secondletto_sec(x:T.t)=(x:>float)letto_minx=x//T.Constant.minuteletto_hrx=x//T.Constant.hourletto_dayx=x//T.Constant.dayletto_int63_seconds_round_down_exnx=Float.int63_round_down_exn(to_secx)let(**)f(t:T.t)=T.of_float(f*.(t:>float))(* Division by 1E3 is more accurate than multiplying by 1E-3 *)letof_nsx=T.of_float(x/.T.Constant.nanoseconds_per_second)letof_usx=T.of_float(x/.T.Constant.microseconds_per_second)letof_msx=T.of_float(x/.T.Constant.milliseconds_per_second)letof_secx=T.of_floatxletof_int_secx=of_sec(Float.of_intx)letof_int32_secondssec=of_sec(Int32.to_floatsec)(* Note that [Int63.to_float] can lose precision, but only on inputs large enough that
[of_sec] in either the Time_ns or Time_float case would lose precision (or just be
plain out of bounds) anyway. *)letof_int63_secondssec=of_sec(Int63.to_floatsec)letof_minx=x**T.Constant.minuteletof_hrx=x**T.Constant.hourletof_dayx=x**T.Constant.dayletdivide_by_unit_of_timetunit_of_time=match(unit_of_time:Unit_of_time.t)with|Nanosecond->to_nst|Microsecond->to_ust|Millisecond->to_mst|Second->to_sect|Minute->to_mint|Hour->to_hrt|Day->to_dayt;;letscale_by_unit_of_timefloatunit_of_time=match(unit_of_time:Unit_of_time.t)with|Nanosecond->of_nsfloat|Microsecond->of_usfloat|Millisecond->of_msfloat|Second->of_secfloat|Minute->of_minfloat|Hour->of_hrfloat|Day->of_dayfloat;;letcreate?(sign=Sign.Pos)?(day=0)?(hr=0)?(min=0)?(sec=0)?(ms=0)?(us=0)?(ns=0)()=let(+)=T.(+)inlett=of_day(Float.of_intday)+of_hr(Float.of_inthr)+of_min(Float.of_intmin)+of_sec(Float.of_intsec)+of_ms(Float.of_intms)+of_us(Float.of_intus)+of_ns(Float.of_intns)inmatchsignwith|Neg->T.(-)T.zerot|Pos|Zero->t;;includeTincludeConstantletrandomizet~percent=Span_helpers.randomizet~percent~scaleletto_short_stringt=let({sign;hr;min;sec;ms;us;ns}:Parts.t)=to_partstinSpan_helpers.short_string~sign~hr~min~sec~ms~us~ns;;(* WARNING: if you are going to change this function in any material way, make sure
you update Stable appropriately. *)letof_string_v1_v2(s:string)~is_v2=trymatchswith|""->failwith"empty string"|_->letfloatn=matchString.drop_suffixsnwith|""->failwith"no number given"|s->letv=Float.of_stringsinValidate.maybe_raise(Float.validate_ordinaryv);vinletlen=String.lengthsin(matchs.[Int.(-)len1]with|'s'->ifInt.(>=)len2&&Char.(=)s.[Int.(-)len2]'m'thenof_ms(float2)elseifis_v2&&Int.(>=)len2&&Char.(=)s.[Int.(-)len2]'u'thenof_us(float2)elseifis_v2&&Int.(>=)len2&&Char.(=)s.[Int.(-)len2]'n'thenof_ns(float2)elseT.of_float(float1)|'m'->of_min(float1)|'h'->of_hr(float1)|'d'->of_day(float1)|_->ifis_v2thenfailwith"Time spans must end in ns, us, ms, s, m, h, or d."elsefailwith"Time spans must end in ms, s, m, h, or d.")with|exn->invalid_argf"Span.of_string could not parse '%s': %s"s(Exn.to_stringexn)();;letof_sexp_error_exnexnsexp=of_sexp_error(Exn.to_stringexn)sexpexceptionT_of_sexpofSexp.t*exn[@@derivingsexp]exceptionT_of_sexp_expected_atom_but_gotofSexp.t[@@derivingsexp]lett_of_sexp_v1_v2sexp~is_v2=matchsexpwith|Sexp.Atomx->(tryof_string_v1_v2x~is_v2with|exn->of_sexp_error_exn(T_of_sexp(sexp,exn))sexp)|Sexp.List_->of_sexp_error_exn(T_of_sexp_expected_atom_but_gotsexp)sexp;;letstring~is_v2suffixfloat=ifis_v2(* This is the same float-to-string conversion used in [Float.sexp_of_t]. It's like
[Float.to_string], but may leave off trailing period. *)then!Sexplib.Conv.default_string_of_floatfloat^suffixelsesprintf"%g%s"floatsuffix;;(* WARNING: if you are going to change this function in any material way, make sure
you update Stable appropriately. *)(* I'd like it to be the case that you could never construct an infinite span, but I
can't think of a good way to enforce it. So this to_string function can produce
strings that will raise an exception when they are fed to of_string *)letto_string_v1_v2(t:T.t)~is_v2=(* this is a sad broken abstraction... *)letmoduleC=Float.ClassinmatchFloat.classify(t:>float)with|C.Subnormal|C.Zero->"0s"|C.Infinite->ifT.(>)tT.zerothen"inf"else"-inf"|C.Nan->"nan"|C.Normal->let(<)=T.(<)inletabs_t=T.of_float(Float.abs(t:>float))inifis_v2&&abs_t<T.Constant.microsecondthenstring~is_v2"ns"(to_nst)elseifis_v2&&abs_t<T.Constant.millisecondthenstring~is_v2"us"(to_ust)elseifabs_t<T.Constant.secondthenstring~is_v2"ms"(to_mst)elseifabs_t<T.Constant.minutethenstring~is_v2"s"(to_sect)elseifabs_t<T.Constant.hourthenstring~is_v2"m"(to_mint)elseifabs_t<T.Constant.daythenstring~is_v2"h"(to_hrt)elsestring~is_v2"d"(to_dayt);;letsexp_of_t_v1_v2t~is_v2=Sexp.Atom(to_string_v1_v2t~is_v2)lett_of_sexpsexp=t_of_sexp_v1_v2sexp~is_v2:falseletsexp_of_tt=sexp_of_t_v1_v2t~is_v2:falselett_sexp_grammar=Sexplib.Sexp_grammar.coerceString.t_sexp_grammarendmoduleV2=structincludeV1lett_of_sexpsexp=t_of_sexp_v1_v2sexp~is_v2:trueletsexp_of_tt=sexp_of_t_v1_v2t~is_v2:trueendmoduleV3=structincludeV1letto_unit_of_timet:Unit_of_time.t=letopenTinletopenConstantinletabs_t=T.abstinifabs_t>=daythenDayelseifabs_t>=hourthenHourelseifabs_t>=minutethenMinuteelseifabs_t>=secondthenSecondelseifabs_t>=millisecondthenMillisecondelseifabs_t>=microsecondthenMicrosecondelseNanosecond;;letof_unit_of_time:Unit_of_time.t->T.t=letopenT.Constantinfunction|Nanosecond->nanosecond|Microsecond->microsecond|Millisecond->millisecond|Second->second|Minute->minute|Hour->hour|Day->day;;letsuffix_of_unit_of_timeunit_of_time=match(unit_of_time:Unit_of_time.t)with|Nanosecond->"ns"|Microsecond->"us"|Millisecond->"ms"|Second->"s"|Minute->"m"|Hour->"h"|Day->"d";;moduleOf_string=structletinvalid_stringstring~reason=letmessage="Time.Span.of_string: "^reasoninraise_s[%messagemessagestring];;letrecfind_unit_of_time_by_suffixstring~indexunit_of_time_list=matchunit_of_time_listwith|[]->invalid_stringstring~reason:"invalid span part suffix"|unit_of_time::rest->letsuffix=suffix_of_unit_of_timeunit_of_timeinifString.is_substring_atstring~pos:index~substring:suffixthenunit_of_timeelsefind_unit_of_time_by_suffixstring~indexrest;;letparse_suffixstring~index=(* We rely on the fact that "ms" comes before "m" in [Unit_of_time.all] to get a
correct match on millisecond timestamps. This assumption is demonstrated in the
expect test below. *)find_unit_of_time_by_suffixstring~indexUnit_of_time.all;;(* We validate magnitude strings so that we know where the unit-of-time suffix
begins, and so that only sensible strings are allowed. We do not want to be as
permissive as [Float.of_string]; for example, hexadecimal span magnitudes are not
allowed. After validation, we still use [Float.of_string] to produce the actual
value. *)moduleFloat_parser=struct(* [In_decimal_have_digit] includes having a digit before the decimal point. *)typestate=|In_integer_need_digit|In_integer_have_digit|In_decimal_need_digit|In_decimal_have_digit|In_exponent_need_digit_or_sign|In_exponent_need_digit|In_exponent_have_digittypetoken=|Digit|Point|Under|Sign|Exptletstate_is_final=function|In_integer_have_digit|In_decimal_have_digit|In_exponent_have_digit->true|In_integer_need_digit|In_decimal_need_digit|In_exponent_need_digit_or_sign|In_exponent_need_digit->false;;lettoken_of_char=function|'0'..'9'->SomeDigit|'.'->SomePoint|'_'->SomeUnder|'-'|'+'->SomeSign|'E'|'e'->SomeExpt|_->None;;letinvalid_stringstring=invalid_stringstring~reason:"invalid span part magnitude";;letrecfind_index_after_float_in_statestring~index~len~state=letopenInt.Oinifindex=lenthenifstate_is_finalstatethenindexelseinvalid_stringstringelse(matchtoken_of_charstring.[index]with|None->ifstate_is_finalstatethenindexelseinvalid_stringstring|Sometoken->letstate=matchstate,tokenwith|In_integer_need_digit,Digit->In_integer_have_digit|In_integer_need_digit,Point->In_decimal_need_digit|In_integer_need_digit,Under|In_integer_need_digit,Sign|In_integer_need_digit,Expt->invalid_stringstring|In_integer_have_digit,Digit|In_integer_have_digit,Under->In_integer_have_digit|In_integer_have_digit,Point->In_decimal_have_digit|In_integer_have_digit,Expt->In_exponent_need_digit_or_sign|In_integer_have_digit,Sign->invalid_stringstring|In_decimal_need_digit,Digit->In_decimal_have_digit|In_decimal_need_digit,Point|In_decimal_need_digit,Under|In_decimal_need_digit,Expt|In_decimal_need_digit,Sign->invalid_stringstring|In_decimal_have_digit,Digit|In_decimal_have_digit,Under->In_decimal_have_digit|In_decimal_have_digit,Expt->In_exponent_need_digit_or_sign|In_decimal_have_digit,Point|In_decimal_have_digit,Sign->invalid_stringstring|In_exponent_need_digit_or_sign,Digit->In_exponent_have_digit|In_exponent_need_digit_or_sign,Sign->In_exponent_need_digit|In_exponent_need_digit_or_sign,Point|In_exponent_need_digit_or_sign,Under|In_exponent_need_digit_or_sign,Expt->invalid_stringstring|In_exponent_need_digit,Digit->In_exponent_have_digit|In_exponent_need_digit,Point|In_exponent_need_digit,Under|In_exponent_need_digit,Expt|In_exponent_need_digit,Sign->invalid_stringstring|In_exponent_have_digit,Digit|In_exponent_have_digit,Under->In_exponent_have_digit|In_exponent_have_digit,Point|In_exponent_have_digit,Expt|In_exponent_have_digit,Sign->invalid_stringstringinfind_index_after_float_in_statestring~index:(index+1)~len~state);;letfind_index_after_floatstring~index~len=find_index_after_float_in_statestring~index~len~state:In_integer_need_digit;;endletrecaccumulate_magnitudestring~magnitude~index~len=ifInt.equalindexlenthenmagnitudeelse(letsuffix_index=Float_parser.find_index_after_floatstring~index~leninletunit_of_time=parse_suffixstring~index:suffix_indexinletuntil_index=Int.(+)suffix_index(String.length(suffix_of_unit_of_timeunit_of_time))inletfloat_string=String.substring~pos:index~len:(Int.(-)suffix_indexindex)inletfloat=Float.of_stringfloat_stringinletmagnitude=magnitude+scale_by_unit_of_timefloatunit_of_timeinaccumulate_magnitudestring~magnitude~index:until_index~len);;letparse_magnitudestring~index~len=accumulate_magnitudestring~magnitude:T.zero~index~len;;letof_stringstring=letopenInt.Oinmatchstringwith|"NANs"->of_secFloat.nan|"-INFs"->of_secFloat.neg_infinity|"INFs"->of_secFloat.infinity|_->letlen=String.lengthstringiniflen=0theninvalid_stringstring~reason:"empty input";letnegative,index=matchstring.[0]with|'-'->true,1|'+'->false,1|_->false,0inifindex>=lentheninvalid_stringstring~reason:"empty input";letmagnitude=parse_magnitudestring~index~leninifnegativethenT.negmagnitudeelsemagnitude;;endletof_string=Of_string.of_stringmoduleTo_string=structletstring_of_float_without_trailing_decimalfloat=letstring=Float.to_stringfloatinletsuffix="."inifString.is_suffixstring~suffixthenString.chop_suffix_exnstring~suffixelsestring;;(* As we build up a string, we keep a running sum of the value that will be read
back in, so that we can compute the remainder that needs to be generated. *)letsum~sum_t~unit_of_time~magnitude=sum_t+scale_by_unit_of_timemagnitudeunit_of_time;;(* For some units (very large numbers of days, or seconds and smaller) we just
render a float directly, with a fix for roundoff error. *)letto_float_string~abs_t~unit_of_time~fixup_unit_of_time=letmagnitude=divide_by_unit_of_timeabs_tunit_of_timeinletsum_t=sum~sum_t:zero~unit_of_time~magnitudeinifsum_t=abs_tthenstring_of_float_without_trailing_decimalmagnitude^suffix_of_unit_of_timeunit_of_timeelse(letmagnitude=ifsum_t<abs_tthenmagnitudeelsedivide_by_unit_of_time(prevabs_t)unit_of_timeinletsum_t=sum~sum_t:zero~unit_of_time~magnitudeinletrem_t=abs_t-sum_tinletfixup_magnitude=divide_by_unit_of_timerem_tfixup_unit_of_timeinstring_of_float_without_trailing_decimalmagnitude^suffix_of_unit_of_timeunit_of_time(* [rem_t] is at ULP size of [abs_t], it needs just one bit of precision *)^sprintf"%.1g"fixup_magnitude^suffix_of_unit_of_timefixup_unit_of_time);;(* For non-decimal units (minutes and greater), we render an integer magnitude, and
return that with the running sum so the remainder can be rendered at a smaller
unit. *)letto_int_string_and_sumunit_of_time~abs_t~sum_t=letunit_span=of_unit_of_timeunit_of_timeinletrem_t=abs_t-sum_tin(* We calculate the approximate multiple of [unit_of_time] that needs to be
added to [sum_t]. Due to rounding, this can be off by one (we've never seen a
case off by two or more), so we re-compute the remainder and correct if
necessary. *)letmagnitude=Float.round_down(rem_t//unit_span)inletnew_sum_t=sum~sum_t~unit_of_time~magnitudeinletnew_rem_t=abs_t-new_sum_tinletmagnitude=ifnew_rem_t=zerothenmagnitudeelseifnew_rem_t<zerothenmagnitude-.1.else(letnext_magnitude=magnitude+.1.inletnext_sum_t=sum~sum_t~unit_of_time~magnitude:next_magnitudeinletnext_rem_t=abs_t-next_sum_tinifnext_rem_t<zerothenmagnitudeelsenext_magnitude)inifFloat.(<=)magnitude0.then"",sum_telse(letnew_sum_t=sum~sum_t~unit_of_time~magnitudeinletstring=Int63.to_string(Int63.of_floatmagnitude)^suffix_of_unit_of_timeunit_of_timeinstring,new_sum_t);;letdecimal_order_of_magnitudet=Float.log10(to_sect)(* The final seconds-or-smaller unit needs to be printed with enough digits to
round-trip the whole span (which is minutes or greater); this can be
significantly fewer digits than would be needed for the seconds-or-smaller
remainder itself. *)letto_float_string_after_int_strings~sum_t~abs_t=ifsum_t>=abs_tthen""else(letrem_t=abs_t-sum_tinletunit_of_time=to_unit_of_timerem_tinletunit_span=of_unit_of_timeunit_of_timeinletmagnitude=rem_t//unit_spaninletnew_sum_t=sum~sum_t~unit_of_time~magnitudeinletnew_rem_t=abs_t-new_sum_tinifabsrem_t<=absnew_rem_tthen""else(letorder_of_magnitude_of_first_digit=Float.iround_down_exn(decimal_order_of_magnituderem_t)inlethalf_ulp=(abs_t-prevabs_t)/2.inletorder_of_magnitude_of_final_digit=(* This works out to rounding down, except in the case of exact integers,
which are decremented. This makes sure we always stop at a digit with
strictly more precision than half the ULP. *)Int.pred(Float.iround_up_exn(decimal_order_of_magnitudehalf_ulp))inletnumber_of_digits=letopenInt.Oin1+order_of_magnitude_of_first_digit-order_of_magnitude_of_final_digitinletsuffix=suffix_of_unit_of_timeunit_of_timeinsprintf"%.*g"number_of_digitsmagnitude^suffix));;(* This helper avoids unnecessary allocation, because for our use below, it is
common to have either or both arguments be empty. Currently (2018-02), the
built-in [^] allocates even when appending to an empty string. *)let(^?)xy=ifString.is_emptyxthenyelseifString.is_emptyythenxelsex^y;;letto_stringt=letfloat=to_floattinifnot(Float.is_finitefloat)thenif(* We print specific special strings for non-finite floats *)Float.is_nanfloatthen"NANs"elseifFloat.is_negativefloatthen"-INFs"else"INFs"elseift=zerothen"0s"else(letunit_of_time=to_unit_of_timetinletabs_t=abstinletsign=ift<zerothen"-"else""inletmagnitude_string=matchunit_of_timewith(* We can use normal float notation for seconds and sub-second units, they are
readable with a decimal point. *)|Nanosecond|Microsecond|Millisecond|Second->to_float_string~abs_t~unit_of_time~fixup_unit_of_time:Nanosecond(* For large enough values that the ULP is a day or more, we can use float
notation because we are expressing a single, very large integer. *)|Daywhennextabs_t-abs_t>=day->to_float_string~abs_t~unit_of_time~fixup_unit_of_time:Day(* For everything in between, we need to use integer units of days, hours,
and/or minutes, because those units are not readable as decimals, and we
tack on a decimal remainder of a seconds-or-smaller unit if necessary. *)|Minute|Hour|Day->letsum_t=zeroinletday_string,sum_t=to_int_string_and_sum~abs_t~sum_tDayinlethour_string,sum_t=to_int_string_and_sum~abs_t~sum_tHourinletminute_string,sum_t=to_int_string_and_sum~abs_t~sum_tMinuteinletfloat_string=to_float_string_after_int_strings~abs_t~sum_tinday_string^?hour_string^?minute_string^?float_stringinsign^?magnitude_string);;endletto_string=To_string.to_stringletsexp_of_tt=Sexp.Atom(to_stringt)lett_of_sexps=matchswith|Sexp.Atomx->(tryof_stringxwith|exn->of_sexp_error(Exn.to_stringexn)s)|Sexp.List_->of_sexp_error"Time.Span.Stable.V3.t_of_sexp: sexp must be an Atom"s;;lett_sexp_grammar=Sexplib.Sexp_grammar.coerceString.t_sexp_grammarendendincludeStable.V3letto_proportional_float=to_floatletto_string_hum?(delimiter='_')?(decimals=3)?(align_decimal=false)?unit_of_timet=letfloat,suffix=matchOption.valueunit_of_time~default:(to_unit_of_timet)with|Day->to_dayt,"d"|Hour->to_hrt,"h"|Minute->to_mint,"m"|Second->to_sect,"s"|Millisecond->to_mst,"ms"|Microsecond->to_ust,"us"|Nanosecond->to_nst,"ns"inletprefix=Float.to_string_humfloat~delimiter~decimals~strip_zero:(notalign_decimal)inletsuffix=ifalign_decimal&&Int.(=)(String.lengthsuffix)1thensuffix^" "elsesuffixinprefix^suffix;;letgen_incllohi=Float.gen_incl(to_seclo)(to_sechi)|>Quickcheck.Generator.map~f:of_sec;;letgen_uniform_incllohi=(* Technically exclusive rather than inclusive, but otherwise satisfies the contract to
within 1ulp of the given bounds. *)Float.gen_uniform_excl(to_seclo)(to_sechi)|>Quickcheck.Generator.map~f:of_sec;;letquickcheck_generator=(* We generate spans up to (slightly more than) a millenium, positive or negative. This
is based on the Gregorian calendar, in which years average 365.2425 days when
accounting for leap days. Covering a two-millenium span is more than enough for most
practical purposes, certainly more than enough to cover the representable range of
[Span_ns], and results in finite spans and times that can be serialized.
We generate by filtering the default generator so that spans are still skewed toward
small values, even though the bounds are large. *)letmillenium=of_day(Float.round_up(365.2425*.1000.))inQuickcheck.Generator.filterquickcheck_generator~f:(funt->negmillenium<=t&&t<=millenium);;includePretty_printer.Register(structtypenonrect=tletto_string=to_stringletmodule_name="Core.Time.Span"end)includeHashable.Make_binable(structtypenonrect=t[@@derivingbin_io,compare,hash,sexp_of](* Previous versions rendered hash-based containers using float serialization rather
than time serialization, so when reading hash-based containers in we accept either
serialization. *)lett_of_sexpsexp=matchFloat.t_of_sexpsexpwith|float->of_floatfloat|exception_->t_of_sexpsexp;;end)moduleC=structtypet=T.t[@@derivingbin_io]typecomparator_witness=T.comparator_witnessletcomparator=T.comparator(* In 108.06a and earlier, spans in sexps of Maps and Sets were raw floats. From 108.07
through 109.13, the output format remained raw as before, but both the raw and pretty
format were accepted as input. From 109.14 on, the output format was changed from
raw to pretty, while continuing to accept both formats. Once we believe most
programs are beyond 109.14, we will switch the input format to no longer accept
raw. *)letsexp_of_t=sexp_of_tlett_of_sexpsexp=matchOption.try_with(fun()->T.of_float(Float.t_of_sexpsexp))with|Somet->t|None->t_of_sexpsexp;;endmoduleMap=Map.Make_binable_using_comparator(C)moduleSet=Set.Make_binable_using_comparator(C)includeComparable.With_zero(structtypenonrect=t[@@derivingcompare,sexp_of]letzero=zeroend)modulePrivate=structletsuffix_of_unit_of_time=suffix_of_unit_of_timeletparse_suffix=Stable.V3.Of_string.parse_suffixend