From b9b4b79ee02bb02ca1f6d40ef296a43c36af5147 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr>
Date: Mon, 14 Jan 2019 15:51:55 +0100
Subject: [PATCH] [Ival] New files int_set and int_interval.

They respectively implements the semantics of small integer sets and integer
intervals.
---
 Makefile                                      |   3 +
 headers/header_spec.txt                       |   5 +
 .../abstract_interp/eva_lattice_type.mli      |  88 +++
 .../abstract_interp/int_interval.ml           | 737 ++++++++++++++++++
 .../abstract_interp/int_interval.mli          |  71 ++
 .../abstract_interp/int_set.ml                | 647 +++++++++++++++
 .../abstract_interp/int_set.mli               |  94 +++
 7 files changed, 1645 insertions(+)
 create mode 100644 src/kernel_services/abstract_interp/eva_lattice_type.mli
 create mode 100644 src/kernel_services/abstract_interp/int_interval.ml
 create mode 100644 src/kernel_services/abstract_interp/int_interval.mli
 create mode 100644 src/kernel_services/abstract_interp/int_set.ml
 create mode 100644 src/kernel_services/abstract_interp/int_set.mli

diff --git a/Makefile b/Makefile
index 483ffe220a2..6c41594b0d7 100644
--- a/Makefile
+++ b/Makefile
@@ -587,6 +587,8 @@ KERNEL_CMO=\
 	src/kernel_services/abstract_interp/fc_float.cmo                \
 	src/kernel_services/abstract_interp/float_interval.cmo          \
 	src/kernel_services/abstract_interp/fval.cmo                    \
+	src/kernel_services/abstract_interp/int_interval.cmo            \
+	src/kernel_services/abstract_interp/int_set.cmo                 \
 	src/kernel_services/abstract_interp/ival.cmo                    \
 	src/kernel_services/abstract_interp/base.cmo                    \
 	src/kernel_services/abstract_interp/origin.cmo                  \
@@ -633,6 +635,7 @@ MLI_ONLY+=\
 	src/kernel_services/abstract_interp/float_sig.mli                    \
 	src/kernel_services/abstract_interp/float_interval_sig.mli           \
 	src/kernel_services/abstract_interp/lattice_type.mli                 \
+	src/kernel_services/abstract_interp/eva_lattice_type.mli             \
 	src/kernel_services/abstract_interp/int_Intervals_sig.mli            \
 	src/kernel_services/abstract_interp/offsetmap_lattice_with_isotropy.mli \
 	src/kernel_services/abstract_interp/offsetmap_sig.mli                \
diff --git a/headers/header_spec.txt b/headers/header_spec.txt
index 5167c3bb180..a84301e7133 100644
--- a/headers/header_spec.txt
+++ b/headers/header_spec.txt
@@ -412,6 +412,7 @@ src/kernel_services/abstract_interp/base.ml: CEA_LGPL
 src/kernel_services/abstract_interp/base.mli: CEA_LGPL
 src/kernel_services/abstract_interp/bottom.ml: CEA_LGPL
 src/kernel_services/abstract_interp/bottom.mli: CEA_LGPL
+src/kernel_services/abstract_interp/eva_lattice_type.mli: CEA_LGPL
 src/kernel_services/abstract_interp/fc_float.ml: CEA_LGPL
 src/kernel_services/abstract_interp/fc_float.mli: CEA_LGPL
 src/kernel_services/abstract_interp/float_sig.mli: CEA_LGPL
@@ -425,6 +426,10 @@ src/kernel_services/abstract_interp/int_Base.mli: CEA_LGPL
 src/kernel_services/abstract_interp/int_Intervals.ml: CEA_LGPL
 src/kernel_services/abstract_interp/int_Intervals.mli: CEA_LGPL
 src/kernel_services/abstract_interp/int_Intervals_sig.mli: CEA_LGPL
+src/kernel_services/abstract_interp/int_interval.ml: CEA_LGPL
+src/kernel_services/abstract_interp/int_interval.mli: CEA_LGPL
+src/kernel_services/abstract_interp/int_set.ml: CEA_LGPL
+src/kernel_services/abstract_interp/int_set.mli: CEA_LGPL
 src/kernel_services/abstract_interp/ival.ml: CEA_LGPL
 src/kernel_services/abstract_interp/ival.mli: CEA_LGPL
 src/kernel_services/abstract_interp/lattice_messages.ml: CEA_LGPL
diff --git a/src/kernel_services/abstract_interp/eva_lattice_type.mli b/src/kernel_services/abstract_interp/eva_lattice_type.mli
new file mode 100644
index 00000000000..99d58f9ed1b
--- /dev/null
+++ b/src/kernel_services/abstract_interp/eva_lattice_type.mli
@@ -0,0 +1,88 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2019                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  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, version 2.1.                                              *)
+(*                                                                        *)
+(*  It 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.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(** Lattice signatures using the Bottom type:
+    these lattices do not include a bottom element, and return `Bottom instead
+    when needed. Except that, they are identical to the module signatures in
+    {!Lattice_type}. *)
+
+open Bottom.Type
+
+module type Join_Semi_Lattice = Lattice_type.Join_Semi_Lattice
+module type With_Top = Lattice_type.With_Top
+module type With_Intersects = Lattice_type.With_Intersects
+module type With_Enumeration = Lattice_type.With_Enumeration
+module type With_Cardinal_One = Lattice_type.With_Cardinal_One
+module type With_Widening = Lattice_type.With_Widening
+
+module type With_Narrow = sig
+  type t
+  val narrow: t -> t -> t or_bottom (** over-approximation of intersection *)
+end
+
+module type With_Under_Approximation = sig
+  type t
+  val link: t -> t -> t (** under-approximation of union *)
+  val meet: t -> t -> t or_bottom (** under-approximation of intersection *)
+end
+
+module type With_Diff = sig
+  type t
+  val diff : t -> t -> t or_bottom
+  (** [diff t1 t2] is an over-approximation of [t1-t2]. [t2] must
+      be an under-approximation or exact. *)
+end
+
+module type With_Diff_One = sig
+  type t
+  val diff_if_one : t -> t -> t or_bottom
+  (** [diff_of_one t1 t2] is an over-approximation of [t1-t2].
+      @return [t1] if [t2] is not a singleton. *)
+end
+
+(** {2 Common signatures} *)
+
+(** Signature shared by some functors of module {!Abstract_interp}. *)
+module type AI_Lattice_with_cardinal_one = sig
+  include Join_Semi_Lattice
+  include With_Top with type t:= t
+  include With_Widening with type t:= t
+  include With_Cardinal_One with type t := t
+  include With_Narrow with type t := t
+  include With_Under_Approximation with type t := t
+  include With_Intersects with type t := t
+end
+
+(** Most complete lattices: all operations plus widening, notion of cardinal
+    (including enumeration) and difference. *)
+module type Full_AI_Lattice_with_cardinality = sig
+  include AI_Lattice_with_cardinal_one
+  include With_Diff with type t := t
+  include With_Diff_One with type t := t
+  include With_Enumeration with type t := t
+end
+
+(*
+Local Variables:
+compile-command: "make -C ../../.."
+End:
+*)
diff --git a/src/kernel_services/abstract_interp/int_interval.ml b/src/kernel_services/abstract_interp/int_interval.ml
new file mode 100644
index 00000000000..c8af47fe9bf
--- /dev/null
+++ b/src/kernel_services/abstract_interp/int_interval.ml
@@ -0,0 +1,737 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2019                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  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, version 2.1.                                              *)
+(*                                                                        *)
+(*  It 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.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Abstract_interp
+
+let emitter = Lattice_messages.register "Int_interval";;
+let log_imprecision s = Lattice_messages.emit_imprecision emitter s
+
+(* Represents the interval between [min] and [max], congruent to [rem] modulo
+   [modulo]. A value of [None] for [min] (resp. [max]) represents -infinity
+   (resp. +infinity). [modulo] is > 0, and [0 <= rem < modulo]. *)
+type itv = { min: Int.t option;
+             max: Int.t option;
+             rem: Int.t;
+             modu: Int.t; }
+
+let top = { min = None; max = None; rem = Int.zero; modu = Int.one }
+
+(* ------------------------------ Datatype ---------------------------------- *)
+
+let bound_compare x y =
+  match x, y with
+  | None, None -> 0
+  | None, Some _ -> 1
+  | Some _, None -> -1
+  | Some x, Some y -> Int.compare x y
+
+let compare t1 t2 =
+  let r1 = bound_compare t1.min t2.min in
+  if r1 <> 0 then r1
+  else
+    let r2 = bound_compare t1.max t2.max in
+    if r2 <> 0 then r2
+    else
+      let r3 = Int.compare t1.rem t2.rem in
+      if r3 <> 0 then r3
+      else Int.compare t1.modu t2.modu
+
+let equal t1 t2 = compare t1 t2 = 0
+
+let hash_v_option = function
+  | None -> 97
+  | Some v -> Int.hash v
+
+let hash t =
+  hash_v_option t.min + 5501 * (hash_v_option t.max) +
+  59 * (Int.hash t.rem) + 13031 * (Int.hash t.modu)
+
+let pretty fmt t =
+  let print_bound fmt = function
+    | None -> Format.fprintf fmt "--"
+    | Some v -> Int.pretty fmt v
+  in
+  Format.fprintf fmt "[%a..%a]%t"
+    print_bound t.min
+    print_bound t.max
+    (fun fmt ->
+       if Int.is_zero t.rem && Int.is_one t.modu then Format.fprintf fmt ""
+       else Format.fprintf fmt ",%a%%%a" Int.pretty t.rem Int.pretty t.modu)
+
+include Datatype.Make_with_collections
+    (struct
+      type t = itv
+      let name = "int_interval"
+      open Structural_descr
+      let structural_descr =
+        let s_int = Descr.str Int.descr in
+        t_record [| pack (t_option s_int);
+                    pack (t_option s_int);
+                    Int.packed_descr;
+                    Int.packed_descr |]
+      let reprs = [ top ]
+      let equal = equal
+      let compare = compare
+      let hash = hash
+      let pretty = pretty
+      let rehash = Datatype.identity
+      let internal_pretty_code = Datatype.pp_fail
+      let mem_project = Datatype.never_any_project
+      let copy = Datatype.undefined
+      let varname = Datatype.undefined
+    end)
+
+(* ------------------------------ Building ---------------------------------- *)
+
+let share_top t =
+  if equal t top then top else t
+
+let fail min max r modu =
+  let bound fmt = function
+    | None -> Format.fprintf fmt "--"
+    | Some(x) -> Int.pretty fmt x
+  in
+  Kernel.fatal "Ival: broken interval, min=%a max=%a r=%a modu=%a"
+    bound min bound max Int.pretty r Int.pretty modu
+
+let is_safe_modulo r modu =
+  (Int.ge r Int.zero ) && (Int.ge modu Int.one) && (Int.lt r modu)
+
+let is_safe_bound bound r modu = match bound with
+  | None -> true
+  | Some m -> Int.equal (Int.e_rem m modu) r
+
+(* Sanity check. *)
+let check t =
+  if not (is_safe_modulo t.rem t.modu
+          && is_safe_bound t.min t.rem t.modu
+          && is_safe_bound t.max t.rem t.modu)
+  then fail t.min t.max t.rem t.modu
+
+let make ~min ~max ~rem ~modu =
+  let t = { min; max; rem; modu } in
+  check t;
+  share_top t
+
+let inject_singleton e =
+  { min = Some e; max = Some e; rem = Int.zero; modu = Int.one }
+
+let inject_range min max =
+  let t = { min; max; rem = Int.zero; modu = Int.one } in
+  share_top t
+
+let build_interval ~min ~max ~rem:r ~modu =
+  assert (is_safe_modulo r modu);
+  let fix_bound fix bound = match bound with
+    | None -> None
+    | Some b -> Some (if Int.equal b (Int.e_rem r modu) then b else fix b)
+  in
+  let min = fix_bound (fun min -> Int.round_up_to_r ~min ~r ~modu) min
+  and max = fix_bound (fun max -> Int.round_down_to_r ~max ~r ~modu) max in
+  make ~min ~max ~rem:r ~modu
+
+let make_or_bottom ~min ~max ~rem ~modu =
+  match min, max with
+  | Some min, Some max when Int.gt min max -> `Bottom
+  | _, _ -> `Value (make ~min ~max ~rem ~modu)
+
+(* ---------------------------------- Utils --------------------------------- *)
+
+let min_le_elt min elt =
+  match min with
+  | None -> true
+  | Some m -> Int.le m elt
+
+let max_ge_elt max elt =
+  match max with
+  | None -> true
+  | Some m -> Int.ge m elt
+
+let all_positives t =
+  match t.min with
+  | None -> false
+  | Some m -> Int.ge m Int.zero
+
+let all_negatives t =
+  match t.max with
+  | None -> false
+  | Some m -> Int.le m Int.zero
+
+let min_and_max t = t.min, t.max
+
+let min_max_rem_modu t = t.min, t.max, t.rem, t.modu
+
+let mem i t =
+  Int.equal (Int.e_rem i t.modu) t.rem
+  && min_le_elt t.min i && max_ge_elt t.max i
+
+let cardinal t =
+  match t.min, t.max with
+  | None, _ | _, None -> None
+  | Some mn, Some mx -> Some Int.(succ ((e_div (sub mx mn) t.modu)))
+
+(* --------------------------------- Lattice -------------------------------- *)
+
+(** [min_is_lower mn1 mn2] is true iff mn1 is a lower min than mn2 *)
+let min_is_lower min1 min2 =
+  match min1, min2 with
+  | None, _ -> true
+  | _, None -> false
+  | Some m1, Some m2 -> Int.le m1 m2
+
+(** [max_is_greater mx1 mx2] is true iff mx1 is a greater max than mx2 *)
+let max_is_greater max1 max2 =
+  match max1, max2 with
+  | None, _ -> true
+  | _, None -> false
+  | Some m1, Some m2 -> Int.ge m1 m2
+
+let rem_is_included r1 m1 r2 m2 =
+  (Int.is_zero (Int.e_rem m1 m2)) && (Int.equal (Int.e_rem r1 m2) r2)
+
+let is_included t1 t2 =
+  (t1 == t2) ||
+  (min_is_lower t2.min t1.min) &&
+  (max_is_greater t2.max t1.max) &&
+  rem_is_included t1.rem t1.modu t2.rem t2.modu
+
+let min_min x y =
+  match x, y with
+  | None,_ | _, None -> None
+  | Some x, Some y -> Some (Int.min x y)
+
+let max_max x y =
+  match x, y with
+  | None,_ | _, None -> None
+  | Some x, Some y -> Some (Int.max x y)
+
+let join t1 t2 =
+  if t1 == t2 then t1 else
+    begin
+      check t1;
+      check t2;
+      let modu = Int.(pgcd (pgcd t1.modu t2.modu) (abs (sub t1.rem t2.rem))) in
+      let rem = Int.e_rem t1.rem modu in
+      let min = min_min t1.min t2.min in
+      let max = max_max t1.max t2.max in
+      let r  = make ~min ~max ~rem ~modu in
+      r
+    end
+
+let link t1 t2 =
+  if Int.equal t1.rem t2.rem && Int.equal t1.modu t2.modu
+  then
+    let min = match t1.min, t2.min with
+      | Some a, Some b -> Some (Int.min a b)
+      | _ -> None in
+    let max = match t1.max, t2.max with
+      | Some a, Some b -> Some (Int.max a b)
+      | _ -> None in
+    make ~min ~max ~rem:t1.rem ~modu:t1.modu
+  else t1 (* No best abstraction anyway. *)
+
+(* [extended_euclidian_algorithm a b] returns x,y,gcd such that
+   a*x+b*y=gcd(x,y). *)
+let extended_euclidian_algorithm a b =
+  assert (Int.gt a Int.zero);
+  assert (Int.gt b Int.zero);
+  let a = ref a and b = ref b in
+  let x = ref Int.zero and lastx = ref Int.one in
+  let y = ref Int.one and lasty = ref Int.zero in
+  while not (Int.is_zero !b) do
+    let (q,r) = Int.e_div_rem !a !b in
+    a := !b;
+    b := r;
+    let tmpx = !x in
+    (x:= Int.sub !lastx (Int.mul q !x); lastx := tmpx);
+    let tmpy = !y in
+    (y:= Int.sub !lasty (Int.mul q !y); lasty := tmpy);
+  done;
+  (!lastx, !lasty, !a)
+
+(* This function provides solutions to the Chinese remainder theorem,
+   i.e. it finds the solutions x such that:
+   x == r1 mod m1 && x == r2 mod m2.
+   If no such solution exists, it raises Error_Bottom; else it returns
+   (r,m) such that all solutions x are such that x == r mod m. *)
+let compute_r_common r1 m1 r2 m2 =
+  (* (E1) x == r1 mod m1 && x == r2 mod m2
+     <=> \E k1,k2: x = r1 + k1*m1 && x = r2 + k2*m2
+     <=> \E k1,k2: x = r1 + k1*m1 && k1*m1 - k2*m2 = r2 - r1
+
+     Let r = r2 - r1. The equation (E2): k1*m1 - k2*m2 = r is
+     diophantine; there are solutions x to (E1) iff there are
+     solutions (k1,k2) to (E2).
+
+     Let d = pgcd(m1,m2). There are solutions to (E2) only if d
+     divides r (because d divides k1*m1 - k2*m2). Else we raise
+     [Error_Bottom]. *)
+  let (x1,_,pgcd) = extended_euclidian_algorithm m1 m2 in
+  let r = Int.sub r2 r1 in
+  let (r_div, r_rem) = Int.e_div_rem r pgcd in
+  if not (Int.equal r_rem Int.zero)
+  then raise Error_Bottom
+  (* The extended euclidian algorithm has provided solutions x1,x2 to
+     the Bezout identity x1*m1 + x2*m2 = d.
+
+     x1*m1 + x2*m2 = d ==> x1*(r/d)*m1 + x2*(r/d)*m2 = d*(r/d).
+
+     Thus, k1 = x1*(r/d), k2=-x2*(r/d) are solutions to (E2)
+     Thus, x = r1 + x1*(r/d)*m1 is a particular solution to (E1). *)
+  else
+    let k1 = Int.mul x1 r_div in
+    let x = Int.add r1 (Int.mul k1 m1) in
+    (* If two solutions x and y exist, they are equal modulo ppcm(m1,m2).
+       We have x == r1 mod m1 && y == r1 mod m1 ==> \E k1: x - y = k1*m1
+       x == r2 mod m2 && y == r2 mod m2 ==> \E k2: x - y = k2*m2
+
+       Thus k1*m1 = k2*m2 is a multiple of m1 and m2, i.e. is a multiple
+       of ppcm(m1,m2). Thus x = y mod ppcm(m1,m2). *)
+    let ppcm = Integer.ppcm m1 m2 in
+    (* x may be bigger than the ppcm, we normalize it. *)
+    (Int.e_rem x ppcm, ppcm)
+
+let compute_first_common mn1 mn2 r modu =
+  if mn1 = None && mn2 = None
+  then None
+  else
+    let m =
+      match mn1, mn2 with
+      | Some m, None | None, Some m -> m
+      | Some m1, Some m2 -> Int.max m1 m2
+      | None, None -> assert false (* already tested above *)
+    in
+    Some (Int.round_up_to_r m r modu)
+
+let compute_last_common mx1 mx2 r modu =
+  if mx1 = None && mx2 = None
+  then None
+  else
+    let m =
+      match mx1, mx2 with
+      | Some m, None | None, Some m -> m
+      | Some m1, Some m2 -> Int.min m1 m2
+      | None, None -> assert false (* already tested above *)
+    in
+    Some (Int.round_down_to_r m r modu)
+
+let meet t1 t2 =
+  if t1 == t2 then `Value t1 else
+    try
+      let rem, modu = compute_r_common t1.rem t1.modu t2.rem t2.modu in
+      let min = compute_first_common t1.min t2.min rem modu in
+      let max = compute_last_common t1.max t2.max rem modu in
+      make_or_bottom ~min ~max ~rem ~modu
+    with Error_Bottom -> `Bottom
+
+let narrow = meet
+
+type size_widen_hint = Integer.t
+type generic_widen_hint = Datatype.Integer.Set.t
+type widen_hint = size_widen_hint * generic_widen_hint
+
+let widen (bitsize,wh) t1 t2 =
+  if equal t1 t2 then t2
+  else
+    (* Add possible interval limits deducted from the bitsize *)
+    let wh =
+      (* If bitsize > 128, the values do not correspond to a scalar type.
+         This can (rarely) happen on structures or arrays that have been
+         reinterpreted as one value by the offsetmaps. In this case, do not
+         use limits, and do not create arbitrarily large integers. *)
+      if Integer.gt bitsize (Integer.of_int 128)
+      then Datatype.Integer.Set.empty
+      else if Integer.is_zero bitsize
+      then wh
+      else
+        let limits =
+          [ Integer.neg (Integer.two_power (Integer.pred bitsize));
+            Integer.pred (Integer.two_power (Integer.pred bitsize));
+            Integer.pred (Integer.two_power bitsize); ]
+        in
+        Datatype.Integer.Set.(union wh (of_list limits))
+    in
+    let modu = Int.(pgcd (pgcd t1.modu t2.modu) (abs (sub t1.rem t2.rem))) in
+    let rem = Int.e_rem t1.rem modu in
+    let min =
+      if bound_compare t1.min t2.min = 0 then t2.min else
+        match t2.min with
+        | None -> None
+        | Some min2 ->
+          try
+            let v = Datatype.Integer.Set.nearest_elt_le min2 wh
+            in Some (Int.round_up_to_r ~r:rem ~modu ~min:v)
+          with Not_found -> None
+    in
+    let max =
+      if bound_compare t1.max t2.max = 0 then t2.max else
+        match t2.max with
+        | None -> None
+        | Some max2 ->
+          try
+            let v = Datatype.Integer.Set.nearest_elt_ge max2 wh
+            in Some (Int.round_down_to_r ~r:rem ~modu ~max:v)
+          with Not_found -> None
+    in
+    make ~min ~max ~rem ~modu
+
+let intersects v1 v2 =
+  v1 == v2 || not (meet v1 v2 = `Bottom) (* YYY: slightly inefficient *)
+
+let cardinal_less_than t n =
+  let c = match t.min, t.max with
+    | None, _ | _, None -> raise Not_less_than
+    | Some min, Some max -> Int.succ ((Int.e_div (Int.sub max min) t.modu))
+  in
+  if Int.le c (Int.of_int n)
+  then Int.to_int c (* This is smaller than the original [n] *)
+  else raise Not_less_than
+
+let cardinal_zero_or_one t =
+  match t.min, t.max with
+  | None, _ | _, None -> false
+  | Some min, Some max -> Integer.equal min max
+
+(* TODO? *)
+let diff _ _ = assert false
+let diff_if_one _ _ = assert false
+
+let fold_int f t acc =
+  match t.min, t.max with
+  | None, _ | _, None -> raise Error_Top
+  | Some inf, Some sup -> Int.fold f ~inf ~sup ~step:t.modu acc
+
+let fold_enum f v acc =
+  fold_int (fun x acc -> f (inject_singleton x) acc) v acc
+
+(* ------------------------------ Arithmetics ------------------------------- *)
+
+let opt_map2 f m1 m2 =
+  match m1, m2 with
+  | None, _ | _, None -> None
+  | Some m1, Some m2 -> Some (f m1 m2)
+
+let add_singleton_int i t =
+  let incr = Int.add i in
+  let min = Extlib.opt_map incr t.min in
+  let max = Extlib.opt_map incr t.max in
+  let rem = Int.e_rem (incr t.rem) t.modu in
+  make ~min ~max ~rem ~modu:t.modu
+
+let add t1 t2 =
+  let modu = Int.pgcd t1.modu t2.modu in
+  let rem = Int.e_rem (Int.add t1.rem t2.rem) modu in
+  let min =
+    opt_map2
+      (fun min1 min2 -> Int.round_up_to_r ~min:(Int.add min1 min2) ~r:rem ~modu)
+      t1.min t2.min
+  in
+  let max =
+    opt_map2
+      (fun m1 m2 -> Int.round_down_to_r ~max:(Int.add m1 m2) ~r:rem ~modu)
+      t1.max t2.max
+  in
+  make ~min ~max ~rem ~modu
+
+let add_under t1 t2 =
+  if Int.equal t1.modu t2.modu
+  then
+    (* Note: min1+min2 % modu = max1 + max2 % modu = r1 + r2 % modu;
+       no need to trim the bounds here.  *)
+    let rem = Int.e_rem (Int.add t1.rem t2.rem) t1.modu in
+    let min = opt_map2 Int.add t1.min t2.min
+    and max = opt_map2 Int.add t1.max t2.max in
+    `Value (make ~min ~max ~rem ~modu:t1.modu)
+  else
+    (* In many cases, there is no best abstraction; for instance when
+       modu1 divides modu2, a part of the resulting interval is
+       congruent to modu1, and a larger part is congruent to modu2.  In
+       general, one can take the intersection. In any case, this code
+       should be rarely called. *)
+    `Bottom
+
+let neg t =
+  make
+    ~min:(Extlib.opt_map Int.neg t.max)
+    ~max:(Extlib.opt_map Int.neg t.min)
+    ~rem:(Int.e_rem (Int.neg t.rem) t.modu)
+    ~modu:t.modu
+
+type ext_value = Ninf | Pinf | Val of Int.t
+let inject_min = function None -> Ninf | Some m -> Val m
+let inject_max = function None -> Pinf | Some m -> Val m
+let ext_neg = function Ninf -> Pinf | Pinf -> Ninf | Val v -> Val (Int.neg v)
+let ext_mul x y =
+  match x, y with
+  | Ninf, Ninf | Pinf, Pinf -> Pinf
+  | Ninf, Pinf | Pinf, Ninf -> Ninf
+  | Val v1, Val v2 -> Val (Int.mul v1 v2)
+  | (Ninf | Pinf as x), Val v | Val v, (Ninf | Pinf as x) ->
+    if Int.gt v Int.zero then x
+    else if Int.lt v Int.zero then ext_neg x
+    else Val Int.zero
+
+let ext_min x y =
+  match x,y with
+    Ninf, _ | _, Ninf -> Ninf
+  | Pinf, x | x, Pinf -> x
+  | Val x, Val y -> Val(Int.min x y)
+
+let ext_max x y =
+  match x,y with
+    Pinf, _ | _, Pinf -> Pinf
+  | Ninf, x | x, Ninf -> x
+  | Val x, Val y -> Val(Int.max x y)
+
+let ext_proj = function Val x -> Some x | _ -> None
+
+let scale f t =
+  let incr = Int.mul f in
+  if Int.gt f Int.zero
+  then
+    let modu = incr t.modu in
+    make
+      ~min:(Extlib.opt_map incr t.min) ~max:(Extlib.opt_map incr t.max)
+      ~rem:(Int.e_rem (incr t.rem) modu) ~modu
+  else
+    let modu = Int.neg (incr t.modu) in
+    make
+      ~min:(Extlib.opt_map incr t.max) ~max:(Extlib.opt_map incr t.min)
+      ~rem:(Int.e_rem (incr t.rem) modu) ~modu
+
+let scale_div_common ~pos f t degenerate =
+  assert (not (Int.is_zero f));
+  let div_f =
+    if pos
+    then fun a -> Int.e_div a f
+    else fun a -> Int.c_div a f
+  in
+  let rem, modu =
+    let negative = max_is_greater (Some Int.zero) t.max in
+    if (negative                                (* all negative *)
+        || pos                                  (* good div *)
+        || (min_is_lower (Some Int.zero) t.min) (* all positive *)
+        ||    (Int.is_zero (Int.e_rem t.rem f)))  (* exact *)
+    && (Int.is_zero (Int.e_rem t.modu f))
+    then
+      let modu = Int.abs (div_f t.modu) in
+      let r = if negative then Int.sub t.rem t.modu else t.rem in
+      (Int.e_rem (div_f r) modu), modu
+    else (* degeneration*)
+      degenerate t.rem t.modu
+  in
+  let divf_mn1 = Extlib.opt_map div_f t.min in
+  let divf_mx1 = Extlib.opt_map div_f t.max in
+  let min, max =
+    if Int.gt f Int.zero
+    then divf_mn1, divf_mx1
+    else divf_mx1, divf_mn1
+  in
+  make ~min ~max ~rem ~modu
+
+let scale_div ~pos f v =
+  scale_div_common ~pos f v (fun _ _ -> Int.zero, Int.one)
+
+let scale_div_under ~pos f v =
+  try
+    (* TODO: a more precise result could be obtained by transforming
+       Top(min,max,r,m) into Top(min,max,r/f,m/gcd(m,f)). But this is
+       more complex to implement when pos or f is negative. *)
+    `Value (scale_div_common ~pos f v (fun _r _m -> raise Exit))
+  with Exit -> `Bottom
+
+let scale_rem ~pos f t =
+  assert (not (Int.is_zero f));
+  let f = if Int.lt f Int.zero then Int.neg f else f in
+  let rem_f a = if pos then Int.e_rem a f else Int.c_rem a f in
+  let modu = Int.pgcd f t.modu in
+  let rr = Int.e_rem t.rem modu in
+  let binf, bsup =
+    if pos
+    then Int.round_up_to_r ~min:Int.zero ~r:rr ~modu,
+         Int.round_down_to_r ~max:(Int.pred f) ~r:rr ~modu
+    else
+      let min = if all_positives t then Int.zero else Int.neg (Int.pred f) in
+      let max = if all_negatives t then Int.zero else Int.pred f in
+      Int.round_up_to_r ~min ~r:rr ~modu,
+      Int.round_down_to_r ~max ~r:rr ~modu
+  in
+  let mn_rem, mx_rem =
+    match t.min, t.max with
+    | Some mn, Some mx ->
+      let div_f a = if pos then Int.e_div a f else Int.c_div a f in
+      (* See if [mn..mx] is included in [k*f..(k+1)*f] for some [k]. In
+         this case, [%] is monotonic and [mn%f .. mx%f] is a more precise
+         result. *)
+      if Int.equal (div_f mn) (div_f mx)
+      then rem_f mn, rem_f mx
+      else binf,bsup
+    | _ -> binf,bsup
+  in
+  make ~min:(Some mn_rem) ~max:(Some mx_rem) ~rem:rr ~modu
+
+let c_rem t1 t2 =
+  match t2.min, t2.max with
+  | None, _ | _, None -> `Value top
+  | Some min2, Some max2 ->
+    if Int.equal max2 Int.zero
+    then `Bottom (* completely undefined. *)
+    else
+      (* Result is of the sign of x. Also, compute |x| to bound the result *)
+      let neg, pos, max_x =
+        min_le_elt t1.min Int.minus_one,
+        max_ge_elt t1.max Int.one,
+        (match t1.min, t1.max with
+         | Some mn, Some mx -> Some (Int.max (Int.abs mn) (Int.abs mx))
+         | _ -> None)
+      in
+      (* Bound the result: no more than |x|, and no more than |y|-1 *)
+      let pos_rem = Integer.max (Int.abs min2) (Int.abs max2) in
+      let bound = Int.pred pos_rem in
+      let bound = Extlib.may_map (Int.min bound) ~dft:bound max_x in
+      (* Compute result bounds using sign information *)
+      let min = if neg then Some (Int.neg bound) else Some Int.zero in
+      let max = if pos then Some bound else Some Int.zero in
+      (* TODO: inject_range_or_bottom? *)
+      `Value (inject_range min max)
+
+let mul t1 t2 =
+  check t1;
+  check t2;
+  let min1 = inject_min t1.min in
+  let max1 = inject_max t1.max in
+  let min2 = inject_min t2.min in
+  let max2 = inject_max t2.max in
+  let a = ext_mul min1 min2 in
+  let b = ext_mul min1 max2 in
+  let c = ext_mul max1 min2 in
+  let d = ext_mul max1 max2 in
+  let min = ext_min (ext_min a b) (ext_min c d) in
+  let max = ext_max (ext_max a b) (ext_max c d) in
+  (* let multipl1 = Int.pgcd m1 r1 in
+     let multipl2 = Int.pgcd m2 r2 in
+     let modu1 = Int.pgcd m1 m2 in
+     let modu2 = Int.mul multipl1 multipl2 in
+     let modu = Int.ppcm modu1 modu2 in    *)
+  let modu =
+    Int.(pgcd
+           (pgcd (mul t1.modu t2.modu) (mul t1.rem t2.modu))
+           (mul t2.rem t1.modu))
+  in
+  let rem = Int.e_rem (Int.mul t1.rem t2.rem) modu in
+  make ~min:(ext_proj min) ~max:(ext_proj max) ~rem ~modu
+
+(* ymin and ymax must be the same sign *)
+let div_range x ymn ymx =
+  match min_and_max x with
+  | Some xmn, Some xmx ->
+    let c1 = Int.c_div xmn ymn in
+    let c2 = Int.c_div xmx ymn in
+    let c3 = Int.c_div xmn ymx in
+    let c4 = Int.c_div xmx ymx in
+    let min = Int.min (Int.min c1 c2) (Int.min c3 c4) in
+    let max = Int.max (Int.max c1 c2) (Int.max c3 c4) in
+    inject_range (Some min) (Some max)
+  | _ ->
+    log_imprecision "Ival.div_range";
+    top
+
+let div x y =
+  match y.min, y.max with
+  | None, _ | _, None -> log_imprecision "Ival.div"; `Value top
+  | Some min, Some max ->
+    let result_pos =
+      if Int.gt max Int.zero
+      then
+        let lpos =
+          if Int.gt min Int.zero
+          then min
+          else Int.round_up_to_r ~min:Int.one ~r:y.rem ~modu:y.modu
+        in
+        `Value (div_range x lpos max)
+      else `Bottom
+    in
+    let result_neg =
+      if Int.lt min Int.zero
+      then
+        let gneg =
+          if Int.lt max Int.zero
+          then max
+          else Int.round_down_to_r ~max:Int.minus_one ~r:y.rem ~modu:y.modu
+        in
+        `Value (div_range x min gneg)
+      else `Bottom
+    in
+    Bottom.join join result_neg result_pos
+
+(* ----------------------------------- Misc --------------------------------- *)
+
+let subdivide t =
+  match t.min, t.max with
+  | Some min, Some max ->
+    let mean = Int.e_div (Int.add min max) Int.two in
+    let succmean = Int.succ mean in
+    build_interval ~min:(Some min) ~max:(Some mean) ~rem:t.rem ~modu:t.modu,
+    build_interval ~min:(Some succmean) ~max:(Some max) ~rem:t.rem ~modu:t.modu
+  | _, _ -> raise Can_not_subdiv
+
+let reduce_sign t b =
+  let r, modu = t.rem, t.modu in
+  if b
+  then
+    if all_positives t
+    then `Value t
+    else
+      let max = Some Int.(round_down_to_r ~max:minus_one ~r ~modu) in
+      make_or_bottom ~min:t.min ~max ~rem:r ~modu
+  else
+  if all_negatives t
+  then `Value t
+  else
+    let min = Some Int.(round_up_to_r ~min:zero ~r ~modu) in
+    make_or_bottom ~min ~max:t.max ~rem:r ~modu
+
+let reduce_bit i t b =
+  let r, modu = t.rem, t.modu in
+  let power = Int.(two_power_of_int i) in (* 001000 *)
+  let mask = Int.(pred (two_power_of_int (i+1))) in (* 001111 *)
+  (* Reduce bounds to the nearest satisfying bound *)
+  let min = match t.min with
+    | Some l when Z.testbit l i <> b ->
+      let min =
+        if b
+        then Int.(logor (logand l (lognot mask)) power) (* ll1000 *)
+        else Int.(succ (logor l mask)) (* ll1111 + 1 *)
+      in
+      Some (Int.round_up_to_r ~min ~r ~modu)
+    | l -> l
+  and max = match t.max with
+    | Some u when Z.testbit u i <> b ->
+      let max =
+        if b
+        then Int.(pred (logand u (lognot mask))) (* uu0000 - 1 *)
+        else Int.(logand (logor u mask) (lognot power)) (* uu0111 *)
+      in
+      Some (Int.round_down_to_r ~max ~r ~modu)
+    | u -> u
+  in
+  make_or_bottom ~min ~max ~rem:r ~modu
diff --git a/src/kernel_services/abstract_interp/int_interval.mli b/src/kernel_services/abstract_interp/int_interval.mli
new file mode 100644
index 00000000000..b9d18d50d45
--- /dev/null
+++ b/src/kernel_services/abstract_interp/int_interval.mli
@@ -0,0 +1,71 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2019                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  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, version 2.1.                                              *)
+(*                                                                        *)
+(*  It 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.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Bottom.Type
+
+include Datatype.S_with_collections
+
+include Eva_lattice_type.Full_AI_Lattice_with_cardinality
+  with type t := t
+   and type widen_hint = Integer.t * Datatype.Integer.Set.t
+
+val make:
+  min:Integer.t option -> max:Integer.t option ->
+  rem:Integer.t -> modu:Integer.t -> t
+
+val inject_range: Integer.t option -> Integer.t option -> t
+
+val min_and_max: t -> Integer.t option * Integer.t option
+
+val min_max_rem_modu:
+  t -> Integer.t option * Integer.t option * Integer.t * Integer.t
+
+val mem: Integer.t -> t -> bool
+
+val cardinal: t -> Integer.t option
+
+val add : t -> t -> t
+(** Addition of two integer (ie. not [Float]) ivals. *)
+val add_under : t -> t -> t or_bottom
+(** Underapproximation of the same operation *)
+val add_singleton_int: Integer.t -> t -> t
+(** Addition of an integer ival with an integer. Exact operation. *)
+
+val neg : t -> t
+(** Negation of an integer ival. Exact operation. *)
+
+
+val scale: Integer.t -> t -> t
+val scale_div: pos:bool -> Integer.t -> t -> t
+val scale_div_under: pos:bool -> Integer.t -> t -> t or_bottom
+val scale_rem: pos:bool -> Integer.t -> t -> t
+
+val mul: t -> t -> t
+val div: t -> t -> t or_bottom
+val c_rem: t -> t -> t or_bottom
+
+val subdivide: t -> t * t
+
+val reduce_sign: t -> bool -> t or_bottom
+val reduce_bit: int -> t -> bool -> t or_bottom
+
+val fold_int: (Integer.t -> 'a -> 'a) -> t -> 'a -> 'a
diff --git a/src/kernel_services/abstract_interp/int_set.ml b/src/kernel_services/abstract_interp/int_set.ml
new file mode 100644
index 00000000000..b04d23e1f60
--- /dev/null
+++ b/src/kernel_services/abstract_interp/int_set.ml
@@ -0,0 +1,647 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2019                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  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, version 2.1.                                              *)
+(*                                                                        *)
+(*  It 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.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Abstract_interp
+open Bottom.Type
+
+(* Make sure all this is synchronized with the default value of -ilevel *)
+let small_cardinal = ref 8
+let set_small_cardinal i = small_cardinal := i
+
+let debug_cardinal = false
+
+let emitter = Lattice_messages.register "Int_set";;
+let log_imprecision s = Lattice_messages.emit_imprecision emitter s
+
+type set = Int.t array
+
+let bottom = Array.make 0 Int.zero
+
+let small_nums = Array.init 33 (fun i -> [| (Integer.of_int i) |])
+
+let zero = small_nums.(0)
+let one = small_nums.(1)
+let minus_one = [| Int.minus_one |]
+let zero_or_one = [| Int.zero ; Int.one |]
+
+let inject_singleton e =
+  if Int.le Int.zero e && Int.le e Int.thirtytwo
+  then small_nums.(Int.to_int e)
+  else [| e |]
+
+let unsafe_share_array a s =
+  let e = a.(0) in
+  if s = 1 && Int.le Int.zero e && Int.le e Int.thirtytwo
+  then small_nums.(Int.to_int e)
+  else if s = 2 && Int.is_zero e && Int.is_one a.(1)
+  then zero_or_one
+  else a
+
+(* TODO: assert s <> 0 *)
+let share_array a s =
+  if s = 0 then bottom else unsafe_share_array a s
+
+let share_array_or_bottom a s =
+  if s = 0 then `Bottom else `Value (unsafe_share_array a s)
+
+let inject_array = share_array
+
+let project_array t = t
+
+(* ------------------------------- Datatype --------------------------------- *)
+
+let hash s = Array.fold_left (fun acc v -> 1031 * acc + (Int.hash v)) 17 s
+
+let rehash s = share_array s (Array.length s)
+
+exception Unequal of int
+
+let compare s1 s2 =
+  if s1 == s2 then 0 else
+    let l1 = Array.length s1 in
+    let l2 = Array.length s2 in
+    if l1 <> l2
+    then l1 - l2 (* no overflow here *)
+    else
+      (try
+         for i = 0 to l1 - 1 do
+           let r = Int.compare s1.(i) s2.(i) in
+           if r <> 0 then raise (Unequal r)
+         done;
+         0
+       with Unequal v -> v)
+
+let equal e1 e2 = compare e1 e2 = 0
+
+let pretty fmt s =
+  if Array.length s = 0 then Format.fprintf fmt "BottomMod"
+  else begin
+    Pretty_utils.pp_iter
+      ~pre:"@[<hov 1>{"
+      ~suf:"}@]"
+      ~sep:";@ "
+      Array.iter Int.pretty fmt s
+  end
+
+include Datatype.Make_with_collections
+    (struct
+      type t = set
+      let name = "int_set"
+      open Structural_descr
+      let structural_descr = t_array (Descr.str Int.descr)
+      let reprs = [ zero ]
+      let equal = equal
+      let compare = compare
+      let hash = hash
+      let pretty = pretty
+      let rehash = rehash
+      let internal_pretty_code = Datatype.pp_fail
+      let mem_project = Datatype.never_any_project
+      let copy = Datatype.undefined
+      let varname = Datatype.undefined
+    end)
+
+(* ---------------------------------- Utils --------------------------------- *)
+
+let cardinal = Array.length
+
+let for_all f (a : Integer.t array) =
+  let l = Array.length a in
+  let rec c i = i = l || ((f a.(i)) && c (succ i)) in
+  c 0
+
+let exists = Extlib.array_exists
+
+let iter = Array.iter
+let fold = Array.fold_left
+
+let truncate r i =
+  if i = 0
+  then `Bottom
+  else if i = 1
+  then `Value (inject_singleton r.(0))
+  else begin
+    (Obj.truncate (Obj.repr r) i);
+    assert (Array.length r = i);
+    `Value r
+  end
+
+exception Empty
+
+let map_reduce (f : 'a -> 'b) (g : 'b -> 'b -> 'b) (set : 'a array) : 'b =
+  if Array.length set <= 0 then
+    raise Empty
+  else
+    let acc = ref (f set.(0)) in
+    for i = 1 to Array.length set - 1 do
+      acc := g !acc (f set.(i))
+    done;
+    !acc
+
+let filter (f : Int.t -> bool) (a : Int.t array) : t or_bottom =
+  let l = Array.length a in
+  let r = Array.make l Int.zero in
+  let j = ref 0 in
+  for i = 0 to l  - 1 do
+    let x = a.(i) in
+    if f x then begin
+      r.(!j) <- x;
+      incr j;
+    end
+  done;
+  truncate r !j
+
+let mem v a =
+  let l = Array.length a in
+  let rec c i =
+    if i = l then (-1)
+    else
+      let ae = a.(i) in
+      if Int.equal ae v
+      then i
+      else if Int.gt ae v
+      then (-1)
+      else c (succ i)
+  in
+  c 0
+
+(* ------------------------------- Set or top ------------------------------- *)
+
+type set_or_top = [ `Set of t | `Top of Integer.t * Integer.t * Integer.t ]
+
+module O = FCSet.Make (Integer)
+
+type pre_set =
+  | Pre_set of O.t * int
+  | Pre_top of Int.t * Int.t * Int.t
+
+let empty_ps = Pre_set (O.empty, 0)
+
+(* TODO: share this code with ival2.make_itv_from_set? *)
+let make_top_from_set s =
+  if debug_cardinal then assert (O.cardinal s >= 2);
+  let min = O.min_elt s in
+  let max = O.max_elt s in
+  let modu = O.fold
+      (fun x acc ->
+         if Int.equal x min then acc else Int.pgcd (Int.sub x min) acc)
+      s Int.zero
+  in
+  (min, max, modu)
+
+let add_ps ps x =
+  match ps with
+  | Pre_set (o, s) ->
+    if debug_cardinal then assert (O.cardinal o = s);
+    if O.mem x o (* TODO: improve *)
+    then ps
+    else
+      let no = O.add x o in
+      if s < !small_cardinal
+      then begin
+        if debug_cardinal then assert (O.cardinal no = succ s);
+        Pre_set (no, succ s)
+      end
+      else
+        let min, max, modu =  make_top_from_set no in
+        Pre_top (min, max, modu)
+  | Pre_top (min, max, modu) ->
+    let new_modu =
+      if Int.equal x min
+      then modu
+      else Int.pgcd (Int.sub x min) modu
+    in
+    let new_min = Int.min min x in
+    let new_max = Int.max max x in
+    Pre_top (new_min, new_max, new_modu)
+
+let o_zero = O.singleton Int.zero
+let o_one = O.singleton Int.one
+let o_zero_or_one = O.union o_zero o_one
+
+let share_set o s =
+  if s = 0 then bottom
+  else if s = 1
+  then begin
+    let e = O.min_elt o in
+    inject_singleton e
+  end
+  else if O.equal o o_zero_or_one
+  then zero_or_one
+  else
+    let a = Array.make s Int.zero in
+    let i = ref 0 in
+    O.iter (fun e -> a.(!i) <- e; incr i) o;
+    assert (!i = s);
+    a
+
+let inject_ps = function
+  | Pre_set (o, s) -> `Set (share_set o s)
+  | Pre_top (min, max, modu) -> `Top (min, max, modu)
+
+(* Given a set of elements that is an under-approximation, returns an
+   ival (while maintaining the ival invariants that the "Set"
+   constructor is used only for small sets of elements. *)
+let set_to_ival_under set =
+  let card = Int.Set.cardinal set in
+  if card <= !small_cardinal
+  then
+    let a = Array.make card Int.zero in
+    ignore (Int.Set.fold (fun elt i -> Array.set a i elt; i + 1) set 0);
+    `Set (share_array a card)
+  else
+    (* If by chance the set is contiguous. *)
+  if (Int.equal
+        (Int.sub (Int.Set.max_elt set) (Int.Set.min_elt set))
+        (Int.of_int (card - 1)))
+  then `Top (Int.Set.min_elt set, Int.Set.max_elt set, Int.one)
+  (* Else: arbitrarily drop some elements of the under approximation. *)
+  else
+    let a = Array.make !small_cardinal Int.zero in
+    log_imprecision "Ival.set_to_ival_under";
+    try
+      ignore (Int.Set.fold (fun elt i ->
+          if i = !small_cardinal then raise Exit;
+          Array.set a i elt;
+          i + 1) set 0);
+      assert false
+    with Exit -> `Set a
+
+(* ------------------------------ Apply and map ----------------------------- *)
+
+let apply_bin_1_strict_incr f x (s : Integer.t array) =
+  let l = Array.length s in
+  let r = Array.make l Int.zero in
+  let rec c i =
+    if i = l
+    then share_array r l
+    else
+      let v = f x s.(i) in
+      r.(i) <- v;
+      c (succ i)
+  in
+  c 0
+
+let apply_bin_1_strict_decr f x (s : Integer.t array) =
+  let l = Array.length s in
+  let r = Array.make l Int.zero in
+  let rec c i =
+    if i = l
+    then share_array r l
+    else
+      let v = f x s.(i) in
+      r.(l - i - 1) <- v;
+      c (succ i)
+  in
+  c 0
+
+let apply2_n f (s1 : Integer.t array) (s2 : Integer.t array) =
+  let ps = ref empty_ps in
+  let l1 = Array.length s1 in
+  let l2 = Array.length s2 in
+  for i1 = 0 to pred l1 do
+    let e1 = s1.(i1) in
+    for i2 = 0 to pred l2 do
+      ps := add_ps !ps (f e1 s2.(i2))
+    done
+  done;
+  inject_ps !ps
+
+let apply2_notzero f (s1 : Integer.t array) s2 =
+  inject_ps
+    (Array.fold_left
+       (fun acc v1 ->
+          Array.fold_left
+            (fun acc v2 ->
+               if Int.is_zero v2
+               then acc
+               else add_ps acc (f v1 v2))
+            acc
+            s2)
+       empty_ps
+       s1)
+
+let map_set_decr f (s : Integer.t array) =
+  let l = Array.length s in
+  if l = 0
+  then `Bottom
+  else
+    let r = Array.make l Int.zero in
+    let rec c srcindex dstindex last =
+      if srcindex < 0
+      then begin
+        r.(dstindex) <- last;
+        truncate r (succ dstindex)
+      end
+      else
+        let v = f s.(srcindex) in
+        if Int.equal v last
+        then
+          c (pred srcindex) dstindex last
+        else begin
+          r.(dstindex) <- last;
+          c (pred srcindex) (succ dstindex) v
+        end
+    in
+    c (l-2) 0 (f s.(pred l))
+
+let map_set_strict_decr f (s : Integer.t array) =
+  let l = Array.length s in
+  let r = Array.make l Int.zero in
+  let rec c i =
+    if i = l
+    then share_array r l
+    else
+      let v = f s.(i) in
+      r.(l - i - 1) <- v;
+      c (succ i)
+  in
+  c 0
+
+let map_set_incr f (s : Integer.t array) =
+  let l = Array.length s in
+  if l = 0
+  then `Bottom
+  else
+    let r = Array.make l Int.zero in
+    let rec c srcindex dstindex last =
+      if srcindex = l
+      then begin
+        r.(dstindex) <- last;
+        truncate r (succ dstindex)
+      end
+      else
+        let v = f s.(srcindex) in
+        if Int.equal v last
+        then
+          c (succ srcindex) dstindex last
+        else begin
+          r.(dstindex) <- last;
+          c (succ srcindex) (succ dstindex) v
+        end
+    in
+    c 1 0 (f s.(0))
+
+let map f s =
+  let pre_set =
+    Array.fold_left (fun acc elt -> add_ps acc (f elt)) empty_ps s
+  in
+  match pre_set with
+  | Pre_set (o, s) -> share_set o s
+  | Pre_top _ -> assert false
+
+(* --------------------------------- Lattice -------------------------------- *)
+
+let is_included s1 s2 =
+  let l1 = Array.length s1 in
+  let l2 = Array.length s2 in
+  if l1 > l2 then false
+  else
+    let rec c i1 i2 =
+      if i1 = l1 then true
+      else if i2 = l2 then false
+      else
+        let e1 = s1.(i1) in
+        let e2 = s2.(i2) in
+        let si2 = succ i2 in
+        if Int.equal e1 e2
+        then c (succ i1) si2
+        else if Int.lt e1 e2
+        then false
+        else c i1 si2 (* TODO: improve by not reading a1.(i1) all the time *)
+    in
+    c 0 0
+
+(* Join [s1] of length [l1] and [s2] of length [l2]. *)
+let join l1 s1 l2 s2 =
+  (* first pass: count unique elements and detect inclusions *)
+  let rec first i1 i2 uniq inc1 inc2 =
+    if i1 = l1
+    then (uniq + l2 - i2), false, inc2
+    else if i2 = l2
+    then (uniq + l1 - i1), inc1, false
+    else
+      let e1 = s1.(i1) in
+      let e2 = s2.(i2) in
+      if Int.lt e2 e1
+      then first i1 (succ i2) (succ uniq) false inc2
+      else if Int.gt e2 e1
+      then first (succ i1) i2 (succ uniq) inc1 false
+      else first (succ i1) (succ i2) (succ uniq) inc1 inc2
+  in
+  let uniq, incl1, incl2 = first 0 0 0 true true in
+  if incl1 then `Set s1 else
+  if incl2 then `Set s2 else
+    (* second pass: make a set or make a top *)
+  if uniq > !small_cardinal
+  then
+    let min = Int.min s1.(0) s2.(0) in
+    let accum acc x =
+      if Int.equal x min
+      then acc
+      else Int.pgcd (Int.sub x min) acc
+    in
+    let modu = ref Int.zero in
+    for j = 0 to pred l1 do
+      modu := accum !modu s1.(j)
+    done;
+    for j = 0 to pred l2 do
+      modu := accum !modu s2.(j)
+    done;
+    `Top (min, Int.max s1.(pred l1) s2.(pred l2), !modu)
+  else
+    let r = Array.make uniq Int.zero in
+    let rec c i i1 i2 =
+      if i1 = l1
+      then begin
+        Array.blit s2 i2 r i (l2 - i2);
+        share_array r uniq
+      end
+      else if i2 = l2
+      then begin
+        Array.blit s1 i1 r i (l1 - i1);
+        share_array r uniq
+      end
+      else
+        let si = succ i in
+        let e1 = s1.(i1) in
+        let e2 = s2.(i2) in
+        if Int.lt e2 e1
+        then begin
+          r.(i) <- e2;
+          c si i1 (succ i2)
+        end
+        else begin
+          r.(i) <- e1;
+          let si1 = succ i1 in
+          if Int.equal e1 e2
+          then c si si1 (succ i2)
+          else c si si1 i2
+        end
+    in
+    `Set (c 0 0 0)
+
+let join s1 s2 =
+  let l1 = Array.length s1 in
+  if l1 = 0
+  then `Set s2
+  else
+    let l2 = Array.length s2 in
+    if l2 = 0
+    then `Set s1
+    else join l1 s1 l2 s2
+
+let link s1 s2 =
+  let s1 = Array.fold_right Int.Set.add s1 Int.Set.empty in
+  let s2 = Array.fold_right Int.Set.add s2 s1 in
+  set_to_ival_under s2
+
+let meet s1 s2 =
+  let l1 = Array.length s1 in
+  let l2 = Array.length s2 in
+  let lr_max = min l1 l2 in
+  let r = Array.make lr_max Int.zero in
+  let rec c i i1 i2 =
+    if i1 = l1 || i2 = l2
+    then truncate r i
+    else
+      let e1 = s1.(i1) in
+      let e2 = s2.(i2) in
+      if Int.equal e1 e2
+      then begin
+        r.(i) <- e1;
+        c (succ i) (succ i1) (succ i2)
+      end
+      else if Int.lt e1 e2
+      then c i (succ i1) i2
+      else c i i1 (succ i2)
+  in
+  c 0 0 0
+
+let narrow = meet (* meet is exact. *)
+
+let intersects s1 s2 =
+  let l1 = Array.length s1 in
+  let l2 = Array.length s2 in
+  let rec aux i1 i2 =
+    if i1 = l1 || i2 = l2 then false
+    else
+      let e1 = s1.(i1) in
+      let e2 = s2.(i2) in
+      if Int.equal e1 e2 then true
+      else if Int.lt e1 e2 then aux (succ i1) i2
+      else aux i1 (succ i2)
+  in
+  aux 0 0
+
+let diff_if_one _value _rem = assert false
+
+let remove s v =
+  let index = mem v s in
+  if index >= 0
+  then
+    let l = Array.length s in
+    let pl = pred l in
+    let r = Array.make pl Int.zero in
+    Array.blit s 0 r 0 index;
+    Array.blit s (succ index) r index (pl-index);
+    share_array_or_bottom r pl
+  else `Value s
+
+(* ------------------------------ Arithmetics ------------------------------- *)
+
+let add_singleton = apply_bin_1_strict_incr Int.add
+
+let add s1 s2 =
+  match s1, s2 with
+  | [| x |], s | s, [| x |] -> `Set (apply_bin_1_strict_incr Int.add x s)
+  | _, _ -> apply2_n Int.add s1 s2
+
+let add_under s1 s2 =
+  match s1, s2 with
+  | [| x |], s | s, [| x |] -> `Set (apply_bin_1_strict_incr Int.add x s)
+  | _, _ ->
+    let set =
+      Array.fold_left (fun acc i1 ->
+          Array.fold_left (fun acc i2 ->
+              Int.Set.add (Int.add i1 i2) acc) acc s2)
+        Int.Set.empty s1
+    in
+    set_to_ival_under set
+
+let neg s = map_set_strict_decr Int.neg s
+
+let scale f s =
+  if Int.ge f Int.zero
+  then apply_bin_1_strict_incr Int.mul f s
+  else apply_bin_1_strict_decr Int.mul f s
+
+let mul s1 s2 =
+  match s1, s2 with
+  | s, [| x |] | [| x |], s -> `Set (scale x s)
+  | _, _ -> apply2_n Int.mul s1 s2
+
+let scale_div ~pos f s =
+  let div_f =
+    if pos
+    then fun a -> Int.e_div a f
+    else fun a -> Int.c_div a f
+  in
+  if Int.lt f Int.zero
+  then map_set_decr div_f s
+  else map_set_incr div_f s
+
+let scale_rem ~pos f s =
+  let f = if Int.lt f Int.zero then Int.neg f else f in
+  let rem_f a =
+    if pos then Int.e_rem a f else Int.c_rem a f
+  in
+  let pre_set =
+    Array.fold_left
+      (fun acc v -> add_ps acc (rem_f v))
+      empty_ps
+      s
+  in
+  inject_ps pre_set
+
+let c_rem s1 s2 = apply2_notzero Int.c_rem s1 s2
+
+let bitwise_signed_not = map_set_strict_decr Int.lognot
+
+(* ------------------------------- Subdivide -------------------------------- *)
+
+let subdivide s =
+  let len = Array.length s in
+  assert (len > 0 );
+  if len <= 1 then raise Can_not_subdiv;
+  let m = len lsr 1 in
+  let lenhi = len - m in
+  let lo = Array.sub s 0 m in
+  let hi = Array.sub s m lenhi in
+  share_array lo m,
+  share_array hi lenhi
+
+
+(* -------------------------------- Export ---------------------------------- *)
+
+let min t = t.(0)
+let max t = t.(Array.length t - 1)
diff --git a/src/kernel_services/abstract_interp/int_set.mli b/src/kernel_services/abstract_interp/int_set.mli
new file mode 100644
index 00000000000..207ff16e960
--- /dev/null
+++ b/src/kernel_services/abstract_interp/int_set.mli
@@ -0,0 +1,94 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2019                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  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, version 2.1.                                              *)
+(*                                                                        *)
+(*  It 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.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Bottom.Type
+
+include Datatype.S_with_collections
+
+val rehash: t -> t
+
+val bottom: t
+
+val inject_singleton: Integer.t -> t
+val inject_array: Integer.t array -> int -> t
+
+val project_array: t -> Integer.t array
+
+val remove: t -> Integer.t -> t or_bottom
+
+val one: t
+val zero: t
+val minus_one: t
+val zero_or_one: t
+
+val min: t -> Integer.t
+val max: t -> Integer.t
+
+val cardinal: t -> int
+
+val mem: Integer.t -> t -> int
+
+val for_all: (Integer.t -> bool) -> t -> bool
+val exists: (Integer.t -> bool) -> t -> bool
+
+val iter: (Integer.t -> unit) -> t -> unit
+val fold: ('a -> Integer.t -> 'a) -> 'a -> t -> 'a
+val map: (Integer.t -> Integer.t) -> t -> t
+val filter: (Integer.t -> bool) -> t -> t or_bottom
+
+exception Empty
+val map_reduce: (Integer.t -> 'a) -> ('a -> 'a -> 'a) -> t -> 'a
+
+type set_or_top = [ `Set of t | `Top of Integer.t * Integer.t * Integer.t ]
+
+val is_included: t -> t -> bool
+val join: t -> t -> [`Set of t | `Top of (Integer.t * Integer.t * Integer.t)]
+val link: t -> t -> set_or_top
+val meet: t -> t -> t or_bottom
+val narrow: t -> t -> t or_bottom
+
+val intersects: t -> t -> bool
+
+val diff_if_one: t -> t -> t or_bottom
+
+val add_singleton: Integer.t -> t -> t
+val add: t -> t -> set_or_top
+val add_under: t -> t -> set_or_top
+val neg: t -> t
+
+val mul: t -> t -> set_or_top
+
+val c_rem: t -> t -> set_or_top
+
+val scale: Integer.t -> t -> t
+val scale_div: pos:bool -> Integer.t -> t -> t or_bottom
+val scale_rem: pos:bool -> Integer.t -> t -> set_or_top
+
+val bitwise_signed_not: t -> t
+
+val subdivide: t -> t * t
+
+
+(**/**)
+
+(* This is used by the Value plugin. Do not use. *)
+val set_small_cardinal: int -> unit
-- 
GitLab