let rec mk_sub n i j a =
  assert (0 <= i && j < n && n >= 0);
  if i = 0 && j = n-1 then 
    a 
  else if j < i then
    mk_eps
  else 
    match d_interp a with
      | Some(Const(b), []) -> 
          mk_const(Bitv.sub b i (j-i+1))
      | Some(Sub(m,k,l), [x]) ->
          mk_sub m (k+i) (k+j) x
      | Some(Conc(n,m), [x;y]) ->
          if j < n then
            mk_sub n i j x
          else if n <= i then
            mk_sub m (i-n) (j-n) y
          else 
            (assert(i < n && n <= j);
            let t = mk_sub n i (n-1) x in
            let u = mk_sub m 0 (j-n) y in
            mk_conc (n-i) (j-n+1) t u)
      | Some(Bitwise(n), [x;y;z]) ->
          mk_bitwise (j-i+1) (mk_sub n i j x) (mk_sub n i j y) (mk_sub n i j z) 
      | None ->
          Term.mk_app (Bv(Sub(n, i, j))) [a]
      | Some _ ->
          failwith "Bv.mk_sub: ill-formed expression"
        
and mk_conc n m a b =
  assert (0 <= n && 0 <= m);
  match n = 0, m = 0 with
    | truetrue -> mk_eps
    | truefalse -> b
    | falsetrue -> a
    | falsefalse ->
        (match merge n m a b with
           | None -> Term.mk_app (Bv(Conc(n,m))) [a;b]
           | Some(c) -> c)

and merge n m a b =
  match d_interp a, d_interp b with
    | _, Some(Conc(m1,m2), [b1;b2]) -> 
        Some(mk_conc (n + m1) m2 (mk_conc n m1 a b1) b2)
    | Some(Conc(m1, m2), [b1; App(Bv(Const(c)), [])]), Some(Const(d), []) ->
        let n = Bitv.length d in
        Some(mk_conc m1 (m2 + n) b1 (mk_const (Bitv.append c d)))
    | Some(Const(c),[]), Some(Const(d),[]) -> 
        Some(mk_const (Bitv.append c d))
    | Some(Sub(n,i,j),[x]), Some(Sub(m,j',k), [y])
        when j' = j + 1 && Term.eq x y ->
        assert(n = m);
          Some(mk_sub n i k x)
    | Some(Bitwise(n1),[x1;y1;z1]), Some(Bitwise(n2),[x2;y2;z2]) ->
        (match merge n1 n2 x1 x2 with
           | None -> None
           | Some(x) ->
               (match merge n1 n2 y1 y2 with
                  | None -> None
                  | Some(y) ->
                      (match merge n1 n2 z1 z2 with
                         | None -> None
                         | Some(z) -> Some(mk_bitwise (n1+n2) x y z))))
    | _ -> 
        None
 

and mk_conc3 n m k a b c =
  mk_conc n (m + k) a (mk_conc m k b c)

and mk_bitwise n a b c =
  assert (n >= 0);
  if n = 0 then 
    mk_eps
  else 
    match d_const a, d_const b, d_const c with
      | Some(c1), Some(c2), Some(c3) ->
          mk_const (Bitv.bw_or (Bitv.bw_and c1 c2) (Bitv.bw_and (Bitv.bw_not c1) c3))
      | _ ->
          (match d_conc a, d_conc b, d_conc c with
             | Some(n1,n2,a1,a2), _, _ ->
                 assert(n = n1 + n2);
                 let b1,b2 = cut n n1 b in
                 let c1,c2 = cut n n1 c in 
                 mk_conc n1 n2 (mk_bitwise n1 a1 b1 c1) (mk_bitwise n2 a2 b2 c2)
             | _, Some(n1,n2,b1,b2), _ ->
                 assert(n = n1 + n2);
                 let a1,a2 = cut n n1 a in
                 let c1,c2 = cut n n1 c in 
                 mk_conc n1 n2 (mk_bitwise n1 a1 b1 c1) (mk_bitwise n2 a2 b2 c2)
             | _, _, Some(n1,n2,c1,c2) ->
                 assert(n = n1 + n2);
                 let a1,a2 = cut n n1 a in
                 let b1,b2 = cut n n1 b in 
                 mk_conc n1 n2 (mk_bitwise n1 a1 b1 c1) (mk_bitwise n2 a2 b2 c2)
             | _ ->
                 drop (build n (lift n a, lift n b, lift n c)))

and lift n a =
  if is_bvbdd a then 
    a 
  else
    Term.mk_app (Bv(Bitwise(n))) [a;mk_one n;mk_zero n]

and drop a =
 match d_bitwise a with
   | Some(_,b1,b2,b3) when is_one b2 && is_zero b3 -> b1
   | _ -> a

and cut n i a =
  (mk_sub n 0 (i - 1) a, mk_sub n i (n - 1) a)