diff --git a/Changelog b/Changelog
index 65f7db922aecdc1a77d11359a81e8627262b66f4..8c9a705d3c372b62fee986ca97d723c021c62eec 100644
--- a/Changelog
+++ b/Changelog
@@ -17,6 +17,12 @@
 Open Source Release <next-release>
 ##################################
 
+-   Eva       [2019/10/21] New octagon domain inferring relations of the form
+              l ≤ ±X±Y ≤ e between pairs of integer variables X and Y. Enabled
+              with option -eva-octagon-domain. Only infers relations between
+              pairs of scalar variables occuring in a same instruction.
+              Intra-procedural by default; octagons can be propagated through
+              function calls with option -eva-octagon-through-calls.
 -   Eva       [2019/10/04] Evaluates ACSL predicates \is_plus_infinity and
               \is_minus_infinity.
 -   Kernel    [2019/10/04] Supports macro INFINITY and NAN.
diff --git a/Makefile b/Makefile
index 476d1807b2c22731957fd4d801361f45cbd6671b..8b6f552a87882d2d156af1737917172b03ee522b 100644
--- a/Makefile
+++ b/Makefile
@@ -875,6 +875,7 @@ PLUGIN_CMO:= slevel/split_strategy value_parameters \
 	domains/printer_domain \
 	domains/traces_domain \
 	domains/simple_memory \
+	domains/octagons \
 	domains/gauges/gauges_domain \
 	domains/hcexprs \
 	domains/equality/equality domains/equality/equality_domain \
diff --git a/doc/value/main.tex b/doc/value/main.tex
index 8a8ef8917377f732c9ad306cc8a44e66b110f564..2cc8c362d514faeb329df932c57bb06fac788a5a 100644
--- a/doc/value/main.tex
+++ b/doc/value/main.tex
@@ -3956,6 +3956,55 @@ while (--x > 0) {
  cannot be analyzed precisely, because the relation between \texttt{x}
 and \texttt{y} is not affine.
 
+
+\subsection{Octagons}
+\label{sec:octagons}
+
+Activating option \texttt{-eva-octagon-domain} instructs Eva to infer relations
+between variables as numerical constraints of the form $l \leq \pm X \pm Y \leq
+u$, where $X$ and $Y$ are program variables, and $l$ and $u$ are constants.
+
+These relations are then used to evaluate more precisely the possible values
+of an expression involving $X$ and $Y$, or to deduce a reduction of
+the possible values of $X$ from a reduction of the possible values of $Y$.
+
+The following code is a minimal working example that illustrates
+the precision improvement provided by the octagon domain.
+\begin{lstlisting}
+void main (int y) {
+  int k = Frama_C_interval(0, 10);
+  int x = y + k;               // x - y $\in$ [0..10]
+  int r = x + 3 - y;           // r $\in$ [-7..3]
+  int t;
+  if (y > 15)
+    t = x;                     // t $\in$ [6..$\infty$]
+}
+\end{lstlisting}
+
+
+Currently, the octagon domain is fast but has two strong limitations:
+\begin{itemize}
+\item
+  The domain only infers relations between scalar variables of integer types.
+  It ignores pointers, arrays and structure fields.
+\item
+  The domain only infers relations between pairs of variables occurring in the
+  same instruction, as \lstinline|x = y + k;| or \lstinline|if (x < y + k)|.
+  If an instruction involves 3 variables or more, relations are inferred for
+  each pair of variables.
+\end{itemize}
+
+The domain is intraprocedural by default: the inferred relations are local to
+the current function and are lost when entering a function call or returning
+to its caller.
+
+The option \verb+-eva-octagon-through-calls+ makes the octagon domain
+interprocedural, which is a more precise but slower mode where
+the inferred relations are propagated through function calls.
+The analysis of a function can then use the relations inferred in the callers,
+and at the end, the inferred relations are propagated back to the caller.
+
+
 \subsection{Bitwise values}
 \label{sec:bitwise}
 
@@ -5044,6 +5093,9 @@ of the domain, shown in Figure~\ref{fig:log-category}.
       Gauges & \lstinline|d-gauges| & \ref{sec:gauges}
       \tabularnewline
       \midrule
+      Octagons & \lstinline|d-octagon| & \ref{sec:octagons}
+      \tabularnewline
+      \midrule
       Bitwise & \lstinline|d-bitwise| & \ref{sec:bitwise}
       \tabularnewline
       \midrule
diff --git a/headers/header_spec.txt b/headers/header_spec.txt
index 2a853e3bc043d0fd819edf2781f28103c9a441b2..e411e9b106f983a77f99baca0fcb6e8cbd697b85 100644
--- a/headers/header_spec.txt
+++ b/headers/header_spec.txt
@@ -1189,6 +1189,8 @@ src/plugins/value/domains/hcexprs.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/hcexprs.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/inout_domain.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/inout_domain.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/octagons.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/domains/octagons.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/offsm_domain.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/offsm_domain.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/domains/powerset.ml: CEA_LGPL_OR_PROPRIETARY
diff --git a/src/plugins/value/domains/octagons.ml b/src/plugins/value/domains/octagons.ml
new file mode 100644
index 0000000000000000000000000000000000000000..48734821cf4f4e84353625637dc78310bdd4b867
--- /dev/null
+++ b/src/plugins/value/domains/octagons.ml
@@ -0,0 +1,1323 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  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 Cil_types
+open Eval
+
+(* If [true], checks invariants of the states created by most functions. *)
+let debug = false
+
+(* Whether the domain infers non-relational intervals (ivals) to improve the
+   precision of the join operation: this avoids losing all relations that have
+   been inferred in only one side of the join. Enhances the domain accuracy
+   for a minimal drop in efficiency. *)
+let infer_intervals = true
+
+(* Whether the domain saturates the octagons: from a relation between (x, y)
+   and a relation between (y, z), infers the relation between (x, z).
+   The saturation is currently partial. Improves the domain accuracy for a
+   minimal drop in efficiency. *)
+let saturate_octagons = true
+
+(* Is the domain intraprocedural, according to the -eva-octagon-through-calls
+   option. In this case, the analysis of each function starts with an empty
+   state, and the relations inferred in a function are not propagated back to
+   the caller either. *)
+let intraprocedural () = not (Value_parameters.OctagonCall.get ())
+
+(* -------------------------------------------------------------------------- *)
+(*                  Basic types: pair of variables and Ival.t                 *)
+(* -------------------------------------------------------------------------- *)
+
+(* Variables of the octagons. Should be extended later to also include
+   symbolic lvalues. *)
+module Variable = struct
+  include Cil_datatype.Varinfo
+  let id v = v.vid
+end
+
+module VariableSet = struct
+  include Variable.Set
+  let pretty_debug = pretty
+end
+
+(* Pairs of related variables in an octagon.
+   This module imposes an order between the two variables X and Y in a pair
+   to avoid creating octagons about X±Y *and* about Y±X. *)
+module Pair = struct
+  module D = Datatype.Pair (Variable) (Variable)
+  module Info = struct
+    let name = "Octagons.Pair"
+    let dependencies = [ Ast.self ]
+    let initial_values = []
+  end
+
+  include State_builder.Hashcons (D) (Info)
+
+  (* Creates a pair, and also returns a boolean that is [true] if x, y are
+     swapped in the pair. *)
+  let make x y =
+    assert (x.vid <> y.vid);
+    let pair, swap = if x.vid < y.vid then (x, y), false else (y, x), true in
+    hashcons pair, swap
+
+  let fst t = fst (get t)
+end
+
+
+(* Kind of relation between two variables X and Y: X+Y or X-Y. *)
+type operation = Add | Sub
+
+(* Extended arithmetic operations over Ival.t. *)
+module Arith = struct
+  open Ival
+
+  let top_float = Ival.inject_float Fval.top
+
+  let narrow x y =
+    let r = narrow x y in
+    if is_bottom r then `Bottom else `Value r
+
+  let widen =
+    let hints = Integer.zero,
+                (Ival.Widen_Hints.default_widen_hints,
+                 Fc_float.Widen_Hints.default_widen_hints)
+    in
+    Ival.widen hints
+
+  (* TODO: do not use Ival.top on floating-point value? *)
+  let project_float ival =
+    if Ival.(equal top ival) then Fval.top
+    else project_float ival
+
+  let neg = function
+    | Float f -> inject_float (Fval.neg f)
+    | ival -> neg_int ival
+
+  let int_or_float_operation i_op f_op = fun typ ->
+    match Cil.unrollType typ with
+    | TInt _ | TEnum _ -> i_op
+    | TFloat _ -> fun i1 i2 ->
+      inject_float (f_op Fval.Real (project_float i1) (project_float i2))
+    | _ -> assert false
+
+  let sub = int_or_float_operation Ival.sub_int Fval.sub
+  let add = int_or_float_operation Ival.add_int Fval.add
+
+  let apply = function
+    | Add -> add
+    | Sub -> sub
+
+  (* Creates the ival covering the integer range [range]. *)
+  let make_range range =
+    let min = Eval_typ.range_lower_bound range in
+    let max = Eval_typ.range_upper_bound range in
+    Ival.inject_range (Some min) (Some max)
+
+  (* Does an ival represent all values of a C type [typ]? *)
+  let is_top_for_typ typ ival =
+    let open Eval_typ in
+    Ival.(equal top ival) ||
+    match classify_as_scalar typ with
+    | None -> assert false
+    | Some (TSFloat _) -> Ival.equal top_float ival
+    | Some (TSInt range | TSPtr range) ->
+      (* TODO: this could be more efficient. *)
+      let range = make_range range in
+      Ival.is_included range ival || Ival.is_included range (neg_int ival)
+
+  (* Does an ival represent all possible values of a pair of variables? *)
+  let is_top_for_pair pair =
+    let x, y = Pair.get pair in
+    if Cil_datatype.Typ.equal x.vtype y.vtype
+    then is_top_for_typ x.vtype
+    else fun ival -> is_top_for_typ x.vtype ival && is_top_for_typ y.vtype ival
+end
+
+(* -------------------------------------------------------------------------- *)
+(*              Rewriting Cil expressions into mathematical octagons          *)
+(* -------------------------------------------------------------------------- *)
+
+(* An octagonal relation between two variables : b ≤ X±Y ≤ e *)
+type octagon =
+  { variables: Pair.t;      (* The two related variables X and Y. *)
+    operation: operation;   (* Whether the relation is about X+Y or X-Y. *)
+    value: Ival.t;          (* The interval of X±Y. *)
+  }
+
+let _pretty_octagon fmt octagon =
+  let x, y = Pair.get octagon.variables in
+  let op = match octagon.operation with Add -> "+" | Sub -> "-" in
+  Format.fprintf fmt "%a %s %a %s %a"
+    Printer.pp_varinfo x op Printer.pp_varinfo y
+    (Unicode.inset_string ()) Ival.pretty octagon.value
+
+(* Transforms Cil expressions into mathematical octagons.
+   Use Ival.t to evaluate expressions. *)
+module Rewriting = struct
+
+  (* Checks if the interval [ival] fits in the C type [typ].
+     This is used to ensure that an expression cannot overflow: this module
+     uses the mathematical semantics of arithmetic operations, and cannot
+     soundly translate overflows in the C semantics.  *)
+  let may_overflow typ ival =
+    let open Eval_typ in
+    match classify_as_scalar typ with
+    | None -> assert false (* This should not happen here. *)
+    | Some (TSFloat _) -> false
+    | Some (TSInt range | TSPtr range) ->
+      not
+        ((range.i_signed && Kernel.SignedOverflow.get ()) ||
+         (not range.i_signed && Kernel.UnsignedOverflow.get ()) ||
+         Ival.is_included ival (Arith.make_range range))
+
+  (* Simplified form [±X-coeff] for expressions,
+     where X is a variable and coeff an interval. *)
+  type var_coeff = { varinfo: varinfo; sign: bool; coeff: Ival.t; }
+
+  (* Negates a simplified form. *)
+  let neg { varinfo; sign; coeff } =
+    { varinfo; sign = not sign; coeff = Arith.neg coeff }
+
+  (* Is the interval computed for a variable a singleton? *)
+  let is_singleton = function
+    | `Top -> false
+    | `Value ival -> Ival.cardinal_zero_or_one ival
+
+  (* If a needed interval is unknown, stop the current computation and return
+     an empty list. *)
+  let (>>) value f = match value with
+    | `Top -> []
+    | `Value ival -> f ival
+
+  (* Apply [f typ v1 v2] if the operation [e1 op e2] does not overflow,
+     where [v1] and [v2] are the intervals for [e1] and [e2], and [typ] is
+     the type of [e1]. Returns the empty list otherwise. *)
+  let apply_binop f evaluate typ e1 op e2 =
+    evaluate e1 >> fun v1 ->
+    evaluate e2 >> fun v2 ->
+    let typ_e1 = Cil.typeOf e1 in
+    let result = Arith.apply op typ_e1 v1 v2 in
+    if may_overflow typ result
+    then []
+    else f typ_e1 v1 v2
+
+  (* Rewrites the Cil expression [expr] into the simplified form [±x-coeff],
+     where [x] is a non-singleton variable and [coeff] is an interval. The
+     result follows the mathematical semantics.
+     If such a simplified form cannot be found, the function returns an empty
+     list. If multiple variables occur in the expression, the function tries to
+     compute a list of equivalent forms [±x-coeff], one for each variable.  The
+     function relies on an evaluation function linking each sub-expression into
+     an interval, used for computing sound coefficients. The evaluation may
+     return Top for some sub-expression, thus preventing the computation. *)
+  let rec rewrite evaluate expr =
+    match expr.enode with
+    | Lval (Var varinfo, NoOffset) ->
+      if Cil.isIntegralType varinfo.vtype
+      && not (Cil.typeHasQualifier "volatile" varinfo.vtype)
+      && not (is_singleton (evaluate expr))
+      then [ { varinfo; sign = true; coeff = Ival.zero } ]
+      else []
+
+    | UnOp (Neg, e, typ) ->
+      evaluate e >> fun v ->
+      if may_overflow typ (Arith.neg v)
+      then [] else List.map neg (rewrite evaluate e)
+
+    | BinOp ((PlusA | MinusA as binop), e1, e2, typ) ->
+      let op = if binop = PlusA then Add else Sub in
+      let rewrite_binop typ v1 v2 =
+        let inverse_op = if binop = PlusA then Arith.sub else Arith.add in
+        let add_v2 var =
+          { var with coeff = inverse_op typ var.coeff v2 }
+        in
+        let add_v1 var =
+          let var = if binop = MinusA then neg var else var in
+          { var with coeff = Arith.sub typ var.coeff v1 }
+        in
+        List.map add_v2 (rewrite evaluate e1) @
+        List.map add_v1 (rewrite evaluate e2)
+      in
+      apply_binop rewrite_binop evaluate typ e1 op e2
+
+    | CastE (typ, e) ->
+      if Cil.(isIntegralType typ && isIntegralType (typeOf e)) then
+        evaluate e >> fun v ->
+        if may_overflow typ v then [] else rewrite evaluate e
+      else []
+
+    | Info (e, _) -> rewrite evaluate e
+
+    | _ -> []
+
+  (* Rewrites the operation [e1 ± e2] into equivalent octagons ±(X±Y-value). *)
+  let rewrite_binop evaluate e1 binop e2 =
+    let vars1 = rewrite evaluate e1 in
+    let vars2 = rewrite evaluate e2 in
+    let vars2 = if binop = Sub then List.map neg vars2 else vars2 in
+    let aux acc var1 var2 =
+      if Cil_datatype.Varinfo.equal var1.varinfo var2.varinfo
+      then acc
+      else
+        let variables, swap = Pair.make var1.varinfo var2.varinfo in
+        let operation = if var1.sign = var2.sign then Add else Sub in
+        let sign = match operation with
+          | Add -> var1.sign
+          | Sub -> if swap then var2.sign else var1.sign
+        in
+        let value = Arith.add (Cil.typeOf e1) var1.coeff var2.coeff in
+        let value = if sign then value else Arith.neg value in
+        (* Do not include this rewriting if the [value] exceeds all possible
+           values for the type of [var1] and [var2]. *)
+        if Arith.is_top_for_pair variables value
+        then acc
+        else (sign, { variables; operation; value }) :: acc
+    in
+    Extlib.product_fold aux [] vars1 vars2
+
+  (* Returns the range of the expression X-Y when the comparison X#Y holds. *)
+  let comparison_range =
+    let open Abstract_interp.Comp in
+    function
+    | Lt -> Ival.inject_range None (Some Integer.minus_one)
+    | Gt -> Ival.inject_range (Some Integer.one) None
+    | Le -> Ival.inject_range None (Some Integer.zero)
+    | Ge -> Ival.inject_range (Some Integer.zero) None
+    | Eq -> Ival.zero
+    | Ne -> Ival.top
+
+  (* Transforms the constraint [expr] ∈ [ival] into a list of octagonal
+     constraints. *)
+  let make_octagons evaluate expr ival =
+    let make_octagons_from_binop typ e1 op e2 ival =
+      (* equivalent octagonal forms ±(X±Y-v) for [e1 op e2]. *)
+      let rewritings = rewrite_binop evaluate e1 op e2 in
+      (* create the final octagon, knowning that [e1 op e2] ∈ [ival]. *)
+      let make_octagon (sign, octagon) =
+        let ival = if sign then ival else Arith.neg ival in
+        let value = Arith.add typ ival octagon.value in
+        { octagon with value }
+      in
+      List.map make_octagon rewritings
+    in
+    match expr.enode with
+    | BinOp ((PlusA | MinusA as binop), e1, e2, typ) ->
+      let op = if binop = PlusA then Add else Sub in
+      let make_octagons typ _ _ = make_octagons_from_binop typ e1 op e2 ival in
+      apply_binop make_octagons evaluate typ e1 op e2
+    | BinOp ((Lt | Gt | Le | Ge | Eq | Ne as binop), e1, e2, _typ) ->
+      let typ = Cil.typeOf e1 in
+      if not (Cil.isIntegralType typ)
+      || (Ival.contains_zero ival && Ival.contains_non_zero ival)
+      then []
+      else
+        let comp = Value_util.conv_comp binop in
+        let comp =
+          if Ival.is_zero ival then Abstract_interp.Comp.inv comp else comp
+        in
+        let range = comparison_range comp in
+        make_octagons_from_binop typ e1 Sub e2 range
+    | _ -> []
+
+  let overflow_alarms typ expr ival =
+    match Eval_typ.classify_as_scalar typ with
+    | Some (Eval_typ.TSInt range) ->
+      let signed = range.Eval_typ.i_signed in
+      let overflow = if signed then Alarms.Signed else Alarms.Unsigned in
+      let max_bound = Eval_typ.range_upper_bound range in
+      let min_bound = Eval_typ.range_lower_bound range in
+      let ival_range = Ival.inject_range (Some min_bound) (Some max_bound) in
+      let aux has_better_bound bound bound_kind alarms =
+        if has_better_bound ival ival_range >= 0
+        then
+          let alarm = Alarms.Overflow (overflow, expr, bound, bound_kind) in
+          Alarmset.set alarm Alarmset.True alarms
+        else alarms
+      in
+      let alarms = Alarmset.all in
+      let alarms =
+        aux Ival.has_greater_min_bound min_bound Alarms.Lower_bound alarms
+      in
+      aux Ival.has_smaller_max_bound max_bound Alarms.Upper_bound alarms
+    | _ -> Alarmset.all
+
+  (* Evaluates the Cil expression [expr], by rewriting it into octagonal
+     constraints using [evaluate_expr] to evaluate sub-expressions, and
+     then using [evaluate_octagon] to evaluate the octagons. *)
+  let evaluate_through_octagons evaluate_expr evaluate_octagon expr =
+    let evaluate_octagon acc (sign, octagon) =
+      match evaluate_octagon octagon with
+      | None -> acc
+      | Some ival ->
+        let ival = if sign then ival else Arith.neg ival in
+        Ival.narrow acc ival
+    in
+    let evaluate_octagons octagons =
+      List.fold_left evaluate_octagon Ival.top octagons
+    in
+    let default = Ival.top, Alarmset.all in
+    match expr.enode with
+    | BinOp ((PlusA | MinusA as binop), e1, e2, typ) ->
+      let op = if binop = PlusA then Add else Sub in
+      let octagons = rewrite_binop evaluate_expr e1 op e2 in
+      let ival = evaluate_octagons octagons in
+      if Ival.(equal top ival) then default else
+        let typ_e1 = Cil.typeOf e1 in
+        let ival2 =
+          match evaluate_expr e1, evaluate_expr e2 with
+          | `Value v1, `Value v2 -> Arith.apply op typ_e1 v1 v2
+          | _, _ -> Ival.top
+        in
+        let ival = Ival.narrow ival ival2 in
+        if may_overflow typ ival
+        then default
+        else ival, overflow_alarms typ expr ival
+    | BinOp ((Lt | Gt | Le | Ge | Eq as binop), e1, e2, _typ)
+      when Cil.isIntegralType (Cil.typeOf e1) ->
+      let comp = Value_util.conv_comp binop in
+      (* Evaluate [e1 - e2] and compare the resulting interval to the interval
+         for which the comparison [e1 # e2] holds. *)
+      let range = comparison_range comp in
+      let octagons = rewrite_binop evaluate_expr e1 Sub e2 in
+      let ival = evaluate_octagons octagons in
+      if Ival.is_included ival range then Ival.one, Alarmset.all
+      else if not (Ival.intersects ival range)
+      then Ival.zero, Alarmset.all else default
+    | _ -> default
+
+end
+
+(* -------------------------------------------------------------------------- *)
+(*           Diamonds and octagons: relations between two variables           *)
+(* -------------------------------------------------------------------------- *)
+
+(* This domain infers relations between pairs of variables (X, Y), by inferring
+   intervals for the mathematical operations X+Y and X-Y.
+   It also infers non-relational intervals for the separate variables X and Y
+   (they could be seen as intervals for X+X and Y+Y, but we chose to store them
+   in another way). These intervals are used to make the join more precise.
+   Geometrically, in a plan, intervals for X and Y shape a straight rectangle,
+   while intervals for X+Y and X-Y shape a "leaning" rectangle; the intersection
+   of these rectangles shapes an octagon.
+   Using a misnomer, we call diamonds the intervals for X+Y and X-Y, and
+   octagons the maps from variables to diamonds, even if they do not exactly
+   shape octagons. *)
+
+(* Relation between a pair of variables (X, Y).
+   [add] is an interval for X+Y, and [sub] is an interval for [X-Y]. *)
+type diamond = { add: Ival.t; sub: Ival.t }
+
+module DiamondDatatype = struct
+  type t = diamond
+  include Datatype.Serializable_undefined
+
+  let name = "Octagons.Diamond"
+  let structural_descr =
+    Structural_descr.t_record [| Ival.packed_descr; Ival.packed_descr |]
+  let reprs = [ { add = Ival.top; sub = Ival.top } ]
+
+  let compare x y =
+    let c = Ival.compare x.add y.add in
+    if c <> 0 then c else Ival.compare x.sub y.sub
+
+  let equal = Datatype.from_compare
+
+  let hash { add; sub } = Hashtbl.hash (Ival.hash add, Ival.hash sub)
+
+  let pretty fmt { add; sub } =
+    Format.fprintf fmt "@[<hov>ADD: @[%a@] ; SUB: @[%a@]@]"
+      Ival.pretty add Ival.pretty sub
+end
+
+module Diamond = struct
+  include Datatype.Make (DiamondDatatype)
+  let pretty_debug = pretty
+
+  let top = { add = Ival.top; sub = Ival.top }
+
+  let is_included x y =
+    Ival.is_included x.add y.add && Ival.is_included x.sub y.sub
+
+  let join x y =
+    { add = Ival.join x.add y.add; sub = Ival.join x.sub y.sub }
+
+  let widen x y =
+    { add = Arith.widen x.add y.add; sub = Arith.widen x.sub y.sub }
+
+  let narrow x y =
+    Arith.narrow x.add y.add >>- fun add ->
+    Arith.narrow x.sub y.sub >>-: fun sub -> {add; sub}
+
+  (* If [swap] is true, makes a diamond about (X, Y) from a diamond
+     about (Y, X). *)
+  let reverse_variables swap t =
+    if swap then { t with sub = Arith.neg t.sub } else t
+
+  (* Normalizes a diamond for the pair of variables [pair]: replaces too large
+     ivals by Ival.top. Returns None if both ivals are meaningless. *)
+  let trim pair t =
+    let is_top = Arith.is_top_for_pair pair in
+    match is_top t.add, is_top t.sub with
+    | true, true -> None
+    | true, false -> Some { t with add = Ival.top }
+    | false, true -> Some { t with sub = Ival.top }
+    | false, false -> Some t
+end
+
+
+(* Maps linking pairs of variables (X, Y) to intervals for X+Y and X-Y. *)
+module Octagons = struct
+  module Initial_Values = struct let v = [[]] end
+  module Dependencies = struct let l = [ Ast.self ] end
+
+  include Hptmap.Make (Pair) (Diamond)
+      (Hptmap.Comp_unused) (Initial_Values) (Dependencies)
+
+  let internal_join = join
+
+  let pretty fmt t =
+    let iter f = iter (fun k v -> f (k, v)) in
+    let pretty fmt (pair, diamond) =
+      let x, y = Pair.get pair in
+      let pretty_one op ival =
+        if not Ival.(equal top ival)
+        then
+          Format.fprintf fmt "@[@[%a %s %a@] %s @[%a@]@]@,"
+            Variable.pretty x op Variable.pretty y
+            (Unicode.inset_string ()) Ival.pretty ival
+      in
+      pretty_one "+" diamond.add;
+      pretty_one "-" diamond.sub
+    in
+    Pretty_utils.pp_iter
+      ~pre:"@[<v 3>{[ " ~suf:" ]}@]" ~sep:""
+      iter pretty fmt t
+
+  let top = empty
+
+  let is_included =
+    let cache = Hptmap_sig.PersistentCache "Octagons.Octagons.is_included" in
+    let decide_fst _ _ = true in
+    let decide_snd _ _ = false in
+    let decide_both _ x y = Diamond.is_included x y in
+    let decide_fast t1 t2 = decide_fast_inclusion t2 t1 in
+    binary_predicate cache UniversalPredicate
+      ~decide_fast ~decide_fst ~decide_snd ~decide_both
+
+  exception EBottom
+
+  let narrow_exc =
+    let cache = Hptmap_sig.NoCache in
+    let decide _pair x y =
+      match Diamond.narrow x y with
+      | `Value v -> v
+      | `Bottom -> raise EBottom
+    in
+    join ~cache ~symmetric:true ~idempotent:true ~decide
+
+  let narrow x y = try `Value (narrow_exc x y) with EBottom -> `Bottom
+
+  let simple_join =
+    let cache = Hptmap_sig.PersistentCache "Octagons.Octagons.join" in
+    let decide pair x y = Diamond.trim pair (Diamond.join x y) in
+    inter ~cache ~symmetric:true ~idempotent:true ~decide
+
+  let join ~decide_left ~decide_right =
+    let cache = Hptmap_sig.NoCache in
+    let decide_left = Traversing decide_left
+    and decide_right = Traversing decide_right in
+    let decide_both pair x y = Diamond.trim pair (Diamond.join x y) in
+    merge ~cache ~symmetric:false ~idempotent:true
+      ~decide_left ~decide_right ~decide_both
+
+  let simple_widen =
+    let cache = Hptmap_sig.PersistentCache "Octagons.Octagons.widen" in
+    let decide pair x y = Diamond.trim pair (Diamond.widen x y) in
+    inter ~cache ~symmetric:false ~idempotent:true ~decide
+
+  let widen ~decide_left ~decide_right =
+    let cache = Hptmap_sig.NoCache in
+    let decide_left = Traversing decide_left
+    and decide_right = Traversing decide_right in
+    let decide_both pair x y = Diamond.trim pair (Diamond.widen x y) in
+    merge ~cache ~symmetric:false ~idempotent:true
+      ~decide_left ~decide_right ~decide_both
+
+  let unsafe_add = add
+
+  let add variables diamond t =
+    try
+      Diamond.narrow diamond (find variables t) >>-: fun diamond ->
+      add variables diamond t
+    with Not_found -> `Value (add variables diamond t)
+
+  let add_octagon { variables; operation; value; } t =
+    let diamond =
+      try find variables t
+      with Not_found -> Diamond.top
+    in
+    let diamond =
+      match operation with
+      | Add ->
+        Arith.narrow diamond.add value >>-: fun add ->
+        { diamond with add }
+      | Sub ->
+        Arith.narrow diamond.sub value >>-: fun sub ->
+        { diamond with sub }
+    in
+    diamond >>-: fun diamond -> unsafe_add variables diamond t
+
+  let evaluate octagon t =
+    try
+      let diamond = find octagon.variables t in
+      let ival = match octagon.operation with
+        | Add -> diamond.add
+        | Sub -> diamond.sub
+      in
+      if Ival.(equal top ival)
+      then None
+      else
+        let typ = (Pair.fst octagon.variables).vtype in
+        let ival = Arith.sub typ ival octagon.value in
+        Some ival
+    with Not_found -> None
+end
+
+(* -------------------------------------------------------------------------- *)
+(*                                  Relations                                 *)
+(* -------------------------------------------------------------------------- *)
+
+(* Keep track of related variables in an octagon state. *)
+module Relations = struct
+  module Initial_Values = struct let v = [[]] end
+  module Dependencies = struct let l = [ Ast.self ] end
+
+  include Hptmap.Make (Variable) (VariableSet)
+      (Hptmap.Comp_unused) (Initial_Values) (Dependencies)
+
+  let inter =
+    let cache = Hptmap_sig.PersistentCache "Octagons.Relations.inter" in
+    let decide _pair x y =
+      let r = Variable.Set.inter x y in
+      if Variable.Set.is_empty r then None else Some r
+    in
+    inter ~cache ~symmetric:true ~idempotent:true ~decide
+
+  let union =
+    let cache = Hptmap_sig.PersistentCache "Octagons.Relations.union" in
+    let decide _pair x y = Variable.Set.union x y in
+    join ~cache ~symmetric:true ~idempotent:true ~decide
+
+  (* Marks y as related to x. *)
+  let relate_aux x y t =
+    let related =
+      try find x t
+      with Not_found -> VariableSet.empty
+    in
+    let updated = VariableSet.add y related in
+    add x updated t
+
+  (* Marks x and y as mutually related. *)
+  let relate pair t =
+    let x, y = Pair.get pair in
+    relate_aux y x (relate_aux x y t)
+
+  let add variable set t =
+    if VariableSet.is_empty set
+    then remove variable t
+    else add variable set t
+end
+
+(* -------------------------------------------------------------------------- *)
+(*                           Non-relational intervals                         *)
+(* -------------------------------------------------------------------------- *)
+
+module Intervals = struct
+  module Initial_Values = struct let v = [[]] end
+  module Dependencies = struct let l = [ Ast.self ] end
+
+  include Hptmap.Make (Variable) (Ival)
+      (Hptmap.Comp_unused) (Initial_Values) (Dependencies)
+
+  let internal_join = join
+
+  let top = empty
+
+  let is_included =
+    let cache = Hptmap_sig.PersistentCache "Octagons.Intervals.is_included" in
+    let decide_fst _ _ = true in
+    let decide_snd _ _ = false in
+    let decide_both _ x y = Ival.is_included x y in
+    let decide_fast t1 t2 = decide_fast_inclusion t2 t1 in
+    binary_predicate cache UniversalPredicate
+      ~decide_fast ~decide_fst ~decide_snd ~decide_both
+
+  exception EBottom
+
+  let narrow_exc =
+    let cache = Hptmap_sig.NoCache in
+    let decide _varinfo x y =
+      let ival = Ival.narrow x y in
+      if Ival.is_bottom ival then raise EBottom else ival
+    in
+    join ~cache ~symmetric:true ~idempotent:true ~decide
+
+  let narrow x y = try `Value (narrow_exc x y) with EBottom -> `Bottom
+
+  let join =
+    let cache = Hptmap_sig.PersistentCache "Octagons.Intervals.join" in
+    let decide _varinfo x y =
+      let r = Ival.join x y in
+      if Ival.(equal top r) then None else Some r
+    in
+    inter ~cache ~symmetric:true ~idempotent:true ~decide
+
+  let widen =
+    let cache = Hptmap_sig.PersistentCache "Octagons.Intervals.widen" in
+    let decide _varinfo x y =
+      let r = Arith.widen x y in
+      if Ival.(equal top r) then None else Some r
+    in
+    inter ~cache ~symmetric:false ~idempotent:true ~decide
+end
+
+(* -------------------------------------------------------------------------- *)
+(*                               Octagon states                               *)
+(* -------------------------------------------------------------------------- *)
+
+module Zone = Locations.Zone
+
+module State = struct
+
+  type state =
+    { octagons: Octagons.t;       (* The intervals for X±Y. *)
+      intervals: Intervals.t;     (* The intervals for the variables X,Y… *)
+      relations: Relations.t;     (* The related variables in [octagons]. *)
+      modified: Locations.Zone.t; (* The memory zone modified by a function. *)
+    }
+
+  include Datatype.Make_with_collections
+      (struct
+        type t = state
+        include Datatype.Serializable_undefined
+
+        let name = "Octagons.State"
+        let structural_descr =
+          Structural_descr.t_record
+            [| Octagons.packed_descr;
+               Intervals.packed_descr;
+               Relations.packed_descr;
+               Zone.packed_descr |]
+        let reprs =
+          [ { octagons = Octagons.top;
+              intervals = Intervals.empty;
+              relations = Relations.empty;
+              modified = Zone.bottom } ]
+
+        let compare s1 s2 =
+          let c = Octagons.compare s1.octagons s2.octagons in
+          if c <> 0 then c else
+            let c = Intervals.compare s1.intervals s2.intervals in
+            if c <> 0 then c else
+              Zone.compare s1.modified s2.modified
+
+        let equal = Datatype.from_compare
+
+        let hash t =
+          Hashtbl.hash (Octagons.hash t.octagons,
+                        Relations.hash t.relations,
+                        Zone.hash t.modified)
+
+        let pretty fmt { octagons } =
+          Format.fprintf fmt "@[%a@]" Octagons.pretty octagons
+      end)
+
+  let pretty_debug fmt { octagons; intervals; relations } =
+    Format.fprintf fmt "@[<v> Octagons: %a@; Intervals: %a@; Relations: %a@]"
+      Octagons.pretty octagons Intervals.pretty intervals
+      Relations.pretty relations
+
+  (* Verify the internal structure of a state [t], depending on the boolean
+     variable [debug]. *)
+  let check =
+    if not debug
+    then fun _ t -> t
+    else fun msg t ->
+      (* Checks that an octagon is properly registered in [t.relations]. This is
+         mandatory for the soundness of the domain. On the other hand, two
+         variables can be related in [t.relations] without an actual octagon
+         between them. *)
+      let check_octagon pair _ =
+        let x, y = Pair.get pair in
+        try VariableSet.mem x (Relations.find y t.relations)
+            && VariableSet.mem y (Relations.find x t.relations)
+        with Not_found -> false
+      in
+      if Octagons.for_all check_octagon t.octagons
+      then t
+      else
+        Value_parameters.abort
+          "Incorrect octagon state computed by function %s:@ %a"
+          msg pretty_debug t
+
+  (* ------------------------------ Lattice --------------------------------- *)
+
+  let top =
+    { octagons = Octagons.top;
+      intervals = Intervals.top;
+      relations = Relations.empty;
+      modified = Zone.top; }
+
+  let empty () =
+    { octagons = Octagons.top;
+      intervals = Intervals.top;
+      relations = Relations.empty;
+      modified = Zone.bottom; }
+
+  let is_included t1 t2 =
+    Octagons.is_included t1.octagons t2.octagons
+    && Intervals.is_included t1.intervals t2.intervals
+    && Zone.is_included t1.modified t2.modified
+
+  let join t1 t2 =
+    let octagons =
+      if not infer_intervals
+      then Octagons.simple_join t1.octagons t2.octagons
+      else
+        let decide_empty intervals pair diamond =
+          let v1, v2 = Pair.get pair in
+          try
+            let i1 = Intervals.find v1 intervals
+            and i2 = Intervals.find v2 intervals in
+            let add = Arith.add v1.vtype i1 i2
+            and sub = Arith.sub v1.vtype i1 i2 in
+            let diamond = Diamond.join diamond { add; sub } in
+            Diamond.trim pair diamond
+          with Not_found -> None
+        in
+        let decide_left = decide_empty t2.intervals
+        and decide_right = decide_empty t1.intervals in
+        Octagons.join ~decide_left ~decide_right t1.octagons t2.octagons
+    in
+    let relations =
+      if infer_intervals
+      then Relations.union t1.relations t2.relations
+      else Relations.inter t1.relations t2.relations
+    in
+    let state =
+      { octagons; relations;
+        intervals = Intervals.join t1.intervals t2.intervals;
+        modified = Zone.join t1.modified t2.modified; }
+    in
+    check "join" state
+
+  let widen _kf _hints t1 t2 =
+    let octagons =
+      if not infer_intervals
+      then Octagons.simple_widen t1.octagons t2.octagons
+      else
+        let decide_empty b intervals pair diamond =
+          let v1, v2 = Pair.get pair in
+          try
+            let i1 = Intervals.find v1 intervals
+            and i2 = Intervals.find v2 intervals in
+            let add = Arith.add v1.vtype i1 i2
+            and sub = Arith.sub v1.vtype i1 i2 in
+            let diamond =
+              if b
+              then Diamond.widen { add; sub } diamond
+              else Diamond.widen diamond { add; sub }
+            in
+            Diamond.trim pair diamond
+          with Not_found -> None
+        in
+        let decide_left = decide_empty false t2.intervals
+        and decide_right = decide_empty true t1.intervals in
+        Octagons.widen ~decide_left ~decide_right t1.octagons t2.octagons
+    in
+    let relations =
+      if infer_intervals
+      then Relations.union t1.relations t2.relations
+      else Relations.inter t1.relations t2.relations
+    in
+    let state =
+      { octagons; relations;
+        intervals = Intervals.widen t1.intervals t2.intervals;
+        modified = Zone.join t1.modified t2.modified; }
+    in
+    check "widen" state
+
+  let narrow t1 t2 =
+    Octagons.narrow t1.octagons t2.octagons >>- fun octagons ->
+    Intervals.narrow t1.intervals t2.intervals >>- fun intervals ->
+    let relations = Relations.union t1.relations t2.relations in
+    let modified = Zone.narrow t1.modified t2.modified in
+    `Value { octagons; intervals; relations; modified; }
+
+  (* -------------- Transitive closure when adding an octagon --------------- *)
+
+  type relation =
+    { vars: varinfo * varinfo;
+      diamond: diamond; }
+
+  let add_diamond state pair diamond =
+    match Diamond.trim pair diamond with
+    | None -> `Value state
+    | Some diamond ->
+      Octagons.add pair diamond state.octagons >>-: fun octagons ->
+      let relations = Relations.relate pair state.relations in
+      { state with octagons; relations }
+
+  let inverse { vars; diamond } =
+    let var1, var2 = vars in
+    { vars = var2, var1; diamond = Diamond.reverse_variables true diamond }
+
+  let transitive_relation y rel1 rel2 =
+    let rel1 =
+      if Variable.equal y (snd rel1.vars) then rel1 else inverse rel1
+    and rel2 =
+      if Variable.equal y (fst rel2.vars) then rel2 else inverse rel2
+    in
+    (* rel1 is about X±Y, rel2 is about Y±Z. *)
+    let typ = y.vtype in
+    (* X+Z = (X+Y) - (Y-Z) and X+Y = (X-Y) + (Y+Z) *)
+    let add =
+      Ival.narrow
+        (Arith.sub typ rel1.diamond.add rel2.diamond.sub)
+        (Arith.add typ rel1.diamond.sub rel2.diamond.add)
+    (* X-Z = (X+Y) - (Y+Z) and X-Z = (X-Y) + (Y-Z) *)
+    and sub =
+      Ival.narrow
+        (Arith.sub typ rel1.diamond.add rel2.diamond.add)
+        (Arith.add typ rel1.diamond.sub rel2.diamond.sub)
+    in
+    let diamond = {add; sub} in
+    let pair, swap = Pair.make (fst rel1.vars) (snd rel2.vars) in
+    let diamond = Diamond.reverse_variables swap diamond in
+    pair, diamond
+
+  let saturate state x y rel1 =
+    try
+      let y_related = Relations.find y state.relations in
+      let y_related = VariableSet.remove x y_related in
+      let aux z state =
+        state >>- fun state ->
+        try
+          let pair, _ = Pair.make y z in
+          let diamond = Octagons.find pair state.octagons in
+          let vars = Pair.get pair in
+          let rel2 = { vars; diamond } in
+          let pair, diamond = transitive_relation y rel1 rel2 in
+          add_diamond state pair diamond
+        with Not_found -> `Value state
+      in
+      VariableSet.fold aux y_related (`Value state)
+    with Not_found -> `Value state
+
+  let add_octagon state octagon =
+    if Arith.is_top_for_pair octagon.variables octagon.value
+    then `Value state
+    else
+      let state =
+        if saturate_octagons
+        then
+          let x, y = Pair.get octagon.variables in
+          let diamond = match octagon.operation with
+            | Add -> { add = octagon.value; sub = Ival.top }
+            | Sub -> { add = Ival.top; sub = octagon.value }
+          in
+          let relation = { vars = x, y; diamond } in
+          saturate state y x relation >>- fun state ->
+          saturate state x y relation
+        else `Value state
+      in
+      state >>- fun state ->
+      Octagons.add_octagon octagon state.octagons >>-: fun octagons ->
+      let relations = Relations.relate octagon.variables state.relations in
+      { state with octagons; relations }
+
+  let remove state x =
+    let intervals = Intervals.remove x state.intervals in
+    let state = { state with intervals } in
+    try
+      let relations = Relations.find x state.relations in
+      let remove_one y state =
+        try
+          let yrelations = Relations.find y state.relations in
+          let yrelations = VariableSet.remove x yrelations in
+          let relations = Relations.add y yrelations state.relations in
+          let pair, _ = Pair.make x y in
+          let octagons = Octagons.remove pair state.octagons in
+          { state with octagons; relations }
+        with Not_found -> state
+      in
+      let state = VariableSet.fold remove_one relations state in
+      let relations = Relations.remove x state.relations in
+      { state with relations }
+    with Not_found -> state
+
+  let related_octagons state x =
+    try
+      let related = Relations.find x state.relations in
+      let aux y acc =
+        let pair, swap = Pair.make x y in
+        try
+          let diamond = Octagons.find pair state.octagons in
+          let diamond = Diamond.reverse_variables swap diamond in
+          (y, diamond) :: acc
+        with Not_found -> acc
+      in
+      VariableSet.fold aux related []
+    with Not_found -> []
+
+  (* x' = ±x - delta *)
+  let sub_delta ~inverse state x delta =
+    let intervals = Intervals.remove x state.intervals in
+    let state = { state with intervals } in
+    let x_related = Relations.find x state.relations in
+    let aux y state =
+      let pair, swap = Pair.make x y in
+      try
+        let diamond = Octagons.find pair state.octagons in
+        let diamond =
+          if inverse
+          then
+            let op = if swap then fun x -> x else Arith.neg in
+            { add = op diamond.sub;
+              sub = op diamond.add }
+          else diamond
+        in
+        let typ = x.vtype in
+        let op = if swap then Arith.add else Arith.sub in
+        let add =
+          if Ival.(equal top diamond.add)
+          then diamond.add
+          else Arith.sub typ diamond.add delta
+        and sub =
+          if Ival.(equal top diamond.sub)
+          then diamond.sub
+          else op typ diamond.sub delta
+        in
+        let diamond' = { add; sub } in
+        let octagons = Octagons.unsafe_add pair diamond' state.octagons in
+        { state with octagons }
+      with Not_found -> state
+    in
+    VariableSet.fold aux x_related state
+end
+
+(* -------------------------------------------------------------------------- *)
+(*                               Octagon domain                               *)
+(* -------------------------------------------------------------------------- *)
+
+module Domain = struct
+
+  include State
+
+  type value = Cvalue.V.t
+  type location = Precise_locs.precise_location
+
+  type origin = unit
+  let top_value = `Value (Cvalue.V.top, ()), Alarmset.all
+
+  let extract_expr oracle state expr =
+    let evaluate_expr expr =
+      match fst (oracle expr) with
+      | `Bottom -> `Top (* should not happen *)
+      | `Value cvalue ->
+        try `Value (Cvalue.V.project_ival cvalue)
+        with Cvalue.V.Not_based_on_null -> `Top
+    in
+    let evaluate_octagon octagon = Octagons.evaluate octagon state.octagons in
+    let ival, alarms =
+      Rewriting.evaluate_through_octagons evaluate_expr evaluate_octagon expr
+    in
+    if Ival.(equal ival top)
+    then top_value
+    else if Ival.is_bottom ival
+    then `Bottom, Alarmset.all
+    else `Value (Cvalue.V.inject_ival ival, ()), alarms
+
+  let extract_lval _oracle _t _lval _typ _loc = top_value
+
+  let backward_location _t _lval _typ loc value = `Value (loc, value)
+
+  let reduce_further state expr value =
+    match expr.enode with
+    | Lval (Var x, NoOffset) when Cil.isIntegralType x.vtype ->
+      begin
+        try
+          let x_ival = Cvalue.V.project_ival value in
+          let octagons = State.related_octagons state x in
+          let reduce acc (y, octagons) =
+            let y_ival1 =
+              if Ival.(equal top octagons.add)
+              then Ival.top
+              else Arith.sub x.vtype octagons.add x_ival
+            in
+            let y_ival2 =
+              if Ival.(equal top octagons.sub)
+              then Ival.top
+              else Arith.sub x.vtype x_ival octagons.sub
+            in
+            let y_ival = Ival.narrow y_ival1 y_ival2 in
+            if Ival.(equal top y_ival) then acc else
+              let y_enode = Lval (Var y, NoOffset) in
+              let y_expr = Cil.new_exp ~loc:expr.eloc y_enode in
+              let y_cvalue = Cvalue.V.inject_ival y_ival in
+              (y_expr, y_cvalue) :: acc
+          in
+          List.fold_left reduce [] octagons
+        with Cvalue.V.Not_based_on_null -> []
+      end
+    | _ -> []
+
+
+  let kill_base base state =
+    try
+      let varinfo = Base.to_varinfo base in
+      State.remove state varinfo
+    with Base.Not_a_C_variable -> state
+
+  let kill zone state =
+    if Locations.Zone.(equal zone top)
+    then top
+    else
+      let modified = Locations.Zone.join state.modified zone in
+      let state = Zone.fold_bases kill_base zone state in
+      { state with modified }
+
+  module Transfer
+      (Valuation: Abstract_domain.Valuation with type value = value
+                                             and type loc = location)
+  = struct
+
+    (* Evaluation function of expressions to ival, from a [valuation]. *)
+    let evaluation_function valuation = fun expr ->
+      match Valuation.find valuation expr with
+      | `Top -> `Top
+      | `Value record ->
+        match record.Eval.value.v with
+        | `Bottom -> `Top (* TODO: why this keeps happening? *)
+        | `Value cvalue ->
+          try `Value (Cvalue.V.project_ival cvalue)
+          with Cvalue.V.Not_based_on_null -> `Top
+
+    exception EBottom
+
+    let infer_octagons evaluate expr ival state =
+      let octagons = Rewriting.make_octagons evaluate expr ival in
+      let add_octagon state octagon =
+        match State.add_octagon state octagon with
+        | `Bottom -> raise EBottom
+        | `Value state -> state
+      in
+      List.fold_left add_octagon state octagons
+
+    let infer_interval expr ival state =
+      if not infer_intervals
+      then state
+      else
+        match expr.enode with
+        | Lval (Var varinfo, NoOffset)
+          when Cil.isIntegralType varinfo.vtype ->
+          let intervals = Intervals.add varinfo ival state.intervals in
+          { state with intervals }
+        | _ -> state
+
+    let update valuation state =
+      let evaluate = evaluation_function valuation in
+      let aux expr record state =
+        let value = record.Eval.value in
+        match record.reductness, value.v, value.initialized, value.escaping with
+        | (Created | Reduced), `Value cvalue, true, false ->
+          begin
+            try
+              let ival = Cvalue.V.project_ival cvalue in
+              let state = infer_octagons evaluate expr ival state in
+              infer_interval expr ival state
+            with Cvalue.V.Not_based_on_null -> state
+          end
+        | _ -> state
+      in
+      try `Value (check "update" (Valuation.fold aux valuation state))
+      with EBottom -> `Bottom
+
+    let assign_interval varinfo assigned state =
+      if not infer_intervals
+      then state
+      else
+        match assigned with
+        | Assign v
+        | Copy (_, { v = `Value v; initialized = true; escaping = false }) ->
+          begin
+            try
+              let ival = Cvalue.V.project_ival v in
+              let intervals = Intervals.add varinfo ival state.intervals in
+              { state with intervals }
+            with Cvalue.V.Not_based_on_null -> state
+          end
+        | _ -> state
+
+    let assign_variable varinfo expr assigned valuation state =
+      let evaluate = evaluation_function valuation in
+      (* TODO: redundant with rewrite_binop below. *)
+      let vars = Rewriting.rewrite evaluate expr in
+      let equal_varinfo v = Variable.equal varinfo v.Rewriting.varinfo in
+      let state =
+        try
+          let var = List.find equal_varinfo vars in
+          let inverse = not var.Rewriting.sign in
+          State.sub_delta ~inverse state varinfo var.Rewriting.coeff
+        with Not_found -> State.remove state varinfo
+      in
+      let state = assign_interval varinfo assigned state in
+      let enode = Lval (Var varinfo, NoOffset) in
+      let left_expr = Cil.new_exp ~loc:expr.eloc enode in
+      (* On the assignment X = E; if X-E can be rewritten as ±(X±Y-v),
+         then the octagonal constraint [X±Y ∈ v] holds. *)
+      let octagons = Rewriting.rewrite_binop evaluate left_expr Sub expr in
+      let state =
+        List.fold_left
+          (fun acc (_sign, octagon) ->
+             acc >>- fun state -> State.add_octagon state octagon)
+          (`Value state) octagons
+      in
+      state >>-: check "precise assign"
+
+    let assign _kinstr left_value expr assigned valuation state =
+      update valuation state >>- fun state ->
+      match left_value.lval with
+      | Var varinfo, NoOffset when Cil.isIntegralType varinfo.vtype ->
+        assign_variable varinfo expr assigned valuation state
+      | _ ->
+        let written_loc = Precise_locs.imprecise_location left_value.lloc in
+        let written_zone =
+          Locations.(enumerate_valid_bits Write written_loc)
+        in
+        let state = kill written_zone state in
+        `Value (check "imprecise assign" state)
+
+    let assume _stmt _exp _bool = update
+
+    let start_call _stmt call valuation state =
+      if intraprocedural ()
+      then `Value (empty ())
+      else
+        let state = { state with modified = Locations.Zone.bottom } in
+        let assign_formal state { formal; concrete; avalue } =
+          state >>- assign_variable formal concrete avalue valuation
+        in
+        List.fold_left assign_formal (`Value state) call.arguments
+
+    let finalize_call _stmt _call ~pre ~post =
+      if intraprocedural ()
+      then `Value (kill post.modified pre)
+      else
+        let modified = Locations.Zone.join post.modified pre.modified in
+        `Value { post with modified }
+
+    let show_expr _valuation _state _fmt _expr = ()
+  end
+
+  let logic_assign _logic_assign location ~pre:_ state =
+    let loc = Precise_locs.imprecise_location location in
+    let zone = Locations.(enumerate_valid_bits Write loc) in
+    let state = kill zone state in
+    check "logic_assign" state
+
+  let evaluate_predicate _env _state _pred = Alarmset.Unknown
+  let reduce_by_predicate _env state _pred _positive = `Value state
+
+  let enter_scope _kf _varinfos state = state
+  let leave_scope _kf varinfos state =
+    let state = List.fold_left State.remove state varinfos in
+    check "leave_scope" state
+
+  let enter_loop _stmt state = state
+  let incr_loop_counter _stmt state = state
+  let leave_loop _stmt state = state
+
+  let introduce_globals _varinfos state = state
+  let initialize_variable _lval _location ~initialized:_ _value state = state
+  let initialize_variable_using_type _kind _varinfo state = state
+
+  let relate _kf bases state =
+    if intraprocedural ()
+    then Base.SetLattice.empty
+    else
+      let aux base acc =
+        try
+          let varinfo = Base.to_varinfo base in
+          let varset = Relations.find varinfo state.relations in
+          let baseset =
+            VariableSet.fold
+              (fun vi acc -> Base.Hptset.add (Base.of_varinfo vi) acc)
+              varset Base.Hptset.empty
+          in
+          Base.SetLattice.(join (inject baseset) acc)
+        with Base.Not_a_C_variable | Not_found -> acc
+      in
+      Base.Hptset.fold aux bases Base.SetLattice.empty
+
+  let filter _kf _kind bases state =
+    if intraprocedural ()
+    then state
+    else
+      let mem_vi varinfo = Base.Hptset.mem (Base.of_varinfo varinfo) bases in
+      let mem_pair pair =
+        let x, y = Pair.get pair in
+        mem_vi x && mem_vi y
+      in
+      let octagons = Octagons.filter mem_pair state.octagons in
+      let intervals = Intervals.filter mem_vi state.intervals in
+      let relations = Relations.filter mem_vi state.relations in
+      { state with octagons; intervals; relations; }
+
+  let reuse =
+    let cache = Hptmap_sig.PersistentCache "Octagons.reuse"
+    and symmetric = false
+    and idempotent = true
+    and decide _key left _right = left in
+    let join_oct = Octagons.internal_join ~cache ~symmetric ~idempotent ~decide
+    and join_itv = Intervals.internal_join ~cache ~symmetric ~idempotent ~decide
+    and join_rel = Relations.union in
+    fun _kf _bases ~current_input ~previous_output ->
+      if intraprocedural ()
+      then previous_output
+      else
+        let current_input = kill previous_output.modified current_input in
+        let prev_output = previous_output in
+        check "reuse result"
+          { octagons = join_oct prev_output.octagons current_input.octagons;
+            intervals = join_itv prev_output.intervals current_input.intervals;
+            relations = join_rel prev_output.relations current_input.relations;
+            modified = current_input.modified }
+
+  let name = "Octagon domain"
+  let log_category = Value_parameters.register_category "d-octagon"
+
+  let storage () = true
+end
+
+include Domain_builder.Complete (Domain)
diff --git a/src/plugins/value/domains/octagons.mli b/src/plugins/value/domains/octagons.mli
new file mode 100644
index 0000000000000000000000000000000000000000..88ba3269a6afbba3a7785bf5e4b4dc903a1969b9
--- /dev/null
+++ b/src/plugins/value/domains/octagons.mli
@@ -0,0 +1,25 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+include Abstract_domain.Leaf
+  with type value = Cvalue.V.t
+   and type location = Precise_locs.precise_location
diff --git a/src/plugins/value/engine/abstractions.ml b/src/plugins/value/engine/abstractions.ml
index 5781d60bf7672faf0129790ab98f89cde419dd68..4261513ad85956b5344086030abbb39c7cef4759 100644
--- a/src/plugins/value/engine/abstractions.ml
+++ b/src/plugins/value/engine/abstractions.ml
@@ -98,6 +98,7 @@ module Config = struct
 
   let cvalue = make 9 "cvalue" CvalueDomain.get (module Cvalue_domain.State)
   let gauges = make 6 "gauges" GaugesDomain.get (module Gauges_domain.D)
+  let octagon = make 6 "octagon" OctagonDomain.get (module Octagons)
   let inout = make 5 "inout" InoutDomain.get (module Inout_domain.D)
   let traces = make 2 "traces" TracesDomain.get (module Traces_domain.D)
   let printer = make 2 "printer" PrinterDomain.get (module Printer_domain)
diff --git a/src/plugins/value/engine/abstractions.mli b/src/plugins/value/engine/abstractions.mli
index 4c2909dbb757e52966e0dab66ec42e86473b87b2..3d4ebe9e437fdf5e913e368f9a5a9a0976ac5d69 100644
--- a/src/plugins/value/engine/abstractions.mli
+++ b/src/plugins/value/engine/abstractions.mli
@@ -132,6 +132,7 @@ module Config : sig
   val equality: flag
   val symbolic_locations: flag
   val gauges: flag
+  val octagon: flag
   val bitwise: flag
   val inout: flag
   val sign: flag
diff --git a/src/plugins/value/engine/mem_exec.ml b/src/plugins/value/engine/mem_exec.ml
index 17adb02719337ef16ce67e9b106b1ac74647c4e8..83629ab5fc926f6d2d59ee2a77e93208b3df5d6a 100644
--- a/src/plugins/value/engine/mem_exec.ml
+++ b/src/plugins/value/engine/mem_exec.ml
@@ -240,18 +240,22 @@ module Make
       raise [Result_found] when this execution exists, or do nothing. *)
   let find_match_in_previous kf (map_inputs: InputBasesToCallEffect.t) state =
     let aux_previous_call binputs hstates =
-      (* restrict [state] to the inputs of this call *)
-      let st_filtered = Domain.filter kf `Pre binputs state in
-      try
-        let bases, outputs, i = Domain.Hashtbl.find hstates st_filtered in
-        (* We have found a previous execution, in which the outputs are
-           [outputs]. Copy them in [state] and return this result. *)
-        let process output =
-          Domain.reuse kf bases ~current_input:state ~previous_output:output
-        in
-        let outputs = List.map process outputs in
-        raise (Result_found (outputs, i))
-      with Not_found -> ()
+      let brelated = Domain.relate kf binputs state in
+      if not Base.SetLattice.(is_included brelated (inject binputs))
+      then ()
+      else
+        (* restrict [state] to the inputs of this call *)
+        let st_filtered = Domain.filter kf `Pre binputs state in
+        try
+          let bases, outputs, i = Domain.Hashtbl.find hstates st_filtered in
+          (* We have found a previous execution, in which the outputs are
+             [outputs]. Copy them in [state] and return this result. *)
+          let process output =
+            Domain.reuse kf bases ~current_input:state ~previous_output:output
+          in
+          let outputs = List.map process outputs in
+          raise (Result_found (outputs, i))
+        with Not_found -> ()
     in
     Base.Hptset.Hashtbl.iter aux_previous_call map_inputs
 
diff --git a/src/plugins/value/utests b/src/plugins/value/utests
index bce097c3b1cfcea2ffac3282c750f757bd950bae..0b2cfea613cd1024532c5bbff89cae377bc921b5 100755
--- a/src/plugins/value/utests
+++ b/src/plugins/value/utests
@@ -12,7 +12,7 @@ if [ $res -ne 0 ]; then
 fi
 
 TESTS=(float value idct builtins)
-CONFIGS=(apron equalities bitwise symblocs gauges)
+CONFIGS=(apron equalities bitwise symblocs gauges octagons)
 
 for C in ${CONFIGS[@]}
 do
diff --git a/src/plugins/value/value_parameters.ml b/src/plugins/value/value_parameters.ml
index 1de1430a7c101b4894bd7aa0176476f575738ee8..0f972433fe475cecf2c6f1a3981bafea11ed233f 100644
--- a/src/plugins/value/value_parameters.ml
+++ b/src/plugins/value/value_parameters.ml
@@ -167,6 +167,13 @@ module SymbolicLocsDomain = Domain_Parameter
       let default = false
     end)
 
+module OctagonDomain = Domain_Parameter
+    (struct
+      let option_name = "-eva-octagon-domain"
+      let help = "Use the octagon domain of Eva."
+      let default = false
+    end)
+
 module BitwiseOffsmDomain = Domain_Parameter
     (struct
       let option_name = "-eva-bitwise-domain"
@@ -312,6 +319,21 @@ module EqualityCallFunction =
     end)
 let () = add_precision_dep EqualityCallFunction.parameter
 
+let () = Parameter_customize.set_group domains
+module OctagonCall =
+  Bool
+    (struct
+      let option_name = "-eva-octagon-through-calls"
+      let help = "Whether the relations inferred by the octagon domain are \
+                  propagated through function calls. Disabled by default: \
+                  the octagon analysis is intra-procedural, starting \
+                  each function with an empty octagon state, \
+                  and losing the octagons inferred at the end. \
+                  The interprocedural analysis is more precise but slower."
+      let default = false
+    end)
+let () = add_precision_dep OctagonCall.parameter
+
 let () = Parameter_customize.set_group domains
 module Numerors_Real_Size =
   Int
@@ -1520,7 +1542,9 @@ let () =
   bind (module EqualityDomain) (fun n -> n > 1);
   bind (module EqualityCall) (fun n -> if n > 2 then "formals" else "none");
   bind (module GaugesDomain) (fun n -> n > 3);
-  bind (module SplitReturn) (fun n -> if n > 4 then "auto" else "");
+  bind (module OctagonDomain) (fun n -> n > 4);
+  bind (module OctagonCall) (fun n -> n > 5);
+  bind (module SplitReturn) (fun n -> if n > 6 then "auto" else "");
   ()
 
 let set_analysis n =
diff --git a/src/plugins/value/value_parameters.mli b/src/plugins/value/value_parameters.mli
index 3e0fb860496b052159093bf18d4239e97379081a..8cda781691b11799c8f0277f35bab8803cc53206 100644
--- a/src/plugins/value/value_parameters.mli
+++ b/src/plugins/value/value_parameters.mli
@@ -32,6 +32,7 @@ module CvalueDomain: Parameter_sig.Bool
 module EqualityDomain: Parameter_sig.Bool
 module GaugesDomain: Parameter_sig.Bool
 module SymbolicLocsDomain: Parameter_sig.Bool
+module OctagonDomain: Parameter_sig.Bool
 module BitwiseOffsmDomain: Parameter_sig.Bool
 module InoutDomain: Parameter_sig.Bool
 module SignDomain: Parameter_sig.Bool
@@ -50,6 +51,8 @@ module EqualityCallFunction:
   Parameter_sig.Map with type key = Cil_types.kernel_function
                      and type value = string
 
+module OctagonCall: Parameter_sig.Bool
+
 module TracesUnrollLoop: Parameter_sig.Bool
 module TracesUnifyLoop: Parameter_sig.Bool
 module TracesDot: Parameter_sig.String
diff --git a/src/plugins/value/vtests b/src/plugins/value/vtests
index f03eac0072fb247668a1005922ae6f62319a7af6..2962dae8d845919d715d58362f08e97b0fd984d0 100755
--- a/src/plugins/value/vtests
+++ b/src/plugins/value/vtests
@@ -1,7 +1,7 @@
 #!/bin/bash -eu
 
 DEFAULT_TESTS=(float value idct builtins)
-CONFIGS=( apron equalities bitwise symblocs gauges)
+CONFIGS=( apron equalities bitwise symblocs gauges octagons)
 
 ARGS=("${@-}")
 
diff --git a/tests/builtins/diff_octagons b/tests/builtins/diff_octagons
new file mode 100644
index 0000000000000000000000000000000000000000..0054c1a3abad131720e7bd2fa952732a6567d75c
--- /dev/null
+++ b/tests/builtins/diff_octagons
@@ -0,0 +1,335 @@
+diff tests/builtins/oracle/Longinit_sequencer.res.oracle tests/builtins/oracle_octagons/Longinit_sequencer.res.oracle
+327c327
+<   tests/builtins/result/Longinit_sequencer.sav
+---
+>   tests/builtins/result_octagons/Longinit_sequencer.sav
+568c568
+<   tests/builtins/result/Longinit_sequencer.sav
+---
+>   tests/builtins/result_octagons/Longinit_sequencer.sav
+diff tests/builtins/oracle/allocated.0.res.oracle tests/builtins/oracle_octagons/allocated.0.res.oracle
+273c273
+<   j ∈ [1..2147483647]
+---
+>   j ∈ {10}
+diff tests/builtins/oracle/allocated.1.res.oracle tests/builtins/oracle_octagons/allocated.1.res.oracle
+191a192,194
+> [eva] tests/builtins/allocated.c:82: 
+>   Call to builtin Frama_C_malloc_fresh for function malloc
+> [eva] tests/builtins/allocated.c:82: allocating variable __malloc_main_l82_7
+208a212,214
+>   strong free on bases: {__malloc_main_l82_7}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+223a230,232
+>   strong free on bases: {__malloc_main_l82_7}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+238a248,250
+>   strong free on bases: {__malloc_main_l82_7}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+252,254c264,266
+< [eva] tests/builtins/allocated.c:82: 
+<   Call to builtin Frama_C_malloc_fresh for function malloc
+< [eva] tests/builtins/allocated.c:82: allocating variable __malloc_main_l82_7
+---
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_7}
+323a336,356
+> [eva] tests/builtins/allocated.c:82: 
+>   Call to builtin Frama_C_malloc_fresh for function malloc
+> [eva] tests/builtins/allocated.c:82: allocating variable __malloc_main_l82_31
+> [eva] tests/builtins/allocated.c:82: 
+>   Call to builtin Frama_C_malloc_fresh for function malloc
+> [eva] tests/builtins/allocated.c:82: allocating variable __malloc_main_l82_32
+> [eva] tests/builtins/allocated.c:82: 
+>   Call to builtin Frama_C_malloc_fresh for function malloc
+> [eva] tests/builtins/allocated.c:82: allocating variable __malloc_main_l82_33
+> [eva] tests/builtins/allocated.c:82: 
+>   Call to builtin Frama_C_malloc_fresh for function malloc
+> [eva] tests/builtins/allocated.c:82: allocating variable __malloc_main_l82_34
+> [eva] tests/builtins/allocated.c:82: 
+>   Call to builtin Frama_C_malloc_fresh for function malloc
+> [eva] tests/builtins/allocated.c:82: allocating variable __malloc_main_l82_35
+> [eva] tests/builtins/allocated.c:82: 
+>   Call to builtin Frama_C_malloc_fresh for function malloc
+> [eva] tests/builtins/allocated.c:82: allocating variable __malloc_main_l82_36
+> [eva] tests/builtins/allocated.c:82: 
+>   Call to builtin Frama_C_malloc_fresh for function malloc
+> [eva] tests/builtins/allocated.c:82: allocating variable __malloc_main_l82_37
+329,330d361
+<   Trace partitioning superposing up to 300 states
+< [eva] tests/builtins/allocated.c:84: 
+333a365,385
+>   strong free on bases: {__malloc_main_l82_37}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_36}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_35}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_34}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_33}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_32}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_31}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+403c455,473
+<   strong free on bases: {__malloc_main_l82_7}
+---
+>   strong free on bases: {__malloc_main_l82_37}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_36}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_35}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_34}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_33}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_32}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_31}
+475c545,563
+<   strong free on bases: {__malloc_main_l82_7}
+---
+>   strong free on bases: {__malloc_main_l82_37}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_36}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_35}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_34}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_33}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_32}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_31}
+547c635,653
+<   strong free on bases: {__malloc_main_l82_7}
+---
+>   strong free on bases: {__malloc_main_l82_37}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_36}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_35}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_34}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_33}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_32}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_31}
+619c725,743
+<   strong free on bases: {__malloc_main_l82_7}
+---
+>   strong free on bases: {__malloc_main_l82_37}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_36}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_35}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_34}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_33}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_32}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_31}
+691c815,833
+<   strong free on bases: {__malloc_main_l82_7}
+---
+>   strong free on bases: {__malloc_main_l82_37}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_36}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_35}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_34}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_33}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_32}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_31}
+763c905,923
+<   strong free on bases: {__malloc_main_l82_7}
+---
+>   strong free on bases: {__malloc_main_l82_37}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_36}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_35}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_34}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_33}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_32}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_31}
+835c995,1013
+<   strong free on bases: {__malloc_main_l82_7}
+---
+>   strong free on bases: {__malloc_main_l82_37}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_36}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_35}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_34}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_33}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_32}
+> [eva] tests/builtins/allocated.c:87: Call to builtin free
+> [eva:malloc] tests/builtins/allocated.c:87: 
+>   strong free on bases: {__malloc_main_l82_31}
+905,907c1083,1084
+< [eva] tests/builtins/allocated.c:87: Call to builtin free
+< [eva:malloc] tests/builtins/allocated.c:87: 
+<   strong free on bases: {__malloc_main_l82_7}
+---
+> [eva] tests/builtins/allocated.c:81: 
+>   Trace partitioning superposing up to 500 states
+1069,1071c1246,1247
+<   __malloc_main_l82_7[0] ∈ {21} or UNINITIALIZED
+<                      [1] ∈ {24} or UNINITIALIZED
+<                      [2] ∈ {27} or UNINITIALIZED
+---
+>   __malloc_main_l82_7[0] ∈ {14} or UNINITIALIZED
+>                      [1] ∈ {17} or UNINITIALIZED
+1140a1317,1337
+>   __malloc_main_l82_31[0] ∈ {21} or UNINITIALIZED
+>                       [1] ∈ {24} or UNINITIALIZED
+>                       [2] ∈ {27} or UNINITIALIZED
+>   __malloc_main_l82_32[0] ∈ {21} or UNINITIALIZED
+>                       [1] ∈ {24} or UNINITIALIZED
+>                       [2] ∈ {27} or UNINITIALIZED
+>   __malloc_main_l82_33[0] ∈ {21} or UNINITIALIZED
+>                       [1] ∈ {24} or UNINITIALIZED
+>                       [2] ∈ {27} or UNINITIALIZED
+>   __malloc_main_l82_34[0] ∈ {21} or UNINITIALIZED
+>                       [1] ∈ {24} or UNINITIALIZED
+>                       [2] ∈ {27} or UNINITIALIZED
+>   __malloc_main_l82_35[0] ∈ {21} or UNINITIALIZED
+>                       [1] ∈ {24} or UNINITIALIZED
+>                       [2] ∈ {27} or UNINITIALIZED
+>   __malloc_main_l82_36[0] ∈ {21} or UNINITIALIZED
+>                       [1] ∈ {24} or UNINITIALIZED
+>                       [2] ∈ {27} or UNINITIALIZED
+>   __malloc_main_l82_37[0] ∈ {21} or UNINITIALIZED
+>                       [1] ∈ {24} or UNINITIALIZED
+>                       [2] ∈ {27} or UNINITIALIZED
+1184c1381
+<   __malloc_main_l82_7[0..2] FROM __fc_heap_status; nondet (and SELF)
+---
+>   __malloc_main_l82_7[0..1] FROM __fc_heap_status; nondet (and SELF)
+1207a1405,1411
+>   __malloc_main_l82_31[0..2] FROM __fc_heap_status; nondet (and SELF)
+>   __malloc_main_l82_32[0..2] FROM __fc_heap_status; nondet (and SELF)
+>   __malloc_main_l82_33[0..2] FROM __fc_heap_status; nondet (and SELF)
+>   __malloc_main_l82_34[0..2] FROM __fc_heap_status; nondet (and SELF)
+>   __malloc_main_l82_35[0..2] FROM __fc_heap_status; nondet (and SELF)
+>   __malloc_main_l82_36[0..2] FROM __fc_heap_status; nondet (and SELF)
+>   __malloc_main_l82_37[0..2] FROM __fc_heap_status; nondet (and SELF)
+1231c1435
+<     __malloc_main_l82_6[0..1]; __malloc_main_l82_7[0..2];
+---
+>     __malloc_main_l82_6[0..1]; __malloc_main_l82_7[0..1];
+1243,1244c1447,1452
+<     __malloc_main_l82_30[0..2]; __malloc_main_l97[0]; __malloc_main_l114[0..3];
+<     __malloc_main_l127; __malloc_main_l127_0[0..1]; __malloc_main_l127_1[0..2];
+---
+>     __malloc_main_l82_30[0..2]; __malloc_main_l82_31[0..2];
+>     __malloc_main_l82_32[0..2]; __malloc_main_l82_33[0..2];
+>     __malloc_main_l82_34[0..2]; __malloc_main_l82_35[0..2];
+>     __malloc_main_l82_36[0..2]; __malloc_main_l82_37[0..2];
+>     __malloc_main_l97[0]; __malloc_main_l114[0..3]; __malloc_main_l127;
+>     __malloc_main_l127_0[0..1]; __malloc_main_l127_1[0..2];
+diff tests/builtins/oracle/imprecise.res.oracle tests/builtins/oracle_octagons/imprecise.res.oracle
+224a225,226
+> [kernel] tests/builtins/imprecise.c:111: 
+>   more than 200(300) elements to enumerate. Approximating.
+233a236,237
+> [kernel] tests/builtins/imprecise.c:114: 
+>   more than 200(300) elements to enumerate. Approximating.
+237,240d240
+< [kernel] tests/builtins/imprecise.c:111: 
+<   more than 200(300) elements to enumerate. Approximating.
+< [kernel] tests/builtins/imprecise.c:114: 
+<   more than 200(300) elements to enumerate. Approximating.
+diff tests/builtins/oracle/linked_list.1.res.oracle tests/builtins/oracle_octagons/linked_list.1.res.oracle
+506a507,508
+> [kernel] tests/builtins/linked_list.c:43: 
+>   more than 100(128) elements to enumerate. Approximating.
+508a511,512
+> [kernel] tests/builtins/linked_list.c:44: 
+>   more than 100(128) elements to enumerate. Approximating.
+600,603d603
+< [kernel] tests/builtins/linked_list.c:43: 
+<   more than 100(128) elements to enumerate. Approximating.
+< [kernel] tests/builtins/linked_list.c:44: 
+<   more than 100(128) elements to enumerate. Approximating.
+diff tests/builtins/oracle/malloc-optimistic.res.oracle tests/builtins/oracle_octagons/malloc-optimistic.res.oracle
+3520c3520
+<   i ∈ [14..100]
+---
+>   i ∈ {98; 99; 100}
+3524c3524
+<   i ∈ [14..100]
+---
+>   i ∈ {98; 99; 100}
diff --git a/tests/builtins/test_config_octagons b/tests/builtins/test_config_octagons
new file mode 100644
index 0000000000000000000000000000000000000000..848a1c5fa4142125e0dc80808d0a8027d9760a2a
--- /dev/null
+++ b/tests/builtins/test_config_octagons
@@ -0,0 +1,3 @@
+MACRO: EVA_OPTIONS @EVA_OPTIONS@ -eva-msg-key malloc -eva-warn-key malloc:weak=feedback -eva-no-alloc-returns-null -eva-octagon-domain
+MACRO: EVA_CONFIG @EVA_OPTIONS@ -no-autoload-plugins -load-module from,inout,eva,scope,variadic
+OPT: -eva @EVA_CONFIG@ -journal-disable -out -input -deps
diff --git a/tests/float/diff_octagons b/tests/float/diff_octagons
new file mode 100644
index 0000000000000000000000000000000000000000..806b63f0fdd00d77aa58baea79238e5893426733
--- /dev/null
+++ b/tests/float/diff_octagons
@@ -0,0 +1,18 @@
+Only in tests/float/oracle: absorb.res.oracle
+Only in tests/float/oracle: absorb_sav.err
+Only in tests/float/oracle: absorb_sav.res
+Only in tests/float/oracle: absorb_sav2.err
+Only in tests/float/oracle: absorb_sav2.res
+Only in tests/float/oracle: fval_test.res.oracle
+diff tests/float/oracle/nonlin.1.res.oracle tests/float/oracle_octagons/nonlin.1.res.oracle
+253a254,255
+> [eva:nonlin] tests/float/nonlin.c:101: non-linear 'f + f', lv 'f'
+> [eva:nonlin] tests/float/nonlin.c:101: subdividing on f
+257d258
+< [eva:nonlin] tests/float/nonlin.c:101: subdividing on f
+diff tests/float/oracle/nonlin.3.res.oracle tests/float/oracle_octagons/nonlin.3.res.oracle
+253a254,255
+> [eva:nonlin] tests/float/nonlin.c:101: non-linear 'f + f', lv 'f'
+> [eva:nonlin] tests/float/nonlin.c:101: subdividing on f
+257d258
+< [eva:nonlin] tests/float/nonlin.c:101: subdividing on f
diff --git a/tests/idct/diff_octagons b/tests/idct/diff_octagons
new file mode 100644
index 0000000000000000000000000000000000000000..46e477e3f73c2fc3d4aa05888afdfc1d43d7b003
--- /dev/null
+++ b/tests/idct/diff_octagons
@@ -0,0 +1,78 @@
+diff tests/idct/oracle/ieee_1180_1990.res.oracle tests/idct/oracle_octagons/ieee_1180_1990.res.oracle
+424a425,432
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+498a507,514
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+571a588,595
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+> [eva] tests/idct/ieee_1180_1990.c:85: 
+>   Reusing old results for call to IEEE_1180_1990_rand
+579a604,617
+> [eva] tests/idct/ieee_1180_1990.c:100: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+> [eva] tests/idct/ieee_1180_1990.c:101: 
+>   Call to builtin Frama_C_cos for function cos
+> [eva] tests/idct/ieee_1180_1990.c:100: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+> [eva] tests/idct/ieee_1180_1990.c:101: 
+>   Call to builtin Frama_C_cos for function cos
+> [eva] tests/idct/ieee_1180_1990.c:100: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+> [eva] tests/idct/ieee_1180_1990.c:101: 
+>   Call to builtin Frama_C_cos for function cos
+> [eva] tests/idct/ieee_1180_1990.c:100: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+581a620,621
+> [eva] tests/idct/ieee_1180_1990.c:100: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+583a624,629
+> [eva] tests/idct/ieee_1180_1990.c:100: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+> [eva] tests/idct/ieee_1180_1990.c:101: 
+>   Call to builtin Frama_C_cos for function cos
+> [eva] tests/idct/ieee_1180_1990.c:100: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+605a652,661
+> [eva] tests/idct/ieee_1180_1990.c:140: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+> [eva] tests/idct/ieee_1180_1990.c:141: 
+>   Call to builtin Frama_C_cos for function cos
+> [eva] tests/idct/ieee_1180_1990.c:140: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+> [eva] tests/idct/ieee_1180_1990.c:141: 
+>   Call to builtin Frama_C_cos for function cos
+> [eva] tests/idct/ieee_1180_1990.c:140: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+607a664,665
+> [eva] tests/idct/ieee_1180_1990.c:140: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+609a668,677
+> [eva] tests/idct/ieee_1180_1990.c:140: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+> [eva] tests/idct/ieee_1180_1990.c:141: 
+>   Call to builtin Frama_C_cos for function cos
+> [eva] tests/idct/ieee_1180_1990.c:140: 
+>   Call to builtin Frama_C_sqrt for function sqrt
+> [eva] tests/idct/ieee_1180_1990.c:141: 
+>   Call to builtin Frama_C_cos for function cos
+> [eva] tests/idct/ieee_1180_1990.c:140: 
+>   Call to builtin Frama_C_sqrt for function sqrt
diff --git a/tests/test_config_octagons b/tests/test_config_octagons
new file mode 100644
index 0000000000000000000000000000000000000000..701bc8421e733eb40844d0b62ab65400925c498e
--- /dev/null
+++ b/tests/test_config_octagons
@@ -0,0 +1,3 @@
+MACRO: EVA_OPTIONS -eva-show-progress -eva-msg-key=-summary -eva-octagon-domain
+MACRO: EVA_CONFIG @EVA_OPTIONS@ -no-autoload-plugins -load-module from,inout,eva,scope,variadic
+OPT: -eva @EVA_CONFIG@ -journal-disable -out -input -deps
diff --git a/tests/value/diff_apron b/tests/value/diff_apron
index 2ead3a445bbc76a876c5e89fb0e57325a79f52a0..e43d97743cb15e14e2c12e5d5ca200006be5b5a1 100644
--- a/tests/value/diff_apron
+++ b/tests/value/diff_apron
@@ -670,6 +670,17 @@ diff tests/value/oracle/modulo.res.oracle tests/value/oracle_apron/modulo.res.or
 60a109,110
 > [eva] tests/value/modulo.i:64: Frama_C_show_each_3: [-10..10], [-9..9], [-8..8]
 > [eva] tests/value/modulo.i:64: Frama_C_show_each_3: [-9..9], [-8..8], [-7..7]
+diff tests/value/oracle/octagons.res.oracle tests/value/oracle_apron/octagons.res.oracle
+270,273c270,273
+<   a ∈ [-1024..2147483647]
+<   b ∈ [-1023..2147483647]
+<   c ∈ [-1023..2147483647]
+<   d ∈ [-1032..2147483647]
+---
+>   a ∈ [-603..2147483646]
+>   b ∈ [-602..2147483647]
+>   c ∈ [-602..1446]
+>   d ∈ [-611..2147483647]
 diff tests/value/oracle/offsetmap.0.res.oracle tests/value/oracle_apron/offsetmap.0.res.oracle
 62,63c62
 <   a[bits 0 to 7] ∈ {1; 6}
diff --git a/tests/value/diff_equalities b/tests/value/diff_equalities
index 821b7e3c2fc6803fa2584c2b2deedc7692ba8b60..83d093f00364f8bd80e50ed2ff37c260660b6c70 100644
--- a/tests/value/diff_equalities
+++ b/tests/value/diff_equalities
@@ -610,6 +610,15 @@ diff tests/value/oracle/nonlin.res.oracle tests/value/oracle_equalities/nonlin.r
 <   q ∈ {{ &x + [-400..400],0%4 }}
 ---
 >   q ∈ {{ &x }}
+diff tests/value/oracle/octagons.res.oracle tests/value/oracle_equalities/octagons.res.oracle
+29c29
+<   Frama_C_show_each_unreduced_unsigned: [0..4294967295], [0..4294967295]
+---
+>   Frama_C_show_each_unreduced_unsigned: [0..4294967295], [6..4294967295]
+255c255
+<   t ∈ [--..--] or UNINITIALIZED
+---
+>   t ∈ [6..4294967295] or UNINITIALIZED
 diff tests/value/oracle/offsetmap.0.res.oracle tests/value/oracle_equalities/offsetmap.0.res.oracle
 38d37
 < [eva] Recording results for g
diff --git a/tests/value/diff_gauges b/tests/value/diff_gauges
index e0312fea2b92804faca6c4ccae3d2e2afc83fda6..ab9ecdc28fc62ed33d66256204e38c85a05476ed 100644
--- a/tests/value/diff_gauges
+++ b/tests/value/diff_gauges
@@ -889,6 +889,34 @@ diff tests/value/oracle/noreturn.res.oracle tests/value/oracle_gauges/noreturn.r
 > [eva] tests/value/noreturn.i:7: starting to merge loop iterations
 36a40
 > [eva] tests/value/noreturn.i:13: starting to merge loop iterations
+diff tests/value/oracle/octagons.res.oracle tests/value/oracle_gauges/octagons.res.oracle
+121,128d120
+< [eva:alarm] tests/value/octagons.c:107: Warning: 
+<   signed overflow. assert a + 2 ≤ 2147483647;
+< [eva:alarm] tests/value/octagons.c:108: Warning: 
+<   signed overflow. assert b + 2 ≤ 2147483647;
+< [eva:alarm] tests/value/octagons.c:110: Warning: 
+<   signed overflow. assert a + k ≤ 2147483647;
+< [eva:alarm] tests/value/octagons.c:113: Warning: 
+<   signed overflow. assert -2147483648 ≤ c - a;
+130c122
+< [eva] tests/value/octagons.c:116: Frama_C_show_each_imprecise: [-2147483648..1]
+---
+> [eva] tests/value/octagons.c:116: Frama_C_show_each_imprecise: [-2468..1]
+270,273c262,265
+<   a ∈ [-1024..2147483647]
+<   b ∈ [-1023..2147483647]
+<   c ∈ [-1023..2147483647]
+<   d ∈ [-1032..2147483647]
+---
+>   a ∈ [-182..1866]
+>   b ∈ [-181..1867]
+>   c ∈ [-602..1446]
+>   d ∈ [-190..1874]
+275c267
+<   d2 ∈ [-2147483648..1]
+---
+>   d2 ∈ [-2468..1]
 diff tests/value/oracle/reduce_formals.res.oracle tests/value/oracle_gauges/reduce_formals.res.oracle
 10a11
 > [eva] tests/value/reduce_formals.i:5: starting to merge loop iterations
diff --git a/tests/value/diff_octagons b/tests/value/diff_octagons
new file mode 100644
index 0000000000000000000000000000000000000000..2b5ae606dfe09be4f1aa091b2742ab58c421eb1f
--- /dev/null
+++ b/tests/value/diff_octagons
@@ -0,0 +1,399 @@
+diff tests/value/oracle/alias.1.res.oracle tests/value/oracle_octagons/alias.1.res.oracle
+85c85
+<   z ∈ {0; 1; 2}
+---
+>   z ∈ {0; 2}
+diff tests/value/oracle/alias.2.res.oracle tests/value/oracle_octagons/alias.2.res.oracle
+76c76
+<   z ∈ {-5; -4; -3; -2; -1; 0; 1; 1000}
+---
+>   z ∈ {-2; -1; 0; 1000}
+diff tests/value/oracle/alias.3.res.oracle tests/value/oracle_octagons/alias.3.res.oracle
+67c67
+<   z ∈ {0; 1; 2}
+---
+>   z ∈ {0; 2}
+diff tests/value/oracle/alias.5.res.oracle tests/value/oracle_octagons/alias.5.res.oracle
+59a60
+> [eva] tests/value/alias.i:260: starting to merge loop iterations
+diff tests/value/oracle/alias.6.res.oracle tests/value/oracle_octagons/alias.6.res.oracle
+82c82
+<   t ∈ {4; 5; 6}
+---
+>   t ∈ {5}
+87c87
+<   y ∈ {0; 1}
+---
+>   y ∈ {1}
+94,96c94,96
+<   tz1 ∈ {0; 1}
+<   tz2 ∈ {0; 1}
+<   tz3 ∈ {0; 1}
+---
+>   tz1 ∈ {1}
+>   tz2 ∈ {1}
+>   tz3 ∈ {1}
+diff tests/value/oracle/bitfield.res.oracle tests/value/oracle_octagons/bitfield.res.oracle
+132a133,135
+> [eva] tests/value/bitfield.i:71: 
+>   Frama_C_show_each:
+>   {{ garbled mix of &{b} (origin: Misaligned {tests/value/bitfield.i:70}) }}
+diff tests/value/oracle/builtins_split.res.oracle tests/value/oracle_octagons/builtins_split.res.oracle
+70a71,84
+> [eva] tests/value/builtins_split.c:104: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:104: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:104: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:104: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:104: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:104: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:104: 
+>   Call to builtin Frama_C_builtin_split_all
+81a96,109
+> [eva] tests/value/builtins_split.c:112: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:112: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:112: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:112: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:112: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:112: 
+>   Call to builtin Frama_C_builtin_split_all
+> [eva] tests/value/builtins_split.c:112: 
+>   Call to builtin Frama_C_builtin_split_all
+diff tests/value/oracle/call_simple.res.oracle tests/value/oracle_octagons/call_simple.res.oracle
+28c28
+<   c ∈ [--..--]
+---
+>   c ∈ [-2147483648..2147483646]
+diff tests/value/oracle/descending.res.oracle tests/value/oracle_octagons/descending.res.oracle
+42c42
+<   i ∈ {31; 32}
+---
+>   i ∈ {31}
+diff tests/value/oracle/downcast.res.oracle tests/value/oracle_octagons/downcast.res.oracle
+61c61
+<   [100000..2147483647], [100145..2147483647], [100145..2147483647]
+---
+>   [100000..2147483502], [100145..2147483647], [100145..2147483647]
+167c167
+<   x_0 ∈ [100000..2147483647]
+---
+>   x_0 ∈ [100000..2147483502]
+diff tests/value/oracle/equality.res.oracle tests/value/oracle_octagons/equality.res.oracle
+29,30c29,30
+<   y ∈ [0..42] or UNINITIALIZED
+<   w ∈ [0..42] or UNINITIALIZED
+---
+>   y ∈ [0..42]
+>   w ∈ [0..42]
+diff tests/value/oracle/find_ivaltop.res.oracle tests/value/oracle_octagons/find_ivaltop.res.oracle
+32,33c32,33
+<   j ∈ {0; 1; 2; 3; 4; 5; 6; 7}
+<   X ∈ {1; 2; 3; 4; 5; 6; 7; 8}
+---
+>   j ∈ {7}
+>   X ∈ {8}
+39c39
+<   \result FROM t[0..7]
+---
+>   \result FROM t[7]
+44c44
+<     t[0..7]
+---
+>     t[7]
+diff tests/value/oracle/for_loops.3.res.oracle tests/value/oracle_octagons/for_loops.3.res.oracle
+20c20
+<   v ∈ [0..2147483647]
+---
+>   v ∈ [5..2147483647]
+diff tests/value/oracle/gauges.res.oracle tests/value/oracle_octagons/gauges.res.oracle
+209,210d208
+< [eva:alarm] tests/value/gauges.c:156: Warning: 
+<   signed overflow. assert -2147483648 ≤ toCopy - 1;
+272,273d269
+< [eva:alarm] tests/value/gauges.c:201: Warning: 
+<   signed overflow. assert -2147483648 ≤ numNonZero - 1;
+296,300d291
+< [eva] tests/value/gauges.c:218: Frama_C_show_each:
+< [eva] tests/value/gauges.c:218: Frama_C_show_each:
+< [eva] tests/value/gauges.c:218: Frama_C_show_each:
+< [eva:alarm] tests/value/gauges.c:220: Warning: 
+<   signed overflow. assert -2147483648 ≤ n - 1;
+787c778
+<   numNonZero ∈ [-2147483648..8]
+---
+>   numNonZero ∈ {-1}
+798c789
+<   n ∈ [-2147483648..99]
+---
+>   n ∈ {-1}
+859c850
+<   toCopy ∈ [-2147483648..99]
+---
+>   toCopy ∈ {-1}
+diff tests/value/oracle/loop.res.oracle tests/value/oracle_octagons/loop.res.oracle
+26c26
+<   r ∈ [0..2147483646],0%2
+---
+>   r ∈ [46..2147483646],0%2
+diff tests/value/oracle/loop_wvar.1.res.oracle tests/value/oracle_octagons/loop_wvar.1.res.oracle
+12,13d11
+< [eva:alarm] tests/value/loop_wvar.i:57: Warning: 
+<   signed overflow. assert next + 1 ≤ 2147483647;
+41c39
+<   next ∈ [0..2147483647]
+---
+>   next ∈ [0..25]
+diff tests/value/oracle/modulo.res.oracle tests/value/oracle_octagons/modulo.res.oracle
+40a41,56
+> [eva] tests/value/modulo.i:41: Frama_C_show_each_1: [-10..-1], [-9..-1], [-8..0]
+> [eva] tests/value/modulo.i:41: Frama_C_show_each_1: [-10..-1], [1..9], [-8..0]
+> [eva] tests/value/modulo.i:41: Frama_C_show_each_1: [1..10], [-9..-1], [0..8]
+> [eva] tests/value/modulo.i:41: Frama_C_show_each_1: [1..10], [1..9], [0..8]
+> [eva] tests/value/modulo.i:41: 
+>   Frama_C_show_each_1:
+>   [1..9], {1; 2; 3; 4; 5; 6; 7; 8}, {0; 1; 2; 3; 4; 5; 6; 7}
+> [eva] tests/value/modulo.i:41: 
+>   Frama_C_show_each_1:
+>   [-9..-1], {1; 2; 3; 4; 5; 6; 7; 8}, {-7; -6; -5; -4; -3; -2; -1; 0}
+> [eva] tests/value/modulo.i:41: 
+>   Frama_C_show_each_1:
+>   [1..9], {-8; -7; -6; -5; -4; -3; -2; -1}, {0; 1; 2; 3; 4; 5; 6; 7}
+> [eva] tests/value/modulo.i:41: 
+>   Frama_C_show_each_1:
+>   [-9..-1], {-8; -7; -6; -5; -4; -3; -2; -1}, {-7; -6; -5; -4; -3; -2; -1; 0}
+50a67,82
+> [eva] tests/value/modulo.i:53: Frama_C_show_each_2: [-10..-1], [1..9], [-8..0]
+> [eva] tests/value/modulo.i:53: Frama_C_show_each_2: [-10..-1], [-9..-1], [-8..0]
+> [eva] tests/value/modulo.i:53: Frama_C_show_each_2: [1..10], [1..9], [0..8]
+> [eva] tests/value/modulo.i:53: Frama_C_show_each_2: [1..10], [-9..-1], [0..8]
+> [eva] tests/value/modulo.i:53: 
+>   Frama_C_show_each_2:
+>   [-9..-1], {1; 2; 3; 4; 5; 6; 7; 8}, {-7; -6; -5; -4; -3; -2; -1; 0}
+> [eva] tests/value/modulo.i:53: 
+>   Frama_C_show_each_2:
+>   [1..9], {1; 2; 3; 4; 5; 6; 7; 8}, {0; 1; 2; 3; 4; 5; 6; 7}
+> [eva] tests/value/modulo.i:53: 
+>   Frama_C_show_each_2:
+>   [-9..-1], {-8; -7; -6; -5; -4; -3; -2; -1}, {-7; -6; -5; -4; -3; -2; -1; 0}
+> [eva] tests/value/modulo.i:53: 
+>   Frama_C_show_each_2:
+>   [1..9], {-8; -7; -6; -5; -4; -3; -2; -1}, {0; 1; 2; 3; 4; 5; 6; 7}
+60a93,94
+> [eva] tests/value/modulo.i:64: Frama_C_show_each_3: [-10..10], [-9..9], [-8..8]
+> [eva] tests/value/modulo.i:64: Frama_C_show_each_3: [-9..9], [-8..8], [-7..7]
+diff tests/value/oracle/non_natural.res.oracle tests/value/oracle_octagons/non_natural.res.oracle
+58a59,60
+> [kernel] tests/value/non_natural.i:30: 
+>   more than 200(12500) elements to enumerate. Approximating.
+65a68,69
+> [kernel] tests/value/non_natural.i:23: 
+>   more than 200(12500) elements to enumerate. Approximating.
+70a75,76
+> [kernel] tests/value/non_natural.i:24: 
+>   more than 200(12500) elements to enumerate. Approximating.
+78a85,86
+> [kernel] tests/value/non_natural.i:25: 
+>   more than 200(12500) elements to enumerate. Approximating.
+86a95,96
+> [kernel] tests/value/non_natural.i:26: 
+>   more than 200(12500) elements to enumerate. Approximating.
+94a105,106
+> [kernel] tests/value/non_natural.i:27: 
+>   more than 200(12500) elements to enumerate. Approximating.
+102a115,116
+> [kernel] tests/value/non_natural.i:28: 
+>   more than 200(12500) elements to enumerate. Approximating.
+110a125,126
+> [kernel] tests/value/non_natural.i:29: 
+>   more than 200(12500) elements to enumerate. Approximating.
+129,130d144
+< [kernel] tests/value/non_natural.i:23: 
+<   more than 200(12500) elements to enumerate. Approximating.
+133,146d146
+< [kernel] tests/value/non_natural.i:24: 
+<   more than 200(12500) elements to enumerate. Approximating.
+< [kernel] tests/value/non_natural.i:25: 
+<   more than 200(12500) elements to enumerate. Approximating.
+< [kernel] tests/value/non_natural.i:26: 
+<   more than 200(12500) elements to enumerate. Approximating.
+< [kernel] tests/value/non_natural.i:27: 
+<   more than 200(12500) elements to enumerate. Approximating.
+< [kernel] tests/value/non_natural.i:28: 
+<   more than 200(12500) elements to enumerate. Approximating.
+< [kernel] tests/value/non_natural.i:29: 
+<   more than 200(12500) elements to enumerate. Approximating.
+< [kernel] tests/value/non_natural.i:30: 
+<   more than 200(12500) elements to enumerate. Approximating.
+199a200,201
+> [kernel] tests/value/non_natural.i:39: 
+>   more than 200(12500) elements to enumerate. Approximating.
+diff tests/value/oracle/nonlin.res.oracle tests/value/oracle_octagons/nonlin.res.oracle
+105a106,107
+> [eva:nonlin] tests/value/nonlin.c:67: non-linear 'x * x', lv 'x'
+> [eva:nonlin] tests/value/nonlin.c:67: subdividing on x
+108a111,113
+> [eva:nonlin] tests/value/nonlin.c:68: subdividing on x
+> [eva:nonlin] tests/value/nonlin.c:68: non-linear 'y * y', lv 'y'
+> [eva:nonlin] tests/value/nonlin.c:68: subdividing on y
+111a117,118
+> [eva:nonlin] tests/value/nonlin.c:70: non-linear 'z * x + x * y', lv 'x'
+> [eva:nonlin] tests/value/nonlin.c:70: subdividing on x
+176,178c183,185
+<   square ∈ [-48..400]
+<   square2 ∈ [-48..400]
+<   res ∈ [-144..400]
+---
+>   square ∈ [-200..400]
+>   square2 ∈ [-200..400]
+>   res ∈ [-200..400]
+diff tests/value/oracle/plevel.res.oracle tests/value/oracle_octagons/plevel.res.oracle
+12d11
+< [eva] Recording results for main
+14a14
+> [eva] Recording results for main
+diff tests/value/oracle/ptr_relation.1.res.oracle tests/value/oracle_octagons/ptr_relation.1.res.oracle
+24c24
+<   j ∈ {-1; 0; 1}
+---
+>   j ∈ {0}
+diff tests/value/oracle/relation_reduction.res.oracle tests/value/oracle_octagons/relation_reduction.res.oracle
+24,27d23
+< [eva:alarm] tests/value/relation_reduction.i:20: Warning: 
+<   accessing out of bounds index. assert 0 ≤ y;
+< [eva:alarm] tests/value/relation_reduction.i:20: Warning: 
+<   accessing out of bounds index. assert y < 9;
+34,37c30,33
+<   R1 ∈ [-2147483648..2147483637]
+<   R2 ∈ [-2147483638..2147483647]
+<   R3 ∈ [--..--]
+<   R4 ∈ {0; 1; 2; 3; 4; 5}
+---
+>   R1 ∈ {0; 2}
+>   R2 ∈ {0; 12}
+>   R3 ∈ {0; 7}
+>   R4 ∈ {0; 2}
+48c44
+<   R4 FROM tab[0..8]; x (and SELF)
+---
+>   R4 FROM tab[0..5]; x (and SELF)
+53c49
+<     y; t; tab[0..8]
+---
+>     y; t; tab[0..5]
+diff tests/value/oracle/relation_shift.res.oracle tests/value/oracle_octagons/relation_shift.res.oracle
+18,25d17
+< [eva:alarm] tests/value/relation_shift.i:15: Warning: 
+<   signed overflow. assert -2147483648 ≤ x - y;
+< [eva:alarm] tests/value/relation_shift.i:15: Warning: 
+<   signed overflow. assert x - y ≤ 2147483647;
+< [eva:alarm] tests/value/relation_shift.i:16: Warning: 
+<   signed overflow. assert -2147483648 ≤ z - y;
+< [eva:alarm] tests/value/relation_shift.i:16: Warning: 
+<   signed overflow. assert z - y ≤ 2147483647;
+31,32c23,24
+<   r1 ∈ [--..--]
+<   r2 ∈ [--..--]
+---
+>   r1 ∈ {2}
+>   r2 ∈ {7}
+35,37c27,29
+<   x ∈ [-2147483647..2147483647]
+<   y ∈ [-2147483648..2147483646]
+<   z ∈ [-2147483642..2147483647]
+---
+>   x ∈ [-2147483646..2147483642]
+>   y ∈ [-2147483648..2147483640]
+>   z ∈ [-2147483641..2147483647]
+49,50c41,42
+<   r1 ∈ [--..--]
+<   r2 ∈ [--..--]
+---
+>   r1 ∈ {2}
+>   r2 ∈ {7}
+53,55c45,47
+<   x ∈ [-2147483647..2147483647]
+<   y ∈ [-2147483648..2147483646]
+<   z ∈ [-2147483642..2147483647]
+---
+>   x ∈ [-2147483646..2147483642]
+>   y ∈ [-2147483648..2147483640]
+>   z ∈ [-2147483641..2147483647]
+diff tests/value/oracle/relations.res.oracle tests/value/oracle_octagons/relations.res.oracle
+80,81c80,82
+<   e ∈ [--..--]
+<   f ∈ [--..--]
+---
+>   e ∈ {1}
+>   f[bits 0 to 7] ∈ {1; 4}
+>    [bits 8 to 31] ∈ [--..--]
+diff tests/value/oracle/relations2.res.oracle tests/value/oracle_octagons/relations2.res.oracle
+25c25
+<   len ∈ [--..--]
+---
+>   len ∈ [0..1023]
+36,37c36
+< [eva] tests/value/relations2.i:17: 
+<   Frama_C_show_each_end: [0..4294967295], [0..64]
+---
+> [eva] tests/value/relations2.i:17: Frama_C_show_each_end: [0..1023], [0..64]
+59c58
+<   n ∈ [0..512]
+---
+>   n ∈ [1..512]
+69,71d67
+< [eva:alarm] tests/value/relations2.i:34: Warning: 
+<   accessing out of bounds index.
+<   assert (unsigned int)(i - (unsigned int)(t + 1)) < 514;
+80c76
+<   n ∈ [0..512]
+---
+>   n ∈ [1..512]
+97c93
+<   n ∈ [0..512]
+---
+>   n ∈ [1..512]
+140c136
+<   len ∈ [--..--]
+---
+>   len ∈ [0..1023]
+diff tests/value/oracle/semaphore.res.oracle tests/value/oracle_octagons/semaphore.res.oracle
+65c65
+<   c ∈ {-26; -1}
+---
+>   c ∈ {-1}
+diff tests/value/oracle/struct2.res.oracle tests/value/oracle_octagons/struct2.res.oracle
+81,84d80
+<   accessing out of bounds index. assert 0 ≤ (int)(i + j);
+< [eva:alarm] tests/value/struct2.i:185: Warning: 
+<   accessing out of bounds index. assert (int)(i + j) < 2;
+< [eva:alarm] tests/value/struct2.i:185: Warning: 
+106d101
+< [scope:rm_asserts] removing 2 assertion(s)
+diff tests/value/oracle/test.0.res.oracle tests/value/oracle_octagons/test.0.res.oracle
+17,18d16
+< [eva:alarm] tests/value/test.i:11: Warning: 
+<   signed overflow. assert j + ecart ≤ 2147483647;
+29c27
+<   j ∈ [-1073741822..1]
+---
+>   j ∈ {-1; 0; 1}
+diff tests/value/oracle/unroll.res.oracle tests/value/oracle_octagons/unroll.res.oracle
+22c22
+<   G ∈ [17739..2147483647]
+---
+>   G ∈ [17854..2147483647]
+diff tests/value/oracle/unroll_simple.res.oracle tests/value/oracle_octagons/unroll_simple.res.oracle
+17c17
+<   G ∈ [8772..2147483647]
+---
+>   G ∈ [8896..2147483647]
diff --git a/tests/value/octagons.c b/tests/value/octagons.c
new file mode 100644
index 0000000000000000000000000000000000000000..6d98523df71429efead57e87dd17633ea4ca4560
--- /dev/null
+++ b/tests/value/octagons.c
@@ -0,0 +1,196 @@
+/* run.config*
+   STDOPT: +" -eva-octagon-domain -eva-octagon-through-calls -eva-msg-key=d-octagon,-d-cvalue"
+*/
+
+#include <__fc_builtin.h>
+
+volatile int undet;
+
+/* Minimal example from the Eva user manual. */
+void demo () {
+  int y = undet;
+  int k = Frama_C_interval(0, 10);
+  int x = y - k;
+  int r = x + 3 - y;  // r \in [-7..3]
+  int t;
+  if (y > 15)
+    t = x;            // t \in [6..]
+}
+
+/* Same example as [demo] but with other integer types. */
+void integer_types () {
+  unsigned int k, x, y, r, t;
+  y = undet;
+  k = Frama_C_interval(0, 10);
+  x = y - k;        // No octagon inferred as [y - k] may overflow.
+  r = x + 3 - y;
+  if (y > 15)
+    t = x;
+  Frama_C_show_each_unreduced_unsigned(r, t);
+  char ck, cx, cy, cr, ct;
+  cy = undet;
+  ck = Frama_C_interval(0, 10);
+  cx = cy - ck;    // An octagon should be inferred despite the casts to int.
+  cr = cx + 3 - cy;
+  if (cy > 15)
+    ct = cx;
+  Frama_C_show_each_reduced_char(cr, ct);
+}
+
+/* A test with multiple mathematical operations to complicate the inference
+   and use of octagons. */
+void arith () {
+  int k = Frama_C_interval(0, 4);
+  int a, b, x, y, r;
+  /* 1. Infer octagons from assignments. */
+  a = Frama_C_interval(5, 25);
+  b = Frama_C_interval(-12, 12);
+  x = 1 - (a + 2*k - 4);                                  // x + a ∈ [-3..5]
+  y = 4*4 - k + (1 + b);                                  // y - b ∈ [13..17]
+  /* 1.1 Use octagons in the evaluation of expressions. */
+  r = 2 * (10 - (b - 1 - y) - (x - 2 + a));               // r ∈ [42..66]
+  Frama_C_show_each_precise(r);
+  r = 2 * (10 - (b + x - 3 - y + a));                    // r ∈ [42..66]
+  Frama_C_show_each_imprecise(r);
+  k = Frama_C_interval(0, 4);
+  /* 1.1 Use octagons to propagate variable reductions. */
+  if (12 - x < (k+1)*3) {                                 // x > -3
+    r = 10 * a;                                           // so a < 8
+    Frama_C_show_each(r);                                 // {50; 60; 70}
+  }
+  /* 2. Infer octagons from conditions. */
+  a = Frama_C_interval(-1024, 1024);
+  b = Frama_C_interval(-1024, 1024);
+  if (20*k - a - 17 < 5 - b + (1 << 3)                    // a - b > -30
+      && a + (k+6)/2 - b <= 32) {                         // a - b <= 29
+    r = b - a;
+    Frama_C_show_each(r);                                 // [-29..29]
+  }
+  if (a < b && b <= a)
+    Frama_C_show_each_BOTTOM(a, b);
+}
+
+/* Tests the join of the octagon domain. */
+void join () {
+  int a, b, r;
+  int k = Frama_C_interval(-1, 4);
+  if (undet) {
+    a = undet;
+    b = a + k;
+  } else {
+    a = Frama_C_interval(-32, -10);
+    b = k * 5;
+  }
+  // In both cases, we have b - a >= -1. The "else" branch was more precise.
+  r = b - a + 1;
+  Frama_C_show_each_join_positive(r);
+  if (undet) {
+    a = undet;
+    b = - (a + k);
+  } else {
+    a = Frama_C_interval(-32, -10);
+    b = k * 5;
+  }
+  // In both cases, we have b + a <= 10. The "then" branch was more precise.
+  r = b + a - 10;
+  Frama_C_show_each_join_negative(r);
+}
+
+/* Tests the octagon domain within loops. */
+void loop () {
+  int k = Frama_C_interval(-8, 8);
+  int a = Frama_C_interval(-1024, 1024);
+  int b = a + 1;
+  int c = a + 1;
+  int d = a + k;
+  for (int i = 0; i < 421; i++) {
+    a = a + 2;
+    b = b + 2; // The relation between a and b should be maintained in the loop.
+    c = c + 1; // The relation between a and c should be lost in the loop.
+    d = a + k; // This relation should be maintained.
+  }
+  int d1 = b - a;
+  int d2 = c - a;
+  int d3 = d - a;
+  Frama_C_show_each_singleton_1(d1);
+  Frama_C_show_each_imprecise(d2);
+  Frama_C_show_each_precise(d3);
+}
+
+/* Tests the soundness of the octagon domain in presence of pointers. */
+void pointers () {
+  int x, y, r;
+  int *px = &x, *pr = &r;
+  x = Frama_C_interval(-1024, 1024);
+  y = x + 1;
+  r = y - x;
+  Frama_C_show_each_singleton_1(r);
+  *px = Frama_C_interval(-1024, 1024);
+  Frama_C_show_each_singleton_1(r);
+  *pr = Frama_C_interval(-1024, 1024);
+  Frama_C_show_each_unknown(r);
+  r = y - x;
+  Frama_C_show_each_unknown(r);
+  y = x + 2;
+  r = y - x;
+  Frama_C_show_each_singleton_2(r);
+}
+
+/* Tests the saturation of octagons: inference of a relation between (x, z)
+   from relations between (x, y) and between (y, z). */
+void saturate () {
+  int k = Frama_C_interval(-6, 4);
+  int x = Frama_C_interval(-1024, 1024);
+  int y = k - x;
+  int z = y + 1;
+  int result = - z - x;                  // result == k + 1
+  Frama_C_show_each_saturate(result);    // ∈ [-5..5]
+}
+
+int diff (int x, int y) { return x - y; }
+int neg (int x) { return -x; }
+
+/* Tests the propagation of octagons through function calls. */
+void interprocedural () {
+  int a = Frama_C_interval(-4, 12);
+  int b = Frama_C_interval(-4, 12);
+  int neg_a = neg(a);
+  int neg_b = neg(b);
+  /* [r1] is the direct difference [a-b], [r2] uses the result of the function
+     [neg] (and thus need the octagon inferred in [neg]), and [r3] calls the
+     function [diff] (and the analysis of [diff] needs the octagons inferred
+     here about a and b). */
+  int r1, r2, r3;
+  if (a > b) {
+    r1 = a - b;
+    r2 = a + neg_b;
+    r3 = diff (a, b);
+  } else {
+    r1 = b - a;
+    r2 = b + neg_a;
+    r3 = diff (b, a);
+  }
+  /* With the interprocedural octagons, r1, r2 and r3 must be equally precise. */
+  Frama_C_show_each_equal(r1, r2, r3);
+}
+
+/* Prints the octagons state. */
+void dump () {
+  char k = Frama_C_interval(0, 8);
+  char a = undet;
+  char b = a + k;
+  char c = b - k;
+  Frama_C_dump_each();
+}
+
+void main () {
+  demo ();
+  integer_types ();
+  arith ();
+  join ();
+  loop ();
+  pointers ();
+  saturate ();
+  interprocedural ();
+  dump ();
+}
diff --git a/tests/value/oracle/octagons.res.oracle b/tests/value/oracle/octagons.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..2f2626a65ffa2d0009b042400cd207ba02de8264
--- /dev/null
+++ b/tests/value/oracle/octagons.res.oracle
@@ -0,0 +1,407 @@
+[kernel] Parsing tests/value/octagons.c (with preprocessing)
+[eva] Analyzing a complete application starting at main
+[eva] Computing initial state
+[eva] Initial state computed
+[eva:initial-state] Values of globals at initialization
+  undet ∈ [--..--]
+[eva] computing for function demo <- main.
+  Called from tests/value/octagons.c:187.
+[eva] computing for function Frama_C_interval <- demo <- main.
+  Called from tests/value/octagons.c:12.
+[eva] using specification for function Frama_C_interval
+[eva] tests/value/octagons.c:12: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva:alarm] tests/value/octagons.c:13: Warning: 
+  signed overflow. assert -2147483648 ≤ y - k;
+[eva:alarm] tests/value/octagons.c:14: Warning: 
+  signed overflow. assert x + 3 ≤ 2147483647;
+[eva] Recording results for demo
+[eva] Done for function demo
+[eva] computing for function integer_types <- main.
+  Called from tests/value/octagons.c:188.
+[eva] computing for function Frama_C_interval <- integer_types <- main.
+  Called from tests/value/octagons.c:24.
+[eva] tests/value/octagons.c:24: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:29: 
+  Frama_C_show_each_unreduced_unsigned: [0..4294967295], [0..4294967295]
+[eva] computing for function Frama_C_interval <- integer_types <- main.
+  Called from tests/value/octagons.c:32.
+[eva] tests/value/octagons.c:32: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:37: 
+  Frama_C_show_each_reduced_char: [-7..3], [6..127]
+[eva] Recording results for integer_types
+[eva] Done for function integer_types
+[eva] computing for function arith <- main.
+  Called from tests/value/octagons.c:189.
+[eva] computing for function Frama_C_interval <- arith <- main.
+  Called from tests/value/octagons.c:43.
+[eva] tests/value/octagons.c:43: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] computing for function Frama_C_interval <- arith <- main.
+  Called from tests/value/octagons.c:46.
+[eva] tests/value/octagons.c:46: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] computing for function Frama_C_interval <- arith <- main.
+  Called from tests/value/octagons.c:47.
+[eva] tests/value/octagons.c:47: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:52: Frama_C_show_each_precise: [42..66],0%2
+[eva] tests/value/octagons.c:54: Frama_C_show_each_imprecise: [2..106],0%2
+[eva] computing for function Frama_C_interval <- arith <- main.
+  Called from tests/value/octagons.c:55.
+[eva] tests/value/octagons.c:55: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:59: Frama_C_show_each: {50; 60; 70}
+[eva] computing for function Frama_C_interval <- arith <- main.
+  Called from tests/value/octagons.c:62.
+[eva] tests/value/octagons.c:62: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] computing for function Frama_C_interval <- arith <- main.
+  Called from tests/value/octagons.c:63.
+[eva] tests/value/octagons.c:63: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:67: Frama_C_show_each: [-29..29]
+[eva] Recording results for arith
+[eva] Done for function arith
+[eva] computing for function join <- main.
+  Called from tests/value/octagons.c:190.
+[eva] computing for function Frama_C_interval <- join <- main.
+  Called from tests/value/octagons.c:76.
+[eva] tests/value/octagons.c:76: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva:alarm] tests/value/octagons.c:79: Warning: 
+  signed overflow. assert -2147483648 ≤ a + k;
+[eva:alarm] tests/value/octagons.c:79: Warning: 
+  signed overflow. assert a + k ≤ 2147483647;
+[eva] computing for function Frama_C_interval <- join <- main.
+  Called from tests/value/octagons.c:81.
+[eva] tests/value/octagons.c:81: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:86: Frama_C_show_each_join_positive: [0..53]
+[eva:alarm] tests/value/octagons.c:89: Warning: 
+  signed overflow. assert -2147483648 ≤ a + k;
+[eva:alarm] tests/value/octagons.c:89: Warning: 
+  signed overflow. assert a + k ≤ 2147483647;
+[eva:alarm] tests/value/octagons.c:89: Warning: 
+  signed overflow. assert -((int)(a + k)) ≤ 2147483647;
+[eva] computing for function Frama_C_interval <- join <- main.
+  Called from tests/value/octagons.c:91.
+[eva] tests/value/octagons.c:91: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:96: Frama_C_show_each_join_negative: [-47..0]
+[eva] Recording results for join
+[eva] Done for function join
+[eva] computing for function loop <- main.
+  Called from tests/value/octagons.c:191.
+[eva] computing for function Frama_C_interval <- loop <- main.
+  Called from tests/value/octagons.c:101.
+[eva] tests/value/octagons.c:101: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] computing for function Frama_C_interval <- loop <- main.
+  Called from tests/value/octagons.c:102.
+[eva] tests/value/octagons.c:102: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:106: starting to merge loop iterations
+[eva:alarm] tests/value/octagons.c:107: Warning: 
+  signed overflow. assert a + 2 ≤ 2147483647;
+[eva:alarm] tests/value/octagons.c:108: Warning: 
+  signed overflow. assert b + 2 ≤ 2147483647;
+[eva:alarm] tests/value/octagons.c:110: Warning: 
+  signed overflow. assert a + k ≤ 2147483647;
+[eva:alarm] tests/value/octagons.c:113: Warning: 
+  signed overflow. assert -2147483648 ≤ c - a;
+[eva] tests/value/octagons.c:115: Frama_C_show_each_singleton_1: {1}
+[eva] tests/value/octagons.c:116: Frama_C_show_each_imprecise: [-2147483648..1]
+[eva] tests/value/octagons.c:117: Frama_C_show_each_precise: [-8..8]
+[eva] Recording results for loop
+[eva] Done for function loop
+[eva] computing for function pointers <- main.
+  Called from tests/value/octagons.c:192.
+[eva] computing for function Frama_C_interval <- pointers <- main.
+  Called from tests/value/octagons.c:124.
+[eva] tests/value/octagons.c:124: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:127: Frama_C_show_each_singleton_1: {1}
+[eva] computing for function Frama_C_interval <- pointers <- main.
+  Called from tests/value/octagons.c:128.
+[eva] tests/value/octagons.c:128: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:129: Frama_C_show_each_singleton_1: {1}
+[eva] computing for function Frama_C_interval <- pointers <- main.
+  Called from tests/value/octagons.c:130.
+[eva] tests/value/octagons.c:130: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:131: Frama_C_show_each_unknown: [-1024..1024]
+[eva] tests/value/octagons.c:133: Frama_C_show_each_unknown: [-2047..2049]
+[eva] tests/value/octagons.c:136: Frama_C_show_each_singleton_2: {2}
+[eva] Recording results for pointers
+[eva] Done for function pointers
+[eva] computing for function saturate <- main.
+  Called from tests/value/octagons.c:193.
+[eva] computing for function Frama_C_interval <- saturate <- main.
+  Called from tests/value/octagons.c:142.
+[eva] tests/value/octagons.c:142: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] computing for function Frama_C_interval <- saturate <- main.
+  Called from tests/value/octagons.c:143.
+[eva] tests/value/octagons.c:143: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:147: Frama_C_show_each_saturate: [-5..5]
+[eva] Recording results for saturate
+[eva] Done for function saturate
+[eva] computing for function interprocedural <- main.
+  Called from tests/value/octagons.c:194.
+[eva] computing for function Frama_C_interval <- interprocedural <- main.
+  Called from tests/value/octagons.c:155.
+[eva] tests/value/octagons.c:155: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] computing for function Frama_C_interval <- interprocedural <- main.
+  Called from tests/value/octagons.c:156.
+[eva] tests/value/octagons.c:156: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] computing for function neg <- interprocedural <- main.
+  Called from tests/value/octagons.c:157.
+[eva] Recording results for neg
+[eva] Done for function neg
+[eva] computing for function neg <- interprocedural <- main.
+  Called from tests/value/octagons.c:158.
+[eva] Recording results for neg
+[eva] Done for function neg
+[eva] computing for function diff <- interprocedural <- main.
+  Called from tests/value/octagons.c:167.
+[eva] Recording results for diff
+[eva] Done for function diff
+[eva] computing for function diff <- interprocedural <- main.
+  Called from tests/value/octagons.c:171.
+[eva] Recording results for diff
+[eva] Done for function diff
+[eva] tests/value/octagons.c:174: 
+  Frama_C_show_each_equal: [0..16], [0..16], [0..16]
+[eva] Recording results for interprocedural
+[eva] Done for function interprocedural
+[eva] computing for function dump <- main.
+  Called from tests/value/octagons.c:195.
+[eva] computing for function Frama_C_interval <- dump <- main.
+  Called from tests/value/octagons.c:179.
+[eva] tests/value/octagons.c:179: 
+  function Frama_C_interval: precondition 'order' got status valid.
+[eva] Done for function Frama_C_interval
+[eva] tests/value/octagons.c:183: 
+  Frama_C_dump_each:
+  # Octagon domain:
+  {[ k - tmp ∈ {0}
+     a - b ∈ [-8..0]
+     b - c ∈ [0..8]
+     a - c ∈ [-8..8]
+      ]}
+  ==END OF DUMP==
+[eva] Recording results for dump
+[eva] Done for function dump
+[eva] Recording results for main
+[eva] done for function main
+[eva] ====== VALUES COMPUTED ======
+[eva:final-states] Values at end of function arith:
+  Frama_C_entropy_source ∈ [--..--]
+  k ∈ {0; 1; 2; 3; 4}
+  a ∈ [-1024..1024]
+  b ∈ [-1024..1024]
+  x ∈ [-28..0]
+  y ∈ [1..29]
+  r ∈ [-29..106]
+[eva:final-states] Values at end of function demo:
+  Frama_C_entropy_source ∈ [--..--]
+  y ∈ [--..--]
+  k ∈ [0..10]
+  x ∈ [-2147483648..2147483644]
+  r ∈ [-7..3]
+  t ∈ [6..2147483644] or UNINITIALIZED
+[eva:final-states] Values at end of function diff:
+  __retres ∈ [0..16]
+[eva:final-states] Values at end of function dump:
+  Frama_C_entropy_source ∈ [--..--]
+  k ∈ [0..8]
+  a ∈ [--..--]
+  b ∈ [--..--]
+  c ∈ [--..--]
+[eva:final-states] Values at end of function integer_types:
+  Frama_C_entropy_source ∈ [--..--]
+  k ∈ [0..10]
+  x ∈ [--..--]
+  y ∈ [--..--]
+  r ∈ [--..--]
+  t ∈ [--..--] or UNINITIALIZED
+  ck ∈ [0..10]
+  cx ∈ [--..--]
+  cy ∈ [--..--]
+  cr ∈ [-7..3]
+  ct ∈ [6..127] or UNINITIALIZED
+[eva:final-states] Values at end of function join:
+  Frama_C_entropy_source ∈ [--..--]
+  a ∈ [--..--]
+  b ∈ [-2147483647..2147483647]
+  r ∈ [-47..0]
+  k ∈ {-1; 0; 1; 2; 3; 4}
+[eva:final-states] Values at end of function loop:
+  Frama_C_entropy_source ∈ [--..--]
+  k ∈ [-8..8]
+  a ∈ [-1024..2147483647]
+  b ∈ [-1023..2147483647]
+  c ∈ [-1023..2147483647]
+  d ∈ [-1032..2147483647]
+  d1 ∈ {1}
+  d2 ∈ [-2147483648..1]
+  d3 ∈ [-8..8]
+[eva:final-states] Values at end of function neg:
+  __retres ∈ [-12..4]
+[eva:final-states] Values at end of function interprocedural:
+  Frama_C_entropy_source ∈ [--..--]
+  a ∈ [-4..12]
+  b ∈ [-4..12]
+  neg_a ∈ [-12..4]
+  neg_b ∈ [-12..4]
+  r1 ∈ [0..16]
+  r2 ∈ [0..16]
+  r3 ∈ [0..16]
+[eva:final-states] Values at end of function pointers:
+  Frama_C_entropy_source ∈ [--..--]
+  x ∈ [-1024..1024]
+  y ∈ [-1022..1026]
+  r ∈ {2}
+  px ∈ {{ &x }}
+  pr ∈ {{ &r }}
+[eva:final-states] Values at end of function saturate:
+  Frama_C_entropy_source ∈ [--..--]
+  k ∈ [-6..4]
+  x ∈ [-1024..1024]
+  y ∈ [-1030..1028]
+  z ∈ [-1029..1029]
+  result ∈ [-5..5]
+[eva:final-states] Values at end of function main:
+  Frama_C_entropy_source ∈ [--..--]
+[from] Computing for function arith
+[from] Computing for function Frama_C_interval <-arith
+[from] Done for function Frama_C_interval
+[from] Done for function arith
+[from] Computing for function demo
+[from] Done for function demo
+[from] Computing for function diff
+[from] Done for function diff
+[from] Computing for function dump
+[from] Done for function dump
+[from] Computing for function integer_types
+[from] Done for function integer_types
+[from] Computing for function join
+[from] Done for function join
+[from] Computing for function loop
+[from] Done for function loop
+[from] Computing for function neg
+[from] Done for function neg
+[from] Computing for function interprocedural
+[from] Done for function interprocedural
+[from] Computing for function pointers
+[from] Done for function pointers
+[from] Computing for function saturate
+[from] Done for function saturate
+[from] Computing for function main
+[from] Done for function main
+[from] ====== DEPENDENCIES COMPUTED ======
+  These dependencies hold at termination for the executions that terminate:
+[from] Function Frama_C_interval:
+  Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF)
+  \result FROM Frama_C_entropy_source; min; max
+[from] Function arith:
+  Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF)
+[from] Function demo:
+  Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF)
+[from] Function diff:
+  \result FROM x; y
+[from] Function dump:
+  Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF)
+[from] Function integer_types:
+  Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF)
+[from] Function join:
+  Frama_C_entropy_source FROM Frama_C_entropy_source; undet (and SELF)
+[from] Function loop:
+  Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF)
+[from] Function neg:
+  \result FROM x
+[from] Function interprocedural:
+  Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF)
+[from] Function pointers:
+  Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF)
+[from] Function saturate:
+  Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF)
+[from] Function main:
+  Frama_C_entropy_source FROM Frama_C_entropy_source; undet (and SELF)
+[from] ====== END OF DEPENDENCIES ======
+[inout] Out (internal) for function arith:
+    Frama_C_entropy_source; k; a; b; x; y; r
+[inout] Inputs for function arith:
+    Frama_C_entropy_source
+[inout] Out (internal) for function demo:
+    Frama_C_entropy_source; y; k; x; r; t
+[inout] Inputs for function demo:
+    Frama_C_entropy_source; undet
+[inout] Out (internal) for function diff:
+    __retres
+[inout] Inputs for function diff:
+    \nothing
+[inout] Out (internal) for function dump:
+    Frama_C_entropy_source; k; tmp; a; b; c
+[inout] Inputs for function dump:
+    Frama_C_entropy_source; undet
+[inout] Out (internal) for function integer_types:
+    Frama_C_entropy_source; k; x; y; r; t; tmp; ck; cx; cy; cr; ct; tmp_0
+[inout] Inputs for function integer_types:
+    Frama_C_entropy_source; undet
+[inout] Out (internal) for function join:
+    Frama_C_entropy_source; a; b; r; k
+[inout] Inputs for function join:
+    Frama_C_entropy_source; undet
+[inout] Out (internal) for function loop:
+    Frama_C_entropy_source; k; a; b; c; d; i; d1; d2; d3
+[inout] Inputs for function loop:
+    Frama_C_entropy_source
+[inout] Out (internal) for function neg:
+    __retres
+[inout] Inputs for function neg:
+    \nothing
+[inout] Out (internal) for function interprocedural:
+    Frama_C_entropy_source; a; b; neg_a; neg_b; r1; r2; r3
+[inout] Inputs for function interprocedural:
+    Frama_C_entropy_source
+[inout] Out (internal) for function pointers:
+    Frama_C_entropy_source; x; y; r; px; pr
+[inout] Inputs for function pointers:
+    Frama_C_entropy_source
+[inout] Out (internal) for function saturate:
+    Frama_C_entropy_source; k; x; y; z; result
+[inout] Inputs for function saturate:
+    Frama_C_entropy_source
+[inout] Out (internal) for function main:
+    Frama_C_entropy_source
+[inout] Inputs for function main:
+    Frama_C_entropy_source; undet