123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2018-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/>. *)(* *)(****************************************************************************)(**
IntCong - Integer congruences.
We rely on Zarith for arithmetic operations.
*)openBot(** {2 Types} *)typet=Z.t(** stride *)*Z.t(** offset *)(**
The type of non-empty congruence sets (a,b) represents aℤ + b.
a must be positive, and 0 ≤ b < a when a > 0.
0ℤ+b represents the singleton {b}.
1ℤ+0 represents the whole set of integers.
aℤ+b represents the set { ak + b | k ∊ ℕ }.
*)typet_with_bot=twith_bot(** The type of possibly empty congruence sets. *)letis_valid((a,b):t):bool=a=Z.zero||(a>Z.zero&&Z.zero<=b&&b<a)moduleI=ItvUtils.IntItvmoduleB=ItvUtils.IntBound(** {2 Arithmetic utilities} *)letgcd(a:Z.t)(b:Z.t):Z.t=ifa=Z.zerothenbelseifb=Z.zerothenaelseZ.gcdab(** Greatest common divisor of |a| and |b|. 0 is neutral. *)letgcd3(a:Z.t)(b:Z.t)(c:Z.t):Z.t=gcd(gcdab)cletgcd_ext(a:Z.t)(b:Z.t):Z.t*Z.t*Z.t*Z.t=letgcd,u,v=Z.gcdextabingcd,Z.div(Z.mulab)gcd,u,v(** Returns the gcd, lcm and cofactors u, v such that ua+vb=gcd.
Undefined if a or b is 0.
*)letdivides(a:Z.t)(b:Z.t):bool=ifa=Z.zerothenb=Z.zeroelseZ.remba=Z.zero(** Wheter b is a multiple of a. Always true if b = 0. *)letrem_zero(a:Z.t)(b:Z.t):Z.t=ifb=Z.zerothenaelseZ.eremab(** As Z.erem, but rem_zero a 0 = a. *)(** {2 Constructors} *)letof_z(a:Z.t)(b:Z.t):t=ifa=Z.zerothena,belseleta=Z.absaina,Z.eremba(** Returns aℤ + b. *)letof_int(a:int)(b:int):t=of_z(Z.of_inta)(Z.of_intb)letof_int64(a:int64)(b:int64):t=of_z(Z.of_int64a)(Z.of_int64b)letcst(b:Z.t):t=Z.zero,b(** Returns 0ℤ + b *)letcst_int(b:int):t=cst(Z.of_intb)letcst_int64(b:int64):t=cst(Z.of_int64b)letzero:t=cst_int0(** 0ℤ+0 *)letone:t=cst_int1(** 0ℤ+1 *)letmone:t=cst_int(-1)(** 0ℤ-1 *)letminf_inf:t=of_int10(** 1ℤ+0 *)letof_range(lo:Z.t)(hi:Z.t):t=iflo=hithencstloelseminf_infletof_range_bot(lo:Z.t)(hi:Z.t):twith_bot=iflo=hithenNb(cstlo)elseiflo>hithenBOTelseNbminf_infletof_bound(lo:B.t)(hi:B.t):t=matchlo,hiwith|B.Finitel,B.Finiteh->of_rangelh|_->minf_infletof_bound_bot(lo:B.t)(hi:B.t):twith_bot=matchlo,hiwith|B.Finitel,B.Finiteh->of_range_botlh|_->Nbminf_inf(** Congruence overapproximating an interval. *)(** {2 Predicates} *)letequal(a:t)(b:t):bool=a=b(** Equality. = also works. *)letequal_bot:t_with_bot->t_with_bot->bool=bot_equalequalletincluded((a,b):t)((c,d):t):bool=dividesca&&rem_zerobc=d(** Set ordering. *)letincluded_bot:t_with_bot->t_with_bot->bool=bot_includedincludedletintersect((a,b):t)((a',b'):t):bool=rem_zero(Z.subbb')(gcdaa')=Z.zero(** Whether the intersection is non-empty. *)letintersect_bot:t_with_bot->t_with_bot->bool=bot_dfl2falseintersectletcontains(x:Z.t)((a,b):t):bool=rem_zeroxa=b(** Whether the set contains some value x. *)letcompare((a,b):t)((a',b'):t):int=ifa=a'thenZ.comparebb'elseZ.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 congruences. *)letcontains_zero((a,b):t):bool=b=Z.zero(** Whether the congruence contains zero. *)letcontains_one((a,b):t):bool=a=Z.one||b=Z.one(** Whether the congruence contains one. *)letcontains_nonzero(ab:t):bool=ab<>zero(** Whether the congruence contains a non-zero value. *)letis_zero(ab:t):bool=ab=zeroletis_positive((a,b):t):bool=a=Z.zero&&b>=Z.zeroletis_negative((a,b):t):bool=a=Z.zero&&b<=Z.zeroletis_positive_strict((a,b):t):bool=a=Z.zero&&b>Z.zeroletis_negative_strict((a,b):t):bool=a=Z.zero&&b<Z.zeroletis_nonzero((a,b):t):bool=b<>Z.zero(** Sign. *)letis_minf_inf((a,b):t):bool=a=Z.one(** The congruence represents [-∞,+∞]. *)letis_singleton((a,b):t):bool=a=Z.zero(** Whether the congruence contains a single element. *)letis_bounded(ab:t):bool=is_singletonab(** Whether the congruence contains a finite number of elements. *)letis_in_range((a,b):t)(lo:Z.t)(up:Z.t)=a=Z.zero&&b>=lo&&b<=up(** Whether the congruence is included in the range [lo,up]. *)(** {2 Printing} *)letto_string((a,b):t):string=ifa=Z.zerothenZ.to_stringbelseletprefix=(ifa=Z.onethen""elseZ.to_stringa)inmatchZ.signbwith|1->prefix^"ℤ+"^(Z.to_stringb)|-1->prefix^"ℤ"^(Z.to_stringb)|_->prefix^"ℤ"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 Set operations} *)letjoin((a,b):t)((a',b'):t):t=of_z(gcd3aa'(Z.subbb'))b'(** Abstract union. *)letjoin_bot(a:t_with_bot)(b:t_with_bot):t_with_bot=bot_neutral2joinabletjoin_list(l:tlist):t_with_bot=List.fold_left(funab->join_bota(Nbb))BOTlletmeet((a,b):t)((a',b'):t):t_with_bot=matcha=Z.zero,a'=Z.zerowith|true,true->ifb=b'thenNb(a,b)elseBOT|true,false->ifcontainsb(a',b')thenNb(a,b)elseBOT|false,true->ifcontainsb'(a,b)thenNb(a',b')elseBOT|false,false->letgcd,lcm,u,_=gcd_extaa'inifZ.rem(Z.subbb')gcd=Z.zerothenNb(of_zlcm(Z.subb(Z.div(Z.mula(Z.mulu(Z.subbb')))gcd)))elseBOT(** Abstract intersection. *)letmeet_bot(a:t_with_bot)(b:t_with_bot):t_with_bot=bot_absorb2meetabletmeet_list(l:tlist):t_with_bot=List.fold_left(funab->meet_bota(Nbb))(Nbminf_inf)lletmeet_range((a,b):t)(lo:Z.t)(up:Z.t):t_with_bot=ifa=Z.zero&&b<lo||b>upthenBOTelseNb(a,b)(** Abstract intersection with [lo,up]. *)letpositive(a:t):t_with_bot=ifis_negative_strictathenBOTelseNbaletnegative(a:t):t_with_bot=ifis_positive_strictathenBOTelseNba(** Positive and negative part. *)letmeet_zero(a:t):t_with_bot=meetazero(** Intersects with {0}. *)letmeet_nonzero(a:t):t_with_bot=ifequalazerothenBOTelseNba(** Keeps only non-zero elements. *)(** {2 Forward operations} *)letneg((a,b):t):t=of_za(Z.negb)(** Negation. *)letabs((a,b):t):t=ifa=Z.zerothena,Z.absbelseifb=Z.zerothena,belsejoin(a,b)(a,Z.subab)(** Absolute value. *)letsucc((a,b):t)=of_za(Z.succb)(** Adding 1. *)letpred((a,b):t)=of_za(Z.predb)(** Subtracting 1. *)letadd((a,b):t)((a',b'):t):t=of_z(gcdaa')(Z.addbb')(** Addition. *)letsub((a,b):t)((a',b'):t):t=of_z(gcdaa')(Z.subbb')(** Subtraction. *)letmul((a,b):t)((a',b'):t):t=of_z(gcd3(Z.mulaa')(Z.mulab')(Z.mula'b))(Z.mulbb')(** Multiplication. *)letdiv((a,b):t)((a',b'):t):t_with_bot=ifa'=Z.zerothenifb'=Z.zerothenBOT(* aℤ+b / 0 *)elseifdividesb'a&÷sb'bthenNb(of_z(Z.divab')(Z.divbb'))(* aℤ+b / b' where b' divides a and b *)elseNbminf_inf(* aℤ+b / b' for other cases *)elseNbminf_inf(* general case *)(** Division (with truncation). *)letrem((a,b):t)((a',b'):t):t_with_bot=ifa'=Z.zerothenifb'=Z.zerothenBOT(* aℤ+b mod 0 *)elseifa=Z.zerothenNb(cst(Z.rembb'))(* b mod b' *)elseNb(of_z(gcdab')(Z.rembb'))(* aℤ+b mod b' *)elseNb(of_z(gcd3aa'b')b)(* general case *)(** Remainder. Uses the C semantics for remainder (%). *)letwrap((a,b):t)(lo:Z.t)(up:Z.t):t=letw=Z.succ(Z.subuplo)inifa=Z.zerothenof_zZ.zero(Z.addlo(Z.erem(Z.subblo)w))(* singleton *)elseof_z(gcdaw)(Z.addlo(Z.erem(Z.subblo)w))(* non-singleton *)(** Put back inside [lo,up] by modular arithmetics. *)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->minf_inf|_->failwith"unreachable case encountered in IntCong.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).
[0;1] is over-approximated as ℤ.
*)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.
[0;1] is over-approximated as ℤ.
*)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_neq(ab:t)(ab':t):t=to_bool(intersectabab')(not(equalabab'&&is_singletonab))letlog_sglop((a,b):t)((a',b'):t):t=ifa<>Z.zero||a'<>Z.zerothenminf_infelseifopbb'thenoneelsezero(* utility function, only handles the case of singletons *)letlog_leq=log_sgl(<=)letlog_geq=log_sgl(>=)letlog_lt=log_sgl(<)letlog_gt=log_sgl(>)(** C comparison tests. Returns an interval included in [0,1] (a boolean) *)letis_log_eq(ab:t)(ab':t):bool=intersectabab'letis_log_neq(ab:t)(ab':t):bool=not(equalabab'&&is_singletonab)letis_log_sglop((a,b):t)((a',b'):t):bool=a<>Z.zero||a'<>Z.zero||opbb'(* utility function, only handles the case of singletons *)letis_log_leq=is_log_sgl(<=)letis_log_geq=is_log_sgl(>=)letis_log_lt=is_log_sgl(<)letis_log_gt=is_log_sgl(>)(** C comparison tests. Returns a boolean if the test may succeed *)letshift_left((a,b):t)((a',b'):t):t_with_bot=tryifa'=Z.zerothenifb'<Z.zerothenBOT(* aℤ+b << negative constant *)elseletbb'=Z.to_intb'inNb(of_z(Z.shift_leftabb')(Z.shift_leftbbb'))(* aℤ+b << b' *)elseNbminf_infwithZ.Overflow->Nbminf_inf(** Bitshift left: multiplication by a power of 2. *)letshift_right((a,b):t)((a',b'):t):t_with_bot=tryifa'=Z.zerothenifb'<Z.zerothenBOT(* aℤ+b >> negative constant *)elseletbb'=Z.to_intb'inletz=Z.shift_leftZ.onebb'inifa=Z.zerothenNb(cst(Z.shift_rightbbb'))(* b >> b' *)elseifdividesza&÷szbthenNb(of_z(Z.shift_rightabb')(Z.shift_rightbbb'))(* aℤ+b >> b' exact *)elseNbminf_infelseNbminf_infwithZ.Overflow->Nbminf_inf(** Bitshift right: division by a power of 2 rounding towards -∞. *)letshift_right_trunc((a,b):t)((a',b'):t):t_with_bot=tryifa'=Z.zerothenifb'<Z.zerothenBOT(* aℤ+b >> negative constant *)elseletbb'=Z.to_intb'inletz=Z.shift_leftZ.onebb'inifa=Z.zerothenNb(cst(Z.shift_right_truncbbb'))(* b >> b' *)elseifdividesza&÷szbthenNb(of_z(Z.shift_rightabb')(Z.shift_rightbbb'))(* aℤ+b >> b' exact *)elseNbminf_infelseNbminf_infwithZ.Overflow->Nbminf_inf(** Unsigned bitshift right: division by a power of 2 with truncation. *)letbit_not(ab:t):t=pred(negab)(** Bitwise negation: ~x = -x-1 *)(* TODO: other bitwise operations? *)(** {2 Filters} *)(** Given two interval aruments, return the arguments assuming that the predicate holds.
*)letfilter_eq(ab:t)(ab':t):(t*t)with_bot=matchmeetabab'withBOT->BOT|Nbx->Nb(x,x)letfilter_sglop((a,b)asab:t)((a',b')asab':t):(t*t)with_bot=ifa=Z.zero&&a'=Z.zero&¬(opbb')thenBOTelseNb(ab,ab')(* utility function: we only handle the simple case of singletons *)letfilter_neq=filter_sgl(<>)letfilter_leq=filter_sgl(<=)letfilter_geq=filter_sgl(>=)letfilter_lt=filter_sgl(<)letfilter_gt=filter_sgl(>)(** {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_neg(a:t)(r:t):t_with_bot=meeta(negr)letbwd_abs(a:t)(r:t):t_with_bot=meeta(joinr(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_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 and intersected with the result. *)letbwd_rem(a:t)((b1,b2)asb:t)(r:t)=(* r = a % b => a = (a/b)*b + r => a in bℤ + r *)bot_merge2(meeta(add(of_z(gcdb1b2)Z.zero)r))(Nbb)letbwd_div(a:t)(b:t)(r:t)=Nb(a,b)letbwd_wrap(a:t)range(r:t):t_with_bot=Nbaletbwd_shift_left(a:t)(b:t)(r:t)=Nb(a,b)letbwd_shift_right(a:t)(b:t)(r:t)=Nb(a,b)letbwd_shift_right_trunc(a:t)(b:t)(r:t)=Nb(a,b)(* TODO: more precise backward for those, and also bit-wise operations *)(** {2 Reduction} *)letmeet_inter((a,b):t)((l,h):I.t):(t*I.t)with_bot=(* smallest element in aℤ+b greater or equal to l *)letl'=matchlwith|B.Finitef->B.Finite(Z.addf(rem_zero(Z.subbf)a))|_->l(* greatest element in aℤ+b smaller or equal to h *)andh'=matchhwith|B.Finitef->B.Finite(Z.subf(rem_zero(Z.subfb)a))|_->hinmatchl',h'with|B.Finitell,B.Finitehh->ifll>hhthenBOTelseifll=hhthenNb(cstll,(l',h'))elseNb((a,b),(l',h'))|_->Nb((a,b),(l',h'))(** Intersects a congruence with an interval, and returns the set represented
both as a congruence and as an interval.
Useful to implement reductions.
*)