123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(**
IntBound - Enriches arbitrary precision integers with +∞ and -∞.
Useful as interval bounds.
*)(** {2 Types} *)typet=FiniteofZ.t|MINF(** -∞ *)|PINF(** +∞ *)(** {2 Internal utilities} *)(** {2 Constructors} *)letzero:t=FiniteZ.zeroletone:t=FiniteZ.oneletminus_one:t=FiniteZ.minus_one(** Useful constants *)letof_int(x:int):t=Finite(Z.of_intx)letof_int64(x:int64):t=Finite(Z.of_int64x)(** Exact conversion from machine integers. *)letof_float(x:float):t=matchclassify_floatxwith|FP_infinite->ifx>=0.thenPINFelseMINF|FP_nan->invalid_arg"IntBound.of_float"|_->Finite(Z.of_floatx)(** Conversion with truncation from floats. Handles infinites. Raises an exception on NaNs. *)letinfinite(sign:int):t=ifsign>0thenPINFelseifsign<0thenMINFelsezero(** Constructs an infinity with the given sign. Zero maps to zero. *)letpow2(nb:int):t=Finite(Z.shift_leftZ.onenb)(** A power of two. The argument must be positive. *)(** {2 Predicates} *)letsign(x:t):int=matchxwithMINF->-1|PINF->1|Finitex->Z.signx(** Sign of x: -1, 0, or 1. *)letis_finite(x:t):bool=matchxwith|Finite_->true|_->false(** Whether x is finite or not. *)letequal(x:t)(y:t):bool=matchx,ywith|Finitea,Finiteb->Z.equalab|MINF,MINF|PINF,PINF->true|_->false(** Equality comparison. = also works. *)letcompare(x:t)(y:t):int=matchx,ywith|PINF,PINF|MINF,MINF->0|MINF,_|_,PINF->-1|PINF,_|_,MINF->1|Finitex,Finitey->Z.comparexy(** Total order. Returns -1 (strictly smaller), 0 (equal), or 1 (strictly greater). *)letleq(x:t)(y:t):bool=comparexy<=0letgeq(x:t)(y:t):bool=comparexy>=0letlt(x:t)(y:t):bool=comparexy<0letgt(x:t)(y:t):bool=comparexy>0leteq(x:t)(y:t):bool=equalxyletneq(x:t)(y:t):bool=not(equalxy)(** Comparison predicates. *)letmin(x:t)(y:t):t=ifleqxythenxelseyletmax(x:t)(y:t):t=ifleqxythenyelsex(** Minimum and maximum. *)letis_zero(x:t):bool=signx=0letis_nonzero(x:t):bool=signx<>0letis_positive(x:t):bool=signx>=0letis_negative(x:t):bool=signx<=0letis_positive_strict(x:t):bool=signx>0letis_negative_strict(x:t):bool=signx<0(** Sign predicates. *)lethash(a:t):int=matchawithPINF->1|MINF->-1|Finitex->Z.hashx(** Hashing function. *)(** {2 Printing} *)letto_string(x:t):string=matchxwith|PINF->"+∞"|MINF->"-∞"|Finitex->Z.to_stringxletprintch(x:t)=output_stringch(to_stringx)letfprintch(x:t)=Format.pp_print_stringch(to_stringx)letbprintch(x:t)=Buffer.add_stringch(to_stringx)(** {2 Operators} *)letsucc(a:t):t=matchawithFinitex->Finite(Z.succx)|_->a(** +1. Infinities are left unchanged. *)letpred(a:t):t=matchawithFinitex->Finite(Z.predx)|_->a(** -1. Infinities are left unchanged. *)letneg(a:t):t=matchawithMINF->PINF|PINF->MINF|Finitex->Finite(Z.negx)(** Negation. *)letabs(a:t):t=matchawithMINF|PINF->PINF|Finitex->Finite(Z.absx)(** Absolute value. *)letadd(a:t)(b:t):t=matcha,bwith|PINF,MINF|MINF,PINF->invalid_arg"IntBound.add"|PINF,_|_,PINF->PINF|MINF,_|_,MINF->MINF|Finitex,Finitey->Finite(Z.addxy)(** Addition. +∞ + -∞ is undefined (invalid argument exception). *)letsub(a:t)(b:t):t=matcha,bwith|PINF,PINF|MINF,MINF->invalid_arg"IntBound.sub"|PINF,_|_,MINF->PINF|MINF,_|_,PINF->MINF|Finitex,Finitey->Finite(Z.subxy)(** Subtraction. +∞ - +∞ is undefined (invalid argument exception). *)letmul(a:t)(b:t)=matcha,bwith|Finitex,Finitey->Finite(Z.mulxy)|_->infinite(signa*signb)(** Multiplication. Always defined: +∞ * 0 = 0 *)letdiv(a:t)(b:t):t=matchbwith|PINF|MINF->zero|Finitey->matchawith|PINF->infinite(Z.signy)|MINF->infinite(-(Z.signy))|Finitex->ify=Z.zerotheninfinite(Z.signx)elseFinite(Z.divxy)(** Division. Always defined: 0 / 0 = 0, x / +∞ = 0, x / 0 = sign(x) * ∞ *)letediv(a:t)(b:t):t=matchbwith|PINF|MINF->zero|Finitey->matchawith|PINF->infinite(Z.signy)|MINF->infinite(-(Z.signy))|Finitex->ify=Z.zerotheninfinite(Z.signx)elseFinite(Z.edivxy)(** Euclidian division. Always defined: 0 / 0 = 0, x / +∞ = 0, x / 0 = sign(x) * ∞ *)letrem(a:t)(b:t):t=matchawith|PINF|MINF->invalid_arg"IntBound.rem INF"|Finitex->matchbwith|PINF|MINF->a|Finitey->ify=Z.zerotheninvalid_arg"IntBound.rem ZERO"elseFinite(Z.remxy)(** Remainder. rem x y has the sign of x, rem x (-y) = rem x y, and rem x +∞ = x.
rem +∞ y and rem x 0 are undefined (invalid argument exception).
*)leterem(a:t)(b:t):t=matchawith|PINF|MINF->invalid_arg"IntBound.rem"|Finitex->matchbwith|PINF|MINF->a|Finitey->ify=Z.zerotheninvalid_arg"IntBound.rem"elseFinite(Z.eremxy)(** Euclidian remainder. erem x y >= 0, rem x y < |b| and a = b * ediv a b + erem a b.
rem +∞ y and rem x 0 are undefined (invalid argument exception).
*)letshift_left(a:t)(b:t):t=matchawith|PINF->PINF|MINF->MINF|Finitex->matchbwith|PINF->infinite(signa)|FiniteywhenZ.geqyZ.zero->(tryifZ.geqy(Z.of_int2048)then(* let's avoid allocating xx GB memory of Z.t *)raiseZ.Overflowelseletr=(Z.shift_leftx(Z.to_inty))inFiniterwithZ.Overflow->infinite(signa))|_->invalid_arg"IntBound.shift_left"(** Left bitshift.
Undefined if the second argument is negative (invalid argument exception).
Returns an infinity if the second argument is too large.
*)letshift_right(a:t)(b:t):t=matchawith|PINF->PINF|MINF->MINF|Finitex->matchbwith|PINF->zero|FiniteywhenZ.geqyZ.zero->(tryFinite(Z.shift_rightx(Z.to_inty))withZ.Overflow->zero)|_->invalid_arg"IntBound.shift_right"(** Right bitshift, rounding towards -∞.
Undefined if the second argument is negative (invalid argument exception).
Returns zero if the second argument is too large.
*)letshift_right_trunc(a:t)(b:t):t=matchawith|PINF->PINF|MINF->MINF|Finitex->matchbwith|PINF->zero|FiniteywhenZ.geqyZ.zero->(tryFinite(Z.shift_right_truncx(Z.to_inty))withZ.Overflow->zero)|_->invalid_arg"IntBound.shift_right_trunc"(** Right bitshift, rounding towards 0 (truncation).
Undefined if the second argument is negative (invalid argument exception).
Returns zero if the second argument is too large.
*)letonly_finitemsgopab=matcha,bwith|Finitex,Finitey->Finite(opxy)|_->invalid_argmsgletbit_or:t->t->t=only_finite"IntBound.bit_or"Z.logorletbit_xor:t->t->t=only_finite"IntBound.bit_xor"Z.logxorletbit_and:t->t->t=only_finite"IntBound.bit_and"Z.logand(** Bitwise operations. Undefined for infinites (invalid argument exception). *)letpow(a:t)(b:t):t=matcha,bwith|Finitex,FiniteywhenZ.geqyZ.zero->(tryifZ.geqy(Z.of_int2048)then(* let's avoid allocating xx GB memory of Z.t *)raiseZ.OverflowelseFinite(Z.powx(Z.to_inty))withZ.Overflow->invalid_arg"IntBound.pow")|_->invalid_arg"IntBound.pow"(** Power. Undefined if the second argument is negative or too large. *)