Skip to content
Snippets Groups Projects
offsetmap.ml 116.20 KiB
(**************************************************************************)
(*                                                                        *)
(*  This file is part of Frama-C.                                         *)
(*                                                                        *)
(*  Copyright (C) 2007-2022                                               *)
(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
(*         alternatives)                                                  *)
(*                                                                        *)
(*  you can redistribute it and/or modify it under the terms of the GNU   *)
(*  Lesser General Public License as published by the Free Software       *)
(*  Foundation, version 2.1.                                              *)
(*                                                                        *)
(*  It is distributed in the hope that it will be useful,                 *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
(*  GNU Lesser General Public License for more details.                   *)
(*                                                                        *)
(*  See the GNU Lesser General Public License version 2.1                 *)
(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
(*                                                                        *)
(**************************************************************************)

open Abstract_interp

(* This module uses Bigints everywhere. Set up some notations *)
let pretty_int = Int.pretty
let ( =~ ) = Integer.equal
let ( <>~ ) x y = not (Integer.equal x y)
let ( <~ ) = Integer.lt
let ( >~ ) = Integer.gt
let ( <=~ ) = Integer.le
let ( >=~ ) = Integer.ge
let ( +~ ) = Integer.add
let ( -~ ) = Integer.sub
(* let ( *~ ) = Integer.mul *)
let ( /~ ) = Integer.e_div
let ( %~ ) = Integer.e_rem
let succ = Integer.succ
let pred = Integer.pred

let msg_emitter = Lattice_messages.register "Offsetmap"

(** Offsetmaps are unbalanced trees that map intervals to values, with
    the additional properties that the shape of the tree is entirely determined
    by the intervals that are mapped (see function [is_above] for the ordering).
    The intervals are contiguous (offsetmaps cannot contain holes), and sorted
    from left to right in the tree.

    In this file, offsetmaps are represented in a relative way to maximise
    sharing. An offsetmap alone does not "know" which intervals it represents.
    When iterating on it, it is necessary to maintain a *current offset*, which
    is the lower index of the interval at the top of the tree. ( *Not* of the
    leftmost interval, which is the smallest binding.) *)
type 'a offsetmap =
  | Empty

  | Node of
      Integer.t *
      Integer.t * 'a offsetmap *
      Integer.t * 'a offsetmap *
      Rel.t * Integer.t * 'a *
      int
  (** [Node(i, offl, subl, offr, subr, rem, size, value, id)]
      - [i]: Relative, upper index of the interval. Thus the interval has length
        [max+1]. The relative lower index of the interval is always zero by
        definition
      - [offl, subl]: subtree on the left: the offset [offl] of its root
        (relative to 0), and the tree [subl]. If [subl] is not empty, it maps at
        least one interval, and [offl] is strictly negative. If [subl] is empty,
        then [offl] is zero
      - [offr, subr]: subtree on the right: the offset [offr] of its root
        (relative to 0), and the tree [subr]. [offr] is greater than [max+1] by
        definition, and equal to it if [subr] is empty. ([offr] may also be
        equal to [max+1] with a non-empty [subr],  when the interval at the root
        of [subr] starts exactly at [max+1]
      - [rem, size, value]: the value, its size [size] and its alignment
        [rem] relative to the start of the interval. [size] can be:
          * strictly more than [max+1], in which case the value is truncated
          * equal to [max+1]:
              + if [rem] is zero, the value is stored exactly once in the
                interval
              + otherwise, two truncated instances of the value are stored
                consecutively.
          * strictly less than [max+1]: the value is stored more than once,
            and implicitly repeats itself to fill the entire interval.
      - [id]: tag: hash-consing id of the node, plus an additional boolean.
        Not related to the contents of the tree.
  *)


(* In a node, the alignment of the value is relative to the start of the
   interval of the node. When splitting or merging nodes, this relative
   alignment must be recomputed wrt the offset of the new interval. The new
   alignment should be consistent with the size of the value. *)
let realign ~offset ~new_offset rem modu =
  Rel.e_rem (Rel.add (Rel.sub_abs offset new_offset) rem) modu

(** plevel-related operation: value + hooks to call when the value is modified*)
let plevel = ref 200
let plevel_hook = ref []
let set_plevel i =
  List.iter (fun f -> f ()) !plevel_hook;
  plevel := i
let add_plevel_hook f = plevel_hook := f :: !plevel_hook
let get_plevel () = !plevel

let debug = false

module Make (V : Offsetmap_lattice_with_isotropy.S) = struct

  open Format

  type v = V.t
  type widen_hint = V.numerical_widen_hint

  let empty = Empty
  (** All high-level functions of this module must handle a size of 0, in which
      case it is sometimes necessary to return Empty. In the current
      implementation, sizes 0 are handled by the outer (exported) functions,
      while the inner functions assume that the arguments [size] are
      strictly positive. *)

  let equal (t1:V.t offsetmap) (t2:V.t offsetmap) = t1 == t2

  let compare t1 t2 = match t1, t2 with
    | Empty, Empty -> 0
    | Empty, Node _ -> -1
    | Node _, Empty -> 1
    | Node (_, _, _, _, _, _, _, _, h1), Node (_, _, _, _, _, _, _, _, h2) ->
      Datatype.Int.compare h1 h2

  (* Does not depend on keys. Exported here for convenience for the users *)
  let size_from_validity = function
    | Base.Invalid -> `Bottom
    | Base.Empty -> `Value Int.zero
    | Base.Known (_, m)
    | Base.Unknown (_, _, m) -> `Value (Int.succ m)
    | Base.Variable { Base.max_allocable } -> `Value (Int.succ max_allocable)

  (** Pretty printing *)

  let pretty_offset_aux s curr_off ppf tree =
    if tree == Empty
    then Format.fprintf ppf "@[empty at %a@]" pretty_int curr_off
    else
      let rec pretty_offset s curr_off ppf tree =
        match tree with
        | Empty -> ()
        | Node (max, offl, subl, offr, subr, rem, modu, v, _) ->
          pretty_offset "" (curr_off +~ offl) ppf subl;
          Format.fprintf ppf "@[%s[%a..%a] -> (%a, %a, %a);@]@ "
            s
            pretty_int curr_off
            pretty_int (max +~ curr_off)
            Rel.pretty rem
            pretty_int modu
            V.pretty v;
          pretty_offset "" (curr_off +~ offr) ppf subr;
      in pretty_offset s curr_off ppf tree
  ;;

  let _pretty_offset fmt (off, t) =
    Format.fprintf fmt "@[<v><off: %a>@ %a@]"
      pretty_int off (pretty_offset_aux "r" off) t;
  ;;

  let pretty fmt t =
    Format.fprintf fmt "@[<v>%a@]" (pretty_offset_aux "r" Integer.zero) t;
  ;;

  let pretty_debug_offset fmt (curr_off, tree) =
    let rec aux_pdebug fmt (curr_off, tree) =
      match tree with
      | Empty -> Format.fprintf fmt "empty"
      | Node (max, offl, subl, offr, subr, rem, modu, v, tag) ->
        Format.fprintf fmt "@[<h 2>@[[%a..%a]@ (%a, %a,@ %a){%d,%x}@]@\n@[<h 2>-- \
                            %a -->@\n%a@]@\n@[<h 2>-- %a -->@\n%a@]@]"
          pretty_int curr_off
          pretty_int (curr_off +~ max)
          Rel.pretty rem
          pretty_int modu
          V.pretty v
          tag
          (Extlib.address_of_value tree)
          pretty_int offl
          aux_pdebug (curr_off +~ offl, subl)
          pretty_int offr
          aux_pdebug (curr_off +~ offr, subr)
    in
    aux_pdebug fmt (curr_off, tree);
    Format.fprintf fmt "@\n";
  ;;

  let pretty_debug fmt m = pretty_debug_offset fmt (Integer.zero, m);;


  include
    (struct

      (* This function is almost injective. Can we do better, eg. by mapping Empty
         to 0 and skipping this value for all nodes? And it is worth it? *)
      let hash = function
        | Empty -> 311
        | Node(_,_,_,_,_,_,_,_,tag) -> tag

      let rehash_ref = ref (fun _ -> assert false)
      module D = Datatype.Make
          (struct
            type t = V.t offsetmap
            let name = Printf.sprintf "Offsetmap(%s)" V.name
            let reprs = [ Empty ]
            open Structural_descr
            let r = Recursive.create ()
            let structural_descr =
              let p_bint = Datatype.Integer.packed_descr in
              t_sum
                [| [| p_bint;
                      p_bint;
                      recursive_pack r;
                      p_bint;
                      recursive_pack r;
                      p_bint;
                      p_bint;
                      V.packed_descr;
                      p_int |] |]
            let () = Recursive.update r structural_descr
            let equal = equal
            let hash = hash
            let compare = compare
            let rehash x = !rehash_ref x
            let copy = Datatype.undefined
            let pretty = pretty
            let mem_project = Datatype.never_any_project
          end)
      include D

      (* Basic operations on nodes *)
      let m_empty = Empty (* Empty is not exported, and we cannot make it private.
                             Instead, we use m_empty to track the places where we
                             create something empty *)
      let is_empty t = t == Empty

      let equal_internal t1 t2 =
        match t1, t2 with
        | Empty, Empty -> true
        | Node _, Empty | Empty, Node _ -> false
        | Node (max1, offl1, subl1, offr1, subr1, rem1, modu1, v1, _),
          Node (max2, offl2, subl2, offr2, subr2, rem2, modu2, v2, _)
          ->
          subl1 == subl2 &&
          subr1 == subr2 &&
          offl1 =~ offl2 &&
          offr1 =~ offr2 &&
          V.equal v1 v2 &&
          max1 =~ max2 &&
          Rel.equal rem1 rem2 &&
          modu1 =~ modu2

      let hash_internal t =
        match t with
          Empty -> 97
        | Node (max, offl, subl, offr, subr, rem, modu, v, _) ->
          let h = Integer.hash max in
          let h = 31 * h + Integer.hash offl in
          let h = 31 * h + hash subl in
          let h = 31 * h + Integer.hash offr in
          let h = 31 * h + hash subr in
          let h = 31 * h + Rel.hash rem in
          let h = 31 * h + Integer.hash modu in
          let h = 31 * h + V.hash v in
          h

      module NewoHashconsTbl =
        State_builder.Hashconsing_tbl
          (struct
            include D
            let hash_internal = hash_internal
            let equal_internal = equal_internal
            let initial_values = []
          end)
          (struct
            let name = name
            let dependencies = [ Ast.self ]
            let size = 137
          end)
      let () = Ast.add_monotonic_state NewoHashconsTbl.self

      let counter = ref 0

      let singleton_tag t =
        match t with
          Empty -> min_int
        | Node(_, _, _, _, _, _, _, _, tag) ->
          tag land min_int

      let nNode cur offl subl offr subr f g v =
        if debug then assert (Integer.ge cur Integer.zero);
        let current_counter = !counter in
        let tag =
          if V.cardinal_zero_or_one v
          then (singleton_tag subl) land (singleton_tag subr)
          else 0
        in
        let tag = tag lor current_counter in
        let tentative_new_node = Node(cur, offl, subl, offr, subr, f, g, v,tag) in
        let hashed_node = NewoHashconsTbl.merge tentative_new_node in
        if hashed_node == tentative_new_node
        then begin
          if current_counter = max_int
          then Kernel.fatal "Offsetmap(%s): internal maximum exceeded" V.name;
          counter := Stdlib.succ current_counter;
        end;
        hashed_node

      let rehash_node x = match x with
        | Empty -> Empty
        | Node _ ->
          NewoHashconsTbl.merge x

      let () = rehash_ref := rehash_node

    end :
     sig
       include Datatype.S with type t = V.t offsetmap

       val m_empty : t
       val hash: t -> int
       val nNode :
         Integer.t ->
         Integer.t -> t ->
         Integer.t -> t ->
         Rel.t -> Integer.t -> V.t ->
         t
       val is_empty : t -> bool
       val singleton_tag : t -> int
     end)

  module Cacheable = struct
    type t = Integer.t * V.t offsetmap
    let hash (i, t: t) = Integer.hash i + 37 * hash t
    let equal (i1, t1: t) (i2, t2: t) = t1 == t2 && i1 =~ i2
    let sentinel = Integer.minus_one, m_empty
  end
  let clear_caches_ref = ref []


  let get_vv node =
    match node with
    | Empty -> assert false
    | Node (_, _, _, _, _, rem, modu, v, _) -> rem, modu, v
  ;;

  let _get_v = function
    | Empty -> assert false
    | Node (_, _, _, _, _, _, _, v, _) ->
      v
  ;;

  let get_max = function
    | Empty -> assert false
    | Node (max, _, _, _, _, _, _, _, _) ->
      max
  ;;

  (* [is_above] provides a static ordering between two adjacent intervals. This
     is used to decide which interval goes on top of the other in an offsetmap,
     and thus governs the shape of the tree. As for patricia trees, this static
     hierarchy optimizes the merge of two offsetmaps.
     [min1..max1] and [min2..max2] must be two adjacent non-overlapping
     intervals. Then:
     - the interval containing 0, if any, is put at the top;
     - otherwise, the interval containing the multiple of the largest power of 2
       is put at the top.
       This ordering of adjacent intervals is an invariant of the offsetmaps. *)
  let is_above min1 max1 min2 max2 =
    if min1 <=~ Integer.zero && max1 >=~ Integer.zero then true
    else if min2 <=~ Integer.zero && max2 >=~ Integer.zero then false
    else
      let signature_interval min max =
        Integer.logxor (pred min) max
      in
      signature_interval min1 max1 >~ signature_interval min2 max2


  (** Zippers : Offset of a node * Node * continuation of the zipper *)
  type zipper =
    | End
    | Right of Integer.t * t * zipper
    | Left of Integer.t * t * zipper;;

  exception End_reached;;
  exception Empty_tree;;

  let _pr_zipper ppf z  =
    printf "[Zipper]---@.";
    let rec aux ppf = function
      | End -> printf "@ E@."
      | Right (o, Node(max, _, _, _, _subr, _, _, _, _),z ) ->
        fprintf ppf "@[<h 2> [%a,%a] R@\n%a@]"
          pretty_int o
          pretty_int (o +~ max)
          aux z
      | Left (o, Node(max, _, _, _, _subr, _, _, _, _),z ) ->
        fprintf ppf "@[<h 2> [%a,%a] L@\n%a@]"
          pretty_int o
          pretty_int (o +~ max)
          aux z
      |  Right (_, Empty, _) | Left (_, Empty, _) -> assert false
    in aux ppf z;
    printf "[/Zipper]---@.@.";
  ;;

  (** Returns an absolute position and an associated new tree *)
  let rec rezip zipper curr_off node =
    match zipper with
    | End -> curr_off, node
    | Right (offset, Node(max, offl, subl, _offr, _subr, rem, modu, v, _), z)
      ->
      rezip z offset
        (nNode max offl subl (curr_off -~ offset) node rem modu v)
    | Left (offset, Node(max, _offl, _subl, offr, subr, rem, modu, v, _), z)
      ->
      rezip z offset
        (nNode max (curr_off -~ offset) node offr subr rem modu v)
    | Right (_, Empty, _) | Left (_, Empty, _) -> assert false
  ;;

  (** Returns an absolute position, a node and a zipper *)
  let rec leftmost_child curr_off zipper node =
    match node with
    | Empty -> raise Empty_tree
    | Node (_, _, Empty, _, _, _, _, _, _) -> curr_off, node, zipper
    | Node (_, offl, subl, _, _, _, _, _, _) ->
      let new_offset = curr_off +~ offl in
      leftmost_child new_offset (Left (curr_off, node, zipper)) subl
  ;;

  (** Returns an absolute position, a node and a zipper *)
  let rec rightmost_child curr_off zipper node =
    match node with
    | Empty -> raise Empty_tree
    | Node (_, _, _, _, Empty, _, _, _, _) -> curr_off, node, zipper
    | Node (_, _offl, _subl, offr, subr, _, _, _, _) ->
      let new_offset = curr_off +~ offr in
      rightmost_child new_offset (Right (curr_off, node, zipper)) subr
  ;;

  (** Move to the right of the current node.
      Uses a zipper for that.
  *)
  let move_right curr_off node zipper =
    match node with
    | Node (_, _, _, offr, ((Node _ ) as subr), _, _, _, _) ->
      let new_offset = curr_off +~ offr in
      leftmost_child new_offset (Right (curr_off, node, zipper)) subr
    | Node (_, _, _, _, Empty, _, _, _, _) ->
      begin
        let rec unzip_until_left zipper =
          match zipper with
          | End -> raise End_reached
          | Right (_, _, z) -> unzip_until_left z
          | Left (offset, tree, z) -> offset, tree, z
        in unzip_until_left zipper
      end
    | Empty -> assert false
  ;;

  type imp_zipper = {
    mutable offset: Integer.t;
    mutable node: t;
    mutable zipper: zipper;
  };;

  let imp_move_right imp_z =
    let o, n, z = move_right imp_z.offset imp_z.node imp_z.zipper in
    imp_z.offset <- o;
    imp_z.node <- n;
    imp_z.zipper <- z;
  ;;

  (* Minimum and maximum bit bounds in the offsetmap (inclusively), assuming
     that [m] starts at [curr_off]. Usually not required, as we use [validity]
     arguments, that give the size of the offsetmap. Beware that this function
     must not be called on empty offsetmaps. *)
  let bounds_offset curr_off m =
    let rec min curr_off = function
      | Empty -> curr_off (* This bit is bound, unless [m] itself is empty *)
      | Node (_, offl, subl, _, _, _, _, _, _) -> min (curr_off +~ offl) subl
    and max curr_off = function
      | Empty -> pred curr_off (* [curr_off] is not bound, [curr_off-1] is. *)
      | Node (_, _, _, offr, subr, _, _, _, _) -> max (curr_off +~ offr) subr
    in
    assert (m != Empty);
    (min curr_off m, max curr_off m)

  let _bounds m = bounds_offset Int.zero m

  (** Folding and iterating from the leftmost node to the rightmost one
      If t =  n0         fold f t i = f n2 (f n0 (f n1 i))
             / \         iter f t   = f n1; fn0; f n2;
            n1  n2
  *)
  let fold_offset f o t acc =
    if t = Empty then
      acc
    else
      let o, n, z = leftmost_child o End t in
      let rec aux_fold o t z pre =
        match t with
        | Empty -> pre
        | Node (max, _, _, _, _, r, m, v, _) ->
          let abs_max = max +~ o in
          let now = f (o, abs_max) (v, m, r) pre in
          match move_right o t z with
          | no, nt, nz -> aux_fold no nt nz now
          | exception End_reached -> now
      in
      aux_fold o n z acc
  ;;

  let fold f t = fold_offset f Integer.zero t
  ;;

  let iter_offset f o t =
    if t <> Empty then
      let o, n, z = leftmost_child o End t in
      let rec aux_iter o t z =
        match t with
        | Empty -> ()
        | Node (max, _, _, _, _, r, m, v, _) ->
          let abs_max = max +~ o in
          f (o, abs_max) (v, m, r);
          match move_right o t z with
          | no, nt, nz -> aux_iter no nt nz
          | exception End_reached -> ()
      in
      aux_iter o n z
  ;;

  let iter f t = iter_offset f Integer.zero t
  ;;

  (* Same as iter, but does not compute offsets (hence more efficient). *)
  let rec iter_on_values f t =
    match t with
    | Empty -> ()
    | Node (_, _, left, _, right, _, _, v, _) ->
      iter_on_values f left;
      f v;
      iter_on_values f right
  ;;

  let rec fold_on_values f t acc =
    match t with
    | Empty -> acc
    | Node (_, _, left, _, right, _, _, v, _) ->
      fold_on_values f right (f v ((fold_on_values f left acc)))
  ;;

  (* Two adjacent nodes can be merged into one when:
     - they contains the same value of the same size (thus repeated with the
       same modulo) and the same alignment wrt the offset of the left node
       (thus the alignment of the value in the right node must be converted
       wrt the left offset).
     - and the offset of the right node is aligned with the repeated value:
       the separation does not cut the value, and can safely be removed.
       Otherwise, a separation that cuts a value can only be removed if the
       concretization of the value is a singleton, ensuring that the two parts
       of the value are always consistent.  *)
  let are_mergeable_nodes ~left_offset ~left ~right_offset ~right =
    let lrem, lmodu, lv = left
    and rrem, rmodu, rv = right in
    V.equal lv rv && lmodu =~ rmodu &&
    let new_rrem =
      realign ~offset:right_offset ~new_offset:left_offset rrem rmodu
    in
    Rel.equal new_rrem lrem &&
    (Rel.is_zero rrem || V.cardinal_zero_or_one lv)

  (** Smart constructor for nodes:
      it glues the node being allocated to potential candidates if needed
      (i.e. leftmost node of right subtree and rightmost node of left subtree),
  *)
  let make_node curr_off max offl subl offr subr rem modu v =
    let rem, modu =
      if V.is_isotropic v
      then Rel.zero, Integer.one
      else rem, modu
    in
    let curr_vv = (rem, modu, v) in
    let max, offr, subr =
      try
        let offset, nr, zr = leftmost_child (curr_off +~ offr) End subr in
        match nr with
        | Node (nmax, _, nsubl , noffr, nsubr, nrem, nmodu, nv, _) ->
          assert (is_empty nsubl);
          let right = nrem, nmodu, nv in
          if are_mergeable_nodes
              ~left_offset:curr_off ~left:curr_vv ~right_offset:offset ~right
          then
            begin
              let curr_offr, new_subr = rezip zr (offset +~ noffr) nsubr in
              let new_max = succ (max +~ nmax) in
              let new_offr = curr_offr -~ curr_off
              in
              new_max, new_offr, new_subr
            end
          else max, offr, subr
        | Empty -> assert false
      with Empty_tree -> max, offr, subr
    in
    if debug then assert (Integer.ge max Integer.zero);
    let curr_off, max, rem, offl, subl, offr =
      try
        let offset, nl, zl =
          rightmost_child (curr_off +~ offl) End subl in
        match nl with
        | Node (nmax, noffl, nsubl , _, noffr, nrem, nmodu, nv, _) ->
          assert (is_empty noffr);
          let left = nrem, nmodu, nv in
          if are_mergeable_nodes
              ~left_offset:offset ~left ~right_offset:curr_off ~right:curr_vv
          then (
            let new_curr_offl, new_subl = rezip zl (offset +~ noffl) nsubl in
            let succ_nmax = succ nmax in
            let lmax = max +~ succ_nmax in
            let new_offl = new_curr_offl -~ offset in
            let new_offr = offr +~ succ_nmax in
            let new_coff = curr_off -~ succ_nmax in
            let rem = realign ~offset:curr_off ~new_offset:offset rem modu in
            (*assert (new_coff =~ offset);*)
            new_coff, lmax, rem, new_offl, new_subl, new_offr)
          else curr_off, max, rem, offl, subl, offr
        |Empty -> assert false
      with Empty_tree -> curr_off, max, rem, offl, subl, offr
    in
    curr_off, nNode max offl subl offr subr rem modu v
  ;;

  (* Creates the tree representing the interval [O..span], bound to [v] *)
  let interval_aux span rem modu v =
    let rem, modu =
      if V.is_isotropic v
      then Rel.zero, Integer.one
      else rem, modu
    in
    nNode span Integer.zero m_empty (succ span) m_empty rem modu v

  (* creates a fresh tree that binds [0..size-1] to the isotropic value [v].
     if [size] if 0, returns [Empty]. *)
  let isotropic_interval size v =
    if Int.(equal size zero) then Empty
    else
      nNode (pred size) Integer.zero m_empty size m_empty Rel.zero Integer.one v

  (** Smart add node:
      Adds a node to the current tree and merges (new) consecutive intervals
      containing the same values
      The node is [min..max] rem, modu, v and
      the tree to which it is added is rooted at offset curr_off
      Hypothesis: the tree is in canonical form w.r.t having no
      mergeable intervals.
  *)
  let add_node ~min ~max rem modu v curr_off tree =
    if debug then assert (min <=~ max);
    let rec aux_add curr_off tree =
      match tree with
      | Empty ->  min, interval_aux (max -~ min) rem modu v
      | Node (nmax, noffl, nsubl, noffr, nsubr, nrem, nmodu, nv, _) ->
        let abs_min = curr_off
        and abs_max = nmax +~ curr_off in
        if max <~ abs_min then
          begin
            if is_above min max abs_min abs_max then
              let new_offr = abs_min -~ min in
              (*Format.printf "add to the left above@."; *)
              make_node min (max -~ min) Integer.zero m_empty
                new_offr tree rem modu v
            else
              begin
                (*     Format.printf "L@ co:%a@ t:%a@ [%a...%a]@.@."
                       pretty_int curr_off
                       (pretty_offset curr_off) tree
                       pretty_int min pretty_int max
                       ; *)
                let new_curr_offl, new_node =
                  aux_add (curr_off +~ noffl) nsubl
                in
                let new_offl = new_curr_offl -~ curr_off in
                make_node
                  curr_off nmax new_offl new_node noffr nsubr nrem nmodu nv
              end
          end
        else
          begin
            if is_above min max abs_min abs_max then
              begin
                let new_offl = abs_min -~ min in
                let new_max = max -~ min in
                make_node
                  min new_max new_offl tree (succ new_max) m_empty rem modu v
              end
            else
              begin
                (*           Format.printf "add to the right Not ABOVE@."; *)
                let new_curr_offr, new_node =
                  aux_add (curr_off +~ noffr) nsubr
                in
                let new_offr = new_curr_offr -~ abs_min in
                make_node abs_min nmax noffl nsubl new_offr new_node nrem
                  nmodu nv
              end
          end

    in aux_add curr_off tree
  ;;

  (* Bind the interval [min..max] to [v], and append it to the zero-rooted
     map [t]. [rem] and [modu] are inferred by considering that [min..max] binds
     a single value (unless [v] is isotropic) *)
  let append_basic_itv ~min ~max ~v m =
    if V.is_isotropic v then
      snd (add_node ~min ~max Rel.zero Integer.one v Integer.zero(*co*) m)
    else
      let size = Integer.length min max in
      let v = V.anisotropic_cast ~size v in
      snd (add_node ~min ~max Rel.zero size v Integer.zero(*co*) m)

  (** Checks that [tree] is sanely built  *)
  let rec check_aux curr_off tree =
    match tree with
    | Empty -> ()
    | Node (max, offl, subl, offr, subr, rem, modu, _v, _) ->
      assert (Rel.check ~rem ~modu);
      assert (not (is_empty subl) || Integer.is_zero offl);
      assert (not (is_empty subr) || offr =~ succ max);
      let abs_min = curr_off
      and abs_max = curr_off +~ max in
      let aux offset tree =
        match tree with
        | Empty -> ()
        | Node (nmax, _, _, _, _, _, _, _, _) ->
          let nabs_min = curr_off +~ offset in
          let nabs_max = nmax +~ nabs_min in
          assert (is_above abs_min abs_max nabs_min nabs_max)
      in aux offl subl; aux offr subr;
      check_aux (curr_off +~ offl) subl;
      check_aux (curr_off +~ offr) subr;
  ;;

  let _check curr_off tree =
    try check_aux curr_off tree
    with Assert_failure _ as e ->
      Kernel.error "INVALID@.%a@." _pretty_offset (curr_off, tree);
      raise e


  (** Inclusion functions *)

  (* Auxiliary function for inclusion: check that, between [mabs_min] and
     [mabs_max], the values (r1, m1, v1) and (r2, m2, v2), respectively
     bound between (amin1, amax1) and (amin2, amax2), are included. *)
  let is_included_nodes_values (amin1 : Integer.t) (amax1 : Integer.t) r1 m1 v1 amin2 amax2 r2 m2 v2 mabs_min mabs_max =
    if V.is_isotropic v1 || V.is_isotropic v2 then
      V.is_included v1 v2
    else
      let max_test =
        if amax1 <~ amax2
        then (succ mabs_max) %~ m1 =~ r1
        else true
      in
      let ok_min = amin1 =~ amin2 || mabs_min %~ m1 =~ r1
      and ok_max = amax1 =~ amax2 || max_test
      in
      if r1 =~ r2 && m1 =~ m2 && ok_min && ok_max
      then V.is_included v1 v2
      else false

  (* Functional for inclusion test. For this function, the equality
     [bounds o1 t1 = bounds o2 t2] does not need to hold. We test the inclusion
     for the range that is common to both trees. *)
  let is_included_aux_cache cache (o1, t1) (o2, t2) =
    match t1, t2 with
    | Empty, _ | _, Empty ->
      true (* no common range. By definition, the inclusion holds *)
    | Node (max1, offl1, subl1, offr1, subr1, r1rel, m1, v1, _),
      Node (max2, offl2, subl2, offr2, subr2, r2rel, m2, v2, _) ->
      let amin1 = o1 in
      let amax1 = max1 +~ o1 in
      let amin2 = o2 in
      let amax2 = max2 +~ o2 in
      let ol1 = o1 +~ offl1 in
      let ol2 = o2 +~ offl2 in
      let or1 = o1 +~ offr1 in
      let or2 = o2 +~ offr2 in
      let r1 = (Rel.add_abs o1 r1rel) %~ m1 in
      let r2 = (Rel.add_abs o2 r2rel) %~ m2 in
      if amax1 <~ amin2  then
        cache (o1, t1) (ol2, subl2) &&
        cache (or1, subr1) (o2, t2)
      else if amin1 >~ amax2 then
        cache (o1, t1) (or2, subr2) &&
        cache (ol1, subl1) (o2, t2)
      else begin (* this node of t2 covers part of the interval of t1 we are
                    focused on *)
        if amin1 =~ amin2 then
          let mabs_min = amin1 in
          begin
            (if amax1 =~ amax2 then begin
                (if (r1 =~ r2 && m1 =~ m2) ||
                    V.is_isotropic v1 || V.is_isotropic v2
                 then V.is_included v1 v2
                 else false)
                &&
                cache (or1, subr1) (or2, subr2)
              end
             else if amax1 >~ amax2 then begin
               is_included_nodes_values
                 amin1 amax1 r1 m1 v1
                 amin2 amax2 r2 m2 v2 mabs_min amax2
               &&
               cache (o1, t1) (or2, subr2)
             end
             else
               begin (* amax1 <~ amax2 *)
                 is_included_nodes_values
                   amin1 amax1 r1 m1 v1
                   amin2 amax2 r2 m2 v2 mabs_min amax1
                 &&
                 cache (or1, subr1) (o2, t2)
               end
            ) &&
            cache (ol1, subl1) (ol2, subl2)
          end
        else
          (* treat the common interval and the right parts of the trees.
             The common interval starts at [mabs_min] and goes up to
             [min amax1 amax2]. *)
          let treat_current_right_nodes mabs_min =
            if amax1 =~ amax2 then begin
              is_included_nodes_values
                amin1 amax1 r1 m1 v1
                amin2 amax2 r2 m2 v2 mabs_min amax1
              &&
              cache (or1, subr1) (or2, subr2)
            end
            else if amax1 >~ amax2 then begin
              is_included_nodes_values
                amin1 amax1 r1 m1 v1
                amin2 amax2 r2 m2 v2 mabs_min amax2
              &&
              cache (o1, t1) (or2, subr2)
            end
            else
              begin (* amax1 <~ amax2 *)
                is_included_nodes_values
                  amin1 amax1 r1 m1 v1
                  amin2 amax2 r2 m2 v2 mabs_min amax1
                &&
                cache (or1, subr1) (o2, t2)
              end;
          in
          (* Find the beginning of the common part of the two intervals (ie.
             [mabs_min] above, which is by definition [max amin1 amin2]), and
             treat this interval and the right trees. Then, check the inclusion
             of the subtree that starts just before [mabs_min] with the
             entire other tree. *)
          if amin1 >~ amin2 then begin
            treat_current_right_nodes amin1 &&
            cache (ol1, subl1) (o2, t2)
          end
          else begin (* amin1 <~ amin2 *)
            treat_current_right_nodes amin2 &&
            cache (o1, t1) (ol2, subl2)
          end
      end
  ;;

  module IsIncludedCache = Binary_cache.Binary_Predicate(Cacheable)(Cacheable)
  let () = clear_caches_ref := IsIncludedCache.clear :: !clear_caches_ref;;

  let rec is_included_aux t1 t2 =
    Cacheable.equal t1 t2 ||
    is_included_aux_cache (IsIncludedCache.merge is_included_aux) t1 t2

  let is_included t1 t2 =
    is_included_aux (Integer.zero, t1) (Integer.zero, t2)
  ;;

  (** Joins two trees with no overlapping intervals.  *)

  let rec union t1_curr_off t1 t2_curr_off t2 =
    (* Format.printf "Union t1:%a t2:%a@."
       (pretty_offset t1_curr_off) t1
       (pretty_offset t2_curr_off) t2;
    *)
    match t1, t2 with
    | Empty, Empty ->
      assert (t1_curr_off =~ t2_curr_off);
      t1_curr_off, t1
    | Empty, Node _ -> t2_curr_off, t2
    | Node _, Empty -> t1_curr_off, t1
    | Node (lmax, loffl, lsubl, loffr, lsubr, lrem, lmodu, lv, _),
      Node (rmax, roffl, rsubl, roffr, rsubr, rrem, rmodu, rv, _) ->
      let labs_min = t1_curr_off
      and labs_max = lmax +~ t1_curr_off
      and rabs_min = t2_curr_off
      and rabs_max = rmax +~ t2_curr_off
      in
      if is_above labs_min labs_max rabs_min rabs_max
      then
        (* t2 is on the right of t1 *)
        let new_curr_offr, new_subr =
          union (t1_curr_off +~ loffr) lsubr t2_curr_off t2
        in
        make_node t1_curr_off lmax loffl lsubl
          (new_curr_offr -~ t1_curr_off) new_subr lrem lmodu lv
      else
        begin
          (* t1 is on the left of t2 *)
          (*            assert (is_above rabs_min rabs_max labs_min labs_max); *)
          let new_curr_offl, new_subl =
            union t1_curr_off t1 (t2_curr_off +~ roffl) rsubl
          in
          make_node t2_curr_off rmax
            (new_curr_offl -~ t2_curr_off) new_subl roffr rsubr
            rrem rmodu rv
        end
  ;;

  (** Merge two trees that span the same range. This function is a functional:
      [cache] must be used for recursive calls on subtrees.
      [f_aux] is the function that merges the intervals point-wise. *)
  let merge cache f_aux (o1, t1) (o2, t2) =
    if debug then (* the two trees must span the exact same range. *)
      assert ((t1 == Empty && t2 == Empty && o1 =~ o2) ||
              let ib1, ie1 = bounds_offset o1 t1 in
              let ib2, ie2 = bounds_offset o2 t2 in
              ib1 =~ ib2 && ie1 =~ ie2);
    match t1, t2 with
    | Empty, Empty -> o1, t1
    | Node _, Empty -> assert false
    | Empty, Node _ -> assert false
    | Node (max1, offl1, subl1, offr1, subr1, rem1, modu1, v1, _),
      Node (max2, offl2, subl2, offr2, subr2, rem2, modu2, v2, _) ->
      let abs_min1 = o1
      and abs_max1 = max1 +~ o1
      and abs_min2 = o2
      and abs_max2 = max2 +~ o2
      in
      if debug then assert (abs_min2 <=~ abs_max1 && abs_min1 <=~ abs_max2);
      (* here n1 \inter n2 <> \emptyset, given the invariants on offsetmaps
         shape and the fact that both trees cover the same range.
         - compute the intersection interval: middle_abs_min, middle_abs_max
         - recompute the alignment of the values wrt middle_abs_min
             (named middle_rem1 and middle_rem2)
         - add the rest of the nodes to their left/right subtree
           depending on the size of the node
         - add the new node in the merged left subtree
           and plug the merged right tree in
      *)
      let (curr_offl, left_t), middle_abs_min, middle_rem1, middle_rem2 =
        let abs_offl1 = o1 +~ offl1
        and abs_offl2 = o2 +~ offl2 in
        if abs_min1 =~ abs_min2  then
          cache (abs_offl1, subl1) (abs_offl2, subl2), abs_min1, rem1, rem2
        else if abs_min1 <~ abs_min2 then
          let new_offl1, new_subl1 =
            add_node ~min:abs_min1 ~max:(pred abs_min2)
              rem1 modu1 v1 abs_offl1 subl1
          in
          let new_rem1 = realign ~offset:o1 ~new_offset:o2 rem1 modu1 in
          cache (new_offl1, new_subl1) (abs_offl2, subl2),
          abs_min2, new_rem1, rem2
        else
          begin (* abs_min1 >~ abs_min2 *)
            let new_offl2, new_subl2 =
              add_node ~min:abs_min2 ~max:(pred abs_min1) rem2 modu2
                v2 abs_offl2 subl2
            in
            let new_rem2 = realign ~offset:o2 ~new_offset:o1 rem2 modu2 in
            cache (abs_offl1, subl1) (new_offl2, new_subl2),
            abs_min1, rem1, new_rem2
          end
      in
      let (curr_offr, right_t), middle_abs_max =
        let abs_offr1 = o1 +~ offr1
        and abs_offr2 = o2 +~ offr2 in
        if abs_max1 =~ abs_max2 then
          cache (abs_offr1, subr1) (abs_offr2, subr2), abs_max1
        else if abs_max1 <~ abs_max2 then
          let min = succ abs_max1 in
          let rem2 = realign ~offset:o2 ~new_offset:min rem2 modu2 in
          let new_offr2, new_subr2 =
            add_node ~min ~max:abs_max2 rem2 modu2 v2 abs_offr2 subr2
          in
          cache (abs_offr1, subr1) (new_offr2, new_subr2), abs_max1
        else
          begin (* abs_max1 >~ abs_max2 *)
            let min = succ abs_max2 in
            let rem1 = Rel.e_rem (Rel.add (Rel.sub_abs o1 min) rem1) modu1 in
            let new_offr1, new_subr1 =
              add_node ~min ~max:abs_max1 rem1 modu1 v1 abs_offr1 subr1
            in
            cache (new_offr1, new_subr1) (abs_offr2, subr2), abs_max2
          end
      in
      let rem, modu, v =
        f_aux middle_abs_min
          middle_abs_max middle_rem1 modu1 v1 middle_rem2 modu2 v2
      in
      let curr_offl, left_t =
        add_node ~min:middle_abs_min ~max:middle_abs_max
          rem modu v curr_offl left_t
      in union curr_offl left_t curr_offr right_t
  ;;

  let rec map_on_values_aux f curr_off t =
    match t with
    | Empty -> curr_off, t
    | Node (max, offl, subl, offr, subr, rem, modu, v, _) ->
      let v' = f v in
      let offl', l' = map_on_values_aux f (curr_off +~ offl) subl in
      let offr', r' = map_on_values_aux f (curr_off +~ offr) subr in
      if l' == subl && r' == subr && V.equal v v'
      then curr_off, t
      else
        make_node
          curr_off max (offl' -~ curr_off) l' (offr' -~ curr_off) r' rem modu v'
  ;;

  let map_on_values f t = snd (map_on_values_aux f Int.zero t);;

  let extract_bits ~start ~stop ~modu v =
    assert (start <=~ stop && stop <=~ modu);
    let start,stop =
      if Cil.theMachine.Cil.theMachine.Cil_types.little_endian then
        start,stop
      else
        let mmodu = pred modu in
        mmodu -~ stop, mmodu -~ start
    in
    V.extract_bits ~start ~stop ~size:modu v
  ;;

  let merge_bits ~topify ~conflate_bottom ~offset ~length ~value ~total_length acc =
    assert (length +~ offset <=~ total_length);
    let offset =
      if Cil.theMachine.Cil.theMachine.Cil_types.little_endian then
        offset
      else
        Int.sub (Int.sub total_length offset) length
    in
    let value = V.shift_bits ~topify ~size:length ~offset value in
    V.merge_distinct_bits ~topify ~conflate_bottom value acc
  ;;

 (*
   [offset] is the offset where the read has begun (ie the global read start).
   [size] is the total size we want to read from [offset].
   [curr_off] and [(rem, modu, v)] refer to the current node to be read.
   [acc] is the current state of accumulated reads.
 *)
  let extract_bits_and_stitch ~topify ~conflate_bottom ~offset ~size curr_off (rem, modu, v) max acc =
    let rem = (Rel.add_abs curr_off rem) %~ modu in
    let r =
      let abs_max = curr_off +~ max in
      (*  last bit to be read,
          be it in the current node or one of its successors *)
      let max_bit = pred (offset +~ size) in
      (* for this function, [min >= offset && min >= curr_off] holds *)
      let extract_single_step min acc =
        assert (not (V.is_isotropic v));
        let interval_offset = min -~ offset in
        let start = (min -~ rem) %~ modu in
        let modu_end = if rem =~ Integer.zero then pred modu else pred rem in
        (* where do we stop reading ?
           either at the end of the current slice (round_up_to_r min) or
           at the end of the interval (abs_max)
        *)
        let read_end =
          Integer.min
            (Integer.min (Integer.round_up_to_r ~min ~r:modu_end ~modu) abs_max)
            max_bit
        in
        let stop = (read_end -~ rem) %~ modu in
        (*       Format.printf "Single step: interval offset %a length %a \
                 start %a stop %a total length %a offset %a max bit %a\
                 @\n current offset %a Rem %a modu %a V %a@."
                 pretty_int interval_offset pretty_int (Integer.length start stop)
                 pretty_int start pretty_int stop pretty_int size
                 pretty_int offset pretty_int max_bit
                 pretty_int curr_off pretty_int rem pretty_int modu V.pretty v ; *)
        (* we ignore the 'inform' information here (and everywhere else in
           this module, since we do not propagate it), because it is mostly
           redundant with the 'origin' information in garbled mix *)
        let _inform, read_bits = extract_bits ~topify ~start ~stop ~modu v in
        (* Format.printf "After single step: read bits %a@." V.pretty read_bits; *)
        let result =
          merge_bits ~topify ~conflate_bottom
            ~offset:interval_offset ~length:(Integer.length start stop)
            ~value:read_bits ~total_length:size acc
        in
        (* Format.printf "After merge_bits: result %a@." V.pretty result; *)
        read_end, result
      in
      let start = Integer.max offset curr_off
      and stop = Integer.min max_bit abs_max in
      if V.is_isotropic v then
        let offset = start -~ offset in
        merge_bits ~topify ~conflate_bottom
          ~offset ~length:(Integer.length start stop)
          ~value:v ~total_length:size acc
      else
        let start_point = ref start in
        let acc = ref acc in
        while !start_point <=~ stop do
          let read_end, result = extract_single_step !start_point !acc in
          acc := result;
          start_point := succ read_end;
        done;
        !acc;
    in
    (* Format.printf "extract_bits_and_stitch istart@ %a@ size %a\
       coff %a abs_max -- val %a@\n  acc %a res %a@."
       pretty_int offset pretty_int size pretty_int curr_off
       (\* pretty_int (curr_off +~ (get_max node)) *\)
       V.pretty v  V.pretty acc V.pretty r; *)
    r
  ;;


  (** Auxiliary function to join 2 trees with {!merge} above. The merge on two
      values is done by [merge_v]. Since this function can be [V.widen], the
      left/right order of arguments must be preserved. When [merge_v] is
      narrow, it is important that [extract_bits_and_stitch] be canonical
      enough -- or that {!V.narrow} handles differences in representations
      soundly. *)
  let f_aux_merge_generic merge_v abs_min abs_max rem1 modu1 v1 rem2 modu2 v2 =
    if Rel.equal rem1 rem2 && modu1 =~ modu2
    then
      rem1, modu1, V.anisotropic_cast ~size:modu1 (merge_v modu1 v1 v2)
      (*  Format.printf "f_aux_merge: [%a, %a]@.(%a %a %a)@.(%a %a %a)@."
          pretty_int abs_min pretty_int abs_max pretty_int rem1 pretty_int
          modu1 V.pretty v1 pretty_int rem2 pretty_int modu2 V.pretty v2 ; *)
    else
      let topify = Origin.K_Merge in
      let offset = abs_min in
      let size = Integer.length abs_min abs_max in
      let v1_fit = modu1 =~ size && Rel.is_zero rem1
      and v2_fit = modu2 =~ size && Rel.is_zero rem2 in
      let v1', v2' =
        if (V.is_isotropic v1 || v1_fit) && (V.is_isotropic v2 || v2_fit)
        then v1, v2
        else
          let reinterpret_bits x =
            extract_bits_and_stitch ~topify ~conflate_bottom:false
              ~offset ~size offset x abs_max V.merge_neutral_element
          in
          reinterpret_bits (rem1, modu1, v1),
          reinterpret_bits (rem2, modu2, v2)
      in
      (* The values were already aligned with the offset or have been
         reinterpreted, so the alignment is always zero here. *)
      let rem = Rel.zero in
      (*     Format.printf "1: (%a, %a, %a);@.2: (%a, %a, %a);@.[%a--%a] -> %a/%a@."
             pretty_int rem1 pretty_int modu1 V.pretty v1
             pretty_int rem2 pretty_int modu2 V.pretty v2
             pretty_int abs_min pretty_int abs_max
             V.pretty v1' V.pretty v2'; *)
      rem, size, merge_v size v1' v2'
  ;;

  (* similar to [f_aux_merge_generic], but we perform a reinterpretation in
     all cases. This is to ensure that [V.narrow] can be applied soundly. *)
  let f_aux_merge_narrow merge_v abs_min abs_max rem1 modu1 v1 rem2 modu2 v2 =
    let topify = Origin.K_Merge in
    let offset = abs_min in
    let size = Integer.length abs_min abs_max in
    let v1' =
      extract_bits_and_stitch ~topify ~conflate_bottom:false
        ~offset ~size offset (rem1, modu1, v1) abs_max V.merge_neutral_element
    in
    let v2' =
      extract_bits_and_stitch ~topify ~conflate_bottom:false
        ~offset ~size offset (rem2, modu2, v2) abs_max V.merge_neutral_element
    in
    Rel.zero, size, (merge_v size v1' v2': v)
  ;;

  (** More efficient version of {!f_aux_merge_generic}, specialized for
      join-like functions. When one of the values is isotropic, we do not
      concretize the other one with {!extract_stitch_and_bits}. Instead,
      we implicitly "extend" the isotropic value to the full range,
      and merge on the whole range. This does not work with narrow, because
      [narrow {0} {1,2}] on the first bit is {0}, but the intersection
      of the two sets is bottom. *)
  let f_aux_merge_join merge_v abs_min abs_max rem1 modu1 v1 rem2 modu2 v2 =
    let joined size v1 v2 = V.anisotropic_cast ~size (merge_v size v1 v2) in
    if V.is_isotropic v2 then
      rem1, modu1, joined modu1 v1 v2
    else if V.is_isotropic v1 then
      rem2, modu2, joined modu2 v1 v2
    else
      f_aux_merge_generic merge_v abs_min abs_max rem1 modu1 v1 rem2 modu2 v2
  ;;


  module JoinCache = Binary_cache.Symmetric_Binary(Cacheable)(Cacheable)
  let () = clear_caches_ref := JoinCache.clear :: !clear_caches_ref;;

  (** Joining two trees that cover the same range *)
  let join t1 t2 =
    let f_join = f_aux_merge_join (fun _size v1 v2 -> V.join v1 v2) in
    let rec aux_cache t1 t2 =
      if Cacheable.equal t1 t2 then t1
      else JoinCache.merge (merge aux_cache f_join) t1 t2
    in
    let _, r = aux_cache (Integer.zero, t1) (Integer.zero, t2) in
    r
  ;;

  module Make_Narrow(X: sig
      include Lattice_type.With_Top with type t := V.t
      include Lattice_type.With_Narrow with type t := V.t
    end) =
  struct

    module NarrowCache = Binary_cache.Symmetric_Binary(Cacheable)(Cacheable)
    module NarrowReinterpretCache =
      Binary_cache.Symmetric_Binary(Cacheable)(Cacheable)
    let () = clear_caches_ref :=
        NarrowReinterpretCache.clear :: NarrowCache.clear :: !clear_caches_ref;;

    let is_top = function
      | Node (_, _, Empty, _, Empty, _ , _, v, _) -> V.equal v X.top
      | _ -> false

    (** Narrowing two trees that cover the same range *)
    let narrow t1 t2 =
      let f_join = f_aux_merge_generic (fun _size v1 v2 -> X.narrow v1 v2) in
      let rec aux_cache t1 t2 =
        if Cacheable.equal t1 t2 || is_top (snd t2) then t1
        else if is_top (snd t1) then t2
        else NarrowCache.merge (merge aux_cache f_join) t1 t2
      in
      let _, r = aux_cache (Integer.zero, t1) (Integer.zero, t2) in
      r
    ;;

    let narrow_reinterpret t1 t2 =
      let f_join = f_aux_merge_narrow (fun _size v1 v2 -> X.narrow v1 v2) in
      let rec aux_cache t1 t2 =
        if Cacheable.equal t1 t2 || is_top (snd t2) then t1
        else if is_top (snd t1) then t2
        else NarrowReinterpretCache.merge (merge aux_cache f_join) t1 t2
      in
      let _, r = aux_cache (Integer.zero, t1) (Integer.zero, t2) in
      r
    ;;

  end

  let widen wh t1 t2 =
    (* Due to the way f_aux_merge is designed, we can obtain intervals on which
       the two bindings do not verify [is_included v1 v2]. The widening
       operations require this, so we correct the arguments here. *)
    let widen size v1 v2 =
      let v2 = if not (V.is_included v1 v2) then V.join v1 v2 else v2 in
      V.widen (size,wh) v1 v2
    in
    let f_widen = f_aux_merge_join widen in
    let rec aux t1 t2 =
      if Cacheable.equal t1 t2 then t1
      else merge aux f_widen t1 t2
    in
    let _, r = aux (Integer.zero, t1) (Integer.zero, t2) in
    r
  ;;


  type map2_decide =
      ReturnLeft | ReturnRight | ReturnConstant of V.t | Recurse

  let map2_on_values_offset cache decide (f: V.t -> V.t -> V.t) =
    let merge_cache =
      match cache with
      | Hptmap_sig.PersistentCache _ | Hptmap_sig.TemporaryCache _ ->
        let module Map2Cache =
          Binary_cache.Arity_Two(Cacheable)(Cacheable)(Cacheable)
        in
        (match cache with
         | Hptmap_sig.PersistentCache _ ->
           clear_caches_ref := Map2Cache.clear :: !clear_caches_ref
         | _ -> ());
        Map2Cache.merge
      | Hptmap_sig.NoCache -> fun f x y -> f x y
    in
    let f' _abs_min _abs_max _rem1 _modu1 v1 _rem2 _modu2 v2 =
      Rel.zero, Int.one, f v1 v2
    in
    (* See the invariants a the top of {!merge}: [bounds o1 n1 = bounds o2 n2]
       holds *)
    let rec aux (o1, n1 as t1) (_o2, n2 as t2) =
      match decide n1 n2 with
      | Recurse ->
        merge_cache (merge aux f') t1 t2
      | ReturnLeft -> t1
      | ReturnRight -> t2
      | ReturnConstant v ->
        if n1 == Empty then begin
          (o1, n1) (* [n2 == Empty] and [o1 =~ o2] hold. *)
        end else begin
          (* build an interval mapped to [v], of the same width as t1 and t2 *)
          let ib1, ie1 = bounds_offset o1 n1 in
          ib1, interval_aux (ie1 -~ ib1) Rel.zero Int.one v
        end
    in
    aux

  let map2_on_values cache decide (f: V.t -> V.t -> V.t) =
    let map2_on_values_cached = map2_on_values_offset cache decide f in
    fun t1 t2 -> snd (map2_on_values_cached (Int.zero, t1) (Int.zero, t2))


  (* Given an integer i,
     find the interval the ith bit belongs to (thus its node)
     Returns: the zipper to navigate from the root to the node found,
     and the node itself
  *)
  exception Bit_Not_found (* TODO: not clear it does not leak outside *)
  let find_bit_offset i zipper offset tree =
    let rec aux_find tree curr_off z =
      match tree with
      | Empty -> raise Bit_Not_found
      | Node (max, offl, subl, offr, subr, _, _modu, _v, _) ->
        let abs_max = curr_off +~ max in
        if (i >=~ curr_off) && (i <=~ abs_max)
        then (z, curr_off, tree)
        else if i <~ curr_off
        then
          aux_find subl (curr_off +~ offl) (Left(curr_off, tree, z))
        else begin
          assert (i >~ abs_max);
          aux_find subr (curr_off +~ offr) (Right(curr_off, tree, z))
        end
    in
    aux_find tree offset zipper
  ;;

  let find_bit i tree = find_bit_offset i End Integer.zero tree
  ;;


  (* First and last bits are included in the interval. The returned value
     is at the very least isotropic, possibly topified. *)
  let find_imprecise_between (first_bit, last_bit) tree =
    let rec aux tree_offset tree =
      match tree with
      | Empty -> V.bottom
      | Node (max, offl, subl, offr, subr, _rem, _m, v, _) ->
        let abs_max = max +~ tree_offset in
        let subl_value =
          if first_bit <~ tree_offset then
            let subl_abs_offset = tree_offset +~ offl in
            aux subl_abs_offset subl
          else V.bottom
        in
        let subr_value =
          if last_bit >~ abs_max then
            let subr_abs_offset = tree_offset +~ offr in
            aux subr_abs_offset subr
          else V.bottom
        in
        let current_node_value =
          if last_bit <~ tree_offset || first_bit >~ abs_max
          then V.bottom
          else
          if V.is_isotropic v
          then v
          else
            let origin = Origin.(current K_Misalign_read) in
            V.topify_with_origin origin v
        in
        V.join subl_value (V.join subr_value current_node_value)
    in
    aux Integer.zero tree

  (* Reads the interval [start, start + size - 1] in the offsetmap [tree].
     Assumes that the interval fits into the offsetmap, and that the offsetmap
     is rooted at offset 0.
     [read_value] and [read_nodes] are used to read the offsetmap:
     - [read_value v size] is used if the read matches exactly a value [v] of
       size [size].
     - otherwise, [read_nodes ~offset node zipper ~start ~size] is called, with
       [node] the node in which the read starts, [offset] the offset of [node],
       and [zipper] a zipper to navigate from the root of [node].
       When the read belongs to a series of periodic reads, [since_and_period]
       should be the first offset and the period of the reads. This function then
       returns the last offset of the node being read if the series of reads can
       skip the rest of the node: it means that all other reads within the node
       will give a value that has already been read in the series. Otherwise, the
       function returns None.  *)
  let read_itv ?since_and_period ~start ~size tree ~read_value ~read_nodes =
    let zipper, cur_off, root = find_bit start tree in
    match root with
    | Empty -> assert false
    | Node (max, _, _, _, _subr, rrel, m, v, _) ->
      let r = (Rel.add_abs cur_off rrel) %~ m in
      let read_ending = pred (start +~ size) in
      let node_ending = cur_off +~ max in
      let isotropic = V.is_isotropic v in
      let read_fit_in_node = read_ending <=~ node_ending in
      let value =
        if read_fit_in_node && (isotropic || (m =~ size && start %~ m =~ r))
        then read_value v size
        else read_nodes ~offset:cur_off root zipper ~start ~size
      in
      (* Could a series of periodic reads jump ahead in the offsetmap (for
         performance issue)? *)
      let read_ahead = match since_and_period with
        | None -> None
        | Some (since, period) ->
          (* If the next read reaches the next node, we cannot optimize *)
          if (read_ending +~ size) >~ node_ending
          then None
          (* If the value of the node is isotropic, or if the size of the
             repeated value divides the period, then all reads in this node
             are equivalent: jump to the next node. *)
          else if isotropic || (Int.is_zero (period %~ m))
          then Some node_ending
          else
            let since = Int.max since cur_off in
            (* The value in the node is repeated every [m] bits, and we have
               read every [period] bits. Once we have read [lcm period m] bits,
               we will have read all possible combinations. *)
            if start -~ since >= Int.ppcm period m
            then Some node_ending
            else None
      in
      read_ahead, value

  (* Reads only one interval by calling [read_itv]. Ignores the argument and
     result dedicated to periodic reads. *)
  let read_one_itv ~start ~size tree ~read_value ~read_nodes =
    snd (read_itv ~start ~size tree ~read_value ~read_nodes)

  (* Performs a series of periodic reads, starting at [min], ending at [max], and
     whose period is [period]. [size] is the size of each read. [read_value] and
     [read_nodes] are used to read the offsetmap (see read_itv for details).
     [join] is used to merge the result of each read, starting with [acc]. *)
  let read_series_itv ~min ~max ~period ~size tree ~read_value ~read_nodes ~join acc =
    let r = min %~ period in
    let since_and_period = min, period in
    let rec read_series start acc =
      let read_ahead, v =
        read_itv ~since_and_period ~start ~size tree ~read_value ~read_nodes
      in
      let acc = join v acc in
      (* Compute the offset of the next read. By default, add the [period] to the
         current [start], unless we can jump to the end of the current node. *)
      let next = match read_ahead with
        | None -> start +~ period
        | Some read_ahead ->
          (* [read_ahead] is the last offset of the node that has been read.
             The next reads within the node are unnecessary, so we could
             theoretically start at [succ read_ahead] (after re-alignement on
             [period]). However, the last read that starts on this node
             may overlap with the next node, and must be performed. So
             we rewind by [pred size] bits, then round up to the next periodic
             index that must be read. *)
          let min_next = (succ read_ahead) -~ (pred size) in
          Integer.round_up_to_r ~min:min_next ~r ~modu:period
      in
      (* Do not read past [max]. *)
      if next <=~ max
      then read_series next acc
      else acc
    in
    read_series min acc

  (* Reads [tree] at each offset of [offsets]. [size] is the size of each read.
     [read_value] and [read_nodes] perform the reads; [join] merges the result
     of each read, starting with [acc]. *)
  let read ~offsets ~size tree ~read_value ~read_nodes ~join acc =
    match offsets with
    | Tr_offset.Interval (min, max, period) ->
      read_series_itv
        ~min ~max ~period ~size tree ~read_value ~read_nodes ~join acc
    | Tr_offset.Set s ->
      List.fold_left
        (fun acc start ->
           let t = read_one_itv ~start ~size tree ~read_value ~read_nodes in
           join acc t)
        acc s
    | Tr_offset.Overlap(min, max, _origin) ->
      let v = find_imprecise_between (min, max) tree in
      read_value v size
    | Tr_offset.Invalid -> acc

  (* Transforms a function reading one node into a function reading successive
     nodes. The resulting function can be supplied to the [read_itv] function.
     It reads the interval [start, start + size - 1], which is supposed to start
     in the node [node]. [offset] is the offset of [node] in the offsetmap, and
     [zipper] is a zipper to navigate from the root of [node]. It is used to
     read the next nodes of the offsetmap if needed. The function [read_one_node]
     performs the read of each node. *)
  let read_successive_nodes ~read_one_node acc =
    fun ~offset node zipper ~start ~size ->
    let read_end = pred (start +~ size) in
    let rec read_nodes offset node zipper acc =
      let node_end = offset +~ (get_max node) in
      let t = read_one_node ~offset node ~start ~size acc in
      if node_end >=~ read_end
      then t
      else
        let offset, node, zipper = move_right offset node zipper in
        read_nodes offset node zipper t
    in
    read_nodes offset node zipper acc

  (*  Finds the value associated to some offsets represented as an ival. *)
  let find ~validity ?(conflate_bottom=true) ~offsets ~size tree =
    let offsets = Tr_offset.trim_by_validity offsets size validity in
    let topify = Origin.K_Misalign_read in
    let read_one_node ~offset node ~start ~size acc =
      extract_bits_and_stitch ~topify ~conflate_bottom
        ~offset:start ~size
        offset (get_vv node) (get_max node)
        acc
    in
    let neutral = V.merge_neutral_element in
    let read_nodes = read_successive_nodes ~read_one_node neutral in
    let read_value v _size = v in
    let join = V.join in
    read ~offsets ~size tree ~read_value ~read_nodes ~join V.bottom

  (* Copies the node [node] at the end of the offsetmap [acc], as part of the
     larger copy of the interval [start..start+size-1] from the englobing
     offsetmap of [node]. [offset] is the offset of [node] in this offsetmap.
     As the new offsetmap represents the interval [0..size-1], the offsets are
     shifted by [start]. *)
  let copy_one_node ~offset node ~start ~size acc  =
    match node with
    | Empty -> assert false
    | Node (max, _, _, _, _subr, rem, modu, v, _) ->
      (* The current copy starts at [offset], unless the overall copy starts in
         the middle of the node. The new start is then shifted by [start]. *)
      let min = (Integer.max offset start) -~ start in
      (* Same kind of reasoning for the end of the current copy. *)
      let node_end = offset +~ max in
      let read_end = pred (start +~ size) in
      let max = (Integer.min read_end node_end) -~ start in
      (* For the first node, if the read starts in the middle of the node,
         realign the value wrt the offset of the read (but not wrt the offset of
         the node in the new offsetmap). *)
      let new_rem =
        if offset <~ start
        then realign ~offset:offset ~new_offset:start rem modu
        else rem
      in
      let o, t = add_node ~min ~max new_rem modu v Integer.zero acc in
      assert (o =~ Integer.zero);
      t

  let copy_slice ~validity ~offsets ~size tree =
    let offsets = Tr_offset.trim_by_validity offsets size validity in
    if Int.(equal size zero) then `Value Empty
    else match offsets with
      | Tr_offset.Invalid -> `Bottom
      | _ ->
        let read_one_node = copy_one_node in
        let neutral = m_empty in
        let read_nodes = read_successive_nodes ~read_one_node neutral in
        let read_value v size = interval_aux (pred size) Rel.zero size v in
        let init = isotropic_interval size V.bottom in
        let t = read ~offsets ~size tree ~read_value ~read_nodes ~join init in
        `Value t

  (* Keep the part of the tree strictly under (i.e. strictly on the left) of a
     given offset. *)
  let rec keep_below ~offset curr_off tree =
    match tree with
    | Empty -> offset, tree
    | Node (max, offl, subl, offr, subr, rem, m, v, _) ->
      let new_offl = offl +~ curr_off in
      if offset <~ curr_off then
        keep_below ~offset new_offl subl
      else if offset =~ curr_off then
        new_offl, subl
      else
        let sup = curr_off +~ max in
        if offset >~ sup then
          let new_offr, new_subr = keep_below ~offset (curr_off +~ offr) subr in
          curr_off,
          nNode max offl subl (new_offr -~ curr_off) new_subr rem m v
        else
          let new_max = pred (offset -~ curr_off) in
          add_node
            ~min:curr_off ~max:(new_max +~ curr_off)
            rem m v
            (curr_off +~ offl ) subl
  ;;

  (* Keep the part of the tree strictly above (e.g. strictly on the right) of a
     given offset. *)
  let rec keep_above ~offset curr_off tree =
    match tree with
    | Empty -> (succ offset), tree
    | Node (max, offl, subl, offr, subr, rem, m, v, _) ->
      let new_offr = offr +~ curr_off in
      let abs_max = curr_off +~ max in
      if offset >~ abs_max then
        (* This node should be forgotten,
           let's look at the right subtree
        *)
        keep_above ~offset new_offr subr
      else if offset =~ abs_max then
        (* we are at the limit,
           the right subtree is the answer
        *)
        new_offr, subr
      else
      if offset <~ curr_off then
        (* we want to keep this node and part of its left subtree *)
        let new_offl, new_subl =
          keep_above ~offset (curr_off +~ offl) subl
        in
        curr_off,
        nNode max (new_offl -~ curr_off) new_subl offr subr rem m v
      else
        (* the cut happens somewhere in this node it should be cut
           accordingly and reinjected into its right subtree *)
        let min = succ offset in
        let new_reml = realign ~offset:curr_off ~new_offset:min rem m in
        add_node ~min ~max:abs_max new_reml m v new_offr subr
  ;;

  let update_itv_with_rem ~exact ~offset ~abs_max ~size ~rem v curr_off tree =
    if Int.(equal size zero) then curr_off, tree else
      let off1, t1 = keep_above ~offset:abs_max curr_off tree in
      let off2, t2 = keep_below ~offset curr_off tree in
      if exact then
        let off_add, t_add =
          add_node ~min:offset ~max:abs_max rem size v off1 t1
        in
        union off2 t2 off_add t_add
      else
        let v_is_isotropic = V.is_isotropic v in
        let z, o, t = find_bit_offset offset End curr_off tree in
        let left_tree = ref t2 in
        let left_offset = ref off2 in
        let impz = { node = t; offset = o; zipper = z; } in
        while impz.offset <=~ abs_max do
          match impz.node with
          | Empty -> assert false
          | Node (max, _offl, _subl, _offr, _subr, r_node, m_node, v_node, _) ->
            let new_offset = Integer.max offset impz.offset in
            let rem = realign ~offset ~new_offset rem size in
            let r_node = realign ~offset:impz.offset ~new_offset r_node m_node in
            let new_r, new_m, new_v =
              let joined_value = V.join v_node v in
              if v_is_isotropic || (Rel.equal rem r_node && m_node =~ size)
              then r_node, m_node, V.anisotropic_cast ~size:m_node joined_value
              else if V.is_isotropic v_node
              then rem, size, V.anisotropic_cast ~size joined_value
              else
                let origin = Origin.(current K_Merge) in
                let new_value = V.topify_with_origin origin joined_value in
                let new_rem = Rel.zero and new_modu = Integer.one in
                new_rem, new_modu, new_value
            in
            let node_abs_max = impz.offset +~ max in
            let end_reached, write_max =
              if node_abs_max >=~ abs_max
              then true, abs_max
              else false, node_abs_max
            in
            let new_left_offset, new_left_tree =
              add_node
                ~min:new_offset ~max:write_max
                new_r new_m new_v !left_offset !left_tree
            in
            left_tree := new_left_tree;
            left_offset := new_left_offset;
            if not end_reached then imp_move_right impz
            else impz.offset <- succ abs_max
        done;
        union !left_offset !left_tree off1 t1
  ;;

  let update_itv = update_itv_with_rem ~rem:Rel.zero;;

  (* This should be in Int_Intervals, but is currently needed here.
     Returns an interval with reversed bounds when the intersection is empty. *)
  let clip_by_validity = function
    | Base.Empty | Base.Invalid ->
      (fun _-> Int.one, Int.zero (* reversed interval -> no intersection*))
    | Base.Known (min, max)
    | Base.Unknown (min, _, max) ->
      (fun (min', max') -> Integer.max min min', Integer.min max max')
    | Base.Variable variable_v ->
      (fun (min', max') -> Integer.max Int.zero min',
                           Integer.min variable_v.Base.max_alloc max')

  (** This function does a weak update of the entire [offsm], by adding the
      topification of [v]. The parameter [validity] is respected, and so is the
      current size of [offsm]: each interval already present in [offsm] and valid
      is overwritten. Interval already present but not valid are bound to
      [V.bottom]. *)
  (* TODO: the convention to write bottom on non-valid locations is strange,
     and only useful for the NULL base in Lmap.ml. It would be simpler an more
     elegant to keep the existing value on non-valid ranges instead. This
     function should also be written as a call to fold_between *)
  let update_imprecise_everywhere ~validity o v offsm =
    let v = V.topify_with_origin o v in
    if Base.Validity.equal validity Base.Invalid then
      `Bottom
    else
      let clip = clip_by_validity validity in
      let r = fold
          (fun (min, max as itv) (bound_v, _, _) acc ->
             let new_v = V.join (V.topify_with_origin o bound_v) v in
             let new_min, new_max = clip itv in
             if new_min <=~ new_max then (* [min..max] and validity intersect *)
               let acc =
                 if min <~ new_min (* Before validity *)
                 then append_basic_itv ~min ~max:(pred new_min) ~v:V.bottom acc
                 else acc
               in
               let acc = append_basic_itv ~min:new_min ~max:new_max ~v:new_v acc in
               let acc =
                 if new_max <~ max (* After validity *)
                 then append_basic_itv ~min:(succ new_max) ~max ~v:V.bottom acc
                 else acc
               in acc
             else
               append_basic_itv ~min ~max ~v:V.bottom acc
          ) offsm m_empty
      in
      `Value r
  ;;


  (** Update a set of intervals in a given rangemap all offsets starting from
      mn ending in mx must be updated with value v, every period *)
  let update_itvs ~exact ~mn ~mx ~period ~size v curr_off tree =
    assert(mx >=~ mn);
    let r = mn %~ period in
    let rec aux_update mn mx curr_off tree =
      match tree with
      | Empty -> curr_off, tree
      | Node (max, offl, subl, offr, subr, r_node, m_node, v_node, _) ->
        let abs_offl = offl +~ curr_off in
        let abs_offr = offr +~ curr_off in

        let new_offl, new_subl, undone_left =
          let last_read_max_offset = curr_off -~ size in
          if pred (mn +~ size) <~ curr_off then
            let new_mx = Integer.round_down_to_r
                ~max:last_read_max_offset ~r ~modu:period
            in
            let new_mx, undone =
              if new_mx >=~ mx
              then mx, None
              else new_mx, Some (new_mx +~ period)
            in
            let o, t = aux_update mn new_mx abs_offl subl in
            o, t, undone
          else abs_offl, subl, Some mn

        and new_offr, new_subr, undone_right =
          let abs_max = curr_off +~ max in
          let first_read_min_offset = succ abs_max in
          if mx >~ abs_max then
            let new_mn = Integer.round_up_to_r
                ~min:first_read_min_offset ~r ~modu:period
            in
            let new_mn, undone =
              if new_mn <=~ mn
              then mn, None
              else new_mn, Some (new_mn -~ period)
            in
            let o, t = aux_update new_mn mx abs_offr subr in
            o, t, undone
          else abs_offr, subr, Some mx

        in
        let o, t =
          add_node
            ~min:curr_off ~max:(curr_off +~ max)
            r_node m_node v_node new_offl new_subl
        in
        let curr_off, tree = union o t new_offr new_subr in
        match undone_left, undone_right with
        | Some min, Some max ->
          begin
            let update = update_itv ~exact in
            if size =~ period
            then
              let abs_max = pred (size +~ max) in
              update ~offset:min ~abs_max ~size v curr_off tree
            else
              let offset = ref min in
              let o = ref curr_off in
              let t = ref tree in
              while !offset <=~ max do
                let abs_max = pred (size +~ !offset) in
                let o', t' =
                  update ~offset:!offset ~abs_max ~size v !o !t
                in
                o := o';
                t := t';
                offset := !offset +~ period;
              done;
              !o, !t;
          end
        | Some _, None
        | None, Some _
        | None, None -> curr_off, tree
    in
    aux_update mn mx curr_off tree
  ;;

  let imprecise_write_msg = ref "locations to update in array"

  exception Update_Result_is_bottom

  (* Returns [true] iff [update_aux_tr_offsets] will approximate the set
     of offsets written *)
  let update_aux_tr_offsets_approximates offsets size =
    match offsets with
    | Tr_offset.Overlap _ -> false
    | Tr_offset.Interval(mn, mx, period) ->
      let number = succ ((mx -~ mn) /~ period) in
      let plevel = !plevel in
      if number <=~ Integer.of_int plevel || period =~ size then false
      else true
    | Tr_offset.Set _
    | Tr_offset.Invalid  -> false

  (* Update [t] by writing [v] of size [size] every offsets. Make sure that this
     function over-approximates the set of location written
     iff [update_aux_approximates] returns [true] *)
  let update_aux_tr_offsets ~exact ~offsets ~size v curr_off t =
    match offsets with
    | Tr_offset.Overlap (mn, mx, origin) ->
      let origin = if origin = Origin.Unknown
        then Origin.(current K_Misalign_read)
        else origin
      in
      let v = V.topify_with_origin origin v in
      (* TODO: check *)
      update_itv ~exact ~offset:mn ~abs_max:mx ~size:Integer.one v curr_off t

    | Tr_offset.Interval(mn, mx, period) ->
      let number = succ ((mx -~ mn) /~ period) in
      let plevel = !plevel in
      assert (period >=~ size); (* Checked by Tr_offset *)
      if number <=~ Integer.of_int plevel || period =~ size then
        update_itvs ~exact ~mn ~mx ~period ~size v curr_off t
      else begin
        if size <~ period then
          (* We are going to write the locations that are between [size+1] and
             [period] unnecessarily, warn the user *)
          Lattice_messages.emit_approximation msg_emitter
            "more than %d(%a) %s. Approximating."
            plevel pretty_int number !imprecise_write_msg;
        let abs_max = pred (mx +~ size) in
        let v =
          if Int.is_zero (period %~ size) then v
          else
            let origin = Origin.(current K_Misalign_read) in
            let v' = V.topify_with_origin origin v in
            if not (V.equal v v') then
              Lattice_messages.emit_approximation msg_emitter
                "approximating value to write.";
            v'
        in
        update_itv ~exact:false ~offset:mn ~abs_max ~size v curr_off t
      end

    | Tr_offset.Set s ->
      List.fold_left
        (fun (curr_off, m) offset ->
           update_itv ~exact ~offset ~size
             ~abs_max:(pred (offset +~ size)) v curr_off m
        ) (curr_off, t) s
    | Tr_offset.Invalid  ->
      raise Update_Result_is_bottom

  (* High-level update function (roughly of type [Ival.t -> v -> offsetmap ->
     offsetmap]. This function does not suppose that offsetmaps are zero-rooted.
     When too many locations must be updated, the result is approximated w.r.t
     the memory zones written. *)
  let update_aux ?origin ~validity ~exact ~offsets ~size v curr_off t =
    let v = V.anisotropic_cast ~size v in
    let reduced = Tr_offset.trim_by_validity ?origin offsets size validity in
    let exact = exact && not (Base.is_weak_validity validity) in
    update_aux_tr_offsets ~exact ~offsets:reduced ~size v curr_off t

  (* Same as update_aux, but on zero-rooted offsetmaps. *)
  let update ?origin ~validity ~exact ~offsets ~size v t =
    try
      let _curr_off, r =
        update_aux ?origin ~validity ~exact ~offsets ~size v Int.zero t
      in
      `Value r
    with Update_Result_is_bottom -> `Bottom

  (* High-level update function (roughly of type [Ival.t -> v -> offsetmap ->
     offsetmap]) that *under*-approximate the set of written locations, when
     there are too many of them. *)
  let update_under ~validity ~exact ~offsets ~size v t =
    let v = V.anisotropic_cast ~size v in
    let offsets = Tr_offset.trim_by_validity offsets size validity in
    if Base.is_weak_validity validity ||
       update_aux_tr_offsets_approximates offsets size
    then
      `Value t
    else
      try
        let _, t = update_aux_tr_offsets ~exact ~offsets ~size v Int.zero t in
        `Value t
      with Update_Result_is_bottom -> `Bottom

  let is_single_interval o =
    match o with
    | Node(_, _, Empty, _, Empty, _, _, _, _) -> true
    | _ -> false

  let single_interval_value o =
    match o with
    | Node(_, _, Empty, _, Empty, _, _, v, _) -> Some v
    | _ -> None

  let is_same_value o v =
    match o with
    | Empty -> true
    | Node(_, _, Empty, _, Empty, _, _, v', _) -> V.equal v v'
    | _ -> false

  let fold_between ?(direction=`LTR) ~entire (imin, imax) f t acc =
    let rec aux curr_off t acc = match t with
      | Empty -> acc
      | Node (max, offl, subl, offr, subr, rem, modu, v, _) ->
        let abs_max = max +~ curr_off in
        (* fold on the left subtree *)
        let acc_left acc =
          if imin <~ curr_off then (
            aux (offl +~ curr_off) subl acc)
          else acc
        in
        let acc_middle acc =
          if imax <~ curr_off || imin >~ abs_max
          then acc
          else
          if entire then
            (* Call f on the entire binding *)
            f (curr_off, abs_max) (v, modu, rem) acc
          else
            (* Cut the interval to [imin..imax] *)
            let lmin = Integer.max imin curr_off in
            let lmax = Integer.min imax abs_max in
            let lrem =
              Rel.e_rem (Rel.sub rem (Rel.sub_abs lmin curr_off)) modu
            in
            f (lmin, lmax) (v, modu, lrem) acc
        in
        (* fold on the right subtree *)
        let acc_right acc =
          if imax >~ abs_max
          then aux (offr +~ curr_off) subr acc
          else acc
        in
        match direction with
        | `LTR -> acc_right (acc_middle (acc_left acc))
        | `RTL -> acc_left (acc_middle (acc_right acc))
    in
    aux Integer.zero t acc
  ;;

  (* weak validity should be handled caller *)
  let paste_slice_itv ~exact from stop start_dest to_ =
    let update = update_itv_with_rem ~exact in
    let treat_interval (imin, imax) (v, modu, rem) acc =
      let dmin, dmax = imin +~ start_dest, imax +~ start_dest in
      snd (update
             ~offset:dmin ~abs_max:dmax ~rem:rem ~size:modu v Integer.zero acc)
    in
    fold_between ~entire:false (Int.zero, stop) treat_interval from to_
  ;;

  (** pastes [from] (of size [size]) at all [offsets] in [dst]. Optimisations
      for the case where [size] and the periodicity of [offsets] match are
      treated in [paste_slice] below. [size] is supposed to be strictly
      positive. *)
  let paste_slice_not_contiguous ~validity ~exact ~from:src ~size ~offsets dst =
    try
      let plevel = !plevel in
      let stop_src = Int.pred size in
      ignore (Ival.cardinal_less_than offsets plevel);
      (* See explanations at the end of [Tr_offset] for what is computed here.*)
      let min_valid, max_maybe_valid = match validity with
        | Base.Invalid | Base.Empty (* size > 0 *) -> Int.zero, Int.minus_one
        | Base.Known (b, e) | Base.Unknown (b, _, e) -> b, e
        | Base.Variable { Base.max_alloc } -> Int.zero, max_alloc
      in
      let aux start_to (acc_offsm, acc_success) =
        let stop_to = Int.pred (Int.add start_to size) in
        (* check if at least one access is possibly valid *)
        if Int.lt start_to min_valid || Int.gt stop_to max_maybe_valid then
          (* at least one bit cannot be written => invalid access *)
          acc_offsm, acc_success
        else
          let exact = exact && not (Base.is_weak_validity validity) in
          paste_slice_itv ~exact src stop_src start_to acc_offsm, true
      in
      (* TODO: this should be improved if offsets if of the form [a..b]c%d
         with d >= size. In this case, the write do not overlap, and
         could be done in one run in the offsetmap itself, using a zipper *)
      let res, success = Ival.fold_int aux offsets (dst, false) in
      if success then `Value res else `Bottom
    with Not_less_than ->
      (* Value to paste, since we cannot be precise *)
      let v =
        (* Under this size, this may be an integer. Try to be a bit precise
           when doing 'find' *)
        if size <=~ Integer.of_int 128 then
          let validity_src = Base.validity_from_size size in
          find ~validity:validity_src ~conflate_bottom:false
            ~offsets:Ival.zero ~size src
        else
          (* This is a struct or an array. Either the result will be imprecise
             because catenating semi-imprecise values through merge_bits
             will result in something really imprecise at the end, or we will
             build very big integers, which is not really helpful either. *)
          find_imprecise_between (Int.zero, Int.pred size) src
      in
      (* Have we produced an imprecision when calling 'find' ? *)
      let imprecise = match src with
        | Node (_, _, Empty, _, Empty, _, _, v', _) -> not (V.equal v v')
        | _ -> true (* at least two nodes *)
      in
      if imprecise then
        Lattice_messages.emit_approximation msg_emitter
          "too many locations to update in array. Approximating.";
      update ~validity ~exact ~offsets ~size v dst

  (** pastes [from] (of size [size]) at all [offsets] in [dst] *)
  let paste_slice ~validity ~exact ~from:src ~size ~offsets dst =
    if Int.(equal size zero) then (* nothing to do *) `Value dst
    else
      let size_ok =
        Ival.is_singleton_int offsets
        || let _, _, _, modu = Ival.min_max_r_mod offsets in size =~ modu
      in
      match src with
      (*Special case: [from] contains a single (aligned) binding [v], and [size]
        matches the periodicity of [offsets] and the size of [v]. In this case,
        it is more efficient to perform an interval update instead of an
        offsetmap copy. *)
      | Node (_,_, Empty,_, Empty, vrem, vsize, v,_)
        when Rel.is_zero vrem && size_ok && (size =~ vsize || V.is_isotropic v)
        ->
        update ~validity ~exact ~offsets ~size v dst
      | _ ->
        paste_slice_not_contiguous ~validity ~exact ~from:src ~size ~offsets dst

  let skip_v v = V.equal V.bottom v

  let pretty_generic ?typ ?(pretty_v=V.pretty_typ) ?(skip_v=skip_v) ?(sep=Unicode.inset_string ()) () fmt m =
    let is_first = ref true in
    let pretty_binding fmt (bk, ek) (v, modu, rel_offs) =
      if not (skip_v v) then begin
        if !is_first then is_first:=false
        else Format.fprintf fmt "@\n";
        Format.fprintf fmt "@[" ;
        (* Print left-member and return misalign condition *)
        let force_misalign, printed_type =
          match typ with
          | None ->
            Format.fprintf fmt "[rbits %a to %a]"
              pretty_int bk pretty_int ek ;
            (* misalign condition: *)
            (not (Rel.is_zero rel_offs) || (ek -~ bk <>~ pred modu))
            && not (V.is_isotropic v),
            None

          | Some typ ->
            (* returns misalign condition. *)
            Bit_utils.pretty_bits typ
              ~use_align:(not (V.is_isotropic v))
              ~align:rel_offs ~rh_size:modu ~start:bk ~stop:ek fmt
        in
        Format.fprintf fmt " %s@ @[<hv 1>%a@]" sep (pretty_v printed_type) v ;
        if force_misalign
        then
          if Rel.is_zero rel_offs && (Int.length bk ek) %~ modu =~ Integer.zero
          then
            (if Int.length bk ek >~ modu then
               Format.fprintf fmt " repeated %%%a " pretty_int modu)
          else (
            let b_bits = Rel.e_rem (Rel.sub Rel.zero rel_offs) modu  in
            let e_bits = Rel.add_abs (ek -~ bk) b_bits in
            Format.fprintf fmt "%s%%%a, bits %a to %a "
              (if e_bits >~ modu then " repeated " else "")
              pretty_int modu Rel.pretty b_bits pretty_int e_bits
          );
        Format.fprintf fmt "@]";
      end
    in
    if is_empty m then
      Format.fprintf fmt "@[[?] %s EMPTY@]" sep
    else begin
      Format.fprintf fmt "@[";
      iter (pretty_binding fmt) m;
      if !is_first then
        Format.fprintf fmt "%s@ %s" sep (Unicode.emptyset_string ());
      Format.fprintf fmt "@]";
    end

  let create_isotropic ~size v =
    assert (Int.ge size Int.zero);
    if Int.(equal size zero) then Empty
    else begin
      assert (V.is_isotropic v);
      isotropic_interval size v
    end

  let create ~size v ~size_v =
    assert (Int.ge size Int.zero);
    if Int.(equal size zero) then Empty
    else snd (Int.zero, interval_aux (pred size) Rel.zero size_v v)

  let cardinal_zero_or_one offsetmap =
    (singleton_tag offsetmap) <> 0

  let of_list fold l size_elt =
    let s = pred size_elt in
    let n = ref Integer.zero in
    let addw acc v =
      let e = !n +~ s in
      let r = append_basic_itv ~min:!n ~max:e ~v acc in
      n := succ e;
      r
    in
    let r = fold addw m_empty l in
    assert (!n >~ Int.zero); (* implies that r <> Empty *)
    r

  let add ?(exact=true) (min, max) (v, modu, rem) m =
    snd (update_itv_with_rem ~exact
           ~offset:min ~abs_max:max ~rem ~size:modu v Integer.zero m)

  let find_imprecise ~validity m =
    match validity with
    | Base.Known (min, max) | Base.Unknown (min, _, max) ->
      find_imprecise_between (min, max) m
    | Base.Variable variable_v ->
      find_imprecise_between (Int.zero, variable_v.Base.max_alloc) m
    | Base.Invalid | Base.Empty -> V.bottom

  let find_imprecise_everywhere m =
    match m with
    | Empty -> V.bottom
    | Node _ ->
      let bounds = bounds_offset Int.zero m in
      find_imprecise_between bounds m


  let clear_caches () = List.iter (fun f -> f ()) !clear_caches_ref
end

(* Generic implementation of {Offsetmap_lattice_with_isotropy} for values
   that are all isotropic. *)
module FullyIsotropic = struct
  let is_isotropic _ = true
  let anisotropic_cast ~size:_ v = v

  let topify_with_origin _o v = v

  let extract_bits ~topify:_ ~start:_ ~stop:_ ~size:_ m = false, m
  let merge_distinct_bits ~topify:_ ~conflate_bottom:_ _ _ =
    assert false (* not called on isotropic values *)
  let shift_bits ~topify:_ ~offset:_ ~size:_ v = v
  let cardinal_zero_or_one _ = false

  let widen _wh _ m = m
  type numerical_widen_hint =  unit
  type size_widen_hint = Integer.t
  type widen_hint = size_widen_hint * numerical_widen_hint
end


(* -------------------------------------------------------------------------- *)
(* --- Intervals                                                          --- *)
(* -------------------------------------------------------------------------- *)

module Int_Intervals_Map = struct

  include Make(struct
      include Datatype.Bool

      let bottom = false
      let join = (||)
      let is_included b1 b2 = b2 || not b1
      let merge_neutral_element = bottom

      let pretty_typ _ fmt v = pretty fmt v

      include FullyIsotropic
    end)

  include Make_Narrow(struct
      let top = true
      let narrow = (&&)
    end)

  let () =
    imprecise_write_msg := "elements to enumerate"


  (* In this auxiliary module, intervals are pairs [(curr_off, m)] where [m]
     has type [bool Offsetmap.t]. However, in order to avoid boxing,
     functions sometimes take two arguments: first the current offset,
     then the map. *)
  type itvs = Int.t * t

  let join : itvs -> itvs -> itvs =
    let stop_join m1 m2 =
      if m1 == m2 then ReturnLeft (* idempotency *)
      (* true everywhere leads to true everywhere. false everywhere leads
         to the other tree. *)
      else match m1 with
        | Node (_, _, Empty, _, Empty, _ , _, b, _) ->
          if b then ReturnLeft else ReturnRight
        | _ ->
          match m2 with
          | Node (_, _, Empty, _, Empty, _ , _, b, _) ->
            if b then ReturnRight else ReturnLeft
          | _ -> Recurse
    in
    let cache = Hptmap_sig.PersistentCache "Int_Intervals.join" in
    map2_on_values_offset cache stop_join (||)

  let narrow : itvs -> itvs -> itvs =
    let stop_narrow m1 m2 =
      if m1 == m2 then ReturnLeft (* idempotency *)
      (* false everywhere leads to false everywhere. true everywhere leads
         to the other tree. *)
      else match m1 with
        | Node (_, _, Empty, _, Empty, _ , _, b, _) ->
          if b then ReturnRight else ReturnLeft
        | _ ->
          match m2 with
          | Node (_, _, Empty, _, Empty, _ , _, b, _) ->
            if b then ReturnLeft else ReturnRight
          | _ -> Recurse
    in
    let cache = Hptmap_sig.PersistentCache "Int_Intervals.narrow" in
    map2_on_values_offset cache stop_narrow (&&)

  let diff : itvs -> itvs -> itvs =
    let stop_diff m1 m2 =
      if m1 == m2 then ReturnConstant false
      else
        match m2 with
        | Node (_, _, Empty, _, Empty, _ , _, false, _) ->
          ReturnLeft (* diff with empty *)
        | _ -> Recurse
    in
    let cache = Hptmap_sig.PersistentCache "Int_Intervals.diff" in
    map2_on_values_offset
      cache stop_diff (fun b1 b2 -> if b2 then false else b1)


  (* Auxiliary function that binds [b] to the interval [min..max], which
     is assumed not to be bound in [m] *)
  let add_itv ~min ~max b co m : itvs =
    add_node ~min ~max Rel.zero Int.one b co m

  (* enlarges the offsetmap [m] from range [prev_min..prev_max] to
     [new_min..new_max], by adding an interval bound to [false] at the left
     and right ends. The inclusion [prev_min..prev_max \subset new_min..new_max]
     must hold *)
  let enlarge_itv co m ~prev_min ~new_min ~prev_max ~new_max : itvs =
    let co, m as i =
      if new_max >~ prev_max then
        add_itv ~min:(succ prev_max) ~max:new_max false co m
      else co, m
    in
    if new_min <~ prev_min then
      add_itv ~min:new_min ~max:(pred prev_min) false co m
    else i

  (* shrinks the offsetmap [m] from range [prev_min..prev_max] to
     [new_min..new_max], by dropping the superfluous intervals. The inclusion
     [new_min..new_max \subset prev_min..prev_max] must hold *)
  let shrink_itv co m ~prev_min ~new_min ~prev_max ~new_max : itvs =
    let co, m as i =
      if new_max <~ prev_max then
        keep_below ~offset:(succ new_max) co m
      else co, m
    in
    if new_min >~ prev_min then
      keep_above ~offset:(pred new_min) co m
    else i

  (* Resize size [m] to size [new_min..new_max], by enlarging or shrinking
     it on both ends. *)
  let resize_itv co m ~prev_min ~new_min ~prev_max ~new_max : itvs =
    let co, m as i =
      if new_max =~ prev_max then co, m
      else if new_max >~ prev_max then
        add_itv ~min:(succ prev_max) ~max:new_max false co m
      else (* new_max <~ prev_max *)
        keep_below ~offset:(succ new_max) co m
    in
    if new_min =~ prev_min then i
    else if new_min <~ prev_min then
      add_itv ~min:new_min ~max:(pred prev_min) false co m
    else (* new_min >~ prev_min *)
      keep_above ~offset:(pred new_min) co m


  (* normalizes a non-empty offsetmap [m], by removing an eventual rightmost
     interval bound to false. Returns the new rightmost bit bound to [true].*)
  let rec drop_righmost_false curr_off node =
    match node with
    | Empty -> assert false
    | Node (max, _, _, _, Empty, _, _, true, _) ->
      (* we are the rightmost interval, and not equal to false: no change *)
      curr_off, node, curr_off +~ max
    | Node (_, offl, subl, _, Empty, _, _, false, _) ->
      (* we are the rightmost interval, and false; keep only the left tree *)
      curr_off +~ offl, subl, pred curr_off
    | Node (max, offl, subl, offr, (Node _ as subr), _, _, v, _) ->
      (* Normalize the right tree and rebuild. *)
      let new_rcurr_off, new_rtree, rbit =
        drop_righmost_false (curr_off +~ offr) subr
      in
      (* We cannot have [v = false] and [new_rtree = empty]: [subr] would need
         contain only [false], and it should have been merged with us. *)
      if new_rtree == subr then
        curr_off, node, rbit
      else
        let curr_off', node' =
          make_node
            curr_off max offl subl (new_rcurr_off -~ curr_off) new_rtree
            Rel.zero Integer.one v
        in
        curr_off', node', rbit

  (* normalizes a non-empty offsetmap [m], by removing an eventual leftmost
     interval bound to false. Returns the new leftmost bit bound to [true].*)
  let rec drop_leftmost_false curr_off node =
    match node with
    | Empty -> assert false
    | Node (_, _, Empty, _, _, _, _, true, _) ->
      (* we are the leftmost interval, and not equal to false: no change *)
      curr_off, node, curr_off
    | Node (max, _, Empty, offr, subr, _, _, false, _) ->
      (* we are the leftmost interval, and false; keep only the right tree *)
      curr_off +~ offr, subr, succ (curr_off +~ max)
    | Node (max, offl, (Node _ as subl), offr, subr, _, _, v, _) ->
      (* normalize the left subtree and rebuild *)
      let new_lcurr_off, new_ltree, lbit =
        drop_leftmost_false (curr_off +~ offl) subl
      in
      if new_ltree == subl then
        curr_off, node, lbit
      else
        let curr_off', node' =
          make_node
            curr_off max (new_lcurr_off -~ curr_off) new_ltree offr subr
            Rel.zero Integer.one v
        in
        curr_off', node', lbit

end

module Int_Intervals = struct

  type intervals =
    | Top
    | Intervals of Int.t * Int_Intervals_Map.t * Int.t * Int.t
    (* The arguments of {!Intervals} are [curr_off, m, min, max] in this
       order. [min] and [max] are the the first and last bit bound to true
       in the map, which is supposed to be non-empty. All operations must
       maintain those two invariants. *)
    | Bottom

  let pretty_debug fmt t =
    match t with
    | Top -> Format.pp_print_string fmt "TopISet"
    | Bottom -> Format.pp_print_string fmt "BottomISet"
    | Intervals (curr_off, i, min, max) ->
      Format.fprintf fmt "@[I(%a-%a, @[%a])@]"
        Int.pretty min Int.pretty max
        Int_Intervals_Map.pretty_debug_offset (curr_off, i)

  include Datatype.Make(struct
      type t = intervals
      let name = "Int_Intervals.t"

      let pretty fmt t =
        match t with
        | Top -> Format.pp_print_string fmt "TopISet"
        | Bottom -> Format.pp_print_string fmt "BottomISet"
        | Intervals (curr_off, i, _, _) ->
          let first = ref true in
          Format.fprintf fmt "@[<hov >{";
          Int_Intervals_Map.iter_offset
            (fun (b, e) (v, _, _) ->
               if v then begin
                 if !first then first := false else Format.pp_print_space fmt ();
                 Format.fprintf fmt "[%a..%a]" Int.pretty b Int.pretty e
               end
            ) curr_off i;
          Format.fprintf fmt "}@]"

      let hash = function
        | Top -> 37
        | Bottom -> 73
        | Intervals (curr_off, i, _, _) ->
          (* Ignore min and max, which are redundant with curr_off + i *)
          Int.hash curr_off + 143 * Int_Intervals_Map.hash i

      let equal i1 i2 = match i1, i2 with
        | Top, Top | Bottom, Bottom -> true
        | Intervals (curr_off1, i1, _, _), Intervals (curr_off2, i2, _, _) ->
          curr_off1 =~ curr_off2 && Int_Intervals_Map.equal i1 i2
        | (Top | Bottom | Intervals _), _ -> false

      let compare i1 i2 = match i1, i2 with
        | Bottom, Bottom
        | Top, Top -> 0
        | Intervals (curr_off1, i1, _, _), Intervals (curr_off2, i2, _, _) ->
          let c = Int.compare curr_off1 curr_off2 in
          if c = 0 then Int_Intervals_Map.compare i1 i2
          else c
        | Bottom, (Intervals _ | Top)
        | Intervals _, Top -> -1
        | Intervals _, Bottom | Top, (Bottom | Intervals _) -> 1

      let reprs = [Bottom; Top]
      let rehash = Datatype.identity
      (* type intervals =
         Top | Intervals of Int.t * Int_Intervals_Map.t * Int.t * Int.t| Bottom *)
      let structural_descr =
        Structural_descr.t_sum
          [| [| Int.packed_descr; Int_Intervals_Map.packed_descr;
                Int.packed_descr; Int.packed_descr |] |]

      let mem_project = Datatype.never_any_project
      let copy = Datatype.undefined
    end)

  let top = Top
  let bottom = Bottom

  let is_top = function
    | Top -> true
    | _ -> false

  let aux_create_interval ~min ~max v =
    (* Use [min] as current offset *)
    Int_Intervals_Map.add_itv ~min ~max v min Int_Intervals_Map.m_empty

  let inject_bounds min max =
    if Int.gt min max then
      Bottom
    else
      let curr_off, i = aux_create_interval ~min ~max true in
      Intervals (curr_off, i, min, max)

  let inject_itv (b, e) = inject_bounds b e

  let is_included i1 i2 = match i1, i2 with
    | Bottom, Bottom
    | Top, Top
    | Bottom, (Intervals _ | Top)
    | Intervals _, Top ->
      true
    | Intervals (co1, i1, min1, max1),
      Intervals (co2, i2, min2, max2) ->
      min1 >=~ min2 && max1 <=~ max2 &&
      Int_Intervals_Map.is_included_aux (co1, i1) (co2, i2)
    | Intervals _, Bottom | Top, (Bottom | Intervals _) -> false

  let join m1 m2 =
    match m1, m2 with
    | Top, _ | _, Top -> Top
    | Bottom, i | i, Bottom -> i
    | Intervals (co1, i1, min1, max1), Intervals (co2, i2, min2, max2) ->
      let new_min = Int.min min1 min2 in
      let new_max = Int.max max1 max2 in
      (* Enlarge both intervals to the largest bounds. *)
      let coi1' =
        Int_Intervals_Map.enlarge_itv
          co1 i1 ~prev_min:min1 ~new_min ~prev_max:max1 ~new_max
      in
      let coi2' =
        Int_Intervals_Map.enlarge_itv
          co2 i2 ~prev_min:min2 ~new_min ~prev_max:max2 ~new_max
      in
      (* No need to normalize, the leftmost and rightmost bits are still there*)
      let co, i = Int_Intervals_Map.join coi1' coi2' in
      Intervals (co, i, new_min, new_max)

  let link = join  (* all constructors but Top, which is never returned,
                       are exact. *)

  (* Drop the leftmost and rightmost intervals if they are equal to
     [false], and detect if the result is [Bottom] *)
  let normalize_itv curr_off m =
    match m with
    | Empty | Node (_, _, Empty, _, Empty, _ ,_, false, _) -> Bottom
    | Node _ ->
      let curr_off, m, right_bit =
        Int_Intervals_Map.drop_righmost_false curr_off m
      in
      let curr_off, m, left_bit =
        Int_Intervals_Map.drop_leftmost_false curr_off m
      in
      if m == Empty then Bottom
      else (Intervals (curr_off, m, left_bit, right_bit))

  let narrow m1 m2 =
    match m1, m2 with
    | Bottom, _ | _, Bottom -> Bottom
    | Top, i | i, Top -> i
    | Intervals (co1, i1, min1, max1), Intervals (co2, i2, min2, max2) ->
      if min1 >~ max2 || min2 >~ max1 then Bottom
      else
        (* Keep only the part common to both intervals *)
        let new_min = Int.max min1 min2 in
        let new_max = Int.min max1 max2 in
        let coi1' =
          Int_Intervals_Map.shrink_itv
            co1 i1 ~prev_min:min1 ~new_min ~prev_max:max1 ~new_max
        in
        let coi2' =
          Int_Intervals_Map.shrink_itv
            co2 i2 ~prev_min:min2 ~new_min ~prev_max:max2 ~new_max
        in
        let co, i = Int_Intervals_Map.narrow coi1' coi2' in
        (* Normalize *)
        normalize_itv co i

  let meet = narrow (* all constructors but Top, which is never returned,
                       are exact. *)

  let intersects_map =
    let rec aux (o1, t1) (o2, t2) =
      match t1, t2 with
      | Empty, Empty | Empty, _ | _, Empty -> false

      | Node (_, offl1, subl1, offr1, subr1, _, _, false, _), Node _ ->
        aux (o1 +~ offl1, subl1) (o2, t2) || aux (o1 +~ offr1, subr1) (o2, t2)

      | Node _, Node (_, offl2, subl2, offr2, subr2, _, _, false, _) ->
        aux (o1, t1) (o2 +~ offl2, subl2) || aux (o1, t1) (o2 +~ offr2, subr2)

      | Node (max1, offl1, subl1, offr1, subr1, _, _, true, _),
        Node (max2, offl2, subl2, offr2, subr2, _, _, true, _) ->
        if max1 +~ o1 <~ o2  then
          aux (o1, t1) (o2 +~ offl2, subl2) || aux (o1 +~ offr1, subr1) (o2, t2)
        else if o1 >~ max2 +~ o2 then
          aux (o1, t1) (o2 +~ offr2, subr2) || aux (o1 +~ offl1, subl1) (o2, t2)
        else true (* the two intervals have a non-empty intersection *)
    in
    aux
  ;;

  let intersects i1 i2 = match i1, i2 with
    | Top, Top | Top, Intervals _ | Intervals _, Top -> true
    | Bottom, Bottom | Bottom, (Top | Intervals _)
    | (Top | Intervals _), Bottom -> false
    | Intervals (co1, i1, min1, max1), Intervals (co2, i2, min2, max2) ->
      min1 <=~ max2 && min2 <=~ max1 && intersects_map (co1, i1) (co2, i2)

  let diff m1 m2 =
    match m1, m2 with
    | Bottom, _ -> Bottom
    | Top, (Bottom | Intervals _ | Top) -> Top
    | Intervals _, Top -> Bottom
    | Intervals _, Bottom -> m1
    | Intervals (co1, i1, min1, max1), Intervals (co2, i2, min2, max2) ->
      if max1 >~ max2 && min1 <~ min2 then
        (* The last bits of i1 will not be unset; grow i2 to the size of i1,
           then no need to renormalize afterwards . *)
        let coi2' =
          Int_Intervals_Map.enlarge_itv
            co2 i2 ~prev_min:min2 ~new_min:min1 ~prev_max:max2 ~new_max:max1
        in
        let co, i = Int_Intervals_Map.diff (co1, i1) coi2' in
        Intervals (co, i, min1, max1)
      else
        (* The result cannot be bigger than i1: resize i2 to the same of i1.
           But some bits may be diffed to false, we need to renormalize *)
        let coi2' =
          Int_Intervals_Map.resize_itv
            co2 i2 ~prev_min:min2 ~new_min:min1 ~prev_max:max2 ~new_max:max1
        in
        let co, i = Int_Intervals_Map.diff (co1, i1) coi2' in
        normalize_itv co i

  let fold f m acc =
    match m with
    | Bottom -> acc
    | Top -> raise Error_Top
    | Intervals (curr_off, i, _, _) ->
      let aux_itv itv (v, _, _) acc =
        if v then f itv acc else acc
      in
      Int_Intervals_Map.fold_offset aux_itv curr_off i acc

  (* Could be slightly improved *)
  let inject l =
    List.fold_left (fun acc itv -> join (inject_itv itv) acc) Bottom l

  let iter f m =
    match m with
    | Bottom -> ()
    | Top -> raise Error_Top
    | Intervals (curr_off, i, _, _) ->
      let aux_itv itv (v, _, _) =
        if v then f itv
      in
      Int_Intervals_Map.iter_offset aux_itv curr_off i

  let project_set i = List.rev (fold (fun x y -> x :: y) i [])

  let project_singleton m =
    match m with
    | Bottom | Top -> None
    | Intervals (curr_offset, i, _, _) ->
      match i with
      | Node (max, _, Empty, _, Empty, _, _, true, _) ->
        Some (curr_offset, curr_offset +~ max)
      | _ -> None

  let pretty_typ typ fmt i =
    let typ =
      match typ with
      | Some t -> t
      | None ->
        Cil_types.(TArray (TInt(IUChar,[]), None, []))
    in
    match i with
    | Top -> Format.pp_print_string fmt "[..]"
    | Bottom -> Format.fprintf fmt "[%s]" (Unicode.emptyset_string ())
    | Intervals _ ->
      let pp_one fmt (b,e)=
        assert (Int.le b e) ;
        ignore (Bit_utils.pretty_bits typ
                  ~use_align:false
                  ~align:Rel.zero
                  ~rh_size:Int.one
                  ~start:b ~stop:e fmt)
      in
      match project_singleton i with
      | Some itv -> pp_one fmt itv
      | None ->
        Pretty_utils.pp_iter ~pre:"@[<hov 1>{" ~sep:";@ " ~suf:"}@]"
          iter pp_one fmt i
  ;;

  (* Conversion from ival+size to integers. The result is cached, and
     over-approximated when the ival points to too many locations. *)
  let from_ival_size_over_cached =
    (* This function uses an internal cache *)
    let module Arg1 = struct include Ival let sentinel = bottom end in
    let module Arg2 = struct include Integer let sentinel = zero end in
    let module Result = struct type t = intervals let sentinel = bottom end in
    let module Cache = Binary_cache.Arity_Two(Arg1)(Arg2)(Result) in
    Int_Intervals_Map.(clear_caches_ref := Cache.clear :: !clear_caches_ref);
    add_plevel_hook Cache.clear;
    (* Uncached version *)
    let from_ival_size_aux ival size =
      (* Auxiliary function when [ival] is precise. The result will be contained
         in [min..start_max+size-1]. Create an englobing offsetmap, then update
         it for all intervals. *)
      let aux_min_max min start_max =
        if Int.(equal size zero) then Bottom else
          let max = pred (start_max +~ size) in
          let curr_off, ifalse = aux_create_interval ~min ~max false in
          let validity = Base.Known (min, max) in
          let curr_off', i =
            try
              Int_Intervals_Map.update_aux
                ~validity ~exact:true ~offsets:ival ~size true curr_off ifalse
            with Int_Intervals_Map.Update_Result_is_bottom ->
              assert false (* in bounds by construction *)
          in
          Intervals (curr_off', i, min, max)
      in
      try
        match Ival.min_and_max ival with
        | None, _ | _, None -> top
        | Some min, Some max -> aux_min_max min max
      with Error_Bottom -> bottom
    in
    Cache.merge from_ival_size_aux

  (* Over-approximation of the conversion of an ival+size to a set of
     intervals *)
  let from_ival_size ival size =
    match size with
    | Int_Base.Top -> top
    | Int_Base.Value size -> from_ival_size_over_cached ival size

  (* Under-approximation of the conversion of an ival+size to a set of
     intervals. Basically, we see if we are going to over-approximate (in which
     case we return Bottom). Otherwise, we use the over-approximating function,
     which is by definition exact in this case, and has a cache *)
  let from_ival_size_under ival size =
    match size with
    | Int_Base.Top -> Bottom (* imprecise *)
    | Int_Base.Value size ->
      if Ival.is_small_set ival
      then from_ival_size_over_cached ival size (* precise *)
      else
        match Ival.min_and_max ival with
        | None, _ | _, None -> Bottom (* imprecise *)
        | Some min, Some start_max ->
          (* See if using [from_ival_size] would cause an approximation *)
          let max = pred (start_max +~ size) in
          let validity = Base.Known (min, max) in
          let offsets = Tr_offset.trim_by_validity ival size validity in
          if Int_Intervals_Map.update_aux_tr_offsets_approximates offsets size
          then bottom (* imprecise *)
          else from_ival_size_over_cached ival size (* precise *)

  let range_covers_whole_type typ itvs =
    match project_singleton itvs with
    | Some (b, e) ->
      (try
         let s = Cil.bitsSizeOf typ in
         Int.equal b Int.zero && Int.equal e (Int.of_int (s-1))
       with Cil.SizeOfError _ -> false)
    | None -> false

  (* Interval bound in a zero-rooted offsetmap, expressed as a value of this
     module. Not currently exported *)
  let bounds_as_itv map =
    match map with
    | Empty -> bottom
    | Node _ ->
      let min, max = Int_Intervals_Map.bounds_offset Int.zero map in
      inject_bounds min max

  (* Although interval functions do not depend on the AST itself, there are
     numerous problems with not clearing the caches when the AST is reset.
     Hence, the weak hash table for boolean offsetmaps depends on Ast.self,
     and the caches are reset on an ast update. *)
  let () = Ast.add_hook_on_update
      (fun () ->
         (* Kernel.debug ~dkey:dkey_caches "Clearing interval caches"; *)
         Int_Intervals_Map.clear_caches ())

end

(* -------------------------------------------------------------------------- *)
(* --- Bitwise offsetmaps                                                 --- *)
(* -------------------------------------------------------------------------- *)


module Make_bitwise(V: sig
    include Lattice_type.Bounded_Join_Semi_Lattice
    include Lattice_type.With_Narrow with type t := t
    include Lattice_type.With_Top with type t := t
  end) = struct

  include Make(struct
      include V
      include FullyIsotropic
      let merge_neutral_element = bottom
      let pretty_typ _ fmt v = pretty fmt v
    end)

  type intervals = Int_Intervals.intervals

  let create = create_isotropic

  let v_size_mod v = (v, Int.one, Rel.zero)

  let add_binding_intervals ~validity ~exact itvs v m =
    try
      match Base.valid_range validity with
      | Base.Invalid_range -> `Bottom
      | Base.Valid_range None -> (* empty validity *) `Value m
      | Base.Valid_range (Some _) ->
        let clip = clip_by_validity validity in
        let aux_itv itv m =
          let itv = clip itv in
          if Int.le (fst itv) (snd itv) then
            add ~exact itv (v_size_mod v) m
          else m
        in
        `Value (Int_Intervals.fold aux_itv itvs m)
    with Error_Top ->
      update_imprecise_everywhere ~validity Origin.top v m

  let add_binding_ival ~validity ~exact offsets ~size v m =
    match size with
    | Int_Base.Value size ->
      update ~validity ~exact ~offsets ~size v m
    | Int_Base.Top ->
      update_imprecise_everywhere ~validity Origin.top v m

  let fold_itv ?direction ~entire f itv m acc =
    let f' itv (v, _, _) acc = f itv v acc in
    fold_between ?direction ~entire itv f' m acc

  let find = find_imprecise_between

  let find_iset ~validity itvs m =
    try
      let aux_itv i acc =  V.join acc (find i m) in
      Int_Intervals.fold aux_itv itvs V.bottom
    with Error_Top -> find_imprecise ~validity m

  module V_Hashtbl = FCHashtbl.Make(V)

  (* Map indexed by sorted lists of integers. Used by function [fold_fuse_same]
     below, to sort bound values by increasing intervals. *)
  module MapIntervals =
    Map.Make(struct
      type t = (Int.t * Int.t) list
      let compare_itv (b1, e1) (b2, e2) =
        let c = Integer.compare b1 b2 in
        if c = 0
        then Integer.compare e1 e2
        else c
      let compare = Extlib.list_compare compare_itv
    end)

  let fold_fuse_same f m acc =
    let h = V_Hashtbl.create 17 in
    (* Map the various values in m to the intervals they appear in*)
    let sort_by_content itv (v, _, _) () =
      let cur =
        try V_Hashtbl.find h v
        with Not_found -> []
      in
      V_Hashtbl.replace h v (itv :: cur)
    in
    fold sort_by_content m ();
    (* Now sort the contents of h by increasing intervals *)
    let m = V_Hashtbl.fold
        (fun v itvs acc -> MapIntervals.add (List.rev itvs) v acc)
        h MapIntervals.empty
    in
    (* Call f on those intervals *)
    MapIntervals.fold
      (fun itvs v acc -> f (Int_Intervals.inject itvs) v acc) m acc

  let fold f m acc =
    let f' (ib, ie) (v, _, _) acc =
      let itv = Int_Intervals.inject_bounds ib ie in
      f itv v acc
    in
    fold f' m acc

  let default_skip _ = false

  let pretty_generic ?typ ?(pretty_v=V.pretty) ?(skip_v=default_skip) ?(sep="<:") () fmt m =
    let range_covers_whole_type itvs =
      match typ with
      | None -> false
      | Some typ -> Int_Intervals.range_covers_whole_type typ itvs
    in
    let pp_itv = Int_Intervals.pretty_typ typ in
    let first = ref true in
    let pretty_binding fmt itvs v () =
      if not (skip_v v) then begin
        if !first then first := false else Format.fprintf fmt "@," ;
        Format.fprintf fmt "@[<hv>@[%a@]%(%)@[%s @[%a@]@]@]"
          pp_itv itvs
          (if range_covers_whole_type itvs
           then (" ": (unit,Format.formatter,unit) format) else "@ ")
          sep pretty_v v
      end
    in
    Format.fprintf fmt "@[<v>";
    fold_fuse_same (pretty_binding fmt) m ();
    if !first then Format.fprintf fmt "%s@ %s" sep (Unicode.emptyset_string ());
    Format.fprintf fmt "@]"

  let map = map_on_values
  let map2 = map2_on_values

  (* Simultaneous recursive descent on an offsetmap bitwise and on an interval
     map. This function handles the case where the intervals and the offsetmap
     do not cover the same range. *)
  let fold_join_itvs_map_offset cache (type r) f join empty =
    let module R = struct type t = r let sentinel = empty end in
    let merge = match cache with
      | Hptmap_sig.PersistentCache _ | Hptmap_sig.TemporaryCache _ ->
        let module Cache =
          Binary_cache.Arity_Two(Cacheable)(Int_Intervals_Map.Cacheable)(R)
        in
        (match cache with
         | Hptmap_sig.PersistentCache _ ->
           clear_caches_ref := Cache.clear :: !clear_caches_ref
         | _ -> ());
        Cache.merge
      | Hptmap_sig.NoCache -> fun f x y -> f x y
    in
    let rec aux cache (o1, t1) (o2, t2) =
      match t1, t2 with
      | Empty, _ | _, Empty
      | _, Node (_, _, Empty, _, Empty, _, _, false, _) ->
        empty (* Notice that we do not present to [f] the intervals that
                 are present in [o2] but not in [o1] (i.e. in the zone but
                 not in the map). For the current users of this module,
                 the map is always of the size of the validity of the base,
                 hence this is not a problem. *)
      | _, Node (_, _, Empty, offr2, (Node _ as subr2), _, _, false, _) ->
        aux cache (o1, t1) (o2 +~ offr2, subr2)
      | _, Node (_, offl2, (Node _ as subl2), _, Empty, _, _, false, _) ->
        aux cache (o1, t1) (o2 +~ offl2, subl2)
      | _, Node (_, offl2, (Node _ as subl2), offr2, (Node _ as subr2),
                 _, _, false, _) ->
        (* This special case seems redundant with the ones above and the next
           one, but it speeds up dramatically this function. Otherwise, we
           would recurse on t1 until the interval bound to false is split in
           many small parts, without never adding anything. *)
        join
          (cache (o1, t1) (o2 +~ offl2, subl2))
          (cache (o1, t1) (o2 +~ offr2, subr2))
      | Node (max1, offl1, subl1, offr1, subr1, _, _, v, _),
        Node (max2, offl2, subl2, offr2, subr2, _, _, true, _) ->
        let amin1 = o1 in
        let amax1 = max1 +~ o1 in
        let amin2 = o2 in
        let amax2 = max2 +~ o2 in
        let ol1 = o1 +~ offl1 in
        let ol2 = o2 +~ offl2 in
        let or1 = o1 +~ offr1 in
        let or2 = o2 +~ offr2 in
        if amax1 <~ amin2  then begin
          join (cache (o1, t1) (ol2, subl2)) (cache (or1, subr1) (o2, t2))
        end else if amin1 >~ amax2 then begin
          join (cache (o1, t1) (or2, subr2)) (cache (ol1, subl1) (o2, t2))
        end else begin
          if amin1 =~ amin2 then begin
            let foo =
              if amax1 =~ amax2 then begin
                join (f amin1 amax1 v) (cache (or1, subr1) (or2, subr2))
              end
              else if amax1 >~ amax2 then begin
                join (f amin1 amax2 v) (cache (o1, t1) (or2, subr2))
              end
              else begin
                join (f amin1 amax1 v) (cache (or1, subr1) (o2, t2))
              end
            in
            join foo (cache (ol1, subl1) (ol2, subl2))
          end
          else
            let treat_right_nodes mabs_min =
              if amax1 =~ amax2 then begin
                join (f mabs_min amax1 v) (cache (or1, subr1) (or2, subr2))
              end
              else if amax1 >~ amax2 then begin
                join (f mabs_min amax2 v) (cache (o1, t1) (or2, subr2))
              end
              else begin
                join (f mabs_min amax1 v) (cache (or1, subr1) (o2, t2))
              end
            in
            if amin1 >~ amin2 then begin
              join (treat_right_nodes amin1) (cache (ol1, subl1) (o2, t2))
            end
            else begin
              join (treat_right_nodes amin2) (cache (o1, t1) (ol2, subl2))
            end
        end
    and compute (_, t1 as v1) (_, t2 as v2) =
      if t1 == Empty || t2 == Empty then empty
      else
        merge (aux compute) v1 v2
    in
    compute
  ;;

  (* Simultaneous recursive descent on an offsetmap bitwise and on an
     interval. *)
  let fold_join_itvs ~cache f join empty =
    (* fold_join on non-degenerate intervals. Partial application is important*)
    let aux_intervals = fold_join_itvs_map_offset cache f join empty in
    fun itvs m ->
      match itvs with
      | Int_Intervals.Bottom -> empty
      | Int_Intervals.Intervals (curr_off, itvs, _, _) ->
        aux_intervals (Int.zero, m) (curr_off, itvs)
      | Int_Intervals.Top ->
        (* Find the range that is bound in [m], and use this as interval.
           We would not return anything outside anyway. *)
        match Int_Intervals.bounds_as_itv m with
        | Int_Intervals.Bottom -> empty
        | Int_Intervals.Intervals (curr_off, itvs, _, _) ->
          aux_intervals (Int.zero, m) (curr_off, itvs)
        | Int_Intervals.Top -> assert false

end

[@@@warning "-60"]
(* The module below is template destined to be copy-pasted. We need to silence
   the warning about unused modules. *)

module Aux
    (V1 : Offsetmap_lattice_with_isotropy.S)
    (V2 : Offsetmap_lattice_with_isotropy.S)
= struct

  module M1 = Make(V1)
  module M2 = Make(V2)

  (* This function is there as a template for people wanting to write a fold-like
      iterator on two offsetmaps simultaneously. [bounds o1 t1 = bounds o2 t2]
      need not to hold; the function returns [empty] when the maps
     have no overlap. Currently, this functor  is not exported. *)
  let _map_fold2 (type s) (type t) f join empty o1 (t1: s offsetmap) o2 (t2: t offsetmap) =
    let rec aux  (o1, t1) (o2, t2) =
      match t1, t2 with
      | Empty, Empty -> empty
      | Empty, _ | _, Empty -> assert false
      | Node (max1, offl1, subl1, offr1, subr1, _, _, v1, _),
        Node (max2, offl2, subl2, offr2, subr2, _, _, v2, _) ->
        let amin1 = o1 in
        let amax1 = max1 +~ o1 in
        let amin2 = o2 in
        let amax2 = max2 +~ o2 in
        let ol1 = o1 +~ offl1 in
        let ol2 = o2 +~ offl2 in
        let or1 = o1 +~ offr1 in
        let or2 = o2 +~ offr2 in
        if amax1 <~ amin2  then begin
          join (aux (o1, t1) (ol2, subl2)) (aux (or1, subr1) (o2, t2))
        end else if amin1 >~ amax2 then begin
          join (aux (o1, t1) (or2, subr2)) (aux (ol1, subl1) (o2, t2))
        end else begin
          if amin1 =~ amin2 then begin
            let foo =
              if amax1 =~ amax2 then begin
                join (f amin1 amax1 v1 v2) (aux (or1, subr1) (or2, subr2))
              end
              else if amax1 >~ amax2 then begin
                join (f amin1 amax2 v1 v2) (aux (o1, t1) (or2, subr2))
              end
              else begin
                join (f amin1 amax1 v1 v2) (aux (or1, subr1) (o2, t2))
              end
            in
            join foo (aux (ol1, subl1) (ol2, subl2))
          end
          else
            let treat_right_nodes mabs_min =
              if amax1 =~ amax2 then begin
                join (f mabs_min amax1 v1 v2) (aux (or1, subr1) (or2, subr2))
              end
              else if amax1 >~ amax2 then begin
                join (f mabs_min amax2 v1 v2) (aux (o1, t1) (or2, subr2))
              end
              else begin
                join (f mabs_min amax1 v1 v2) (aux (or1, subr1) (o2, t2))
              end;
            in
            if amin1 >~ amin2 then begin
              join (treat_right_nodes amin1) (aux (ol1, subl1) (o2, t2))
            end
            else begin
              join (treat_right_nodes amin2) (aux (o1, t1) (ol2, subl2))
            end
        end
    in
    aux (o1, t1) (o2, t2)
  ;;

end

[@@@warning "+60"]

(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)