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 
    try
      (match d_interp a with
         | Sym.Const(b), [] -> 
             mk_const (Bitv.sub b i (j - i + 1))
         | Sym.Sub(m,k,l), [x] ->
             mk_sub m (k + i) (k + j) x
         | Sym.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)
         | _ ->
             failwith "Bv.mk_sub: ill-formed expression")
    with
        Not_found -> 
          Term.App.mk_app (Sym.Bv.mk_sub n i j) [a]
        
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 ->
        (try
           merge n m a b
         with
             Not_found -> Term.App.mk_app (Sym.Bv.mk_conc n m) [a; b])

and merge n m a b =
  match to_option d_interp a, to_option d_interp b with
    | _, Some(Sym.Conc(m1,m2), [b1;b2]) -> 
        mk_conc (n + m1) m2 (mk_conc n m1 a b1) b2
    | Some(Sym.Const(c),[]), Some(Sym.Const(d),[]) -> 
        mk_const (Bitv.append c d)
    | Some(Sym.Sub(n,i,j),[x]), Some(Sym.Sub(m,j',k), [y])
        when j' = j + 1 && Term.eq x y ->
        assert(n = m);
          mk_sub n i k x
    | Some(Sym.Conc(m1, m2), [b1; b2]), Some(Sym.Const(d), []) ->
        let c = d_const b2 in
        let n = Bitv.length d in
          mk_conc m1 (m2 + n) b1 (mk_const (Bitv.append c d))
    | _ -> 
        raise Not_found
 
and mk_conc3 n m k a b c =
  mk_conc n (m + k) a (mk_conc m k b c)

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