123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program 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 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program 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 program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(**
FloatItvNan - Floating-point intervals with special IEEE numbers.
Adds special IEEE number managenement (NaN, infinities) to FloatItv.
*)openBotmoduleF=FloatmoduleFI=FloatItvmoduleB=IntBoundmoduleII=IntItv(** {2 Types} *)typet={itv:FI.twith_bot;(** Interval of non-special values.
Bounds cannot be NaN.
Bounds can be infinities to represent non-infinity floats outside the range of doubles.
*)nan:bool;(** Whether to include NaN. *)pinf:bool;(** Whether to include +∞. *)minf:bool;(** Whether to include -∞. *)}(** A set of IEEE floating-point values.
Represented as a set of non-special values, and boolean flags to
indicate the presence of each special IEEE value.
The value 0 in the interval represents both IEEE +0 and -0.
Note: the type can naturally represent the empty set.
*)letis_valid(a:t):bool=matcha.itvwith|BOT->true|Nbi->not(F.is_nani.FI.lo||i.FI.lo=infinity)&¬(F.is_nani.FI.up||i.FI.up=neg_infinity)&&i.FI.lo<=i.FI.up(** All elements of type t whould satisfy this predicate. *)typeprec=[`SINGLE(** 32-bit single precision *)|`DOUBLE(** 64-bit double precision *)|`EXTRA(** anything larger than 64-bit double precision floats *)|`REAL(** real arithmetic *)](** Precision.
All bounds are represented as double, whatever the precision.
*)typeround=[`NEAR(** To nearest *)|`UP(** Upwards *)|`DOWN(** Downwards *)|`ZERO(** Towards 0 *)|`ANY(** Any rounding mode *)](** Rounding direction. *)(** {2 Constructors} *)letbot={itv=BOT;nan=false;pinf=false;minf=false;}(** Empty float set. *)letpinf:t={botwithpinf=true;}letminf:t={botwithminf=true;}letnan:t={botwithnan=true;}letinfinities:t={itv=BOT;nan=false;pinf=true;minf=true;}letspecials:t={itv=BOT;nan=true;pinf=true;minf=true;}(** Special values. *)letof_float(lo:float)(up:float):t=iflo>upthenbotelseifF.is_nanlo||lo=infinity||F.is_nanup||up=neg_infinitytheninvalid_arg(Printf.sprintf"FloatItvNan.of_float: invalid bound [%g,%g]"loup)else{botwithitv=Nb{FI.lo;FI.up;};}(** Float set reduced to an interval of non-special values.
lo should not be +oo nor NaN.
up should not be -oo nor NaN.
We can have lo = -oo and up = +oo to represent sets of non-infinity
floats larger than the range of double.
*)letof_interval(a:FI.t):t=of_floata.FI.loa.FI.upletof_interval_bot(a:FI.t_with_bot):t=matchawithBOT->bot|Nbaa->of_intervalaalethull(a:float)(b:float):t=of_float(minab)(maxab)(** Constructs the smallest interval containing a and b. *)letcst(x:float):t=matchclassify_floatxwith|FP_nan->nan|FP_infinite->ifx>0.thenpinfelseminf|_->{botwithitv=Nb{FI.lo=x;FI.up=x;};}(** Singleton (possibly infinity or NaN). *)letzero:t=cst0.letone:t=cst1.lettwo:t=cst2.letmone:t=cst(-1.)letzero_one:t=of_float0.1.letmone_zero:t=of_float(-1.)0.letmone_one:t=of_float(-1.)1.letmhalf_half:t=of_float(-0.5)0.5(** Useful intervals. *)letadd_special(x:t):t={xwithnan=true;pinf=true;minf=true;}(** Adds NaN and infinities to a set. *)letremove_special(x:t):t={xwithnan=false;pinf=false;minf=false;}(** Removes NaN and infinities from a set. *)letsingle:t=of_float(-.F.Single.max_normal)F.Single.max_normal(** Non-special single precision floats. *)letdouble:t=of_float(-.F.Double.max_normal)F.Double.max_normal(** Non-special double precision floats. *)letextra:t=of_floatneg_infinityinfinity(** Non-special extra (> double) precision. *)letreal:t=of_floatneg_infinityinfinity(** Reals. *)letsingle_special:t=add_specialsingle(** Single precision floats with specials. *)letdouble_special:t=add_specialdouble(** Double precision floats with specials. *)letextra_special:t=add_specialextra(** Extra (> double) precision floats with specials. *)(** {2 Set-theoretic} *)letequal(a:t)(b:t):bool=a=b(** Set equality. = also works. *)letincluded(a:t)(b:t):bool=(matcha.itv,b.itvwith|BOT,_->true|_,BOT->false|Nbaa,Nbbb->aa.FI.lo>=bb.FI.lo&&aa.FI.up<=bb.FI.up)&&(b.nan||nota.nan)&&(b.minf||nota.minf)&&(b.pinf||nota.pinf)(** Set inclusion. *)letintersect_finite(a:t)(b:t):bool=(matcha.itv,b.itvwith|BOT,_|_,BOT->false|Nbaa,Nbbb->aa.FI.lo<=bb.FI.up&&aa.FI.up>=bb.FI.lo)(** Whether the finite parts of the sets have a non-empty intersection. *)letintersect(a:t)(b:t):bool=(intersect_finiteab)||(a.nan&&b.nan)||(a.minf&&b.minf)||(a.pinf&&b.pinf)(** Whether the sets have an non-empty intersection. *)letcontains(x:float)(a:t)=matchclassify_floatxwith|FP_nan->a.nan|FP_infinite->(x>0.&&a.pinf)||(x<0.&&a.minf)|_->matcha.itvwith|BOT->false|Nbaa->aa.lo<=x&&aa.up>=x(** Whether the set contains a certain (finite, infinite or NaN) value. *)letcompare(a:t)(b:t):int=Compare.compose[(fun()->FI.compare_bota.itvb.itv);(fun()->Stdlib.comparea.nanb.nan);(fun()->Stdlib.comparea.minfb.minf);(fun()->Stdlib.comparea.pinfb.pinf);](** A total ordering returning -1, 0, or 1. *)letis_bot(a:t):bool=(a.itv=BOT)&&(nota.nan)&&(nota.pinf)&&(nota.minf)(** Whether the argument is the empty set. *)letis_finite(a:t):bool=(nota.nan)&&(nota.pinf)&&(nota.minf)(** Whether the argument contains only finite values (or is empty). *)letis_infinity(a:t):bool=(nota.nan)&&(a.itv=BOT)(** Whether the argument contains only infinities (or is empty). *)letis_special(a:t):bool=a.itv=BOT(** Whether the argument contains only special values (or is empty). *)letis_zero(a:t):bool=a=zero(** Whether the argument is the singleton 0. *)letis_null(a:t):bool=a=zero||a=bot(** Whether the argument contains only 0 (or is empty). *)letis_positive(a:t):bool=(nota.nan)&&(nota.minf)&&(matcha.itvwithBOT->true|Nbx->x.FI.lo>=0.)(** Whether the argument contains only (possibly infinite) positive non-NaN values (or is empty). *)letis_negative(a:t):bool=(nota.nan)&&(nota.pinf)&&(matcha.itvwithBOT->true|Nbx->x.FI.up<=0.)(** Whether the argument contains only (possibly infinite) negative non-NaN values (or is empty). *)letis_positive_strict(a:t):bool=(nota.nan)&&(nota.minf)&&(matcha.itvwithBOT->true|Nbx->x.FI.lo>0.)(** Whether the argument contains only (possibly infinite) strictly positive non-NaN values (or is empty). *)letis_negative_strict(a:t):bool=(nota.nan)&&(nota.pinf)&&(matcha.itvwithBOT->true|Nbx->x.FI.up<0.)(** Whether the argument contains only (possibly infinite) strictly negative non-NaN values (or is empty). *)letis_nonzero(a:t):bool=(matcha.itvwithBOT->true|Nbx->x.FI.lo>0.||x.FI.up<0.)(** Whether the argument contains only (possibly infinite or NaN) non-zero values (or is empty). *)letapprox_size(a:t):int=(ifa.nanthen1else0)+(ifa.pinfthen1else0)+(ifa.minfthen1else0)+(matcha.itvwith|BOT->0|Nbx->ifx.FI.lo=x.FI.upthen1else2)(* approximate size: 0 is empty, 1 is singleton, > 1 is non-singleton. *)letis_singleton(a:t):bool=approx_sizea==1(** Whether the argument contains only a single element. *)letcontains_finite(a:t):bool=a.itv<>BOT(** Whether the argument contains at least one finite value. *)letcontains_infinity(a:t):bool=a.pinf||a.minf(** Whether the argument contains an infinity. *)letcontains_special(a:t):bool=a.nan||a.pinf||a.minf(** Whether the argument contains an infinity or NaN. *)letcontains_zero(a:t):bool=matcha.itvwithNbx->x.FI.lo<=0.&&x.FI.up>=0.|BOT->false(** Whether the argument contains 0. *)letcontains_nonzero(a:t):bool=a.nan||a.pinf||a.minf||(matcha.itvwithBOT->false|Nbx->x.FI.lo<>0.||x.FI.up<>0.)(** Whether the argument contains a (possibly NaN or infinite) non-0 value. *)letcontains_positive(a:t):bool=a.pinf||(matcha.itvwithBOT->false|Nbx->x.FI.up>=0.)(** Whether the argument contains a (possibly infinite) positive value. *)letcontains_negative(a:t):bool=a.minf||(matcha.itvwithBOT->false|Nbx->x.FI.lo<=0.)(** Whether the argument contains a (possibly infinite) negative value. *)letcontains_positive_strict(a:t):bool=a.pinf||(matcha.itvwithBOT->false|Nbx->x.FI.up>0.)(** Whether the argument contains a (possibly infinite) strictly positive value. *)letcontains_negative_strict(a:t):bool=a.minf||(matcha.itvwithBOT->false|Nbx->x.FI.lo<0.)(** Whether the argument contains a (possibly infinite) strictly negative value. *)letcontains_non_nan(a:t):bool=a.minf||a.pinf||a.itv<>BOT(** Whether the argument contains a non-NaN value. *)letis_in_range(a:t)(lo:float)(up:float)=(nota.nan)&&(nota.pinf)&&(nota.minf)&&(matcha.itvwithBOT->true|Nbx->x.FI.lo>=lo||x.FI.up<=up)(** Whether the argument contains only finite values, and they are included in the range [lo,up]. *)letjoin(a:t)(b:t)={itv=bot_neutral2FI.joina.itvb.itv;nan=a.nan||b.nan;minf=a.minf||b.minf;pinf=a.pinf||b.pinf;}letjoin_list:tlist->t=List.fold_leftjoinbotletmeet(a:t)(b:t)={itv=bot_absorb2FI.meeta.itvb.itv;nan=a.nan&&b.nan;minf=a.minf&&b.minf;pinf=a.pinf&&b.pinf;}letwiden(a:t)(b:t)={itv=bot_neutral2FI.widena.itvb.itv;nan=a.nan||b.nan;minf=a.minf||b.minf;pinf=a.pinf||b.pinf;}letpositive(x:t):t={itv=bot_absorb1FI.positivex.itv;nan=false;pinf=x.pinf;minf=false;}(** Positive part of the argument, excluding NaN. *)letnegative(x:t):t={itv=bot_absorb1FI.negativex.itv;nan=false;minf=x.minf;pinf=false;}(** Negative part of the argument, excluding NaN. *)letmeet_zero(a:t):t=meetazero(** Intersects with {0} (excluding infinities and NaN). *)(** {2 Printing} *)typeprint_format=FI.print_formatletdfl_fmt=FI.dfl_fmtletto_string(fmt:print_format)(x:t):string=letappxy=ifx=""thenyelsex^"∨"^yinletr=(matchx.itvwithNbi->FI.to_stringfmti|BOT->"")inletr=ifx.pinfthenappr"+∞"elserinletr=ifx.minfthenappr"-∞"elserinletr=ifx.nanthenappr"NaN"elserinifr=""thenbot_stringelserletprintfmtch(x:t)=output_stringch(to_stringfmtx)letfprintfmtch(x:t)=Format.pp_print_stringch(to_stringfmtx)letbprintfmtch(x:t)=Buffer.add_stringch(to_stringfmtx)(** {2 C predicates} *)letis_log_eq(a:t)(b:t):bool=(intersect_finiteab)||(a.pinf&&b.pinf)||(a.minf&&b.minf)letis_log_leq(a:t)(b:t):bool=(matcha.itv,b.itvwithNbx,Nby->x.FI.lo<=y.FI.up|_->false)||(a.minf&&contains_non_nanb)||(b.pinf&&contains_non_nana)letis_log_lt(a:t)(b:t):bool=(matcha.itv,b.itvwithNbx,Nby->x.FI.lo<y.FI.up|_->false)||(a.minf&&(b.pinf||b.itv<>BOT))||(b.pinf&&(a.minf||a.itv<>BOT))letis_log_geq(a:t)(b:t):bool=is_log_leqbaletis_log_gt(a:t)(b:t):bool=is_log_ltbaletis_log_neq(a:t)(b:t):bool=matchapprox_sizea,approx_sizebwith|0,_|_,0->false|1,1->a.nan||b.nan||not(equalab)|_->true(** C comparison tests.
Returns true if the test may succeed, false if it cannot.
Note that NaN always compares different to all values (including NaN).
*)letis_log_leq_false(a:t)(b:t):bool=is_log_gtab||(a.nan&¬(is_botb))||(b.nan&¬(is_bota))letis_log_lt_false(a:t)(b:t):bool=is_log_geqab||(a.nan&¬(is_botb))||(b.nan&¬(is_bota))letis_log_geq_false(a:t)(b:t):bool=is_log_leq_falsebaletis_log_gt_false(a:t)(b:t):bool=is_log_lt_falsebaletis_log_eq_false=is_log_neqletis_log_neq_false=is_log_eq(** Returns true if the test may fail, false if it cannot.
Due to NaN, which compare always different, <= (resp. >) do not
return the boolean negation of > (resp. <).
However, == is the negation of != even for NaN.
*)(** {2 Forward arithmetic} *)letfix_itv(prec:prec)(x:t):t=matchprecwith|(`SINGLE|`DOUBLE)asprec->(* map infinite and NaN bounds back to finite bounds and set flags *)letm=F.max_normalprecin(matchx.itvwith|BOT->x|Nbi->letlo,minf,nan1=ifF.is_nani.FI.lothen-.m,false,trueelseifi.FI.lo<-.mthen-.m,true,falseelsei.FI.lo,false,falseandup,pinf,nan2=ifF.is_nani.FI.upthenm,false,trueelseifi.FI.up>mthenm,true,falseelsei.FI.up,false,falsein{itv=FI.of_float_botloup;nan=x.nan||nan1||nan2;minf=x.minf||minf;pinf=x.pinf||pinf;})|`EXTRA->(* keep infinite bounds to infinity and set flags *)(matchx.itvwith|BOT->x|Nbi->letlo,minf,nan1=ifF.is_nani.FI.lothenneg_infinity,true,trueelseifi.FI.lo=neg_infinitythenneg_infinity,true,falseelsei.FI.lo,false,falseandup,pinf,nan2=ifF.is_nani.FI.uptheninfinity,true,trueelseifi.FI.up=infinitytheninfinity,true,falseelsei.FI.up,false,falsein{itv=FI.of_float_botloup;nan=x.nan||nan1||nan2;minf=x.minf||minf;pinf=x.pinf||pinf;})|`REAL->(* no flags *){botwithitv=x.itv;}(* Utility to fix interval bounds after an operation.
NaN, infinities and overflowing bounds are reset to maximal
bounds according to the precision, and the nan, minf, and pinf fields
are updated.
*)letneg(x:t):t={itv=bot_lift1FI.negx.itv;nan=x.nan;pinf=x.minf;minf=x.pinf;}(** Negation. *)letabs(x:t):t={itv=bot_lift1FI.absx.itv;nan=x.nan;pinf=x.pinf||x.minf;minf=false;}(** Absolute value. *)letfix_prec(prec:prec):FI.prec=matchprecwith|`SINGLE->`SINGLE|`DOUBLE->`DOUBLE|`REAL|`EXTRA->`REALletadd(prec:prec)(round:round)(x:t)(y:t)=fix_itvprec{itv=bot_lift2(FI.add(fix_precprec)round)x.itvy.itv;nan=x.nan||y.nan||(x.pinf&&y.minf)||(x.minf&&y.pinf);pinf=(x.pinf&&y.pinf)||(x.pinf&&contains_finitey)||(y.pinf&&contains_finitex);minf=(x.minf&&y.minf)||(x.minf&&contains_finitey)||(y.minf&&contains_finitex);}(** Addition. *)letsub(prec:prec)(round:round)(x:t)(y:t)=fix_itvprec{itv=bot_lift2(FI.sub(fix_precprec)round)x.itvy.itv;nan=x.nan||y.nan||(x.pinf&&y.pinf)||(x.minf&&y.minf);pinf=(x.pinf&&y.minf)||(x.pinf&&contains_finitey)||(y.minf&&contains_finitex);minf=(x.minf&&y.pinf)||(x.minf&&contains_finitey)||(y.pinf&&contains_finitex);}(** Subtraction. *)letmul(prec:prec)(round:round)(x:t)(y:t)=(* signs of x and y *)letxm,xz,xp,xi=contains_negative_strictx,contains_zerox,contains_positive_strictx,contains_infinityxandym,yz,yp,yi=contains_negative_stricty,contains_zeroy,contains_positive_stricty,contains_infinityyinfix_itvprec{itv=bot_lift2(FI.mul(fix_precprec)round)x.itvy.itv;nan=x.nan||y.nan||(xz&&yi)||(xi&&yz);pinf=(x.pinf&&yp)||(x.minf&&ym)||(y.pinf&&xp)||(y.minf&&xm);minf=(x.pinf&&ym)||(x.minf&&yp)||(y.pinf&&xm)||(y.minf&&xp);}(** Multiplication. *)letdiv(prec:prec)(round:round)(x:t)(y:t)=(* signs of x and y *)letxm,xz,xp,xi=contains_negative_strictx,contains_zerox,contains_positive_strictx,contains_infinityxandym,yz,yp,yi=contains_negativey,contains_zeroy,contains_positivey,contains_infinityyinletr=fix_itvprec{itv=bot_absorb2(FI.div(fix_precprec)round)x.itvy.itv;nan=x.nan||y.nan||(xi&&yi)||(xz&&yz);pinf=yz||(x.pinf&&yp)||(x.minf&&ym);minf=yz||(x.pinf&&ym)||(x.minf&&yp);}in(* add zero if dividing by infinity *)ifyithenjoinzerorelser(** Division. *)letfmod(prec:prec)(round:round)(x:t)(y:t):t=fix_itvprec{itv=bot_absorb2FI.fmodx.itvy.itv;nan=(contains_specialx)||y.nan||(contains_zeroy);minf=false;pinf=false;}(** Remainder (modulo). *)letsquare(prec:prec)(round:round)(x:t):t=fix_itvprec{itv=bot_lift1(FI.square(fix_precprec)round)x.itv;nan=x.nan;pinf=x.pinf||x.minf;minf=false;}(** Square. *)letsqrt(prec:prec)(round:round)(x:t):t=fix_itvprec{itv=bot_absorb1(FI.sqrt(fix_precprec)round)x.itv;nan=x.nan||(contains_negative_strictx);pinf=x.pinf;minf=false;}(** Square root. *)letround_int(prec:prec)(round:round)(x:t):t=fix_itvprec{xwithitv=bot_lift1(FI.round_int(fix_precprec)round)x.itv;}(** Round to integer. *)letround(prec:prec)(round:round)(x:t):t=fix_itvprec{xwithitv=bot_lift1(FI.round(fix_precprec)round)x.itv;}(** Round to float. *)letof_int(prec:prec)(round:round)(x:int)(y:int):t=fix_itvprec{botwithitv=Nb(FI.of_int(fix_precprec)roundxy);}(** Conversion from integer range. *)letof_int64(prec:prec)(round:round)(x:int64)(y:int64):t=fix_itvprec{botwithitv=Nb(FI.of_int64(fix_precprec)roundxy);}(** Conversion from int64 range. *)letof_z(prec:prec)(round:round)(x:Z.t)(y:Z.t):t=fix_itvprec{botwithitv=Nb(FI.of_z(fix_precprec)roundxy);}(** Conversion from integer range. *)letto_z(x:t):(Z.t*Z.t)with_bot=bot_lift1FI.to_zx.itv(** Conversion to integer range with truncation. NaN and infinities are discarded. *)letof_float_prec(prec:prec)(round:round)(lo:float)(up:float):t=letr=FI.of_float_botloupinfix_itvprec{botwithitv=bot_lift1(FI.round(fix_precprec)round)r;}(** From bounds, with rounding, precision and handling of specials. *)letof_int_itv(prec:prec)(round:round)((lo,up):II.t):t=letprec,round=matchprecwith|`SINGLE->`SINGLE,round|`DOUBLE->`DOUBLE,round|`REAL|`EXTRA->`DOUBLE,`ANYinletlo=matchlo,roundwith|B.Finitel,`NEAR->F.of_zprec`NEARl|B.Finitel,(`DOWN|`ANY)->F.of_zprec`DOWNl|B.Finitel,`UP->F.of_zprec`UPl|B.Finitel,`ZERO->F.of_zprec`ZEROl|_->neg_infinityandup=matchup,roundwith|B.Finitel,`NEAR->F.of_zprec`NEARl|B.Finitel,`DOWN->F.of_zprec`DOWNl|B.Finitel,(`ANY|`UP)->F.of_zprec`UPl|B.Finitel,`ZERO->F.of_zprec`ZEROl|_->infinityiniflo>upthenbotelsefix_itvprec{botwithitv=Nb{lo;up;};}(** Conversion from integer intervals (handling overflows to infinities). *)letof_int_itv_bot(prec:prec)(round:round)(i:II.twith_bot):t=matchiwith|BOT->bot|Nb(lo,up)->of_int_itvprecround(lo,up)(** Conversion from integer intervals (handling overflows to infinities). *)letto_int_itv(r:t):II.twith_bot=matchr.itvwith|BOT->ifis_botrthenBOTelseNbII.minf_inf|Nbi->II.of_bound_bot(ifr.minf||Float.is_infinitei.lothenB.MINFelseB.Finite(Z.of_floati.lo))(ifr.pinf||Float.is_infinitei.upthenB.PINFelseB.Finite(Z.of_floati.up))(** Conversion to integer interval with truncation. Handles infinities. *)(** {2 Filters} *)letfilter_eq(prec:prec)(x:t)(y:t):t*t=letr=meetxyinletr={rwithnan=false;}inr,rletlift_filter_itvfxy=matchbot_absorb2fx.itvy.itvwith|BOT->BOT,BOT|Nb(xx,yy)->Nbxx,Nbyyletfilter_leq(prec:prec)(x:t)(y:t):t*t=(* compare finite with finite *)letix,iy=lift_filter_itv(FI.filter_leq(fix_precprec))xyin(* compare finite with infinity *)letix=ify.pinfthenx.itvelseixandiy=ifx.minftheny.itvelseiyin{itv=ix;nan=false;minf=x.minf;pinf=x.pinf&&y.pinf;},{itv=iy;nan=false;pinf=y.pinf;minf=x.minf&&y.minf;}letfilter_lt(prec:prec)(x:t)(y:t):t*t=(* compare finite with finite *)letix,iy=lift_filter_itv(FI.filter_lt(fix_precprec))xyin(* compare finite with infinity *)letix=ify.pinfthenx.itvelseixandiy=ifx.minftheny.itvelseiyin{itv=ix;nan=false;minf=x.minf;pinf=false;},{itv=iy;nan=false;pinf=y.pinf;minf=false;}(** C comparison filters.
Keep the parts of the arguments that can satisfy the condition.
NaN is assumed to be different from any value (including NaN).
*)letfilter_geq(prec:prec)(x:t)(y:t):t*t=letyy,xx=filter_leqprecyxinxx,yyletfilter_gt(prec:prec)(x:t)(y:t):t*t=letyy,xx=filter_ltprecyxinxx,yyletrecfilter_neq(prec:prec)(x:t)(y:t):t*t=ifx.nan||y.nanthenx,y(* NaN -> no refinement *)elseifis_singletonxthen(* case: remove infinity *)ifx.pinfthenx,{ywithpinf=false;}elseifx.minfthenx,{ywithminf=false;}else(* case: remove finite value *)letix,iy=matchbot_absorb2(FI.filter_neq(fix_precprec))x.itvy.itvwith|BOT->BOT,BOT|Nb(xx,yy)->Nbxx,Nbyyin{xwithitv=ix;},{ywithitv=iy;}elseifis_singletonythenletyy,xx=filter_neqprecyxinxx,yy(* symmetric case *)elsex,y(* no singleton -> no refinement *)letfilter_nonzero(prec:prec)(x:t):t=matchbot_absorb2(FI.filter_neq(fix_precprec))x.itv(NbFI.zero)with|BOT->{xwithitv=BOT;}|Nb(xx,_)->{xwithitv=Nbxx;}letfilter_zero(prec:prec)(x:t):t=letr=meetxzeroin{rwithnan=false;}(** Refine both arguments assuming that the test is true. *)letfilter_leq_false(prec:prec)(x:t)(y:t):t*t=ifx.nan||y.nanthenx,yelsefilter_gtprecxyletfilter_lt_false(prec:prec)(x:t)(y:t):t*t=ifx.nan||y.nanthenx,yelsefilter_geqprecxyletfilter_geq_false(prec:prec)(x:t)(y:t):t*t=letyy,xx=filter_leq_falseprecyxinxx,yyletfilter_gt_false(prec:prec)(x:t)(y:t):t*t=letyy,xx=filter_lt_falseprecyxinxx,yyletfilter_eq_false=filter_neqletfilter_neq_false=filter_eqletfilter_zero_false=filter_nonzeroletfilter_nonzero_false=filter_zero(** Refine both arguments assuming that the test is false. *)(** {2 Backward arithmetic} *)letbwd_neg(a:t)(r:t):t=meeta(negr)(** Backward negation. *)letbwd_abs(a:t)(r:t):t=join(meetar)(meeta(negr))(** Backward absolute value. *)letbwd_generic2(prec:prec)(round:round)f(x:t)(y:t)(r:t):t*t=ifcontains_specialrthen(* no refinement if specials in the result *)x,yelse(* no special in the result -> no special in the arguments *)matchx.itv,y.itv,r.itvwith|_,_,BOT->bot,bot|BOT,_,_|_,BOT,_->x,y|Nbxx,Nbyy,Nbrr->matchf(fix_precprec)roundxxyyrrwith|BOT->x,y|Nb(ix,iy)->meetx(fix_itvprec{botwithitv=Nbix;}),meety(fix_itvprec{botwithitv=Nbiy;})(* utility function for all binary operations *)letbwd_add(prec:prec)(round:round)(x:t)(y:t)(r:t):t*t=bwd_generic2precroundFI.bwd_addxyr(** Backward addition. *)letbwd_sub(prec:prec)(round:round)(x:t)(y:t)(r:t):t*t=bwd_generic2precroundFI.bwd_subxyr(** Backward subtraction. *)letbwd_mul(prec:prec)(round:round)(x:t)(y:t)(r:t):t*t=bwd_generic2precroundFI.bwd_mulxyr(** Backward multiplication. *)letbwd_div(prec:prec)(round:round)(x:t)(y:t)(r:t):t*t=letxx,yy=bwd_generic2precroundFI.bwd_divxyrin(* add back infinities to y if the result can be 0 *)letyy=ifcontains_zerorthen{yywithpinf=y.pinf;minf=y.minf;}elseyyinxx,yy(** Backward division. *)letbwd_fmod(prec:prec)(round:round)(x:t)(y:t)(r:t):t*t=bwd_generic2precround(fun__->FI.bwd_fmod)xyr(** Backward modulo. *)letbwd_generic1(prec:prec)(round:round)f(x:t)(r:t):t=ifcontains_specialrthen(* no refinement if specials in the result *)xelse(* no special in the result -> no special in the arguments *)matchx.itv,r.itvwith|_,BOT->bot|BOT,_->x|Nbix,Nbir->letitv=f(fix_precprec)roundixirinmeetx(fix_itvprec{botwithitv;})letbwd_round_int(prec:prec)(round:round)(x:t)(r:t):t=bwd_generic1precroundFI.bwd_round_intxr(** Backward rounding to int. *)letbwd_round(prec:prec)(round:round)(x:t)(r:t):t=matchx.itv,r.itvwith|_,BOT->bot|BOT,_->x|Nbix,Nbir->letm=matchprecwith|(`SINGLE|`DOUBLE)asprec->F.max_normalprec|`EXTRA->F.max_normal`DOUBLE|`REAL->infinityin(* special floats *)letpinf=r.pinf&&(x.pinf||ix.FI.up>m)andminf=r.minf&&(x.minf||ix.FI.lo<-.m)andnan=r.nan&&x.nanin(* interval of non-special floats *)letitv=FI.bwd_round(fix_precprec)roundixirinmeetx(fix_itvprec{itv;pinf;minf;nan;})(** Backward rounding to float. *)letbwd_square(prec:prec)(round:round)(x:t)(r:t):t=bwd_generic1precroundFI.bwd_squarexr(** Backward square. *)letbwd_sqrt(prec:prec)(round:round)(x:t)(r:t):t=bwd_generic1precroundFI.bwd_sqrtxr(** Backward square root. *)letbwd_of_int_itv(prec:prec)(round:round)((lo,up):II.t)(r:t):II.t_with_bot=matchr.itvwith|Nbi->leti=FI.unround_int(fix_precprec)roundiinletl=ifF.is_finitei.lo&¬r.minfthenB.Finite(Z.of_floati.lo)elseB.MINFandu=ifF.is_finitei.up&¬r.pinfthenB.Finite(Z.of_floati.up)elseB.PINFinII.meet(lo,up)(l,u)|BOT->ifis_botrthenBOTelseNbII.minf_inf(** Backward conversion from integer interval. *)letbwd_to_int_itv(a:t)((lo,up):II.t):t=letl=matchlowith|B.Finitex->F.of_z`DOUBLE`DOWNx|_->neg_infinityandu=matchupwith|B.Finitex->F.of_z`DOUBLE`UPx|_->infinityinletitv=bot_absorb1(funi->FI.bwd_round_int`DOUBLE`ZEROi(FI.mklu))a.itvin{itv;nan=a.nan;minf=a.pinf&&(l==neg_infinity);pinf=a.minf&&(u==infinity);}(** Backward conversion to integer interval (with truncation). *)