123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2021 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/>. *)(* *)(****************************************************************************)(**
IntItv - Intervals for arbitrary precision integers.
We rely on Zarith for arithmetic operations, and IntBounds to
represent unbounded intervals.
*)openBotmoduleB=IntBound(** {2 Types} *)typet=B.t(** lower bound *)*B.t(** upper bound *)(**
The type of non-empty intervals: a lower bound and an upper bound.
The lower bound can be MINF, but not PINF; the upper bound can be PINF, but not MINF.
Moreover, lower bound ≤ upper bound.
*)typet_with_bot=twith_bot(** The type of possibly empty intervals. *)letis_valid((a,b):t):bool=B.leqab&&a<>B.PINF&&b<>B.MINF(** {2 Constructors} *)letof_bound(a:B.t)(b:B.t):t=ifa=B.PINF||b=B.MINF||B.gtabtheninvalid_arg(Printf.sprintf"IntItv.of_bound [%s,%s]"(B.to_stringa)(B.to_stringb));a,bletof_z(a:Z.t)(b:Z.t):t=ifZ.gtabtheninvalid_arg(Printf.sprintf"IntItv.of_z [%s,%s]"(Z.to_stringa)(Z.to_stringb));B.Finitea,B.Finitebletof_int(a:int)(b:int):t=ifa>btheninvalid_arg(Printf.sprintf"IntItv.of_int [%i,%i]"ab);B.of_inta,B.of_intbletof_int64(a:int64)(b:int64):t=ifa>btheninvalid_arg(Printf.sprintf"IntItv.of_int64 [%Li,%Li]"ab);B.of_int64a,B.of_int64b(** Constructs a non-empty interval. *)letof_float(a:float)(b:float):t=ifa>b||a=infinity||b=neg_infinitytheninvalid_arg(Printf.sprintf"IntItv.of_float [%f,%f]"ab);B.of_floata,B.of_floatb(** Constructs a non-empty interval. *)letof_range=of_zletof_bound_bot(a:B.t)(b:B.t):t_with_bot=ifB.gtab||a=B.PINF||b=B.MINFthenBOTelseNb(a,b)letof_range_bot(a:Z.t)(b:Z.t):t_with_bot=ifZ.gtabthenBOTelseNb(B.Finitea,B.Finiteb)letof_int_bot(a:int)(b:int):t_with_bot=ifa>bthenBOTelseNb(B.of_inta,B.of_intb)letof_int64_bot(a:int64)(b:int64):t_with_bot=ifa>bthenBOTelseNb(B.of_int64a,B.of_int64b)(** Constructs a possibly empty interval. *)letof_float_bot(a:float)(b:float):t_with_bot=ifa>b||a=infinity||b=neg_infinitythenBOTelseNb(B.of_floata,B.of_floatb)(** Constructs a possibly empty interval. *)lethull(a:B.t)(b:B.t):t=B.minab,B.maxab(** Constructs the smallest interval containing a and b. *)letcst(c:Z.t):t=B.Finitec,B.Finitec(** Singleton interval. *)letcst_int(c:int):t=cst(Z.of_intc)letcst_int64(c:int64):t=cst(Z.of_int64c)letzero:t=cstZ.zero(** [0,0] *)letone:t=cstZ.one(** [1,1] *)letmone:t=cstZ.minus_one(** [-1,-1] *)letzero_one:t=B.FiniteZ.zero,B.FiniteZ.one(** [0,1] *)letmone_zero:t=B.FiniteZ.minus_one,B.FiniteZ.zero(** [-1,0] *)letmone_one:t=B.FiniteZ.minus_one,B.FiniteZ.one(** [-1,1] *)letzero_inf:t=B.FiniteZ.zero,B.PINF(** [0,+∞] *)letminf_zero:t=B.MINF,B.FiniteZ.zero(** [-∞,0] *)letminf_inf:t=B.MINF,B.PINF(** [-∞,+∞] *)letunsigned(bits:int):t=B.zero,B.pred(B.pow2bits)letunsigned8:t=unsigned8letunsigned16:t=unsigned16letunsigned32:t=unsigned32letunsigned64:t=unsigned64(** Intervals of unsigned integers with the specified bitsize. *)letsigned(bits:int):t=B.neg(B.pow2(bits-1)),B.pred(B.pow2(bits-1))letsigned8:t=signed8letsigned16:t=signed16letsigned32:t=signed32letsigned64:t=signed64(** Intervals of two compement's integers with the specified bitsize. *)(** {2 Predicates} *)letequal((a,b):t)((a',b'):t):bool=B.eqaa'&&B.eqbb'(** Equality. = also works *)letequal_bot:t_with_bot->t_with_bot->bool=bot_equalequalletincluded((a,b):t)((a',b'):t):bool=B.geqaa'&&B.leqbb'(** Set ordering. *)letincluded_bot:t_with_bot->t_with_bot->bool=bot_includedincludedletintersect((a,b):t)((a',b'):t):bool=B.leqab'&&B.leqa'b(** Whether the intervals have an non-empty intersection. *)letintersect_bot:t_with_bot->t_with_bot->bool=bot_dfl2falseintersectletcontains(x:Z.t)((a,b):t):bool=B.leqa(B.Finitex)&&B.leq(B.Finitex)b(** Whether the interval contains a (finite) value. *)letcompare((a,b):t)((a',b'):t):int=ifB.eqaa'thenB.comparebb'elseB.compareaa'(**
A total ordering (lexical ordering) returning -1, 0, or 1.
Can be used as compare for sets, maps, etc.
*)letcompare_bot(x:twith_bot)(y:twith_bot):int=Bot.bot_comparecomparexy(** Total ordering on possibly empty intervals. *)letcontains_zero((a,b):t):bool=B.signa<=0&&B.signb>=0(** [a,b] contains 0. *)letcontains_one((a,b):t):bool=B.leqaB.one&&B.geqbB.one(** [a,b] contains 1. *)letcontains_nonzero((a,b):t):bool=B.neqaB.zero||B.neqbB.zero(** [a,b] contains a non-zero value. *)letis_zero(ab:t):bool=ab=zeroletis_one(ab:t):bool=ab=oneletis_positive((a,b):t):bool=B.is_positivealetis_negative((a,b):t):bool=B.is_negativebletis_positive_strict((a,b):t):bool=B.is_positive_strictaletis_negative_strict((a,b):t):bool=B.is_negative_strictbletis_nonzero((a,b):t):bool=B.gtaB.zero||B.ltbB.zero(** Interval sign. *)letis_singleton((a,b):t):bool=B.eqab(** [a,b] contains a single element. *)letis_bounded((a,b):t):bool=a<>B.MINF&&b<>B.PINF(** [a,b] has finite bounds. *)letis_minf_inf((a,b):t):bool=a=B.MINF&&b=B.PINF(** [a,b] represents [-∞,+∞]. *)letis_in_range(a:t)(lo:Z.t)(up:Z.t)=includeda(B.Finitelo,B.Finiteup)(** Whether the interval is included in the range [lo,up]. *)(** {2 Printing} *)letto_string((a,b):t):string="["^(B.to_stringa)^","^(B.to_stringb)^"]"letprintch(x:t)=output_stringch(to_stringx)letfprintch(x:t)=Format.pp_print_stringch(to_stringx)letbprintch(x:t)=Buffer.add_stringch(to_stringx)letto_string_bot=bot_to_stringto_stringletprint_bot=bot_printprintletfprint_bot=bot_fprintfprintletbprint_bot=bot_bprintbprint(** {2 Enumeration} *)letsize((a,b):t)=matcha,bwith|B.Finitex,B.Finitey->Z.succ(Z.subyx)|_->invalid_arg(Printf.sprintf"IntItv.size: unbounded interval %s"(to_string(a,b)))(** Number of elements. Raises an invalid argument if it is unbounded. *)letto_list((a,b):t)=letrecdoitlhacc=ifl=hthenl::accelsedoitl(Z.predh)(h::acc)inmatcha,bwith|B.Finitex,B.Finitey->doitxy[]|_->invalid_arg(Printf.sprintf"IntItv.to_list: unbounded interval %s"(to_string(a,b)))(** List of elements, in increasing order. Raises an invalid argument if it is unbounded. *)(** {2 Set operations} *)letjoin((a,b):t)((a',b'):t):t=B.minaa',B.maxbb'(** Join of non-empty intervals. *)letjoin_bot(a:t_with_bot)(b:t_with_bot):t_with_bot=bot_neutral2joinab(** Join of possibly empty intervals. *)letjoin_list(l:tlist):t_with_bot=List.fold_left(funab->join_bota(Nbb))BOTl(** Join of a list of (non-empty) intervals. *)letmeet((a,b):t)((a',b'):t):t_with_bot=of_bound_bot(B.maxaa')(B.minbb')(** Intersection of non-emtpty intervals (possibly empty) *)letmeet_bot(a:t_with_bot)(b:t_with_bot):t_with_bot=bot_absorb2meetab(** Intersection of possibly empty intervals. *)letmeet_list(l:tlist):t_with_bot=List.fold_left(funab->meet_bota(Nbb))(Nbminf_inf)l(** Meet of a list of (non-empty) intervals. *)letwiden((a,b):t)((a',b'):t):t=(ifB.lta'athenB.MINFelsea),(ifB.gtb'bthenB.PINFelseb)(** Basic widening: put unstable bounds to infinity. *)letwiden_bot(a:t_with_bot)(b:t_with_bot):t_with_bot=bot_neutral2widenabletpositive(a:t):t_with_bot=meetazero_infletnegative(a:t):t_with_bot=meetaminf_zero(** Positive and negative part. *)letmeet_zero(a:t):t_with_bot=meetazero(** Intersects with {0}. *)letmeet_nonzero((a,b):t):t_with_bot=matchB.is_zeroa,B.is_zerobwith|true,true->BOT|true,false->Nb(B.succa,b)|false,true->Nb(a,B.predb)|false,false->Nb(a,b)(** Keeps only non-zero elements. *)(** {2 Forward operations} *)(** Given one or two interval argument(s), return the interval result. *)letneg((a,b):t):t=B.negb,B.nega(** Negation. *)letabs((a,b):t):t=ifB.signa<=0thenifB.signb<=0thenneg(a,b)elseB.zero,B.max(B.nega)belsea,b(** Absolute value. *)letsucc((a,b):t):t=B.succa,B.succb(** Add 1. *)letpred((a,b):t):t=B.preda,B.predb(** Subtract 1. *)letadd((a,b):t)((a',b'):t):t=B.addaa',B.addbb'(** Addition. *)letsub((a,b):t)((a',b'):t):t=B.subab',B.subba'(** Subtraction. *)letminmax4op(a,b)(c,d)=letx,y,z,t=opac,opad,opbc,opbdinB.min(B.minxy)(B.minzt),B.max(B.maxxy)(B.maxzt)(* utility used internally for multiplication and others *)letmul(ab:t)(ab':t):t=minmax4B.mulabab'(** Multiplication. *)letdiv_unmerged(ab:t)((a',b'):t):tlist=(* division by an interval of constant sign *)letdiv_posabab'=minmax4B.divabab'in(* split denominator and do 2 cases *)(ifB.is_positive_strictb'then[div_posab(B.maxa'B.one,b')]else[])@(ifB.is_negative_stricta'then[div_posab(a',B.minb'B.minus_one)]else[])(** Division (with truncation).
Returns a list of 0, 1, or 2 intervals to remain precise.
*)letediv_unmerged(ab:t)((a',b'):t):tlist=(* division by an interval of constant sign *)letdiv_posabab'=minmax4B.edivabab'in(* split denominator and do 2 cases *)(ifB.is_positive_strictb'then[div_posab(B.maxa'B.one,b')]else[])@(ifB.is_negative_stricta'then[div_posab(a',B.minb'B.minus_one)]else[])(** Euclidian division (towards -oo).
Returns a list of 0, 1, or 2 intervals to remain precise.
*)letdiv(a:t)(b:t):t_with_bot=join_list(div_unmergedab)(** Division (with truncation).
Returns a single (possibly empty) overapproximating interval.
*)letediv(a:t)(b:t):t_with_bot=join_list(ediv_unmergedab)(** Division (euclidian, towards -oo)
Returns a single (possibly empty) overapproximating interval.
*)letrem((a,b):t)(ab':t):t_with_bot=(* x % y = x % |y| *)leta',b'=absab'inifB.is_zerob'thenBOTelse(* case [a,b] % {0} ⟹ ⊥ *)letm=B.predb'inifB.gta(B.nega')&&B.ltba'then(* case [a,b] ⊆ [-a+1',a'-1] ⟹ identity *)Nb(a,b)elseifB.equala'b'&&B.equal(B.divaa')(B.divba')then(* case [a,b] % {a'} and [a,b] ⊆ [a'k,a'(k+1)-1] *)Nb(B.remaa',B.remba')elseifB.is_positiveathen(* case [a,b] % [a',b'] positive *)Nb(B.zero,m)elseifB.is_negativebthen(* case [a,b] % [a',b'] negative *)Nb(B.negm,B.zero)else(* general case *)Nb(B.negm,m)(** Remainder. Uses the C semantics for remainder (%). *)leterem((a,b):t)(ab':t):t_with_bot=(* x erem y = x erem |y| *)leta',b'=absab'inifB.is_zerob'thenBOTelse(* case [a,b] erem {0} ⟹ ⊥ *)letm=B.predb'inifB.equala'b'&&B.equal(B.edivaa')(B.edivba')then(* case [a,b] erem {a'} and [a,b] ⊆ [a'k,a'(k+1)-1] *)Nb(B.eremaa',B.eremba')else(* general case *)Nb(B.zero,m)(** Euclidian remainder. rounding towards -oo *)letpow(ab:t)(ab':t):t=minmax4B.powabab'(** Power. *)letwrap((a,b):t)(lo:Z.t)(up:Z.t):t=matcha,bwith|B.MINF,_|_,B.PINF->B.Finitelo,B.Finiteup|B.Finiteaa,B.Finitebb->ifaa>=lo&&bb<=upthena,b(* no wrap-around case *)elseletw=Z.succ(Z.subuplo)inlet(aq,ar),(bq,br)=Z.ediv_rem(Z.subaalo)w,Z.ediv_rem(Z.subbblo)winifaq=bqthen(* included in some [lo,up]+kw *)B.Finite(Z.addarlo),B.Finite(Z.addbrlo)else(* crosses interval boundaries *)B.Finitelo,B.Finiteup|_->invalid_arg(Printf.sprintf"IntItv.wrap %s in [%s,%s]"(to_string(a,b))(Z.to_stringlo)(Z.to_stringup))(** Put back the interval inside [lo,up] by modular arithmetics.
Useful to model the effect of arithmetic or conversion overflow. *)letto_bool(can_be_zero:bool)(can_be_one:bool):t=matchcan_be_zero,can_be_onewith|true,false->zero|false,true->one|true,true->zero_one|_->failwith"unreachable case encountered in IntItv.to_bool"(* helper function for operators returning a boolean that can be zero and/or one *)letlog_cast(ab:t):t=to_bool(contains_zeroab)(contains_nonzeroab)(** Conversion from integer to boolean in [0,1]: maps 0 to 0 (false) and non-zero to 1 (true). *)letlog_not(ab:t):t=to_bool(contains_nonzeroab)(contains_zeroab)(** Logical negation.
Logical operation use the C semantics: they accept 0 and non-0 respectively as false and true, but they always return 0 and 1 respectively for false and true.
*)letlog_and(ab:t)(ab':t):t=to_bool(contains_zeroab||contains_zeroab')(contains_nonzeroab&&contains_nonzeroab')(** Logical and. *)letlog_or(ab:t)(ab':t):t=to_bool(contains_zeroab&&contains_zeroab')(contains_nonzeroab||contains_nonzeroab')(** Logical or. *)letlog_xor(ab:t)(ab':t):t=letf,f'=contains_zeroab,contains_zeroab'andt,t'=contains_nonzeroab,contains_nonzeroab'into_bool((f&&f')||(t&&t'))((f&&t')||(t&&f'))(** Logical exclusive or. *)letlog_eq(ab:t)(ab':t):t=to_bool(not(equalabab'&&is_singletonab))(intersectabab')letlog_leq((a,b):t)((a',b'):t):t=to_bool(B.gtba')(B.leqab')letlog_geq((a,b):t)((a',b'):t):t=to_bool(B.ltab')(B.geqba')letlog_lt((a,b):t)((a',b'):t):t=to_bool(B.geqba')(B.ltab')letlog_gt((a,b):t)((a',b'):t):t=to_bool(B.leqab')(B.gtba')letlog_neq(ab:t)(ab':t):t=to_bool(intersectabab')(not(equalabab'&&is_singletonab))(** C comparison tests. Returns an interval included in [0,1] (a boolean) *)letis_log_eq(ab:t)(ab':t):bool=intersectabab'letis_log_leq((a,b):t)((a',b'):t):bool=B.leqab'letis_log_geq((a,b):t)((a',b'):t):bool=B.geqba'letis_log_lt((a,b):t)((a',b'):t):bool=B.ltab'letis_log_gt((a,b):t)((a',b'):t):bool=B.gtba'letis_log_neq(ab:t)(ab':t):bool=not(equalabab'&&is_singletonab)(** C comparison tests. Returns a boolean if the test may succeed *)(** {2 Bit operations} *)letshift_left(ab:t)(ab':t):t_with_bot=matchpositiveab'with|BOT->BOT|Nbab''->Nb(minmax4B.shift_leftabab'')(** Bitshift left: multiplication by a power of 2. *)letshift_right(ab:t)(ab':t):t_with_bot=matchpositiveab'with|BOT->BOT|Nbab''->Nb(minmax4B.shift_rightabab'')(** Bitshift right: division by a power of 2 rounding towards -∞. *)letshift_right_trunc(ab:t)(ab':t):t_with_bot=matchpositiveab'with|BOT->BOT|Nbab''->Nb(minmax4B.shift_right_truncabab'')(** Unsigned bitshift right: division by a power of 2 with truncation. *)letbit_not(ab:t):t=pred(negab)(** Bitwise negation: ~x = -x-1 *)(** Internal functions *)(* minimum value of [a,b] | [c,d]
Hacker's delight, Sec. 4.3, Fig. 4-3
slightly changed to make bit masking more explicit
assumes that neither argument contains both positive and strictly negative values
*)letmin_or(a:Z.t)(b:Z.t)(c:Z.t)(d:Z.t):Z.t=letrecdoiti=ifi<0thenZ.logoracelseletai,ci=Z.shift_rightai,Z.shift_rightciinmatchZ.is_evenai,Z.is_evenciwith|true,false->leta'=Z.shift_left(Z.logoraiZ.one)iinifa'<=bthenZ.logora'celsedoit(i-1)|false,true->letc'=Z.shift_left(Z.logorciZ.one)iinifc'<=dthenZ.logorac'elsedoit(i-1)|_->doit(i-1)inletmag=if(a>=Z.zero)=(c>=Z.zero)then(* start the search at the leftmost bit that differ between a and c *)Z.numbits(Z.logxorac)-1else(* if infinitely many differ, fallback to operator magnitude *)max(max(Z.numbitsa)(Z.numbitsb))(max(Z.numbitsc)(Z.numbitsd))indoitmag(* maximum value of [a,b] | [c,d]
Hacker's delight, Sec. 4.3, Fig. 4-4
assumes that neither argument contains both positive and strictly negative values
*)letmax_or(a:Z.t)(b:Z.t)(c:Z.t)(d:Z.t):Z.t=letrecdoiti=ifi<0thenZ.logorbdelseletbi,di=Z.shift_rightbi,Z.shift_rightdiinifZ.is_oddbi&&Z.is_odddithenletb'=Z.pred(Z.shift_leftbii)inifa<=b'thenZ.logorb'delseletd'=Z.pred(Z.shift_leftdii)inifc<=d'thenZ.logorbd'elsedoit(i-1)elsedoit(i-1)inletmag=if(a>=Z.zero)||(c>=Z.zero)then(* start the search at the leftmost bit set in both a and c *)Z.numbits(Z.logandbd)-1else(* if infinitely many, fallback to operator magnitude *)max(max(Z.numbitsa)(Z.numbitsb))(max(Z.numbitsc)(Z.numbitsd))indoitmag(* handles the cases of intervals crossing zero for [a,b] | [c,d]
Hacker's delight, Sec. 4.3, Table 4.1
*)letbounds_or(a:Z.t)(b:Z.t)(c:Z.t)(d:Z.t):Z.t*Z.t=matcha>=Z.zero||b<Z.zero,c>=Z.zero||d<Z.zerowith|true,true->min_orabcd,max_orabcd|false,true->ifc>=Z.zerothenmin_oraZ.minus_onecd,max_orZ.zerobcdelsec,Z.minus_one|true,false->ifa>=Z.zerothenmin_orabcZ.minus_one,max_orabZ.zerodelsea,Z.minus_one|false,false->Z.minac,max_orZ.zerobZ.zerod(* [a,b] & [c,d]
Hacker's delight, Sec. 4.3, algebraic method
because: x & y = ~(~x | y)
and: (a <= x <= b) <=> (~b <= ~x <= ~a)
*)letbounds_and(a:Z.t)(b:Z.t)(c:Z.t)(d:Z.t):Z.t*Z.t=letnh,nl=bounds_or(Z.lognotb)(Z.lognota)(Z.lognotd)(Z.lognotc)inZ.lognotnl,Z.lognotnh(* minimum value of [a,b] ^ [c,d]
Hacker's delight, Sec. 4.3
assumes that neither argument contains both positive and strictly negative values
*)letmin_xor(a:Z.t)(b:Z.t)(c:Z.t)(d:Z.t):Z.t=letrecdoitaci=ifi<0thenZ.logxoracelseletai,ci=Z.shift_rightai,Z.shift_rightciinmatchZ.is_evenai,Z.is_evenciwith|true,false->leta'=Z.shift_left(Z.logoraiZ.one)iindoit(ifa'<=bthena'elsea)c(i-1)|false,true->letc'=Z.shift_left(Z.logorciZ.one)iindoita(ifc'<=dthenc'elsec)(i-1)|_->doitac(i-1)inletmag=if(a>=Z.zero)=(c>=Z.zero)thenZ.numbits(Z.logxorac)-1elsemax(max(Z.numbitsa)(Z.numbitsb))(max(Z.numbitsc)(Z.numbitsd))indoitacmag(* maximum value of [a,b] ^ [c,d]
Hacker's delight, Sec. 4.3
assumes that neither argument contains both positive and strictly negative values
*)letmax_xor(a:Z.t)(b:Z.t)(c:Z.t)(d:Z.t):Z.t=letrecdoitbdi=ifi<0thenZ.logxorbdelseletbi,di=Z.shift_rightbi,Z.shift_rightdiinifZ.is_oddbi&&Z.is_odddithenletb'=Z.pred(Z.shift_leftbii)inifa<=b'thendoitb'd(i-1)elseletd'=Z.pred(Z.shift_leftdii)inifc<=d'thendoitbd'(i-1)elsedoitbd(i-1)elsedoitbd(i-1)inletmag=if(a>=Z.zero)||(c>=Z.zero)thenZ.numbits(Z.logandbd)-1elsemax(max(Z.numbitsa)(Z.numbitsb))(max(Z.numbitsc)(Z.numbitsd))indoitbdmag(* handles the cases of intervals crossing zero for [a,b] ^ [c,d] *)letbounds_xor(a:Z.t)(b:Z.t)(c:Z.t)(d:Z.t):Z.t*Z.t=letcombine2a1b1c1d1a2b2c2d2=(* join two cases *)letl1,h1=min_xora1b1c1d1,max_xora1b1c1d1andl2,h2=min_xora2b2c2d2,max_xora2b2c2d2inZ.minl1l2,Z.maxh1h2inmatcha>=Z.zero||b<Z.zero,c>=Z.zero||d<Z.zerowith|true,true->min_xorabcd,max_xorabcd|false,true->(* 2-way split *)combine2aZ.minus_onecdZ.zerobcd|true,false->(* 2-way split *)combine2abcZ.minus_oneabZ.zerod|false,false->(* 4-way split *)letl1,h1=combine2aZ.minus_onecZ.minus_oneZ.zerobcZ.minus_oneandl2,h2=combine2aZ.minus_oneZ.zerodZ.zerobZ.zerodinZ.minl1l2,Z.maxh1h2(** Interval functions, based on the previous ones *)letbit_or(ab:t)(ab':t):t=matchab,ab'with|(B.Finiteal,B.Finiteah),(B.Finitebl,B.Finitebh)->(* finite case *)ifal=ah&&bl=bhthen(* singleton case *)cst(Z.logoralbl)else(* general case *)letl,h=bounds_oralahblbhinB.Finitel,B.Finiteh|_->(* infinite cases (might be improvable) *)ifis_positiveab&&is_positiveab'then(* positive case *)zero_infelse(* general case *)minf_inf(** Bitwise or. *)letbit_and(ab:t)(ab':t):t=matchab,ab'with|(B.Finiteal,B.Finiteah),(B.Finitebl,B.Finitebh)->(* finite cases *)ifal=ah&&bl=bhthen(* singleton case *)cst(Z.logandalbl)else(* general case *)letl,h=bounds_andalahblbhinB.Finitel,B.Finiteh|_->(* infinite cases (might be improvable) *)ifis_positiveab||is_positiveab'then(* positive case *)zero_infelse(* general case *)minf_inf(** Bitwise and. *)letbit_xor(ab:t)(ab':t):t=matchab,ab'with|(B.Finiteal,B.Finiteah),(B.Finitebl,B.Finitebh)->(* finite cases *)ifal=ah&&bl=bhthen(* singleton case *)cst(Z.logxoralbl)else(* general case *)letl,h=bounds_xoralahblbhinB.Finitel,B.Finiteh|_->(* infinite cases (might be improvable) *)ifis_positiveab&&is_positiveab'then(* positive case *)zero_infelse(* general case *)minf_inf(** Bitwise exclusive or. *)(** {2 Filters} *)(** Given two interval aruments, return the arguments assuming that the predicate holds.
*)letfilter_leq((a,b):t)((a',b'):t):(t*t)with_bot=bot_merge2(of_bound_bota(B.minbb'))(of_bound_bot(B.maxaa')b')letfilter_geq((a,b):t)((a',b'):t):(t*t)with_bot=bot_merge2(of_bound_bot(B.maxaa')b)(of_bound_bota'(B.minbb'))letfilter_lt((a,b):t)((a',b'):t):(t*t)with_bot=bot_merge2(of_bound_bota(B.minb(B.predb')))(of_bound_bot(B.max(B.succa)a')b')letfilter_gt((a,b):t)((a',b'):t):(t*t)with_bot=bot_merge2(of_bound_bot(B.maxa(B.succa'))b)(of_bound_bota'(B.min(B.predb)b'))letfilter_eq((a,b):t)((a',b'):t):(t*t)with_bot=matchmeet(a,b)(a',b')withBOT->BOT|Nbx->Nb(x,x)letfilter_neq((a,b):t)((a',b'):t):(t*t)with_bot=matchB.equalab,B.equala'b'with|true,truewhenB.equalaa'->BOT|true,falsewhenB.equalaa'->bot_merge2(Nb(a,b))(of_bound_bot(B.succa')b')|true,falsewhenB.equalbb'->bot_merge2(Nb(a,b))(of_bound_bota'(B.predb'))|false,truewhenB.equalaa'->bot_merge2(of_bound_bot(B.succa)b)(Nb(a',b'))|false,truewhenB.equalbb'->bot_merge2(of_bound_bota(B.predb))(Nb(a',b'))|_->Nb((a,b),(a',b'))(** {2 Backward operations} *)(** Given one or two interval argument(s) and a result interval, return the
argument(s) assuming the result in the operation is in the given result.
*)letbwd_default_unary(a:t)(r:t):t_with_bot=Nba(** Fallback for backward unary operators *)letbwd_default_binary(a:t)(b:t)(r:t):(t*t)with_bot=Nb(a,b)(** Fallback for backward binary operators *)letbwd_neg(a:t)(r:t):t_with_bot=meeta(negr)letbwd_abs(a:t)(r:t):t_with_bot=join_bot(meetar)(meeta(negr))letbwd_succ(a:t)(r:t):t_with_bot=meeta(predr)letbwd_pred(a:t)(r:t):t_with_bot=meeta(succr)letbwd_add(a:t)(b:t)(r:t):(t*t)with_bot=(* r = a + b ⇒ a = r - b ∧ b = r - a *)bot_merge2(meeta(subrb))(meetb(subra))letbwd_sub(a:t)(b:t)(r:t):(t*t)with_bot=(* r = a - b ⇒ a = b + r ∧ b = a - r *)bot_merge2(meeta(addbr))(meetb(subar))letbwd_mul(a:t)(b:t)(r:t):(t*t)with_bot=(* r = a * b ⇒ ((a = r / b) ∨ (b = r = 0)) ∧ ((b = r / a) ∨ (a = r = 0)) *)letaa=ifcontains_zerob&&contains_zerorthenNbaelsedivrbandbb=ifcontains_zeroa&&contains_zerorthenNbbelsedivrainbot_merge2aabbletbwd_div((a,a'):t)((b,b'):t)(r:t):(t*t)with_bot=(* r = a / b ⇒ (a = r * b + r % b) ∧ ((b = (a - r % b) / r) ∨ ((a - r % b) = r = 0)) *)(* m = max [b,b'] - 1 *)letm=B.pred(B.max(B.absb)(B.absb'))in(* md = approximate r % b *)letmd=(ifB.is_negative_strictathenB.negmelseB.zero),(ifB.is_positive_stricta'thenmelseB.zero)in(* aa = r * b + r % b *)letaa=meet(a,a')(add(mulr(b,b'))md)in(* (bb = a / r) ∨ (bb = b ∧ (a - r % b) = r = 0)*)letax=sub(a,a')mdinletbb=ifcontains_zeroax&&contains_zerorthenNb(b,b')elsemeet_bot(Nb(b,b'))(divaxr)inbot_merge2aabbletbwd_ediv((a,a'):t)((b,b'):t)(r:t):(t*t)with_bot=(* m = max [b,b'] - 1 *)letm=B.pred(B.max(B.absb)(B.absb'))in(* md = approximate r erem b *)letmd=B.zero,min(* aa = r * b + r erem b *)letaa=meet(a,a')(add(mulr(b,b'))md)in(* (bb = a ediv r) ∨ (bb = b ∧ (a - r erem b) = r = 0) *)letax=sub(a,a')mdinletbb=ifcontains_zeroax&&contains_zerorthenNb(b,b')elsemeet_bot(Nb(b,b'))(edivaxr)inbot_merge2aabbletbwd_bit_not(a:t)(r:t):t_with_bot=meeta(bit_notr)letbwd_join(a:t)(b:t)(r:t):(t*t)with_bot=bot_merge2(meetar)(meetbr)(** Backward join: both arguments are intersected with the result. *)letbwd_shift_left(a:t)(b:t)(r:t):(t*t)with_bot=(* r = a << b ⇒ a = r >> b *)matchshift_rightrbwith|Nbaa->bot_merge2(meetaaa)(Nbb)|BOT->Nb(a,b)letbwd_shift_right(a:t)(b:t)(r:t):(t*t)with_bot=(* r = a >> b ⇒ r << b <= a < (r << b) + (1 << b) *)matchshift_leftrbwith|Nb(l,h)->letaa=l,B.addh(B.pred(B.shift_leftB.one(sndb)))inbot_merge2(meetaaa)(Nbb)|BOT->Nb(a,b)letbwd_shift_right_trunc(a:t)(b:t)(r:t):(t*t)with_bot=(* r = a >>> b ⇒ (r << b) - (1 << b) < a < (r << b) + (1 << b) *)matchshift_leftrbwith|Nb(l,h)->letm=B.pred(B.shift_leftB.one(sndb))inletl=ifB.is_negative_strict(fsta)thenB.sublmelselandh=ifB.is_positive_strict(snda)thenB.addhmelsehinbot_merge2(meeta(l,h))(Nbb)|BOT->Nb(a,b)letbwd_bit_or(a:t)(b:t)(r:t):(t*t)with_bot=(* r = a | b ⇒ a = a & r ∧ b = b & r, might be improved *)letaa=meeta(bit_andra)andbb=meetb(bit_andrb)inbot_merge2aabbletbwd_bit_and(a:t)(b:t)(r:t):(t*t)with_bot=(* r = a & b ⇒ a = ~(~a & ~r) ∧ b = ~(~b & ~r), might be improved *)letaa=meeta(bit_not(bit_and(bit_nota)(bit_notr)))andbb=meetb(bit_not(bit_and(bit_notb)(bit_notr)))inbot_merge2aabbletbwd_bit_xor(a:t)(b:t)(r:t):(t*t)with_bot=(* r = a xor b ⇒ a = r xor b ∧ b = r xor a *)bot_merge2(meeta(bit_xorbr))(meetb(bit_xorar))letbwd_convex_join(a:t)(b:t)(r:t):(t*t)with_bot=bot_merge2(meetar)(meetbr)(* utility for bwd_log_xxx *)letbwd_log_genif_oneif_zero(a:t)(b:t)(r:t):(t*t)with_bot=matchcontains_zeror,contains_onerwith|true,true->Nb(a,b)|true,false->if_zeroab|false,true->if_oneab|false,false->BOTletbwd_log_eq=bwd_log_genfilter_eqfilter_neqletbwd_log_neq=bwd_log_genfilter_neqfilter_eqletbwd_log_lt=bwd_log_genfilter_ltfilter_geqletbwd_log_gt=bwd_log_genfilter_gtfilter_leqletbwd_log_leq=bwd_log_genfilter_leqfilter_gtletbwd_log_geq=bwd_log_genfilter_geqfilter_ltletbwd_wrap(a:t)range(r:t):t_with_bot=ifincludeda(of_z(fstrange)(sndrange))thenmeetar(* no overflow *)elsematchmeetr(of_z(fstrange)(sndrange))with|BOT->BOT|Nb(Finitelr,Finiteur)->letrange_size=Z.sub(sndrange)(fstrange)|>Z.succin(* bwd_wrap(., [l,u], [lr,ur]) ⊆ ⋃_{k ∈ ℤ} [lr+k*(u-l+1), ur+k*(u-l+1)] *)letcompute_bounddiva(r1,r2)=matchawith|B.Finitea->letk=divZ.(a-r2)range_sizeinB.FiniteZ.(r1+k*range_size)|MINF|PINF->ainletlower_bound=compute_boundZ.cdiv(fsta)(lr,ur)inletupper_bound=compute_boundZ.fdiv(snda)(ur,lr)inmeet_bot(Nba)(of_bound_botlower_boundupper_bound)|Nb_->assertfalseletpos_modab=letr=IntBound.remabinifIntBound.geqrIntBound.zerothenrelseIntBound.addbrletbwd_rem(a:t)(b:t)(r:t):(t*t)with_bot=ifis_singletonr&&is_singletonb&¬(contains_zerob)&&is_boundedathenletal,ah=ainletrs,_=rinletbs,_=binletal=IntBound.addal(pos_mod(IntBound.subrsal)bs)inletah=IntBound.subah(pos_mod(IntBound.subahrs)bs)inifah<althenBOTelseNb((al,ah),b)elsebwd_default_binaryabrletbwd_erem:t->t->t->(t*t)with_bot=bwd_default_binaryletbwd_pow=bwd_default_binary(* TODO: more precise backward functions *)