diff --git a/src/plugins/value/domains/octagons.ml b/src/plugins/value/domains/octagons.ml
index c003f9f35bf77362155c81dbd52b77242f5abf7d..788b50d806d9634321b1df5cf8e6b7daa6f847d0 100644
--- a/src/plugins/value/domains/octagons.ml
+++ b/src/plugins/value/domains/octagons.ml
@@ -29,6 +29,12 @@ open Eval
    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
+
 (* -------------------------------------------------------------------------- *)
 (*                  Basic types: pair of variables and Ival.t                 *)
 (* -------------------------------------------------------------------------- *)
@@ -395,8 +401,10 @@ module Diamond = struct
     Arith.narrow x.add y.add >>- fun add ->
     Arith.narrow x.sub y.sub >>-: fun sub -> {add; sub}
 
-  (* Makes a diamond about (X, Y) from a diamond about (Y, X). *)
-  let reverse_variables t = { t with sub = Arith.neg t.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
 end
 
 
@@ -643,7 +651,7 @@ module State = struct
         type t = state
         include Datatype.Serializable_undefined
 
-        let name = "Octagons.Octagons"
+        let name = "Octagons.State"
         let structural_descr =
           Structural_descr.t_record
             [| Octagons.packed_descr;
@@ -676,6 +684,8 @@ module State = struct
             Octagons.pretty octagons Intervals.pretty intervals
       end)
 
+  (* ------------------------------ Lattice --------------------------------- *)
+
   let top =
     { octagons = Octagons.top;
       intervals = Intervals.top;
@@ -762,7 +772,67 @@ module State = struct
     let modified = Zone.narrow t1.modified t2.modified in
     `Value { octagons; intervals; relations; modified; }
 
-  let add_octagon state octagon =
+  (* -------------- Transitive closure when adding an octagon --------------- *)
+
+  type relation =
+    { vars: varinfo * varinfo;
+      diamond: diamond; }
+
+  let add_relation state rel =
+    let x, z = rel.vars in
+    let pair, swap = Pair.make x z in
+    let diamond = Diamond.reverse_variables swap rel.diamond in
+    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
+    if Diamond.is_top diamond then raise Not_found else
+      let vars = fst rel1.vars, snd rel2.vars in
+      { vars; 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 new_relation = transitive_relation y rel1 rel2 in
+          add_relation state new_relation
+        with Not_found -> `Value state
+      in
+      VariableSet.fold aux y_related (`Value state)
+    with Not_found -> `Value state
+
+  let add_octagon_aux state octagon =
     if Ival.(equal top octagon.value)
     then `Value state
     else
@@ -770,6 +840,22 @@ module State = struct
       let relations = Relations.relate octagon.variables state.relations in
       { state with octagons; relations }
 
+  let add_octagon state octagon =
+    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 -> add_octagon_aux state octagon
+
   let remove state x =
     let intervals = Intervals.remove x state.intervals in
     let state = { state with intervals } in
@@ -797,11 +883,7 @@ module State = struct
         let pair, swap = Pair.make x y in
         try
           let diamond = Octagons.find pair state.octagons in
-          let diamond =
-            if swap
-            then Diamond.reverse_variables diamond
-            else diamond
-          in
+          let diamond = Diamond.reverse_variables swap diamond in
           (y, diamond) :: acc
         with Not_found -> acc
       in
@@ -814,19 +896,19 @@ module State = struct
     let state = { state with intervals } in
     let x_related = Relations.find x state.relations in
     let aux y state =
-      let pair, order = Pair.make x y in
+      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 order then Arith.neg else fun x -> x in
+            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 order then Arith.sub else Arith.add in
+        let op = if swap then Arith.add else Arith.sub in
         let add =
           if Ival.(equal top diamond.add)
           then diamond.add