Theory AWN_Cterms

(*  Title:       AWN_Cterms.thy
    License:     BSD 2-Clause. See LICENSE.
    Author:      Timothy Bourke
*)

section "Control terms and well-definedness of sequential processes"

theory AWN_Cterms
imports AWN
begin

subsection "Microsteps "

text ‹
  We distinguish microsteps from `external' transitions (observable or not). Here, they are
  a kind of `hypothetical computation', since, unlike τ›-transitions, they do not make
  choices but rather `compute' which choices are possible.
›

inductive
  microstep :: "('s, 'm, 'p, 'l) seqp_env
                 ('s, 'm, 'p, 'l) seqp
                 ('s, 'm, 'p, 'l) seqp
                 bool"
for Γ :: "('s, 'm, 'p, 'l) seqp_env"
where
    microstep_choiceI1 [intro, simp]: "microstep Γ (p1  p2) p1"
  | microstep_choiceI2 [intro, simp]: "microstep Γ (p1  p2) p2"
  | microstep_callI [intro, simp]: "microstep Γ (call(pn)) (Γ pn)"

abbreviation microstep_rtcl
where "microstep_rtcl Γ p q  (microstep Γ)** p q"

abbreviation microstep_tcl
where "microstep_tcl Γ p q  (microstep Γ)++ p q"

syntax
  "_microstep"
     :: "[('s, 'm, 'p, 'l) seqp, ('s, 'm, 'p, 'l) seqp_env, ('s, 'm, 'p, 'l) seqp]  bool"
     ("(_) ↝⇘_ (_)" [61, 0, 61] 50)
  "_microstep_rtcl"
     :: "[('s, 'm, 'p, 'l) seqp, ('s, 'm, 'p, 'l) seqp_env, ('s, 'm, 'p, 'l) seqp]  bool"
     ("(_) ↝⇘_* (_)" [61, 0, 61] 50)
  "_microstep_tcl"
     :: "[('s, 'm, 'p, 'l) seqp, ('s, 'm, 'p, 'l) seqp_env, ('s, 'm, 'p, 'l) seqp]  bool"
     ("(_) ↝⇘_+ (_)" [61, 0, 61] 50)

translations
  "p1 ↝⇘Γp2"   "CONST microstep Γ p1 p2"
  "p1 ↝⇘Γ* p2"  "CONST microstep_rtcl Γ p1 p2"
  "p1 ↝⇘Γ+ p2"  "CONST microstep_tcl Γ p1 p2"

lemma microstep_choiceD [dest]:
  "(p1  p2) ↝⇘Γp  p = p1  p = p2"
  by (ind_cases "(p1  p2) ↝⇘Γp") auto

lemma microstep_choiceE [elim]:
  " (p1  p2) ↝⇘Γp;
     (p1  p2) ↝⇘Γp1  P;
     (p1  p2) ↝⇘Γp2  P   P"
  by (blast)

lemma microstep_callD [dest]:
  "(call(pn)) ↝⇘Γp  p = Γ pn"
  by (ind_cases "(call(pn)) ↝⇘Γp")

lemma microstep_callE [elim]:
  " (call(pn)) ↝⇘Γp;  p = Γ(pn)  P   P"
  by auto

lemma no_microstep_guard: "¬ (({l}g p) ↝⇘Γq)"
  by (rule notI) (ind_cases "({l}g p) ↝⇘Γq")

lemma no_microstep_assign: "¬ ({l}f p) ↝⇘Γq"
  by (rule notI) (ind_cases "({l}f p) ↝⇘Γq")

lemma no_microstep_unicast: "¬ (({l}unicast(sip, smsg).p  q) ↝⇘Γr)"
  by (rule notI) (ind_cases "({l}unicast(sip, smsg).p  q) ↝⇘Γr")

lemma no_microstep_broadcast: "¬ (({l}broadcast(smsg).p) ↝⇘Γq)"
  by (rule notI) (ind_cases "({l}broadcast(smsg).p) ↝⇘Γq")

lemma no_microstep_groupcast: "¬ (({l}groupcast(sips, smsg).p) ↝⇘Γq)"
  by (rule notI) (ind_cases "({l}groupcast(sips, smsg).p) ↝⇘Γq")

lemma no_microstep_send: "¬ (({l}send(smsg).p) ↝⇘Γq)"
  by (rule notI) (ind_cases "({l}send(smsg).p) ↝⇘Γq")

lemma no_microstep_deliver: "¬ (({l}deliver(sdata).p) ↝⇘Γq)"
  by (rule notI) (ind_cases "({l}deliver(sdata).p) ↝⇘Γq")

lemma no_microstep_receive: "¬ (({l}receive(umsg).p) ↝⇘Γq)"
  by (rule notI) (ind_cases "({l}receive(umsg).p) ↝⇘Γq")

lemma microstep_call_or_choice [dest]:
  assumes "p ↝⇘Γq"
    shows "(pn. p = call(pn))  (p1 p2. p = p1  p2)"
  using assms by clarsimp (metis microstep.simps)

lemmas no_microstep [intro,simp] =
  no_microstep_guard
  no_microstep_assign
  no_microstep_unicast
  no_microstep_broadcast
  no_microstep_groupcast
  no_microstep_send
  no_microstep_deliver
  no_microstep_receive

subsection "Wellformed process specifications "

text ‹
  A process specification Γ› is wellformed if its @{term "microstep Γ"} relation is
  free of loops and infinite chains.

  For example, these specifications are not wellformed:
    @{term "Γ1(p1) = call(p1)"}

    @{term "Γ2(p1) = send(msg).call(p1)  call(p1)"}

    @{term "Γ3(p1) = send(msg).call(p2)"}
    @{term "Γ3(p2) = call(p3)"}
    @{term "Γ3(p3) = call(p4)"}
    @{term "Γ3(p4) = call(p5)"}
    \ldots
›

definition
  wellformed :: "('s, 'm, 'p, 'l) seqp_env  bool"
where
  "wellformed Γ = wf {(q, p). p ↝⇘Γq}"

lemma wellformed_defP: "wellformed Γ = wfP (λq p. p ↝⇘Γq)"
  unfolding wellformed_def wfP_def by simp

text ‹
  The induction rule for @{term "wellformed Γ"} is stronger than @{thm seqp.induct} because
  the case for @{term "call(pn)"} can be shown with the assumption on @{term "Γ pn"}.
›

lemma wellformed_induct
  [consumes 1, case_names ASSIGN CHOICE CALL GUARD UCAST BCAST GCAST SEND DELIVER RECEIVE,
   induct set: wellformed]:
  assumes "wellformed Γ"
      and ASSIGN:  "l f p.          wellformed Γ  P ({l}f p)"
      and GUARD:   "l f p.          wellformed Γ  P ({l}f p)"
      and UCAST:   "l fip fmsg p q. wellformed Γ  P ({l}unicast(fip, fmsg). p  q)"
      and BCAST:   "l fmsg p.       wellformed Γ  P ({l}broadcast(fmsg). p)"
      and GCAST:   "l fips fmsg p.  wellformed Γ  P ({l}groupcast(fips, fmsg). p)"
      and SEND:    "l fmsg p.       wellformed Γ  P ({l}send(fmsg). p)"
      and DELIVER: "l fdata p.      wellformed Γ  P ({l}deliver(fdata). p)"
      and RECEIVE: "l fmsg p.       wellformed Γ  P ({l}receive(fmsg). p)"
      and CHOICE:  "p1 p2.         wellformed Γ; P p1; P p2   P (p1  p2)"
      and CALL:    "pn.            wellformed Γ; P (Γ pn)   P (call(pn))"
    shows "P a"
  using assms(1) unfolding wellformed_defP
  proof (rule wfP_induct_rule, case_tac x, simp_all)
    fix p1 p2
    assume "q. (p1  p2) ↝⇘Γq  P q"
    then obtain "P p1" and "P p2" by (auto intro!: microstep.intros)
    thus "P (p1  p2)" by (rule CHOICE [OF wellformed Γ])
  next
    fix pn
    assume "q. (call(pn)) ↝⇘Γq  P q"
    hence "P (Γ pn)" by (auto intro!: microstep.intros)
    thus "P (call(pn))" by (rule CALL [OF wellformed Γ])
  qed (auto intro: assms)

subsection "Start terms (sterms) "

text ‹
  Formulate sets of local subterms from which an action is directly possible. Since the
  process specification @{term "Γ"} is not considered, only choice terms @{term "p1  p2"}
  are traversed, and not @{term "call(p)"} terms.
›

fun stermsl :: "('s, 'm, 'p, 'l) seqp  ('s, 'm, 'p , 'l) seqp set"
where
    "stermsl (p1  p2) = stermsl p1  stermsl p2"
  | "stermsl p          = {p}"

lemma stermsl_nobigger: "q  stermsl p  size q  size p"
  by (induct p) auto

lemma stermsl_no_choice[simp]: "p1  p2  stermsl p"
  by (induct p) simp_all

lemma stermsl_choice_disj[simp]:
  "p  stermsl (p1  p2) = (p  stermsl p1  p  stermsl p2)"
  by simp

lemma stermsl_in_branch[elim]:
  "p  stermsl (p1  p2); p  stermsl p1  P; p  stermsl p2  P  P"
  by auto

lemma stermsl_commute:
  "stermsl (p1  p2) = stermsl (p2  p1)"
  by simp (rule Un_commute)

lemma stermsl_not_empty:
  "stermsl p  {}"
  by (induct p) auto

lemma stermsl_idem [simp]:
  "(qstermsl p. stermsl q) = stermsl p"
  by (induct p) simp_all

lemma stermsl_in_wfpf:
  assumes AA: "A  {(q, p). p ↝⇘Γq} `` A"
      and *: "p  A"
    shows "rstermsl p. r  A"
  using *
  proof (induction p)
    fix p1 p2
    assume IH1: "p1  A  rstermsl p1. r  A"
       and IH2: "p2  A  rstermsl p2. r  A"
       and *: "p1  p2  A"
    from * and AA have "p1  p2  {(q, p). p ↝⇘Γq} `` A" by auto
    hence "p1  A  p2  A" by auto
    hence "(rstermsl p1. r  A)  (rstermsl p2. r  A)"
      proof
        assume "p1  A" hence "rstermsl p1. r  A" by (rule IH1) thus ?thesis ..
      next
        assume "p2  A" hence "rstermsl p2. r  A" by (rule IH2) thus ?thesis ..
      qed
      hence "rstermsl p1  stermsl p2. r  A" by blast
      thus "rstermsl (p1  p2). r  A" by simp
    next case UCAST from UCAST.prems show ?case by auto
  qed auto

lemma nocall_stermsl_max:
  assumes "r  stermsl p"
      and "not_call r"
    shows "¬ (r ↝⇘Γq)"
  using assms
  by (induction p) auto

theorem wf_no_direct_calls[intro]:
    fixes Γ :: "('s, 'm, 'p, 'l) seqp_env"
  assumes no_calls: "pn. pn'. call(pn')  stermsl(Γ(pn))"
    shows "wellformed Γ"
  unfolding wellformed_def wfP_def
  proof (rule wfI_pf)
    fix A
    assume ARA: "A  {(q, p). p ↝⇘Γq} `` A"
    hence hasnext: "p. p  A  q. p ↝⇘Γq  q  A" by auto
    show "A = {}"
    proof (rule Set.equals0I)
      fix p assume "p  A" thus "False"
      proof (induction p)
        fix l f p'
        assume *: "{l}f p'  A"
        from hasnext [OF *] have "q. ({l}f p') ↝⇘Γq" by simp
        thus "False" by simp
      next
        fix p1 p2
        assume *: "p1  p2  A"
         and IH1: "p1  A  False"
         and IH2: "p2  A  False"
        have "q. (p1  p2) ↝⇘Γq  q  A" by (rule hasnext [OF *])
        hence "p1  A  p2  A" by auto
        thus "False" by (auto dest: IH1 IH2)
      next
        fix pn
        assume "call(pn)  A"
        hence "q. (call(pn)) ↝⇘Γq  q  A" by (rule hasnext)
        hence "Γ(pn)  A" by auto

        with ARA [THEN stermsl_in_wfpf] obtain q where "qstermsl (Γ pn)" and "q  A" by metis
        hence "not_call q" using no_calls [of pn]
          unfolding not_call_def by auto

        from hasnext [OF q  A] obtain q' where "q ↝⇘Γq'" by auto
        moreover from  q  stermsl (Γ pn) not_call q have "¬ (q ↝⇘Γq')"
          by (rule nocall_stermsl_max)
        ultimately show "False" by simp
      qed (auto dest: hasnext)
    qed
  qed

subsection "Start terms"

text ‹
  The start terms are those terms, relative to a wellformed process specification Γ›,
  from which transitions can occur directly.
›

function (domintros, sequential) sterms
  :: "('s, 'm, 'p, 'l) seqp_env  ('s, 'm, 'p, 'l) seqp  ('s, 'm, 'p, 'l) seqp set"
  where
    sterms_choice: "sterms Γ (p1  p2)  = sterms Γ p1  sterms Γ p2"
  | sterms_call:   "sterms Γ (call(pn))  = sterms Γ (Γ pn)"
  | sterms_other:  "sterms Γ p           = {p}"
  by pat_completeness auto

lemma sterms_dom_basic[simp]:
  assumes "not_call p"
      and "not_choice p"
    shows "sterms_dom (Γ, p)"
  proof (rule accpI)
    fix y
    assume "sterms_rel y (Γ, p)"
    with assms show "sterms_dom y"
      by (cases p) (auto simp: sterms_rel.simps)
  qed

lemma sterms_termination:
  assumes "wellformed Γ"
    shows "sterms_dom (Γ, p)"
  proof -
    have sterms_rel':
      "sterms_rel = (λgq gp. (gq, gp)  {((Γ, q), (Γ', p)). Γ = Γ'  p ↝⇘Γq})"
      by (rule ext)+ (auto simp: sterms_rel.simps elim: microstep.cases)

    from assms have "x. x  Wellfounded.acc {(q, p). p ↝⇘Γq}"
      unfolding wellformed_def by (simp add: wf_acc_iff)
    hence "p  Wellfounded.acc {(q, p). p ↝⇘Γq}" ..

    hence "(Γ, p)  Wellfounded.acc {((Γ, q), (Γ', p)). Γ = Γ'  p ↝⇘Γq}"
      by (rule acc_induct) (auto intro: accI)

    thus "sterms_dom (Γ, p)" unfolding sterms_rel' accp_acc_eq .
  qed

declare sterms.psimps [simp]

lemmas sterms_psimps[simp] = sterms.psimps [OF sterms_termination]
   and sterms_pinduct = sterms.pinduct [OF sterms_termination]

lemma sterms_reflD [dest]:
  assumes "q  sterms Γ p"
      and "not_choice p" "not_call p"
    shows "q = p"
  using assms by (cases p) auto

lemma sterms_choice_disj [simp]:
  assumes "wellformed Γ"
    shows "p  sterms Γ (p1  p2) = (p  sterms Γ p1  p  sterms Γ p2)"
  using assms by (simp)

lemma sterms_no_choice [simp]:
  assumes "wellformed Γ"
    shows "p1  p2  sterms Γ p"
  using assms by induction auto

lemma sterms_not_choice [simp]:
  assumes "wellformed Γ"
      and "q  sterms Γ p"
    shows "not_choice q"
  using assms unfolding not_choice_def
  by (auto dest: sterms_no_choice)

lemma sterms_no_call [simp]:
  assumes "wellformed Γ"
    shows "call(pn)  sterms Γ p"
  using assms by induction auto

lemma sterms_not_call [simp]:
  assumes "wellformed Γ"
      and "q  sterms Γ p"
    shows "not_call q"
  using assms unfolding not_call_def
  by (auto dest: sterms_no_call)

lemma sterms_in_branch:
  assumes "wellformed Γ"
      and "p  sterms Γ (p1  p2)"
      and "p  sterms Γ p1  P"
      and "p  sterms Γ p2  P"
  shows "P"
  using assms by auto

lemma sterms_commute:
  assumes "wellformed Γ"
    shows "sterms Γ (p1  p2) = sterms Γ (p2  p1)"
  using assms by simp (rule Un_commute)

lemma sterms_not_empty:
  assumes "wellformed Γ"
    shows "sterms Γ p  {}"
  using assms
  by (induct p rule: sterms_pinduct [OF wellformed Γ]) simp_all

lemma sterms_sterms [simp]:
  assumes "wellformed Γ"
    shows "(xsterms Γ p. sterms Γ x) = sterms Γ p"
  using assms by induction simp_all

lemma sterms_stermsl:
  assumes "ps  sterms Γ p"
      and "wellformed Γ"
    shows "ps  stermsl p  (pn. ps  stermsl (Γ pn))"
  using assms by (induction p rule: sterms_pinduct [OF wellformed Γ]) auto

lemma stermsl_sterms [elim]:
  assumes "q  stermsl p"
      and "not_call q"
      and "wellformed Γ"
    shows "q  sterms Γ p"
  using assms by (induct p) auto

lemma sterms_stermsl_heads:
  assumes "ps  sterms Γ (Γ pn)"
      and "wellformed Γ"
    shows "pn. ps  stermsl (Γ pn)"
  proof -
    from assms have "ps  stermsl (Γ pn)  (pn'. ps  stermsl (Γ pn'))"
      by (rule sterms_stermsl)
    thus ?thesis by auto
  qed

lemma sterms_subterms [dest]:
  assumes "wellformed Γ"
      and "pn. p  subterms (Γ pn)"
      and "q  sterms Γ p"
    shows "pn. q  subterms (Γ pn)"
  using assms by (induct p) auto

lemma no_microsteps_sterms_refl:
  assumes "wellformed Γ"
  shows "(¬(q. p ↝⇘Γq)) = (sterms Γ p = {p})"
  proof (cases p)
    fix p1 p2
    assume "p = p1  p2"
    from wellformed Γ have "p1  p2  sterms Γ (p1  p2)" by simp
    hence "sterms Γ (p1  p2)  {p1  p2}" by auto
    moreover have "q. (p1  p2) ↝⇘Γq" by auto
    ultimately show ?thesis
      using p = p1  p2 by simp
  next
    fix pn
    assume "p = call(pn)"
    from wellformed Γ have "call(pn)  sterms Γ (call(pn))" by simp
    hence "sterms Γ (call(pn))  {call(pn)}" by auto
    moreover have "q. (call(pn)) ↝⇘Γq" by auto
    ultimately show ?thesis
      using p = call(pn) by simp
  qed simp_all

lemma sterms_maximal [elim]:
  assumes "wellformed Γ"
      and "q  sterms Γ p"
    shows "sterms Γ q = {q}"
  using assms by (cases q) auto

lemma microstep_rtranscl_equal:
  assumes "not_call p"
      and "not_choice p"
      and "p ↝⇘Γ* q"
    shows "q = p"
  using assms(3) proof (rule converse_rtranclpE)
    fix p'
    assume "p ↝⇘Γp'"
    with assms(1-2) show "q = p"
      by (cases p) simp_all
  qed simp

lemma microstep_rtranscl_singleton [simp]:
  assumes "not_call p"
      and "not_choice p"
    shows "{q. p ↝⇘Γ* q  sterms Γ q = {q}} = {p}"
  proof (rule set_eqI)
    fix p'
    show "(p'  {q. p ↝⇘Γ* q  sterms Γ q = {q}}) = (p'  {p})"
    proof
      assume "p'  {q. p ↝⇘Γ* q  sterms Γ q = {q}}"
      hence "(microstep Γ)** p p'" and "sterms Γ p' = {p'}" by auto
      from this(1) have "p' = p"
      proof (rule converse_rtranclpE)
        fix q assume "p ↝⇘Γq"
        with not_call p and not_choice p have False
          by (cases p) auto
        thus "p' = p" ..
      qed simp
      thus "p'  {p}" by simp
    next
      assume "p'  {p}"
      hence "p' = p" ..
      with not_call p and not_choice p show "p'  {q. p ↝⇘Γ* q  sterms Γ q = {q}}"
        by (cases p) simp_all
    qed
  qed

theorem sterms_maximal_microstep:
  assumes "wellformed Γ"
    shows "sterms Γ p = {q. p ↝⇘Γ* q  ¬(q'. q ↝⇘Γq')}"
  proof
    from wellformed Γ have "sterms Γ p  {q. p ↝⇘Γ* q  sterms Γ q = {q}}"
    proof induction
     fix p1 p2
     assume IH1: "sterms Γ p1  {q. p1 ↝⇘Γ* q  sterms Γ q = {q}}"
        and IH2: "sterms Γ p2  {q. p2 ↝⇘Γ* q  sterms Γ q = {q}}"
     have "sterms Γ p1  {q. (p1  p2) ↝⇘Γ* q  sterms Γ q = {q}}"
     proof
       fix p'
       assume "p'  sterms Γ p1"
       with IH1 have "p1 ↝⇘Γ* p'" by auto
       moreover have "(p1  p2) ↝⇘Γp1" ..
       ultimately have "(p1  p2) ↝⇘Γ* p'"
         by - (rule converse_rtranclp_into_rtranclp)
       moreover from wellformed Γ and p'  sterms Γ p1 have "sterms Γ p' = {p'}" ..
       ultimately show "p'  {q. (p1  p2) ↝⇘Γ* q  sterms Γ q = {q}}"
         by simp
     qed
     moreover have "sterms Γ p2  {q. (p1  p2) ↝⇘Γ* q  sterms Γ q = {q}}"
     proof
       fix p'
       assume "p'  sterms Γ p2"
       with IH2 have "p2 ↝⇘Γ* p'" and "sterms Γ p' = {p'}" by auto
       moreover have "(p1  p2) ↝⇘Γp2" ..
       ultimately have "(p1  p2) ↝⇘Γ* p'"
         by - (rule converse_rtranclp_into_rtranclp)
       with sterms Γ p' = {p'} show "p'  {q. (p1  p2) ↝⇘Γ* q  sterms Γ q = {q}}"
         by simp
     qed
     ultimately show "sterms Γ (p1  p2)  {q. (p1  p2) ↝⇘Γ* q  sterms Γ q = {q}}"
       using wellformed Γ by simp
    next
      fix pn
      assume IH: "sterms Γ (Γ pn)  {q. Γ pn ↝⇘Γ* q  sterms Γ q = {q}}"
      show "sterms Γ (call(pn))  {q. (call(pn)) ↝⇘Γ* q  sterms Γ q = {q}}"
      proof
        fix p'
        assume "p'  sterms Γ (call(pn))"
        with wellformed Γ have "p'  sterms Γ (Γ pn)" by simp
        with IH have "Γ pn ↝⇘Γ* p'" and "sterms Γ p' = {p'}" by auto
        note this(1)
        moreover have "(call(pn)) ↝⇘ΓΓ pn" by simp
        ultimately have "(call(pn)) ↝⇘Γ* p'"
          by - (rule converse_rtranclp_into_rtranclp)
        with sterms Γ p' = {p'} show "p'  {q. (call(pn)) ↝⇘Γ* q  sterms Γ q = {q}}"
          by simp
      qed
    qed simp_all
    with wellformed Γ show "sterms Γ p  {q. p ↝⇘Γ* q  ¬(q'. q ↝⇘Γq')}"
      by (simp only: no_microsteps_sterms_refl)
  next
    from wellformed Γ have "{q. p ↝⇘Γ* q  sterms Γ q = {q}}  sterms Γ p"
    proof (induction)
      fix p1 p2
      assume IH1: "{q. p1 ↝⇘Γ* q  sterms Γ q = {q}}  sterms Γ p1"
         and IH2: "{q. p2 ↝⇘Γ* q  sterms Γ q = {q}}  sterms Γ p2"
      show "{q. (p1  p2) ↝⇘Γ* q  sterms Γ q = {q}}  sterms Γ (p1  p2)"
      proof (rule, drule CollectD, erule conjE)
        fix q'
        assume "(p1  p2) ↝⇘Γ* q'"
           and "sterms Γ q' = {q'}"
        with wellformed Γ have "(p1  p2) ↝⇘Γ+ q'"          
          by (auto dest!: rtranclpD sterms_no_choice)
        hence "p1 ↝⇘Γ* q'  p2 ↝⇘Γ* q'"
          by (auto dest: tranclpD)
        thus "q'  sterms Γ (p1  p2)"
        proof
          assume "p1 ↝⇘Γ* q'"
          with IH1 and sterms Γ q' = {q'} have "q'  sterms Γ p1" by auto
          with wellformed Γ show ?thesis by auto
        next
          assume "p2 ↝⇘Γ* q'"
          with IH2 and sterms Γ q' = {q'} have "q'  sterms Γ p2" by auto
          with wellformed Γ show ?thesis by auto
        qed
      qed
    next
      fix pn
      assume IH: "{q. Γ pn ↝⇘Γ* q  sterms Γ q = {q}}  sterms Γ (Γ pn)"
      show   "{q. (call(pn)) ↝⇘Γ* q  sterms Γ q = {q}}  sterms Γ (call(pn))"
      proof (rule, drule CollectD, erule conjE)
        fix q'
        assume "(call(pn)) ↝⇘Γ* q'"
           and "sterms Γ q' = {q'}"
        with wellformed Γ have "(call(pn)) ↝⇘Γ+ q'"
          by (auto dest!: rtranclpD sterms_no_call)
        moreover have "(call(pn)) ↝⇘ΓΓ pn" ..
        ultimately have "Γ pn ↝⇘Γ* q'"
          by (auto dest!: tranclpD)
        with sterms Γ q' = {q'} and IH have "q'  sterms Γ (Γ pn)" by auto
        with wellformed Γ show "q'  sterms Γ (call(pn))" by simp
      qed
    qed simp_all
    with wellformed Γ show "{q. p ↝⇘Γ* q  ¬(q'. q ↝⇘Γq')}  sterms Γ p"
    by (simp only: no_microsteps_sterms_refl)
  qed

subsection "Derivative terms "

text ‹
  The derivatives of a term are those @{term sterm}s potentially reachable by taking a
  transition, relative to a wellformed process specification Γ›. These terms
  overapproximate the reachable sterms, since the truth of guards is not considered.
›

function (domintros) dterms
  :: "('s, 'm, 'p, 'l) seqp_env  ('s, 'm, 'p, 'l) seqp  ('s, 'm, 'p, 'l) seqp set"
  where
    "dterms Γ ({l}g p)                     = sterms Γ p"
  | "dterms Γ ({l}u p)                     = sterms Γ p"
  | "dterms Γ (p1  p2)                     = dterms Γ p1  dterms Γ p2"
  | "dterms Γ ({l}unicast(sip, smsg).p  q)  = sterms Γ p  sterms Γ q"
  | "dterms Γ ({l}broadcast(smsg). p)        = sterms Γ p"
  | "dterms Γ ({l}groupcast(sips, smsg). p)  = sterms Γ p"
  | "dterms Γ ({l}send(smsg).p)              = sterms Γ p"
  | "dterms Γ ({l}deliver(sdata).p)          = sterms Γ p"
  | "dterms Γ ({l}receive(umsg).p)           = sterms Γ p"
  | "dterms Γ (call(pn))                     = dterms Γ (Γ pn)"
  by pat_completeness auto

lemma dterms_dom_basic [simp]:
  assumes "not_call p"
      and "not_choice p"
    shows "dterms_dom (Γ, p)"
  proof (rule accpI)
    fix y
    assume "dterms_rel y (Γ, p)"
    with assms show "dterms_dom y"
      by (cases p) (auto simp: dterms_rel.simps)
  qed

lemma dterms_termination:
  assumes "wellformed Γ"
    shows "dterms_dom (Γ, p)"
  proof -
    have dterms_rel': "dterms_rel = (λgq gp. (gq, gp)  {((Γ, q), (Γ', p)). Γ = Γ'  p ↝⇘Γq})"
      by (rule ext)+ (auto simp: dterms_rel.simps elim: microstep.cases)
    from wellformed(Γ) have "x. x  Wellfounded.acc {(q, p). p ↝⇘Γq}"
      unfolding wellformed_def by (simp add: wf_acc_iff)
    hence "p  Wellfounded.acc {(q, p). p ↝⇘Γq}" ..
    hence "(Γ, p)  Wellfounded.acc {((Γ, q), Γ', p). Γ = Γ'  p ↝⇘Γq}"
      by (rule acc_induct) (auto intro: accI)
    thus "dterms_dom (Γ, p)"
      unfolding dterms_rel' by (subst accp_acc_eq)
  qed

lemmas dterms_psimps [simp] = dterms.psimps [OF dterms_termination]
   and dterms_pinduct = dterms.pinduct [OF dterms_termination]

lemma sterms_after_dterms [simp]:
  assumes "wellformed Γ"
  shows "(xdterms Γ p. sterms Γ x) = dterms Γ p"
  using assms by (induction p) simp_all

lemma sterms_before_dterms [simp]:
  assumes "wellformed Γ"
  shows "(xsterms Γ p. dterms Γ x) = dterms Γ p"
  using assms by (induction p) simp_all

lemma dterms_choice_disj [simp]:
  assumes "wellformed Γ"
    shows "p  dterms Γ (p1  p2) = (p  dterms Γ p1  p  dterms Γ p2)"
  using assms by (simp)

lemma dterms_in_branch:
  assumes "wellformed Γ"
      and "p  dterms Γ (p1  p2)"
      and "p  dterms Γ p1  P"
      and "p  dterms Γ p2  P"
  shows "P"
  using assms by auto

lemma dterms_no_choice:
  assumes "wellformed Γ"
    shows "p1  p2  dterms Γ p"
  using assms by induction simp_all

lemma dterms_not_choice [simp]:
  assumes "wellformed Γ"
      and "q  dterms Γ p"
    shows "not_choice q"
  using assms unfolding not_choice_def
  by (auto dest: dterms_no_choice)

lemma dterms_no_call:
  assumes "wellformed Γ"
    shows "call(pn)  dterms Γ p"
  using assms by induction simp_all

lemma dterms_not_call [simp]:
  assumes "wellformed Γ"
      and "q  dterms Γ p"
    shows "not_call q"
  using assms unfolding not_call_def
  by (auto dest: dterms_no_call)

lemma dterms_subterms:
  assumes wf: "wellformed Γ"
      and "pn. p  subterms (Γ pn)"
      and "q  dterms Γ p"
    shows "pn. q  subterms (Γ pn)"
  using assms
  proof (induct p)
       fix p1 p2
    assume IH1: "pn. p1  subterms (Γ pn)  q  dterms Γ p1  pn. q  subterms (Γ pn)"
       and IH2: "pn. p2  subterms (Γ pn)  q  dterms Γ p2  pn. q  subterms (Γ pn)"
       and *: "pn. p1  p2  subterms (Γ pn)"
       and "q  dterms Γ (p1  p2)"
    from * obtain pn where "p1  p2  subterms (Γ pn)"
      by auto
    hence "p1  subterms (Γ pn)" and "p2  subterms (Γ pn)"
      by auto
    from q  dterms Γ (p1  p2) wf have "q  dterms Γ p1  q  dterms Γ p2"
      by auto
    thus "pn. q  subterms (Γ pn)"
      proof
        assume "q  dterms Γ p1"
        with p1  subterms (Γ pn) show ?thesis
          by (auto intro: IH1)
      next
        assume "q  dterms Γ p2"
        with p2  subterms (Γ pn) show ?thesis
          by (auto intro: IH2)
      qed
  qed auto

text ‹
  Note that the converse of @{thm dterms_subterms} is not true because @{term dterm}s are an
  over-approximation; i.e., we cannot show, in general, that guards return a non-empty set
  of post-states.
›

subsection "Control terms "

text ‹
  The control terms of a process specification @{term Γ} are those subterms from which
  transitions are directly possible. We can omit @{term "call(pn)"} terms, since
  the root terms of all processes are considered, and also @{term "p1  p2"} terms
  since they effectively combine the transitions of the subterms @{term p1} and
  @{term p2}.

  It will be shown that only the control terms, rather than all subterms, need be
  considered in invariant proofs.
›

inductive_set
  cterms :: "('s, 'm, 'p, 'l) seqp_env  ('s, 'm, 'p, 'l) seqp set"
  for Γ :: "('s, 'm, 'p, 'l) seqp_env"
where
    ctermsSI[intro]: "p  sterms Γ (Γ pn)  p  cterms Γ"
  | ctermsDI[intro]: " pp  cterms Γ; p  dterms Γ pp   p  cterms Γ"

lemma cterms_not_choice [simp]:
  assumes "wellformed Γ"
      and "p  cterms Γ"
    shows "not_choice p"
  using assms
  proof (cases p)
    case CHOICE from p  cterms Γ show ?thesis
      using wellformed Γ by cases simp_all
  qed simp_all

lemma cterms_no_choice [simp]:
  assumes "wellformed Γ"
    shows "p1  p2  cterms Γ"
  using assms by (auto dest: cterms_not_choice)

lemma cterms_not_call [simp]:
  assumes "wellformed Γ"
      and "p  cterms Γ"
    shows "not_call p"
  using assms
  proof (cases p)
    case CALL from p  cterms Γ show ?thesis
      using wellformed Γ by cases simp_all
  qed simp_all

lemma cterms_no_call [simp]:
  assumes "wellformed Γ"
    shows "call(pn)  cterms Γ"
  using assms by (auto dest: cterms_not_call)

lemma sterms_cterms [elim]:
  assumes "p  cterms Γ"
      and "q  sterms Γ p"
      and "wellformed Γ"
    shows "q  cterms Γ"
  using assms by - (cases p, auto)

lemma dterms_cterms [elim]:
  assumes "p  cterms Γ"
      and "q  dterms Γ p"
      and "wellformed Γ"
    shows "q  cterms Γ"
  using assms by (cases p) auto

lemma derivs_in_cterms [simp]:
  "l f p. {l}f p  cterms Γ                             sterms Γ p  cterms Γ"
  "l f p. {l}f p  cterms Γ                            sterms Γ p  cterms Γ"
  "l fip fmsg q p. {l}unicast(fip, fmsg). p  q  cterms Γ
                             sterms Γ p  cterms Γ  sterms Γ q  cterms Γ"
  "l fmsg p.      {l}broadcast(fmsg).p  cterms Γ        sterms Γ p  cterms Γ"
  "l fips fmsg p. {l}groupcast(fips, fmsg).p  cterms Γ  sterms Γ p  cterms Γ"
  "l fmsg p.      {l}send(fmsg).p  cterms Γ             sterms Γ p  cterms Γ"
  "l fdata p.     {l}deliver(fdata).p  cterms Γ         sterms Γ p  cterms Γ"
  "l fmsg p.      {l}receive(fmsg).p  cterms Γ          sterms Γ p  cterms Γ"
  by (auto simp: dterms.psimps)

subsection "Local control terms"

text ‹
  We introduce a `local' version of @{term cterms} that does not step through calls and,
  thus, that is defined independently of a process specification @{term Γ}.
  This allows an alternative, terminating characterisation of cterms as a set of
  subterms. Including @{term "call(pn)"}s in the set makes for a simpler relation with
  @{term "stermsl"}, even if they must be filtered out for the desired characterisation.
›

function
  ctermsl :: "('s, 'm, 'p, 'l) seqp  ('s, 'm, 'p , 'l) seqp set"
where
    "ctermsl ({l}g p)                     = insert ({l}g p)  (ctermsl p)"
  | "ctermsl ({l}u p)                     = insert ({l}u p)  (ctermsl p)"
  | "ctermsl ({l}unicast(sip, smsg). p  q) = insert ({l}unicast(sip, smsg). p  q)
                                                                      (ctermsl p  ctermsl q)"
  | "ctermsl ({l}broadcast(smsg). p)       = insert ({l}broadcast(smsg). p)       (ctermsl p)"
  | "ctermsl ({l}groupcast(sips, smsg). p)  = insert ({l}groupcast(sips, smsg). p) (ctermsl p)"
  | "ctermsl ({l}send(smsg). p)            = insert ({l}send(smsg). p)            (ctermsl p)"
  | "ctermsl ({l}deliver(sdata). p)        = insert ({l}deliver(sdata). p)        (ctermsl p)"
  | "ctermsl ({l}receive(umsg). p)         = insert ({l}receive(umsg). p)         (ctermsl p)"
  | "ctermsl (p1  p2)                    = ctermsl p1  ctermsl p2"
  | "ctermsl (call(pn))                   = {call(pn)}"
  by pat_completeness auto
  termination by (relation "measure(size)") (auto dest: stermsl_nobigger)

lemmas ctermsl_induct =
  ctermsl.induct [case_names GUARD ASSIGN UCAST BCAST GCAST
                             SEND DELIVER RECEIVE CHOICE CALL]

lemma ctermsl_refl [intro]: "not_choice p  p  ctermsl p"
  by (cases p) auto

lemma ctermsl_subterms:
  "ctermsl p = {q. q  subterms p  not_choice q }" (is "?lhs = ?rhs")
  proof
    show "?lhs  ?rhs" by (induct p, auto) next
    show "?rhs  ?lhs" by (induct p, auto)
  qed

lemma ctermsl_trans [elim]:
  assumes "q  ctermsl p"
      and "r  ctermsl q"
    shows "r  ctermsl p"
  using assms
  proof (induction p rule: ctermsl_induct)
    case (CHOICE p1 p2)
      have "(q  ctermsl p1)  (q  ctermsl p2)"
        using CHOICE.prems(1) by simp
      hence "r  ctermsl p1  r  ctermsl p2"
      proof (rule disj_forward)
        assume "q  ctermsl p1"
        thus "r  ctermsl p1" using r  ctermsl q by (rule CHOICE.IH)
      next
        assume "q  ctermsl p2"
        thus "r  ctermsl p2" using r  ctermsl q by (rule CHOICE.IH)
      qed
      thus "r  ctermsl (p1  p2)" by simp
    qed auto

lemma ctermsl_ex_trans [elim]:
  assumes "q  ctermsl p. r  ctermsl q"
    shows "r  ctermsl p"
  using assms by auto

lemma call_ctermsl_empty [elim]:
  " p  ctermsl p'; not_call p   not_call p'"
  unfolding not_call_def by (cases p) auto

lemma stermsl_ctermsl_choice1 [simp]:
  assumes "q  stermsl p1"
    shows "q  ctermsl (p1  p2)"
  using assms by (induction p1) auto

lemma stermsl_ctermsl_choice2 [simp]:
  assumes "q  stermsl p2"
    shows "q  ctermsl (p1  p2)"
  using assms by (induction p2) auto

lemma stermsl_ctermsl [elim]:
  assumes "q  stermsl p"
    shows "q  ctermsl p"
  using assms
  proof (cases p)
    case (CHOICE p1 p2)
    hence "q  stermsl (p1  p2)" using assms by simp
    hence "q  stermsl p1  q  stermsl p2" by simp
    hence "q  ctermsl (p1  p2)" by (rule) (simp_all del: ctermsl.simps)
    thus  "q  ctermsl p" using CHOICE by simp
  qed simp_all

lemma stermsl_after_ctermsl [simp]:
  "(xctermsl p. stermsl x) = ctermsl p"
  by (induct p) auto

lemma stermsl_before_ctermsl [simp]:
  "(xstermsl p. ctermsl x) = ctermsl p"
  by (induct p) simp_all

lemma ctermsl_no_choice: "p1  p2  ctermsl p"
  by (induct p) simp_all

lemma ctermsl_ex_stermsl: "q  ctermsl p  psstermsl p. q  ctermsl ps"
  by (induct p) auto

lemma dterms_ctermsl [intro]:
  assumes "q  dterms Γ p"
      and "wellformed Γ"
    shows "q  ctermsl p  (pn. q  ctermsl (Γ pn))"
  using assms(1-2)
  proof (induction p rule: dterms_pinduct [OF wellformed Γ])
    fix Γ l fg p
    assume "q  dterms Γ ({l}fg p)"
       and "wellformed Γ"
    hence "q  sterms Γ p" by simp
    hence "q  stermsl p  (pn. q  stermsl (Γ pn))"
      using wellformed Γ  by (rule sterms_stermsl)
    thus "q  ctermsl ({l}fg p)  (pn. q  ctermsl (Γ pn))"
    proof
      assume "q  stermsl p"
      hence "q  ctermsl p" by (rule stermsl_ctermsl)
      hence "q  ctermsl ({l}fg p)" by simp
      thus ?thesis ..
    next
      assume "pn. q  stermsl (Γ pn)"
      then obtain pn where "q  stermsl (Γ pn)" by auto
      hence "q  ctermsl (Γ pn)" by (rule stermsl_ctermsl)
      hence "pn. q  ctermsl (Γ pn)" ..
      thus ?thesis ..
    qed
  next
    fix Γ p1 p2
    assume "q  dterms Γ (p1  p2)"
       and IH1: " q  dterms Γ p1; wellformed Γ   q  ctermsl p1  (pn. q  ctermsl (Γ pn))"
       and IH2: " q  dterms Γ p2; wellformed Γ   q  ctermsl p2  (pn. q  ctermsl (Γ pn))"
       and "wellformed Γ"
    thus "q  ctermsl (p1  p2)  (pn. q  ctermsl (Γ pn))"
      by auto
  next
    fix Γ pn
    assume "q  dterms Γ (call(pn))"
       and "wellformed Γ"
       and " q  dterms Γ (Γ pn); wellformed Γ   q  ctermsl (Γ pn)  (pn. q  ctermsl (Γ pn))"
    thus "q  ctermsl (call(pn))  (pn. q  ctermsl (Γ pn))"
      by auto
  qed (simp_all, (metis sterms_stermsl stermsl_ctermsl)+)

lemma ctermsl_cterms [elim]:
  assumes "q  ctermsl p"
      and "not_call q"
      and "sterms Γ p  cterms Γ"
      and "wellformed Γ"
    shows "q  cterms Γ"
  using assms by (induct p rule: ctermsl.induct) auto

subsection "Local deriviative terms"

text ‹
  We define local @{term "dterm"}s for use in the theorem that relates @{term "cterms"}
  and sets of @{term "ctermsl"}.
›

function dtermsl
  :: "('s, 'm, 'p, 'l) seqp  ('s, 'm, 'p, 'l) seqp set"
  where
    "dtermsl ({l}fg p)                     = stermsl p"
  | "dtermsl ({l}fa p)                     = stermsl p"
  | "dtermsl (p1  p2)                      = dtermsl p1  dtermsl p2"
  | "dtermsl ({l}unicast(fip, fmsg).p  q)  = stermsl p  stermsl q"
  | "dtermsl ({l}broadcast(fmsg). p)         = stermsl p"
  | "dtermsl ({l}groupcast(fips, fmsg). p)   = stermsl p"
  | "dtermsl ({l}send(fmsg).p)               = stermsl p"
  | "dtermsl ({l}deliver(fdata).p)           = stermsl p"
  | "dtermsl ({l}receive(fmsg).p)            = stermsl p"
  | "dtermsl (call(pn))                      = {}"
  by pat_completeness auto
  termination by (relation "measure(size)") (auto dest: stermsl_nobigger)

lemma stermsl_after_dtermsl [simp]:
  shows "(xdtermsl p. stermsl x) = dtermsl p"
  by (induct p) simp_all

lemma stermsl_before_dtermsl [simp]:
  "(xstermsl p. dtermsl x) = dtermsl p"
  by (induct p) simp_all

lemma dtermsl_no_choice [simp]: "p1  p2  dtermsl p"
  by (induct p) simp_all

lemma dtermsl_choice_disj [simp]:
  "p  dtermsl (p1  p2) = (p  dtermsl p1  p  dtermsl p2)"
  by simp

lemma dtermsl_in_branch [elim]:
  "p  dtermsl (p1  p2); p  dtermsl p1  P; p  dtermsl p2  P  P"
  by auto

lemma ctermsl_dtermsl [elim]:
  assumes "q  dtermsl p"
    shows "q  ctermsl p"
  using assms by (induct p) (simp_all, (metis stermsl_ctermsl)+)

lemma dtermsl_dterms [elim]:
  assumes "q  dtermsl p"
      and "not_call q"
      and "wellformed Γ"
    shows "q  dterms Γ p"
  using assms
  using assms by (induct p) (simp_all, (metis stermsl_sterms)+)

lemma ctermsl_stermsl_or_dtermsl:
  assumes "q  ctermsl p"
    shows "q  stermsl p  (p'dtermsl p. q  ctermsl p')"
  using assms by (induct p) (auto dest: ctermsl_ex_stermsl)

lemma dtermsl_add_stermsl_beforeD:
  assumes "q  dtermsl p"
    shows "psstermsl p. q  dtermsl ps"
  proof -
    from assms have "q  (xstermsl p. dtermsl x)" by auto
    thus ?thesis
      by (rule UN_E) auto
  qed

lemma call_dtermsl_empty [elim]:
 "q  dtermsl p  not_call p"
  by (cases p) simp_all

subsection "More properties of control terms"

text ‹
  We now show an alternative definition of @{term "cterms"} based on sets of local control
  terms. While the original definition has convenient induction and simplification rules,
  useful for proving properties like cterms\_includes\_sterms\_of\_seq\_reachable, this
  definition makes it easier to systematically generate the set of control terms of a
  process specification.
›

theorem cterms_def':
  assumes wfg: "wellformed Γ"
    shows "cterms Γ = { p |p pn. p  ctermsl (Γ pn)  not_call p }"
          (is "_ = ?ctermsl_set")
  proof (rule iffI [THEN set_eqI])
    fix p
    assume "p  cterms Γ"
    thus "p  ?ctermsl_set"
    proof (induction p)
      fix p pn
      assume "p  sterms Γ (Γ pn)"
      then obtain pn' where "p  stermsl (Γ pn')" using wfg
        by (blast dest: sterms_stermsl_heads)
      hence "p  ctermsl (Γ pn')" ..
      moreover from p  sterms Γ (Γ pn) wfg have "not_call p" by simp
      ultimately show "p  ?ctermsl_set" by auto
    next
      fix pp p
      assume "pp  cterms Γ"
         and IH: "pp  ?ctermsl_set"
         and *: "p  dterms Γ pp"
      from * have "p  ctermsl pp  (pn. p  ctermsl (Γ pn))"
        using wfg by (rule dterms_ctermsl)
      hence "pn. p  ctermsl (Γ pn)"
        proof
          assume "p  ctermsl pp"
          from pp  cterms Γ and IH obtain pn' where "pp  ctermsl (Γ pn')"
            by auto
          with p  ctermsl pp have "p  ctermsl (Γ pn')" by auto
          thus "pn. p  ctermsl (Γ pn)" ..
        qed -
      moreover from p  dterms Γ pp wfg have "not_call p" by simp
      ultimately show "p  ?ctermsl_set" by auto
    qed
  next
    fix p
    assume "p  ?ctermsl_set"
    then obtain pn where *: "p  ctermsl (Γ pn)" and "not_call p" by auto
    from * have "p  stermsl (Γ pn)  (p'dtermsl (Γ pn). p  ctermsl p')"
      by (rule ctermsl_stermsl_or_dtermsl)
    thus "p  cterms Γ"
    proof
      assume "p  stermsl (Γ pn)"
      hence "p  sterms Γ (Γ pn)" using not_call p wfg ..
      thus "p  cterms Γ" ..
    next
      assume "p'dtermsl (Γ pn). p  ctermsl p'"
      then obtain p' where p'1: "p'  dtermsl (Γ pn)"
                       and p'2: "p  ctermsl p'" ..
      from p'2 and not_call p have "not_call p'" ..
      from p'1 obtain ps where ps1: "ps  stermsl (Γ pn)"
                           and ps2: "p'  dtermsl ps"
        by (blast dest: dtermsl_add_stermsl_beforeD)
      from ps2 have "not_call ps" ..
      with ps1 have "ps  cterms Γ" using wfg by auto
      with p'  dtermsl ps and not_call p' have "p'  cterms Γ" using wfg by auto
      hence "sterms Γ p'  cterms Γ" using wfg by auto
      with p  ctermsl p' not_call p show "p  cterms Γ" using wfg ..
    qed
  qed

lemma ctermsE [elim]:
  assumes "wellformed Γ"
      and "p  cterms Γ"
  obtains pn where "p  ctermsl (Γ pn)"
               and "not_call p"
  using assms(2) unfolding cterms_def' [OF assms(1)] by auto

corollary cterms_subterms:
  assumes "wellformed Γ"
    shows "cterms Γ = {p|p pn. psubterms (Γ pn)  not_call p  not_choice p}"
  by (subst cterms_def' [OF assms(1)], subst ctermsl_subterms) auto

lemma subterms_in_cterms [elim]:
  assumes "wellformed Γ"
      and "psubterms (Γ pn)"
      and "not_call p"
      and "not_choice p"
    shows "p  cterms Γ"
  using assms unfolding cterms_subterms [OF wellformed Γ] by auto

lemma subterms_stermsl_ctermsl:
  assumes "q  subterms p"
      and "r  stermsl q"
    shows "r  ctermsl p"
  using assms
  proof (induct p)
    fix p1 p2
    assume IH1: "q  subterms p1  r  stermsl q  r  ctermsl p1"
       and IH2: "q  subterms p2  r  stermsl q  r  ctermsl p2"
       and *: "q  subterms (p1  p2)"
       and "r  stermsl q"
    from * have "q  {p1  p2}  subterms p1  subterms p2" by simp
    thus "r  ctermsl (p1  p2)"
    proof (elim UnE)
      assume "q  {p1  p2}" with r  stermsl q show ?thesis
      by simp (metis stermsl_ctermsl)
    next
      assume "q  subterms p1" hence "r  ctermsl p1" using r  stermsl q by (rule IH1)
      thus ?thesis by simp
    next
      assume "q  subterms p2" hence "r  ctermsl p2" using r  stermsl q by (rule IH2)
      thus ?thesis by simp
    qed
  qed auto

lemma subterms_sterms_cterms:
  assumes wf: "wellformed Γ"
      and "p  subterms (Γ pn)"
    shows "sterms Γ p  cterms Γ"
  using assms(2)
  proof (induct p)
    fix p
    assume "call(p)  subterms (Γ pn)"
    from wf have "sterms Γ (call(p)) = sterms Γ (Γ p)" by simp
      thus "sterms Γ (call(p))  cterms Γ" by auto
  next
    fix p1 p2
    assume IH1: "p1  subterms (Γ pn)  sterms Γ p1  cterms Γ"
       and IH2: "p2  subterms (Γ pn)  sterms Γ p2  cterms Γ"
       and *: "p1  p2  subterms (Γ pn)"
    from * have "p1  subterms (Γ pn)" by auto
    hence "sterms Γ p1  cterms Γ" by (rule IH1)
    moreover from * have "p2  subterms (Γ pn)" by auto
      hence "sterms Γ p2  cterms Γ" by (rule IH2)
    ultimately show "sterms Γ (p1  p2 )  cterms Γ" using wf by simp
  qed (auto elim!: subterms_in_cterms [OF wellformed Γ])

lemma subterms_sterms_in_cterms:
  assumes "wellformed Γ"
      and "p  subterms (Γ pn)"
      and "q  sterms Γ p"
    shows "q  cterms Γ"
  using assms
  by (auto dest!: subterms_sterms_cterms [OF wellformed Γ])

end