Theory Unary_PCF

(*  Title:       Towards Decidability of Behavioral Equivalence for Unary PCF
    Author:      Dmitriy Traytel <traytel at inf.ethz.ch>, 2017
    Maintainer:  Dmitriy Traytel <traytel at inf.ethz.ch>
*)

section ‹Towards Decidability of Behavioral Equivalence for Unary PCF›

theory Unary_PCF
  imports
    "HOL-Library.FSet"
    "HOL-Library.Countable_Set_Type"
    "HOL-Library.Nat_Bijection"
    Hereditary_Multiset
    "List-Index.List_Index"
begin

subsection ‹Preliminaries›

lemma prod_UNIV: "UNIV = UNIV × UNIV"
  by auto

lemma infinite_cartesian_productI1: "infinite A  B  {}  infinite (A × B)"
  by (auto dest!: finite_cartesian_productD1)


subsection ‹Types›

datatype type =  ("") | Fun type type (infixr "" 65)

definition mk_fun  (infixr "→→" 65) where
  "Ts →→ T = fold (→) (rev Ts) T"

primrec dest_fun where
  "dest_fun  = []"
| "dest_fun (T  U) = T # dest_fun U"

definition arity where
  "arity T = length (dest_fun T)"

lemma mk_fun_dest_fun[simp]: "dest_fun T →→  = T"
  by (induct T) (auto simp: mk_fun_def)

lemma dest_fun_mk_fun[simp]: "dest_fun (Ts →→ T) = Ts @ dest_fun T"
  by (induct Ts) (auto simp: mk_fun_def)

primrec δ where
  "δ  = HMSet {#}"
| "δ (T  U) = HMSet (add_mset (δ T) (hmsetmset (δ U)))"

lemma δ_mk_fun: "δ (Ts →→ T) = HMSet (hmsetmset (δ T) + mset (map δ Ts))"
  by (induct Ts) (auto simp: mk_fun_def)

lemma type_induct [case_names Fun]:
  assumes
   "(T. (T1 T2. T = T1  T2  P T1) 
    (T1 T2. T = T1  T2  P T2)  P T)"
  shows "P T"
proof (induct T)
  case 
  show ?case by (rule assms) simp_all
next
  case Fun
  show ?case by (rule assms) (insert Fun, simp_all)
qed


subsection ‹Terms›

type_synonym name = string
type_synonym idx = nat
datatype expr =
    Var "name * type" ("_") | Bound idx | B bool
  | Seq expr expr  (infixr "?" 75) | App expr expr (infixl "" 75)
  | Abs type expr ("Λ⟨_ _" [100, 100] 800)

declare [[coercion_enabled]]
declare [[coercion B]]
declare [[coercion Bound]]

notation (output) B ("_")
notation (output) Bound ("_")

primrec "open" :: "idx  expr  expr  expr" where
  "open i t (j :: idx) = (if i = j then t else j)"
| "open i t yU = yU"
| "open i t (b :: bool) = b"
| "open i t (e1 ? e2) = open i t e1 ? open i t e2"
| "open i t (e1  e2) = open i t e1  open i t e2"
| "open i t (Λ⟨U e) = Λ⟨U (open (i + 1) t e)"

abbreviation "open0  open 0"
abbreviation "open_Var i xT  open i xT"
abbreviation "open0_Var xT  open 0 xT"

primrec "close_Var" :: "idx  name × type  expr  expr" where
  "close_Var i xT (j :: idx) = j"
| "close_Var i xT yU = (if xT = yU then i else yU)"
| "close_Var i xT (b :: bool) = b"
| "close_Var i xT (e1 ? e2) = close_Var i xT e1 ? close_Var i xT e2"
| "close_Var i xT (e1  e2) = close_Var i xT e1  close_Var i xT e2"
| "close_Var i xT (Λ⟨U e) = Λ⟨U (close_Var (i + 1) xT e)"

abbreviation "close0_Var  close_Var 0"

primrec "fv" :: "expr  (name × type) fset" where
  "fv (j :: idx) = {||}"
| "fv yU = {|yU|}"
| "fv (b :: bool) = {||}"
| "fv (e1 ? e2) = fv e1 |∪| fv e2"
| "fv (e1  e2) = fv e1 |∪| fv e2"
| "fv (Λ⟨U e) = fv e"

abbreviation "fresh x e  x |∉| fv e"

lemma ex_fresh: "x. (x :: char list, T) |∉| A"
proof (rule ccontr, unfold not_ex not_not)
  assume "x. (x, T) |∈| A"
  then have "infinite {x. (x, T) |∈| A}" (is "infinite ?P")
    by (auto simp add: infinite_UNIV_listI)
  moreover
  have "?P  fst ` fset A"
    by force
  from finite_surj[OF _ this] have "finite ?P"
    by simp
  ultimately show False by blast
qed

inductive lc where
  lc_Var[simp]: "lc xT"
| lc_B[simp]: "lc (b :: bool)"
| lc_Seq: "lc e1  lc e2  lc (e1 ? e2)"
| lc_App: "lc e1  lc e2  lc (e1  e2)"
| lc_Abs: "(x. (x, T) |∉| X  lc (open0_Var (x, T) e))  lc (Λ⟨T e)"

declare lc.intros[intro]

definition "body T t  (X. x. (x, T) |∉| X  lc (open0_Var (x, T) t))"

lemma lc_Abs_iff_body: "lc (Λ⟨T t)  body T t"
  unfolding body_def by (subst lc.simps) simp

lemma fv_open_Var: "fresh xT t  fv (open_Var i xT t) |⊆| finsert xT (fv t)"
  by (induct t arbitrary: i) auto

lemma fv_close_Var[simp]: "fv (close_Var i xT t) = fv t |-| {|xT|}"
  by (induct t arbitrary: i) auto

lemma close_Var_open_Var[simp]: "fresh xT t  close_Var i xT (open_Var i xT t) = t"
  by (induct t arbitrary: i) auto

lemma open_Var_inj: "fresh xT t  fresh xT u  open_Var i xT t = open_Var i xT u  t = u"
  by (metis close_Var_open_Var)

context begin

private lemma open_Var_open_Var_close_Var: "i  j  xT  yU  fresh yU t 
  open_Var i yU (open_Var j zV (close_Var j xT t)) = open_Var j zV (close_Var j xT (open_Var i yU t))"
  by (induct t arbitrary: i j) auto

lemma open_Var_close_Var[simp]: "lc t  open_Var i xT (close_Var i xT t) = t"
proof (induction t arbitrary: i rule: lc.induct)
  case (lc_Abs T X e i)
  obtain x where x: "fresh (x, T) e" "(x, T)  xT" "(x, T) |∉| X"
    using ex_fresh[of _ "fv e |∪| finsert xT X"] by blast
  with lc_Abs.IH have "lc (open0_Var (x, T) e)"
    "open_Var (i + 1) xT (close_Var (i + 1) xT (open0_Var (x, T) e)) = open0_Var (x, T) e"
    by auto
  with x show ?case
    by (auto simp: open_Var_open_Var_close_Var
      dest: fset_mp[OF fv_open_Var, rotated]
      intro!: open_Var_inj[of "(x, T)" _ e 0])
qed auto

end

lemma close_Var_inj: "lc t  lc u  close_Var i xT t = close_Var i xT u  t = u"
  by (metis open_Var_close_Var)

primrec Apps (infixl "" 75) where
  "f  [] = f"
| "f  (x # xs) = f  x  xs"

lemma Apps_snoc: "f  (xs @ [x]) = f  xs  x"
  by (induct xs arbitrary: f) auto

lemma Apps_append: "f  (xs @ ys) = f   xs   ys"
  by (induct xs arbitrary: f) auto

lemma Apps_inj[simp]: "f  ts = g  ts  f = g"
  by (induct ts arbitrary: f g) auto

lemma eq_Apps_conv[simp]:
  fixes i :: idx and b :: bool and f :: expr and ts :: "expr list"
  shows
    "(m = f  ts) = (m = f  ts = [])"
    "(f   ts = m) = (m = f  ts = [])"
    "(i = f  ts) = (i = f  ts = [])"
    "(f  ts = i) = (i = f  ts = [])"
    "(b = f  ts) = (b = f  ts = [])"
    "(f  ts = b) = (b = f  ts = [])"
    "(e1 ? e2 = f  ts) = (e1 ? e2 = f  ts = [])"
    "(f  ts = e1 ? e2) = (e1 ? e2 = f  ts = [])"
    "(Λ⟨T t = f  ts) = (Λ⟨T t = f  ts = [])"
    "(f  ts = Λ⟨T t) = (Λ⟨T t = f  ts = [])"
  by (induct ts arbitrary: f) auto

lemma Apps_Var_eq[simp]: "xT  ss = yU  ts  xT = yU  ss = ts"
proof (induct ss arbitrary: ts rule: rev_induct)
  case snoc
  then show ?case by (induct ts rule: rev_induct) (auto simp: Apps_snoc)
qed auto

lemma Apps_Abs_neq_Apps[simp, symmetric, simp]:
  "Λ⟨T r  t  xT  ss"
  "Λ⟨T r  t  (i :: idx)  ss"
  "Λ⟨T r  t  (b :: bool)  ss"
  "Λ⟨T r  t  (e1 ? e2)  ss"
  by (induct ss rule: rev_induct) (auto simp: Apps_snoc)

lemma App_Abs_eq_Apps_Abs[simp]: "Λ⟨T r  t = Λ⟨T' r'  ss  T = T'  r = r'  ss = [t]"
  by (induct ss rule: rev_induct) (auto simp: Apps_snoc)

lemma Apps_Var_neq_Apps_Abs[simp, symmetric, simp]: "xT  ss  Λ⟨T r  ts"
proof (induct ss arbitrary: ts rule: rev_induct)
  case (snoc a ss)
  then show ?case by (induct ts rule: rev_induct) (auto simp: Apps_snoc)
qed simp

lemma Apps_Var_neq_Apps_beta[simp, THEN not_sym, simp]:
  "xT  ss  Λ⟨T r  s  ts"
  by (metis Apps_Var_neq_Apps_Abs Apps_append Apps_snoc eq_Apps_conv(9))

lemma [simp]:
  "(Λ⟨T r  ts = Λ⟨T' r'  s'  ts') = (T = T'  r = r'  ts = s' # ts')"
proof (induct ts arbitrary: ts' rule: rev_induct)
  case Nil
  then show ?case by (induct ts' rule: rev_induct) (auto simp: Apps_snoc)
next
  case snoc
  then show ?case by (induct ts' rule: rev_induct) (auto simp: Apps_snoc)
qed

lemma fold_eq_Bool_iff[simp]:
  "fold (→) (rev Ts) T =   Ts = []  T = "
  " = fold (→) (rev Ts) T  Ts = []  T = "
  by (induct Ts) auto

lemma fold_eq_Fun_iff[simp]:
  "fold (→) (rev Ts) T = U  V 
   (Ts = []  T = U  V  (Us. Ts = U # Us  fold (→) (rev Us) T = V))"
  by (induct Ts) auto


subsection ‹Substitution›

primrec subst where
  "subst xT t yU = (if xT = yU then t else yU)"
| "subst xT t (i :: idx) = i"
| "subst xT t (b :: bool) = b"
| "subst xT t (e1 ? e2) = subst xT t e1 ? subst xT t e2"
| "subst xT t (e1  e2) = subst xT t e1  subst xT t e2"
| "subst xT t (Λ⟨T e) = Λ⟨T (subst xT t e)"

lemma fv_subst:
  "fv (subst xT t u) = fv u |-| {|xT|} |∪| (if xT |∈| fv u then fv t else {||})"
  by (induct u) auto

lemma subst_fresh: "fresh xT u  subst xT t u = u"
  by (induct u) auto

context begin

private lemma open_open_id: "i  j  open i t (open j t' u) = open j t' u  open i t u = u"
  by (induct u arbitrary: i j) (auto 6 0)

lemma lc_open_id: "lc u  open k t u = u"
proof (induct u arbitrary: k rule: lc.induct)
  case (lc_Abs T X e)
  obtain x where x: "fresh (x, T) e" "(x, T) |∉| X"
    using ex_fresh[of _ "fv e |∪| X"] by blast
  with lc_Abs show ?case
    by (auto intro: open_open_id dest: spec[of _ x] spec[of _ "Suc k"])
qed auto

lemma subst_open: "lc u  subst xT u (open i t v) = open i (subst xT u t) (subst xT u v)"
  by (induction v arbitrary: i) (auto intro: lc_open_id[symmetric])

lemma subst_open_Var:
  "xT  yU  lc u  subst xT u (open_Var i yU v) = open_Var i yU (subst xT u v)"
  by (auto simp: subst_open)

lemma subst_Apps[simp]:
  "subst xT u (f  xs) = subst xT u f  map (subst xT u) xs"
  by (induct xs arbitrary: f) auto

end

context begin

private lemma fresh_close_Var_id: "fresh xT t  close_Var k xT t = t"
  by (induct t arbitrary: k) auto

lemma subst_close_Var:
  "xT  yU  fresh yU u  subst xT u (close_Var i yU t) = close_Var i yU (subst xT u t)"
  by (induct t arbitrary: i) (auto simp: fresh_close_Var_id)

end

lemma subst_intro: "fresh xT t  lc u  open0 u t = subst xT u (open0_Var xT t)"
  by (auto simp: subst_fresh subst_open)

lemma lc_subst[simp]: "lc u  lc t  lc (subst xT t u)"
proof (induct u rule: lc.induct)
  case (lc_Abs T X e)
  then show ?case
    by (auto simp: subst_open_Var intro!: lc.lc_Abs[of _ "fv e |∪| X |∪| {|xT|}"])
qed auto

lemma body_subst[simp]: "body U u  lc t  body U (subst xT t u)"
proof (subst (asm) body_def, elim conjE exE)
  fix X
  assume [simp]: "lc t" "x. (x, U) |∉| X  lc (open0_Var (x, U) u)"
  show "body U (subst xT t u)"
  proof (unfold body_def, intro exI[of _ "finsert xT X"] conjI allI impI)
    fix x
    assume "(x, U) |∉| finsert xT X"
    then show "lc (open0_Var (x, U) (subst xT t u))"
      by (auto simp: subst_open_Var[symmetric])
  qed
qed

lemma lc_open_Var: "lc u  lc (open_Var i xT u)"
  by (simp add: lc_open_id)

lemma lc_open[simp]: "body U u  lc t  lc (open0 t u)"
proof (unfold body_def, elim conjE exE)
  fix X
  assume [simp]: "lc t" "x. (x, U) |∉| X  lc (open0_Var (x, U) u)"
  with ex_fresh[of _ "fv u |∪| X"] obtain x where [simp]: "fresh (x, U) u" "(x, U) |∉| X" by blast
  show ?thesis by (subst subst_intro[of "(x, U)"]) auto
qed


subsection ‹Typing›

inductive welltyped :: "expr  type  bool" (infix ":::" 60) where
  welltyped_Var[intro!]: "(x, T) ::: T"
| welltyped_B[intro!]: "(b :: bool) ::: "
| welltyped_Seq[intro!]: "e1 :::   e2 :::   e1 ? e2 ::: "
| welltyped_App[intro]: "e1 ::: T  U  e2 ::: T  e1  e2 ::: U"
| welltyped_Abs[intro]: "(x. (x, T) |∉| X  open0_Var (x, T) e ::: U)  Λ⟨T e ::: T  U"

inductive_cases welltypedE[elim!]:
   "x ::: T"
   "(i :: idx) ::: T"
   "(b :: bool) ::: T"
   "e1 ? e2 ::: T"
   "e1  e2 ::: T"
   "Λ⟨T e ::: U"

lemma welltyped_unique: "t ::: T  t ::: U  T = U"
proof (induction t T arbitrary: U rule: welltyped.induct)
  case (welltyped_Abs T X t U T')
  from welltyped_Abs.prems show ?case
  proof (elim welltypedE)
    fix Y U'
    obtain x where [simp]: "(x, T) |∉| X" "(x, T) |∉| Y"
      using ex_fresh[of _ "X |∪| Y"] by blast
    assume [simp]: "T' = T  U'" "x. (x, T) |∉| Y  open0_Var (x, T) t ::: U'"
    show "T  U = T'"
      by (auto intro: conjunct2[OF welltyped_Abs.IH[rule_format], rule_format, of x])
  qed
qed blast+

lemma welltyped_lc[simp]: "t ::: T  lc t"
  by (induction t T rule: welltyped.induct) auto

lemma welltyped_subst[intro]:
  "u ::: U  t ::: snd xT  subst xT t u ::: U"
proof (induction u U rule: welltyped.induct)
  case (welltyped_Abs T' X u U)
  then show ?case unfolding subst.simps
    by (intro welltyped.welltyped_Abs[of _ "finsert xT X"]) (auto simp: subst_open_Var[symmetric])
qed auto

lemma rename_welltyped: "u ::: U  subst (x, T) (y, T) u ::: U"
  by (rule welltyped_subst) auto

lemma welltyped_Abs_fresh:
  assumes "fresh (x, T) u" "open0_Var (x, T) u ::: U"
  shows "Λ⟨T u ::: T  U"
proof (intro welltyped_Abs[of _ "fv u"] allI impI)
  fix y
  assume "fresh (y, T) u"
  with assms(2) have "subst (x, T) (y, T) (open0_Var (x, T) u) ::: U" (is "?t ::: _")
    by (auto intro: rename_welltyped)
  also have "?t = open0_Var (y, T) u"
    by (subst subst_intro[symmetric]) (auto simp: assms(1))
  finally show "open0_Var (y, T) u ::: U" .
qed

lemma Apps_alt: "f  ts ::: T 
  (Ts. f ::: fold (→) (rev Ts) T  list_all2 (:::) ts Ts)"
proof (induct ts arbitrary: f)
  case (Cons t ts)
  from Cons(1)[of "f  t"] show ?case
    by (force simp: list_all2_Cons1)
qed simp


subsection ‹Definition 10 and Lemma 11 from Schmidt-Schau{\ss}'s paper›

abbreviation "closed t  fv t = {||}"

primrec constant0 where
  "constant0  = Var (''bool'', )"
| "constant0 (T  U) = Λ⟨T (constant0 U)"

definition "constant T = Λ⟨ (close0_Var (''bool'', ) (constant0 T))"

lemma fv_constant0[simp]: "fv (constant0 T) = {|(''bool'', )|}"
  by (induct T) auto

lemma closed_constant[simp]: "closed (constant T)"
  unfolding constant_def by auto

lemma welltyped_constant0[simp]: "constant0 T ::: T"
  by (induct T) (auto simp: lc_open_id)

lemma lc_constant0[simp]: "lc (constant0 T)"
  using welltyped_constant0 welltyped_lc by blast

lemma welltyped_constant[simp]: "constant T :::   T"
  unfolding constant_def by (auto intro: welltyped_Abs_fresh[of "''bool''"])

definition nth_drop where
  "nth_drop i xs  take i xs @ drop (Suc i) xs"

definition nth_arg (infixl "!-" 100) where
  "nth_arg T i  nth (dest_fun T) i"

abbreviation ar where
  "ar T  length (dest_fun T)"

lemma size_nth_arg[simp]: "i < ar T  size (T !- i) < size T"
  by (induct T arbitrary: i) (force simp: nth_Cons' nth_arg_def gr0_conv_Suc)+

fun π :: "type  nat  nat  type" where
  "π T i 0 = (if i < ar T then nth_drop i (dest_fun T) →→  else )"
| "π T i (Suc j) = (if i < ar T  j < ar (T!-i)
    then π (T!-i) j 0 
      map (π (T!-i) j o Suc) [0 ..< ar (T!-i!-j)] →→ π T i 0 else )"

theorem π_induct[rotated -2, consumes 2, case_names 0 Suc]:
  assumes "T i. i < ar T  P T i 0"
    and "T i j. i < ar T  j < ar (T !- i)  P (T !- i) j 0 
       (x < ar (T !- i !- j). P (T !- i) j (x + 1))  P T i (j + 1)"
  shows "i < ar T  j  ar (T !- i)  P T i j"
  by (induct T i j rule: π.induct) (auto intro: assms[simplified])

definition ε :: "type  nat  type" where
  "ε T i = π T i 0  map (π T i o Suc) [0 ..< ar (T!-i)] →→ T"

definition Abss ("Λ[_] _" [100, 100] 800) where
  "Λ[xTs] b = fold (λxT t. Λ⟨snd xT close0_Var xT t) (rev xTs) b"

definition Seqs (infixr "??" 75) where
  "ts ?? t = fold (λu t. u ? t) (rev ts) t"

definition "variant k base = base @ replicate k CHR ''*''"

lemma variant_inj: "variant i base = variant j base  i = j"
  unfolding variant_def by auto

lemma variant_inj2:
  "CHR ''*''  set b1  CHR ''*''  set b2  variant i b1 = variant j b2  b1 = b2"
  unfolding variant_def
  by (auto simp: append_eq_append_conv2)
    (metis Nil_is_append_conv hd_append2 hd_in_set hd_rev last_ConsR
     last_snoc replicate_append_same rev_replicate)+

fun E :: "type  nat  expr" and P :: "type  nat  nat  expr" where
  "E T i = (if i < ar T then (let
       Ti = T!-i;
       x = λk. (variant k ''x'', T!-k);
       xs = map x [0 ..< ar T];
       xx_var = nth xs i;
       x_vars = map (λx. x) (nth_drop i xs);
       yy = (''z'', π T i 0);
       yy_var = yy;
       y = λj. (variant j ''y'', π T i (j + 1));
       ys = map y [0 ..< ar Ti];
       e = λj. y j  (P Ti j 0  xx_var # map (λk. P Ti j (k + 1)  xx_var) [0 ..< ar (Ti!-j)]);
       guards = map (λi. xx_var 
           map (λj. constant (Ti!-j)  (if i = j then e i  x_vars else True)) [0 ..< ar Ti])
         [0 ..< ar Ti]
     in Λ[(yy # ys @ xs)] (guards ?? (yy_var  x_vars))) else constant (ε T i)  False)"
| "P T i 0 =
     (if i < ar T then (let
       f = (''f'', T);
       f_var = f;
       x = λk. (variant k ''x'', T!-k);
       xs = nth_drop i (map x [0 ..< ar T]);
       x_vars = insert_nth i (constant (T!-i)  True) (map (λx. x) xs)
     in Λ[(f # xs)] (f_var  x_vars)) else constant (T  π T i 0)  False)"
| "P T i (Suc j) = (if i < ar T  j < ar (T!-i) then (let
       Ti = T!-i;
       Tij = Ti!-j;
       f = (''f'', T);
       f_var = f;
       x = λk. (variant k ''x'', T!-k);
       xs = nth_drop i (map x [0 ..< ar T]);
       yy = (''z'', π Ti j 0);
       yy_var = yy;
       y = λk. (variant k ''y'', π Ti j (k + 1));
       ys = map y [0 ..< ar Tij];
       y_vars = yy_var # map (λx. x) ys;
       x_vars = insert_nth i (E Ti j  y_vars) (map (λx. x) xs)
     in Λ[(f # yy # ys @ xs)] (f_var  x_vars)) else constant (T  π T i (j + 1))  False)"

lemma Abss_Nil[simp]: "Λ[[]] b = b"
  unfolding Abss_def by simp

lemma Abss_Cons[simp]: "Λ[(x#xs)] b = Λ⟨snd x (close0_Var x (Λ[xs] b))"
  unfolding Abss_def by simp

lemma welltyped_Abss: "b ::: U  T = map snd xTs →→ U  Λ[xTs] b ::: T"
  by (hypsubst_thin, induct xTs) (auto simp: mk_fun_def intro!: welltyped_Abs_fresh)

lemma welltyped_Apps: "list_all2 (:::) ts Ts  f ::: Ts →→ U  f  ts ::: U"
  by (induct ts Ts arbitrary: f rule: list.rel_induct) (auto simp: mk_fun_def)

lemma welltyped_open_Var_close_Var[intro!]:
  "t ::: T  open0_Var xT (close0_Var xT t) ::: T"
  by auto

lemma welltyped_Var_iff[simp]:
  "(x, T) ::: U  T = U"
  by auto

lemma welltyped_bool_iff[simp]: "(b :: bool) ::: T  T = "
  by auto

lemma welltyped_constant0_iff[simp]: "constant0 T ::: U  (U = T)"
  by (induct T arbitrary: U) (auto simp: ex_fresh lc_open_id)

lemma welltyped_constant_iff[simp]: "constant T ::: U  (U =   T)"
  unfolding constant_def
proof (intro iffI, elim welltypedE, hypsubst_thin, unfold type.inject simp_thms)
  fix X U
  assume "x. (x, ) |∉| X  open0_Var (x, ) (close0_Var (''bool'', ) (constant0 T)) ::: U"
  moreover obtain x where "(x, ) |∉| X" using ex_fresh[of  X] by blast
  ultimately have "open0_Var (x, ) (close0_Var (''bool'', ) (constant0 T)) ::: U" by simp
  then have "open0_Var (''bool'', ) (close0_Var (''bool'', ) (constant0 T)) ::: U"
    using rename_welltyped[of open0_Var (x, ) (close0_Var (''bool'', ) (constant0 T))
       U x  "''bool''"]
    by (auto simp: subst_open subst_fresh)
  then show "U = T" by auto
qed (auto intro!: welltyped_Abs_fresh)

lemma welltyped_Seq_iff[simp]: "e1 ? e2 ::: T  (T =   e1 :::   e2 ::: )"
  by auto

lemma welltyped_Seqs_iff[simp]: "es ?? e ::: T 
  ((es  []  T = )  (e  set es. e ::: )  e ::: T)"
  by (induct es arbitrary: e) (auto simp: Seqs_def)

lemma welltyped_App_iff[simp]: "f  t ::: U  (T. f ::: T  U  t ::: T)"
  by auto

lemma welltyped_Apps_iff[simp]: "f  ts ::: U  (Ts. f ::: Ts →→ U  list_all2 (:::) ts Ts)"
  by (induct ts arbitrary: f) (auto 0 3 simp: mk_fun_def list_all2_Cons1 intro: exI[of _ "_ # _"])

lemma eq_mk_fun_iff[simp]: "T = Ts →→   Ts = dest_fun T"
  by auto

lemma map_nth_eq_drop_take[simp]: "j  length xs  map (nth xs) [i ..< j] = drop i (take j xs)"
  by (induct j) (auto simp: take_Suc_conv_app_nth)

lemma dest_fun_π_0: "i < ar T  dest_fun (π T i 0) = nth_drop i (dest_fun T)"
  by auto

lemma welltyped_E: "E T i ::: ε T i" and welltyped_P: "P T i j ::: T  π T i j"
proof (induct T i and T i j rule: E_P.induct)
  case (1 T i)
  note P.simps[simp del] π.simps[simp del] ε_def[simp] nth_drop_def[simp] nth_arg_def[simp]
  from 1(1)[OF _ refl refl refl refl refl refl refl refl refl]
       1(2)[OF _ refl refl refl refl refl refl refl refl refl]
  show ?case
    by (auto 0 4 simp: Let_def o_def take_map[symmetric] drop_map[symmetric]
      list_all2_conv_all_nth nth_append min_def dest_fun_π_0 π.simps[of T i]
      intro!: welltyped_Abs_fresh welltyped_Abss[of _ ])
next
  case (2 T i)
  show ?case
    by (auto simp: Let_def take_map drop_map o_def list_all2_conv_all_nth nth_append nth_Cons'
       nth_drop_def nth_arg_def
      intro!: welltyped_constant welltyped_Abs_fresh welltyped_Abss[of _ ])
next
  case (3 T i j)
  note E.simps[simp del] π.simps[simp del] Abss_Cons[simp del] ε_def[simp]
    nth_drop_def[simp] nth_arg_def[simp]
  from 3(1)[OF _ refl refl refl refl refl refl refl refl refl refl refl]
  show ?case
    by (auto 0 3 simp: Let_def o_def take_map[symmetric] drop_map[symmetric]
      list_all2_conv_all_nth nth_append nth_Cons' min_def π.simps[of T i]
      intro!: welltyped_Abs_fresh welltyped_Abss[of _ ])
qed

lemma δ_gt_0[simp]: "T    HMSet {#} < δ T"
  by (cases T) auto

lemma mset_nth_drop_less: "i < length xs  mset (nth_drop i xs) < mset xs"
  by (induct xs arbitrary: i) (auto simp: take_Cons' nth_drop_def gr0_conv_Suc)

lemma map_nth_drop: "i < length xs  map f (nth_drop i xs) = nth_drop i (map f xs)"
  by (induct xs arbitrary: i) (auto simp: take_Cons' nth_drop_def gr0_conv_Suc)

lemma empty_less_mset: "{#} < mset xs  xs  []"
  by auto

lemma dest_fun_alt: "dest_fun T = map (λi. T !- i) [0..<ar T]"
  unfolding list_eq_iff_nth_eq nth_arg_def by auto

context notes π.simps[simp del] notes One_nat_def[simp del] begin

lemma δ_π:
  assumes "i < ar T" "j  ar (T !- i)"
  shows "δ (π T i j) < δ T"
using assms proof (induct T i j rule: π_induct)
  fix T i
  assume "i < ar T"
  then show "δ (π T i 0) < δ T"
    by (subst (2) mk_fun_dest_fun[symmetric, of T], unfold δ_mk_fun)
      (auto simp: δ_mk_fun mset_map[symmetric] take_map[symmetric] drop_map[symmetric] π.simps
        mset_nth_drop_less map_nth_drop simp del: mset_map)
next
  fix T i j
  let ?Ti = "T !- i"
  assume [rule_format, simp]: "i < ar T" "j < ar ?Ti" "δ (π ?Ti j 0) < δ ?Ti"
    "k < ar (?Ti !- j). δ (π ?Ti j (k + 1)) < δ ?Ti"
  define X and Y and M where
    [simp]: "X = {#δ ?Ti#}" and
    [simp]: "Y = {#δ (π ?Ti j 0)#} + {#δ (π ?Ti j (k + 1)). k ∈# mset [0 ..< ar (?Ti !- j)]#}" and
    [simp]: "M  {# δ z. z ∈# mset (nth_drop i (dest_fun T))#}"
  have "δ (π T i (j + 1)) = HMSet (Y + M)"
    by (auto simp: One_nat_def π.simps δ_mk_fun)
  also have "Y + M < X + M"
    unfolding less_multisetDM by (rule exI[of _ "X"], rule exI[of _ "Y"]) auto
  also have "HMSet (X + M) = δ T"
    unfolding M_def
    by (subst (2) mk_fun_dest_fun[symmetric, of T], subst (2) id_take_nth_drop[of i "dest_fun T"])
      (auto simp: δ_mk_fun nth_arg_def nth_drop_def)
  finally show "δ (π T i (j + 1)) < δ T" by simp
qed

end

end