Theory Stateful_Strands

(*  Title:      Stateful_Strands.thy
    Author:     Andreas Viktor Hess, DTU
    SPDX-License-Identifier: BSD-3-Clause
*)


section ‹Stateful Strands›
theory Stateful_Strands
imports Strands_and_Constraints
begin

subsection ‹Stateful Constraints›
datatype (funssstp: 'a, varssstp: 'b) stateful_strand_step = 
  Send (the_msgs: "('a,'b) term list") (send⟨_ 80)
| Receive (the_msgs: "('a,'b) term list") (receive⟨_ 80)
| Equality (the_check: poscheckvariant) (the_lhs: "('a,'b) term") (the_rhs: "('a,'b) term")
    (_: _  _ [80,80])
| Insert (the_elem_term: "('a,'b) term") (the_set_term: "('a,'b) term") (insert⟨_,_ 80)
| Delete (the_elem_term: "('a,'b) term") (the_set_term: "('a,'b) term") (delete⟨_,_ 80)
| InSet (the_check: poscheckvariant) (the_elem_term: "('a,'b) term") (the_set_term: "('a,'b) term")
    (_: _  _ [80,80])
| NegChecks (bvarssstp: "'b list")
    (the_eqs: "(('a,'b) term × ('a,'b) term) list")
    (the_ins: "(('a,'b) term × ('a,'b) term) list")
    (_⟨∨≠: _ ∨∉: _ [80,80])
where
  "bvarssstp (Send _) = []"
| "bvarssstp (Receive _) = []"
| "bvarssstp (Equality _ _ _) = []"
| "bvarssstp (Insert _ _) = []"
| "bvarssstp (Delete _ _) = []"
| "bvarssstp (InSet _ _ _) = []"

type_synonym ('a,'b) stateful_strand = "('a,'b) stateful_strand_step list"
type_synonym ('a,'b) dbstatelist = "(('a,'b) term × ('a,'b) term) list"
type_synonym ('a,'b) dbstate = "(('a,'b) term × ('a,'b) term) set"

abbreviation
  "is_Assignment x  (is_Equality x  is_InSet x)  the_check x = Assign"

abbreviation
  "is_Check x  ((is_Equality x  is_InSet x)  the_check x = Check)  is_NegChecks x"

abbreviation
  "is_Check_or_Assignment x  is_Equality x  is_InSet x  is_NegChecks x"

abbreviation
  "is_Update x  is_Insert x  is_Delete x"

abbreviation InSet_select (select⟨_,_) where "select⟨t,s  InSet Assign t s"
abbreviation InSet_check (_ in _) where "t in s  InSet Check t s"
abbreviation Equality_assign (_ := _) where "t := s  Equality Assign t s"
abbreviation Equality_check (_ == _) where "t == s  Equality Check t s"

abbreviation NegChecks_Inequality1 (_ != _) where
  "t != s  NegChecks [] [(t,s)] []"

abbreviation NegChecks_Inequality2 (__ != _) where
  "xt != s  NegChecks [x] [(t,s)] []"

abbreviation NegChecks_Inequality3 (_,__ != _) where
  "x,yt != s  NegChecks [x,y] [(t,s)] []"

abbreviation NegChecks_Inequality4 (_,_,__ != _) where
  "x,y,zt != s  NegChecks [x,y,z] [(t,s)] []"

abbreviation NegChecks_NotInSet1 (_ not in _) where
  "t not in s  NegChecks [] [] [(t,s)]"

abbreviation NegChecks_NotInSet2 (__ not in _) where
  "xt not in s  NegChecks [x] [] [(t,s)]"

abbreviation NegChecks_NotInSet3 (_,__ not in _) where
  "x,yt not in s  NegChecks [x,y] [] [(t,s)]"

abbreviation NegChecks_NotInSet4 (_,_,__ not in _) where
  "x,y,zt not in s  NegChecks [x,y,z] [] [(t,s)]"

fun trmssstp where
  "trmssstp (Send ts) = set ts"
| "trmssstp (Receive ts) = set ts"
| "trmssstp (Equality _ t t') = {t,t'}"
| "trmssstp (Insert t t') = {t,t'}"
| "trmssstp (Delete t t') = {t,t'}"
| "trmssstp (InSet _ t t') = {t,t'}"
| "trmssstp (NegChecks _ F F') = trmspairs F  trmspairs F'"

definition trmssst where "trmssst S  (trmssstp ` set S)"
declare trmssst_def[simp]

fun trms_listsstp where
  "trms_listsstp (Send ts) = ts"
| "trms_listsstp (Receive ts) = ts"
| "trms_listsstp (Equality _ t t') = [t,t']"
| "trms_listsstp (Insert t t') = [t,t']"
| "trms_listsstp (Delete t t') = [t,t']"
| "trms_listsstp (InSet _ t t') = [t,t']"
| "trms_listsstp (NegChecks _ F F') = concat (map (λ(t,t'). [t,t']) (F@F'))"

definition trms_listsst where "trms_listsst S  remdups (concat (map trms_listsstp S))"

definition iksst where "iksst A  {t | t ts. Receive ts  set A  t  set ts}"

definition bvarssst::"('a,'b) stateful_strand  'b set" where
  "bvarssst S  (set (map (set  bvarssstp) S))"

fun fvsstp::"('a,'b) stateful_strand_step  'b set" where
  "fvsstp (Send ts) = fvset (set ts)"
| "fvsstp (Receive ts) = fvset (set ts)"
| "fvsstp (Equality _ t t') = fv t  fv t'"
| "fvsstp (Insert t t') = fv t  fv t'"
| "fvsstp (Delete t t') = fv t  fv t'"
| "fvsstp (InSet _ t t') = fv t  fv t'"
| "fvsstp (NegChecks X F F') = fvpairs F  fvpairs F' - set X"

definition fvsst::"('a,'b) stateful_strand  'b set" where
  "fvsst S  (set (map fvsstp S))"

fun fv_listsstp where
  "fv_listsstp (send⟨ts) = concat (map fv_list ts)"
| "fv_listsstp (receive⟨ts) = concat (map fv_list ts)"
| "fv_listsstp (_: t  s) = fv_list t@fv_list s"
| "fv_listsstp (insert⟨t,s) = fv_list t@fv_list s"
| "fv_listsstp (delete⟨t,s) = fv_list t@fv_list s"
| "fv_listsstp (_: t  s) = fv_list t@fv_list s"
| "fv_listsstp (X⟨∨≠: F ∨∉: F') = filter (λx. x  set X) (fv_listpairs (F@F'))"

definition fv_listsst where
  "fv_listsst S  remdups (concat (map fv_listsstp S))"

declare bvarssst_def[simp]
declare fvsst_def[simp]

definition varssst::"('a,'b) stateful_strand  'b set" where
  "varssst S  (set (map varssstp S))"

abbreviation wfrestrictedvarssstp::"('a,'b) stateful_strand_step  'b set" where
  "wfrestrictedvarssstp x 
    case x of
      NegChecks _ _ _  {}
    | Equality Check _ _  {}
    | InSet Check _ _  {}
    | Delete _ _  {}
    | _  varssstp x"

definition wfrestrictedvarssst::"('a,'b) stateful_strand  'b set" where
  "wfrestrictedvarssst S  (set (map wfrestrictedvarssstp S))"

abbreviation wfvarsoccssstp where
  "wfvarsoccssstp x  
    case x of
      Send ts  fvset (set ts)
    | Equality Assign s t  fv s
    | InSet Assign s t  fv s  fv t
    | _  {}"

definition wfvarsoccssst where
  "wfvarsoccssst S  (set (map wfvarsoccssstp S))"

fun wf'sst::"'b set  ('a,'b) stateful_strand  bool" where
  "wf'sst V [] = True"
| "wf'sst V (Receive ts#S) = (fvset (set ts)  V  wf'sst V S)"
| "wf'sst V (Send ts#S) = wf'sst (V  fvset (set ts)) S"
| "wf'sst V (Equality Assign t t'#S) = (fv t'  V  wf'sst (V  fv t) S)"
| "wf'sst V (Equality Check _ _#S) = wf'sst V S"
| "wf'sst V (Insert t s#S) = (fv t  V  fv s  V  wf'sst V S)"
| "wf'sst V (Delete _ _#S) = wf'sst V S"
| "wf'sst V (InSet Assign t s#S) = wf'sst (V  fv t  fv s) S"
| "wf'sst V (InSet Check _ _#S) = wf'sst V S"
| "wf'sst V (NegChecks _ _ _#S) = wf'sst V S"

abbreviation "wfsst S  wf'sst {} S  fvsst S  bvarssst S = {}"

fun subst_apply_stateful_strand_step::
  "('a,'b) stateful_strand_step  ('a,'b) subst  ('a,'b) stateful_strand_step"
  (infix sstp 51) where
  "send⟨ts sstp θ = send⟨ts list θ"
| "receive⟨ts sstp θ = receive⟨ts list θ"
| "a: t  s sstp θ = a: (t  θ)  (s  θ)"
| "a: t  s sstp θ = a: (t  θ)  (s  θ)"
| "insert⟨t,s sstp θ = insert⟨t  θ, s  θ"
| "delete⟨t,s sstp θ = delete⟨t  θ, s  θ"
| "X⟨∨≠: F ∨∉: G sstp θ = X⟨∨≠: (F pairs rm_vars (set X) θ) ∨∉: (G pairs rm_vars (set X) θ)"

definition subst_apply_stateful_strand::
  "('a,'b) stateful_strand  ('a,'b) subst  ('a,'b) stateful_strand"
  (infix sst 51) where
  "S sst θ  map (λx. x sstp θ) S"

fun dbupdsst::"('f,'v) stateful_strand  ('f,'v) subst  ('f,'v) dbstate  ('f,'v) dbstate"
where
  "dbupdsst [] I D = D"
| "dbupdsst (Insert t s#A) I D = dbupdsst A I (insert ((t,s) p I) D)"
| "dbupdsst (Delete t s#A) I D = dbupdsst A I (D - {((t,s) p I)})"
| "dbupdsst (_#A) I D = dbupdsst A I D"

fun db'sst::"('f,'v) stateful_strand  ('f,'v) subst  ('f,'v) dbstatelist  ('f,'v) dbstatelist"
where
  "db'sst [] I D = D"
| "db'sst (Insert t s#A) I D = db'sst A I (List.insert ((t,s) p I) D)"
| "db'sst (Delete t s#A) I D = db'sst A I (List.removeAll ((t,s) p I) D)"
| "db'sst (_#A) I D = db'sst A I D"

definition dbsst where
  "dbsst S I  db'sst S I []"

fun setopssstp where
  "setopssstp (Insert t s) = {(t,s)}"
| "setopssstp (Delete t s) = {(t,s)}"
| "setopssstp (InSet _ t s) = {(t,s)}"
| "setopssstp (NegChecks _ _ F') = set F'"
| "setopssstp _ = {}"

text ‹The set-operations of a stateful strand›
definition setopssst where
  "setopssst S  (setopssstp ` set S)"

fun setops_listsstp where
  "setops_listsstp (Insert t s) = [(t,s)]"
| "setops_listsstp (Delete t s) = [(t,s)]"
| "setops_listsstp (InSet _ t s) = [(t,s)]"
| "setops_listsstp (NegChecks _ _ F') = F'"
| "setops_listsstp _ = []"

text ‹The set-operations of a stateful strand (list variant)›
definition setops_listsst where
  "setops_listsst S  remdups (concat (map setops_listsstp S))"


subsection ‹Small Lemmata›
lemma is_Check_or_Assignment_iff[simp]:
  "is_Check x  is_Assignment x  is_Check_or_Assignment x"
by (cases x) (blast intro: poscheckvariant.exhaust)+

lemma subst_apply_stateful_strand_step_Inequality[simp]:
  "t != s sstp θ = t  θ != s  θ"
  "xt != s sstp θ = xt  rm_vars {x} θ != s  rm_vars {x} θ"
  "x,yt != s sstp θ = x,yt  rm_vars {x,y} θ != s  rm_vars {x,y} θ"
  "x,y,zt != s sstp θ = x,y,zt  rm_vars {x,y,z} θ != s  rm_vars {x,y,z} θ"
by simp_all

lemma subst_apply_stateful_strand_step_NotInSet[simp]:
  "t not in s sstp θ = t  θ not in s  θ"
  "xt not in s sstp θ = xt  rm_vars {x} θ not in s  rm_vars {x} θ"
  "x,yt not in s sstp θ = x,yt  rm_vars {x,y} θ not in s  rm_vars {x,y} θ"
  "x,y,zt not in s sstp θ = x,y,zt  rm_vars {x,y,z} θ not in s  rm_vars {x,y,z} θ"
by simp_all

lemma trms_listsst_is_trmssst: "trmssst S = set (trms_listsst S)"
unfolding trmsst_def trms_listsst_def
proof (induction S)
  case (Cons x S) thus ?case by (cases x) auto
qed simp

lemma setops_listsst_is_setopssst: "setopssst S = set (setops_listsst S)"
unfolding setopssst_def setops_listsst_def
proof (induction S)
  case (Cons x S) thus ?case by (cases x) auto
qed simp

lemma fv_listsstp_is_fvsstp: "fvsstp a = set (fv_listsstp a)"
proof (cases a)
  case (NegChecks X F G) thus ?thesis
    using fvpairs_append[of F G] fv_listpairs_append[of F G]
          fv_listpairs_is_fvpairs[of "F@G"]
    by auto
qed (simp_all add: fv_listpairs_is_fvpairs fv_list_is_fv)

lemma fv_listsst_is_fvsst: "fvsst S = set (fv_listsst S)"
unfolding fvsst_def fv_listsst_def by (induct S) (simp_all add: fv_listsstp_is_fvsstp)

lemma trmssstp_finite[simp]: "finite (trmssstp x)"
by (cases x) auto

lemma trmssst_finite[simp]: "finite (trmssst S)"
using trmssstp_finite unfolding trmssst_def by (induct S) auto

lemma varssstp_finite[simp]: "finite (varssstp x)"
by (cases x) auto

lemma varssst_finite[simp]: "finite (varssst S)"
using varssstp_finite unfolding varssst_def by (induct S) auto

lemma fvsstp_finite[simp]: "finite (fvsstp x)"
by (cases x) auto

lemma fvsst_finite[simp]: "finite (fvsst S)"
using fvsstp_finite unfolding fvsst_def by (induct S) auto

lemma bvarssstp_finite[simp]: "finite (set (bvarssstp x))"
by (rule finite_set)

lemma bvarssst_finite[simp]: "finite (bvarssst S)"
using bvarssstp_finite unfolding bvarssst_def by (induct S) auto

lemma subst_sst_nil[simp]: "[] sst δ = []"
by (simp add: subst_apply_stateful_strand_def)

lemma dbsst_nil[simp]: "dbsst []  = []"
by (simp add: dbsst_def)

lemma iksst_nil[simp]: "iksst [] = {}"
by (simp add: iksst_def)

lemma in_iksst_iff: "t  iksst A  (ts. receive⟨ts  set A  t  set ts)"
unfolding iksst_def by blast

lemma iksst_append[simp]: "iksst (A@B) = iksst A  iksst B"
by (auto simp add: iksst_def)

lemma iksst_concat: "iksst (concat xs) = (iksst ` set xs)"
by (induct xs) auto

lemma iksst_subst: "iksst (A sst δ) = iksst A set δ"
proof (induction A)
  case (Cons a A)
  have "iksst ([a] sst δ) = iksst [a] set δ"
  proof (cases a)
    case (Receive ts) thus ?thesis
      using in_iksst_iff[of _ "[a]"] in_iksst_iff[of _ "[a] sst δ"]
      unfolding subst_apply_stateful_strand_def by auto
  qed (simp_all add: iksst_def subst_apply_stateful_strand_def)
  thus ?case
    using Cons.IH iksst_append
    by (metis append_Cons append_Nil image_Un map_append subst_apply_stateful_strand_def) 
qed simp

lemma iksst_set_subset:
  "set A  set B  iksst A  iksst B"
unfolding iksst_def by blast

lemma iksst_prefix_subset:
  "prefix A B  iksst A  iksst B" (is "?P A B  ?P' A B")
  "prefix A (C@D)  ¬prefix A C  iksst C  iksst A" (is "?Q  ?Q'  ?Q''")
proof -
  show "?P A B  ?P' A B" for A B by (metis set_mono_prefix iksst_set_subset)
  thus "?Q  ?Q'  ?Q''" by (metis prefixI prefix_same_cases)
qed

lemma iksst_snoc_no_receive_empty:
  assumes "a  set A. ¬is_Receive a"
  shows "iksst A set I = {}"
using assms in_iksst_iff[of _ A] by fastforce

lemma iksst_snoc_no_receive_eq:
  assumes "s. a = receive⟨s"
  shows "iksst (A@[a]) set  = iksst A set "
using assms iksst_snoc_no_receive_empty[of "[a]" ] iksst_append[of A "[a]"]
unfolding is_Receive_def by auto

lemma dbsst_set_is_dbupdsst: "set (db'sst A I D) = dbupdsst A I (set D)" (is "?A = ?B")
proof
  show "?A  ?B"
  proof
    fix t s show "(t,s)  ?A  (t,s)  ?B" by (induct rule: db'sst.induct) auto
  qed

  show "?B  ?A"
  proof
    fix t s show "(t,s)  ?B  (t,s)  ?A" by (induct arbitrary: D rule: dbupdsst.induct) auto
  qed
qed

lemma dbsst_no_upd:
  assumes "a  set A. ¬is_Insert a  ¬is_Delete a"
  shows "db'sst A I D = D"
using assms
proof (induction A)
  case (Cons a A) thus ?case by (cases a) auto
qed simp

lemma dbsst_no_upd_append:
  assumes "b  set B. ¬is_Insert b  ¬is_Delete b"
  shows "db'sst A = db'sst (A@B)"
  using assms
proof (induction A)
  case Nil thus ?case by (simp add: dbsst_no_upd)
next
  case (Cons a A) thus ?case by (cases a) simp_all
qed

lemma dbsst_append:
  "db'sst (A@B) I D = db'sst B I (db'sst A I D)"
proof (induction A arbitrary: D)
  case (Cons a A) thus ?case by (cases a) auto
qed simp

lemma dbsst_in_cases:
  assumes "(t,s)  set (db'sst A I D)"
  shows "(t,s)  set D  (t' s'. insert⟨t',s'  set A  t = t'  I  s = s'  I)"
  using assms
proof (induction A arbitrary: D)
  case (Cons a A) thus ?case by (cases a) fastforce+
qed simp

lemma dbsst_in_cases':
  assumes "(t,s)  set (db'sst A I D)"
    and "(t,s)  set D"
  shows "B C t' s'. A = B@insert⟨t',s'#C  t = t'  I  s = s'  I 
                     (t'' s''. delete⟨t'',s''  set C  t  t''  I  s  s''  I)"
  using assms(1)
proof (induction A rule: List.rev_induct)
  case (snoc a A)
  note * = snoc dbsst_append[of A "[a]" I D]
  thus ?case
  proof (cases a)
    case (Insert t' s')
    thus ?thesis using * by (cases "(t,s)  set (db'sst A I D)") force+
  next
    case (Delete t' s')
    hence **: "t  t'  I  s  s'  I" using * by simp

    have "(t,s)  set (db'sst A I D)" using * Delete by force
    then obtain B C u v where B:
        "A = B@insert⟨u,v#C" "t = u  I" "s = v  I"
        "t' s'. delete⟨t',s'  set C  t  t'  I  s  s'  I"
      using snoc.IH by atomize_elim auto

    have "A@[a] = B@insert⟨u,v#(C@[a])"
         "t' s'. delete⟨t',s'  set (C@[a])  t  t'  I  s  s'  I"
      using B(1,4) Delete ** by auto
    thus ?thesis using B(2,3) by blast
  qed force+
qed (simp add: assms(2))

lemma dbsst_filter:
  "db'sst A I D = db'sst (filter is_Update A) I D"
by (induct A I D rule: db'sst.induct) simp_all

lemma dbsst_subst_swap:
  assumes "x  fvsst A. I x = J x"
  shows "db'sst A I D = db'sst A J D"
using assms
proof (induction A arbitrary: D)
  case (Cons a A)
  hence "x  fvsstp a. I x = J x" "D. db'sst A I D = db'sst A J D" by auto
  thus ?case by (cases a) (simp_all add: term_subst_eq[of _ I J])
qed simp

lemma dbupdsst_no_upd:
  assumes "a  set A. ¬is_Insert a  ¬is_Delete a"
  shows "dbupdsst A I D = D"
using assms
proof (induction A)
  case (Cons a A) thus ?case by (cases a) auto
qed simp

lemma dbupdsst_no_deletes:
  assumes "list_all (λa. ¬is_Delete a) A"
  shows "dbupdsst A I D = D  {(t  I, s  I) | t s. insert⟨t,s  set A}" (is "?Q A D")
using assms
proof (induction A arbitrary: D)
  case (Cons a A)
  hence IH: "?Q A D" for D by auto
  have "¬is_Delete a" using Cons.prems by simp
  thus ?case using IH by (cases a) auto
qed simp

lemma dbupdsst_append:
  "dbupdsst (A@B) I D = dbupdsst B I (dbupdsst A I D)"
proof (induction A arbitrary: D)
  case (Cons a A) thus ?case by (cases a) auto
qed simp

lemma dbupdsst_filter:
  "dbupdsst A I D = dbupdsst (filter is_Update A) I D"
by (induct A I D rule: dbupdsst.induct) simp_all

lemma dbupdsst_in_cases:
  assumes "(t,s)  dbupdsst A I D"
  shows "(t,s)  D  (t' s'. insert⟨t',s'  set A  t = t'  I  s = s'  I)" (is ?P)
    and "u v B. suffix (delete⟨u,v#B) A  (t,s) = (u,v) p I 
                  (u' v'. (t,s) = (u',v') p I  insert⟨u',v'  set B)" (is ?Q)
proof -
  show ?P using assms
  proof (induction A arbitrary: D)
    case (Cons a A) thus ?case by (cases a) fastforce+
  qed simp

  show ?Q using assms
  proof (induction A arbitrary: D rule: List.rev_induct)
    case (snoc a A)
    note 0 = snoc.IH snoc.prems
    note 1 = suffix_snoc[of _ A a]

    have 2: "dbupdsst (A@[a]) I D = dbupdsst A I D" when "¬is_Update a"
      using that dbupdsst_append[of A "[a]" I D] by (cases a) auto

    have 3: "suffix (delete⟨u,v#B) A  suffix (delete⟨u,v#B@[a]) (A@[a])"
      when "¬is_Update a" for u v B
      using that by simp

    have 4: "C. B = C@[a]  suffix (delete⟨u,v#C) A"
      when a: "¬is_Delete a" "suffix (delete⟨u,v#B) (A@[a])" for u v B
    proof -
      have a': "a  delete⟨u,v" using a(1) by force
      obtain C where C: "delete⟨u,v#B = C@[a]" "suffix C A" using 1 a(2) by blast
      show ?thesis using a' C by (cases C) auto
    qed

    note 5 = dbupdsst_append[of A "[a]" I]

    show ?case
    proof (cases "is_Update a")
      case True
      then obtain u v where "a = insert⟨u,v  a = delete⟨u,v" by (cases a) auto
      thus ?thesis
      proof
        assume a: "a = insert⟨u,v"
        hence a': "¬is_Delete a" by simp

        have 6: "insert⟨u,v  set B"
          when B: "suffix (delete⟨u',v'#B) (A@[a])" for u' v' B
          using 4[OF a' B] unfolding a by fastforce

        have 7: "(t,s) = (u,v) p I  (t,s)  dbupdsst A I D" using snoc.prems 5 a by auto
        show ?thesis
        proof (cases "(t,s) = (u,v) p I")
          case True
          have "insert⟨u,v  set B"
            when B: "suffix (delete⟨u',v'#B) (A@[a])" for u' v' B
            using 4[OF a' B] unfolding a by fastforce
          thus ?thesis using True by blast
        next
          case False
          hence 8: "(t,s)  dbupdsst A I D" using 7 by blast
          have "u'' v''. (t,s) = (u'',v'') p I  insert⟨u'',v''  set B"
            when B: "suffix (delete⟨u',v'#B) (A @ [a])" "(t,s) = (u',v') p I" for u' v' B
          proof -
            obtain C where C: "B = C@[a]" "suffix (delete⟨u',v'#C) A" using 4[OF a' B(1)] by blast
            thus ?thesis using snoc.IH[OF 8] B(2) unfolding a by fastforce
          qed
          thus ?thesis by blast
        qed
      next
        assume a: "a = delete⟨u,v"
        hence "(t,s)  dbupdsst A I D - {((u,v) p I)}" using snoc.prems 5 by auto
        hence 6: "(t,s)  dbupdsst A I D" "(t,s)  (u,v) p I" by (blast,blast)
        
        have "(C. B = C@[a]  suffix (delete⟨u',v'#C) A)  (B = []  u' = u  v' = v)"
          when B: "suffix (delete⟨u',v'#B) (A@[a])" for B u' v'
        proof -
          obtain C where C: "delete⟨u',v'#B = C@[a]" "suffix C A" using B 1 by blast
          show ?thesis
          proof (cases "B = []")
            case True thus ?thesis using C unfolding a by simp
          next
            case False
            then obtain b B' where B': "B = B'@[b]" by (meson rev_exhaust)
            show ?thesis using C unfolding a B' by auto
          qed 
        qed
        hence "C. B = C@[a]  suffix (delete⟨u',v'#C) A"
          when "suffix (delete⟨u',v'#B) (A@[a])" "(t,s) = (u',v') p I" for B u' v'
          using that 6 by blast
        thus ?thesis using snoc.IH[OF 6(1)] unfolding a by fastforce
      qed
    next
      case False
      have "u' v'. (t,s) = (u',v') p I  insert⟨u',v'  set B"
        when B: "suffix (delete⟨u,v#B) (A@[a])" "(t,s) = (u,v) p I" for u v B
      proof -
        obtain C where C: "B = C@[a]" "suffix (delete⟨u,v#C) A" using 4[OF _ B(1)] False by blast
        show ?thesis using B(2) snoc.IH[OF snoc.prems[unfolded 2[OF False]]] C by fastforce
      qed
      thus ?thesis by blast
    qed
  qed simp
qed

lemma dbupdsst_in_iff:
  "(t,s)  dbupdsst A I D 
   ((u v B. suffix (delete⟨u,v#B) A  (t,s) = (u,v) p I 
              (u' v'. (t,s) = (u',v') p I  insert⟨u',v'  set B)) 
    ((t,s)  D  (u v. (t,s) = (u,v) p I  insert⟨u,v  set A)))"
  (is "?P A D  ?Q1 A  ?Q2 A D")
proof
  show "?P A D  ?Q1 A  ?Q2 A D" using dbupdsst_in_cases by fast

  show "?Q1 A  ?Q2 A D  ?P A D"
  proof (induction A arbitrary: D)
    case (Cons a A)
    have Q1: "?Q1 A" using Cons.prems suffix_Cons[of _ a A] by blast

    show ?case
    proof (cases "is_Update a")
      case False thus ?thesis using Q1 Cons.IH Cons.prems by (cases a) auto
    next
      case True
      then obtain t' s' where "a = insert⟨t',s'  a = delete⟨t',s'" by (cases a) auto
      thus ?thesis
      proof
        assume a: "a = insert⟨t',s'"
        hence "?Q2 A (insert ((t',s') p I) D)" using Cons.prems by auto
        thus ?thesis using Q1 Cons.IH unfolding a by auto
      next
        assume a: "a = delete⟨t',s'"
        hence "?Q2 A (D - {(t',s') p I})" using Cons.prems by auto
        thus ?thesis using Q1 Cons.IH unfolding a by auto
      qed
    qed
  qed simp
qed

lemma dbupdsst_in_cases':
  fixes A::"('a,'b) stateful_strand"
  assumes "(t,s)  dbupdsst A I D"
    and "(t,s)  D"
  shows "B C t' s'. A = B@insert⟨t',s'#C  t = t'  I  s = s'  I 
                     (t'' s''. delete⟨t'',s''  set C  t  t''  I  s  s''  I)"
using assms(1)
proof (induction A rule: List.rev_induct)
  case (snoc a A)
  note 0 = dbupdsst_append[of A "[a]" I D]
  have 1: "(t,s)  dbupdsst A I D" when "¬is_Update a" using that snoc.prems 0 by (cases a) auto
  show ?case
  proof (cases "is_Update a")
    case False
    obtain B C t' s' where B:
        "A = B@insert⟨t',s'#C" "t = t'  I" "s = s'  I"
        "t'' s''. delete⟨t'',s''  set C  t  t''  I  s  s''  I"
      using snoc.IH[OF 1[OF False]] by blast

    have "A@[a] = B@insert⟨t',s'#(C@[a])"
         "t'' s''. delete⟨t'',s''  set (C@[a])  t  t''  I  s  s''  I"
      using False B(1,4) by auto
    thus ?thesis using B(2,3) by blast
  next
    case True
    then obtain t' s' where "a = insert⟨t',s'  a = delete⟨t',s'" by (cases a) auto
    thus ?thesis
    proof
      assume a: "a = insert⟨t',s'"
      hence "dbupdsst (A@[a]) I D = insert ((t',s') p I) (dbupdsst A I D)" using 0 by simp
      hence "(t,s) = (t',s') p I  (t,s)  dbupdsst A I D" using snoc.prems by blast
      thus ?thesis
      proof
        assume 2: "(t,s)  dbupdsst A I D" show ?thesis using snoc.IH[OF 2] unfolding a by force
      qed (force simp add: a)
    next
      assume a: "a = delete⟨t',s'"
      hence 2: "t  t'  I  s  s'  I" using 0 snoc.prems by simp
  
      have "(t,s)  dbupdsst A I D" using 0 snoc.prems a by force
      then obtain B C u v where B:
          "A = B@insert⟨u,v#C" "t = u  I" "s = v  I"
          "t' s'. delete⟨t',s'  set C  t  t'  I  s  s'  I"
        using snoc.IH by atomize_elim auto
  
      have "A@[a] = B@insert⟨u,v#(C@[a])"
           "t' s'. delete⟨t',s'  set (C@[a])  t  t'  I  s  s'  I"
        using B(1,4) a 2 by auto
      thus ?thesis using B(2,3) by blast
    qed
  qed
qed (simp add: assms(2))

lemma dbupdsst_mono:
  assumes "D  E"
  shows "dbupdsst A I D  dbupdsst A I E"
using assms
proof (induction A arbitrary: D E)
  case (Cons a A) thus ?case
  proof (cases a)
    case (Insert t s)
    have "insert ((t,s) p I) D  insert ((t,s) p I) E" using Cons.prems by fast
    thus ?thesis using Cons.IH unfolding Insert by simp
  next
    case (Delete t s)
    have "D - {(t,s) p I}  E - {(t,s) p I}" using Cons.prems by fast
    thus ?thesis using Cons.IH unfolding Delete by simp
  qed auto
qed simp

lemma dbupdsst_db_narrow:
  assumes "(t,s)  dbupdsst A I (D  E)"
    and "(t,s)  D"
  shows "(t,s)  dbupdsst A I E"
using assms
proof (induction A arbitrary: D E)
  case (Cons a A) thus ?case
  proof (cases a)
    case (Delete t' s') thus ?thesis
      using Cons.prems Cons.IH[of "D - {(t',s') p I}" "E - {(t',s') p I}"] by (simp add: Un_Diff)
  qed auto
qed simp

lemma dbupdsst_set_term_neq_in_iff:
  assumes f: "f  k"
    and A: "t s. insert⟨t,s  set A  (g ts. s = Fun g ts)"
  shows "(t,Fun f ts)  dbupdsst A I D 
         (t,Fun f ts)  dbupdsst (filter (λa. s ss. a = insert⟨s,Fun k ss) A) I D"
    (is "?P A D  ?P (?f A) D")
proof
  show "?P A D  ?P (?f A) D" using A
  proof (induction A arbitrary: D)
    case (Cons a A)
    have IH: "?P A D  ?P (?f A) D" for D
      using Cons.prems(2) Cons.IH by simp

    show ?case
    proof (cases "is_Update a")
      case True
      then obtain u s where "a = insert⟨u,s  a = delete⟨u,s" by (cases a) auto
      thus ?thesis
      proof
        assume a: "a = insert⟨u,s"
        obtain g ss where s: "s = Fun g ss" using a Cons.prems(2) by fastforce

        have 0: "?P A (insert ((u, s) p I) D)" using a Cons.prems(1) by fastforce
        show ?thesis
        proof (cases "g = k")
          case g: True
          have "?f (a#A) = ?f A" unfolding a s g by force
          moreover have "(t,Fun f ts)  (u, Fun g ss) p I" using f unfolding g by auto
          ultimately show ?thesis
            using IH[OF 0] dbupdsst_db_narrow[of t "Fun f ts" "?f A" I "{(u, s) p I}" D]
            unfolding a s g by force
        next
          case g: False
          have "?f (a#A) = a#?f A" using g unfolding a s by force
          thus ?thesis using Cons.prems Cons.IH g unfolding a s by force
        qed
      next
        assume a: "a = delete⟨u,s"
        hence "?f (a#A) = a#?f A" by auto
        thus ?thesis using Cons.prems Cons.IH unfolding a by fastforce
      qed
    next
      case a: False
      hence "?P A D" using Cons.prems(1) by (cases a) auto
      hence "?P (?f A) D" using Cons.IH Cons.prems(2) a by fastforce
      thus ?thesis using a by (cases a) auto
    qed
  qed simp

  have "dbupdsst (?f A) I D  dbupdsst A I D"
  proof (induction A arbitrary: D)
    case (Cons a A) show ?case
    proof (cases a)
      case (Insert t s)
      have "?f (a#A) = a#?f A  ?f (a#A) = ?f A" unfolding Insert by force
      hence "dbupdsst (?f (a#A)) I D  dbupdsst (?f A) I (insert ((t,s) p I) D)"
        using dbupdsst_mono[of D "insert ((t, s) p I) D"] unfolding Insert by auto
      thus ?thesis using Cons.IH unfolding Insert by fastforce
    qed (use Cons.prems Cons.IH in auto)
  qed simp
  thus "?P (?f A) D  ?P A D" by blast
qed

lemma dbupdsst_subst_const_swap:
  fixes t s
  defines "fvs  λA D. fvsst A  fv t  fv s  (fvpair ` D)"
  assumes "(t  δ, s  δ)  dbupdsst A δ (D pset δ)" (is "?in δ A D")
    and "x  fvs A D.
          δ x = θ x 
          (¬(δ x  t)  ¬(δ x  s)  ¬(θ x  t)  ¬(θ x  s) 
           ((u,v)  D. ¬(δ x  u)  ¬(δ x  v)  ¬(θ x  u)  ¬(θ x  v)) 
           (u v. insert⟨u,v  set A  delete⟨u,v  set A 
                    ¬(δ x  u)  ¬(δ x  v)  ¬(θ x  u)  ¬(θ x  v)))"
      (is "?A δ θ D")
    and "x  fvs A D. c. δ x = Fun c []" (is "?B δ")
    and "x  fvs A D. c. θ x = Fun c []" (is "?B θ")
    and "x  fvs A D. y  fvs A D. δ x = δ y  θ x = θ y" (is "?C δ θ A D")
  shows "(t  θ, s  θ)  dbupdsst A θ (D pset θ)" (is "?in θ A D")
using assms(2-)
proof (induction A arbitrary: D rule: List.rev_induct)
  case Nil
  then obtain u v where u: "(u,v)  D" "t  δ = u  δ" "s  δ = v  δ" by auto

  let ?X = "fv t  fv u"
  let ?Y = "fv s  fv v"

  have 0: "fv u  fvs [] D" "fv v  fvs [] D" "fv t  fvs [] D" "fv s  fvs [] D"
    using u(1) unfolding fvs_def by (blast, blast, blast, blast)

  have 1: "x  ?X. δ x = θ x  (¬(δ x  t)  ¬(δ x  u))"
          "x  ?Y. δ x = θ x  (¬(δ x  s)  ¬(δ x  v))"
    using Nil.prems(2) u(1) unfolding fvs_def by (blast,blast)

  have 2: "x  ?X. c. δ x = Fun c []" "x  ?X. c. θ x = Fun c []"
          "x  ?Y. c. δ x = Fun c []" "x  ?Y. c. θ x = Fun c []"
    using Nil.prems(3,4) 0 by (blast,blast,blast,blast)

  have 3: "x  ?X. y  ?X. δ x = δ y  θ x = θ y"
          "x  ?Y. y  ?Y. δ x = δ y  θ x = θ y"
    using Nil.prems(5) 0 by (blast,blast)

  have "t  θ = u  θ" "s  θ = v  θ"
    using subst_const_swap_eq'[OF u(2) 1(1) 2(1,2) 3(1)]
          subst_const_swap_eq'[OF u(3) 1(2) 2(3,4) 3(2)]
    by argo+
  thus ?case using u(1) by force
next
  case (snoc a A)
  have 0: "fvs A D  fvs (A@[a]) D" "set A  set (A@[a])" unfolding fvs_def by auto

  note 1 = dbupdsst_append[of A "[a]"]

  have IH: "(t  δ, s  δ)  dbupdsst A δ (D pset δ)  (t  θ, s  θ)  dbupdsst A θ (D pset θ)"
    using snoc.IH[of D] snoc.prems(2-) 0 by blast

  let ?q0 = "λt s δ θ. x  fv t  fv s. δ x = θ x  (¬(δ x  t)  ¬(δ x  s))"
  let ?q1 = "λt s δ.   x  fv t  fv s. c. δ x = Fun c []"
  let ?q2 = "λt s δ θ. x  fv t  fv s. y  fv t  fv s. δ x = δ y  θ x = θ y"

  show ?case
  proof (cases "is_Update a")
    case False
    hence "dbupdsst (A@[a]) δ (D pset δ) = dbupdsst A δ (D pset δ)"
          "dbupdsst (A@[a]) θ (D pset θ) = dbupdsst A θ (D pset θ)"
      using 1 by (cases a; auto)+
    thus ?thesis using IH snoc.prems(1) by blast
  next
    case True
    then obtain u v where u: "a = insert⟨u,v  a = delete⟨u,v" by (cases a) auto

    have uv_in: "insert⟨u,v  set (A@[a])  delete⟨u,v  set (A@[a])" using u by force
    hence fv_uv: "fv u  fvs (A@[a]) D" "fv v  fvs (A@[a]) D" unfolding fvs_def by (force,force)

    have fv_ts: "fv t  fvs (A@[a]) D" "fv s  fvs (A@[a]) D" unfolding fvs_def by (blast,blast)

    have q0: "?q0 t u δ θ" "?q0 s v δ θ"
             "?q0 t u θ δ" "?q0 s v θ δ"
    proof -
      show "?q0 t u δ θ" "?q0 s v δ θ"
        using snoc.prems(2) 0 fv_ts fv_uv uv_in by (blast,blast)

      show "?q0 t u θ δ"
      proof
        fix x assume "x  fv t  fv u"
        hence "x  fvs (A@[a]) D" using fv_ts(1) fv_uv(1) by blast
        thus "θ x = δ x  (¬(θ x  t)  ¬(θ x  u))" 
          using snoc.prems(2) uv_in by auto
      qed

      show "?q0 s v θ δ"
      proof
        fix x assume "x  fv s  fv v"
        hence "x  fvs (A@[a]) D" using fv_ts(2) fv_uv(2) by blast
        thus "θ x = δ x  (¬(θ x  s)  ¬(θ x  v))" 
          using snoc.prems(2) uv_in by auto
      qed
    qed

    have q1: "?q1 t u δ" "?q1 t u θ"
             "?q1 s v δ" "?q1 s v θ"
      using snoc.prems(3,4) 0 fv_ts fv_uv by (blast,blast,blast,blast)

    have q2: "?q2 t u δ θ" "?q2 s v δ θ"
             "?q2 t u θ δ" "?q2 s v θ δ"
      using snoc.prems(5) 0 fv_ts fv_uv by (blast,blast,blast,blast)

    from u show ?thesis
    proof
      assume a: "a = insert⟨u,v"
      show ?thesis
      proof (cases "(t  δ, s  δ) = (u,v) p δ")
        case True
        hence "(t  θ, s  θ) = (u,v) p θ"
          using subst_const_swap_eq'[OF _ q0(1) q1(1,2) q2(1)]
                subst_const_swap_eq'[OF _ q0(2) q1(3,4) q2(2)]
          by fast
        thus ?thesis using 1 unfolding a by simp
      next
        case False
        hence "(t  δ, s  δ)  dbupdsst A δ (D pset δ)"
          using snoc.prems(1) 1 unfolding a by force
        hence "(t  θ, s  θ)  dbupdsst A θ (D pset θ)"
          using IH by blast
        thus ?thesis using 1 unfolding a by simp
      qed
    next
      assume a: "a = delete⟨u,v"
      have "(t  δ, s  δ)  (u,v) p δ"
        using snoc.prems(1) dbupdsst_append[of A "[a]"] unfolding a by fastforce
      hence 2: "(t  θ, s  θ)  (u,v) p θ"
        using subst_const_swap_eq'[OF _ q0(3) q1(2,1) q2(3)]
              subst_const_swap_eq'[OF _ q0(4) q1(4,3) q2(4)]
        by fast

      have "(t  δ, s  δ)  dbupdsst A δ (D pset δ)"
        using snoc.prems(1) 1 unfolding a by fastforce
      hence 3: "(t  θ, s  θ)  dbupdsst A θ (D pset θ)"
        using IH by blast

      show ?thesis using 2 3 dbupdsst_append[of A "[a]"] unfolding a by auto
    qed
  qed
qed

lemma subst_sst_cons: "a#A sst δ = (a sstp δ)#(A sst δ)"
by (simp add: subst_apply_stateful_strand_def)

lemma subst_sst_snoc: "A@[a] sst δ = (A sst δ)@[a sstp δ]"
by (simp add: subst_apply_stateful_strand_def)

lemma subst_sst_append[simp]: "A@B sst δ = (A sst δ)@(B sst δ)"
by (simp add: subst_apply_stateful_strand_def)

lemma subst_sst_list_all:
  "list_all is_Send S  list_all is_Send (S sst δ)"
  "list_all is_Receive S  list_all is_Receive (S sst δ)"
  "list_all is_Equality S  list_all is_Equality (S sst δ)"
  "list_all is_Insert S  list_all is_Insert (S sst δ)"
  "list_all is_Delete S  list_all is_Delete (S sst δ)"
  "list_all is_InSet S  list_all is_InSet (S sst δ)"
  "list_all is_NegChecks S  list_all is_NegChecks (S sst δ)"
  "list_all is_Assignment S  list_all is_Assignment (S sst δ)"
  "list_all is_Check S  list_all is_Check (S sst δ)"
  "list_all is_Update S  list_all is_Update (S sst δ)"
  "list_all is_Check_or_Assignment S  list_all is_Check_or_Assignment (S sst δ)"
proof (induction S)
  case (Cons x S)
  note * = list_all_def subst_apply_stateful_strand_def
  { case 1 thus ?case using Cons.IH(1) unfolding * by (cases x) auto }
  { case 2 thus ?case using Cons.IH(2) unfolding * by (cases x) auto }
  { case 3 thus ?case using Cons.IH(3) unfolding * by (cases x) auto }
  { case 4 thus ?case using Cons.IH(4) unfolding * by (cases x) auto }
  { case 5 thus ?case using Cons.IH(5) unfolding * by (cases x) auto }
  { case 6 thus ?case using Cons.IH(6) unfolding * by (cases x) auto }
  { case 7 thus ?case using Cons.IH(7) unfolding * by (cases x) auto }
  { case 8 thus ?case using Cons.IH(8) unfolding * by (cases x) fastforce+ }
  { case 9 thus ?case using Cons.IH(9) unfolding * by (cases x) auto }
  { case 10 thus ?case using Cons.IH(10) unfolding * by (cases x) auto }
  { case 11 thus ?case using Cons.IH(11) unfolding * by (cases x) auto }
qed simp_all

lemma subst_sstp_id_subst: "a sstp Var = a"
by (cases a) auto

lemma subst_sst_id_subst: "A sst Var = A"
by (induct A) (simp, metis subst_sstp_id_subst subst_sst_cons)

lemma sst_vars_append_subset:
  "fvsst A  fvsst (A@B)" "bvarssst A  bvarssst (A@B)"
  "fvsst B  fvsst (A@B)" "bvarssst B  bvarssst (A@B)"
by auto

lemma sst_vars_disj_cons[simp]: "fvsst (a#A)  bvarssst (a#A) = {}  fvsst A  bvarssst A = {}"
unfolding fvsst_def bvarssst_def by auto

lemma fvsst_cons_subset[simp]: "fvsst A  fvsst (a#A)"
by auto

lemma fvsstp_subst_cases[simp]:
  "fvsstp (send⟨ts sstp θ) = fvset (set ts set θ)"
  "fvsstp (receive⟨ts sstp θ) = fvset (set ts set θ)"
  "fvsstp (c: t  s sstp θ) = fv (t  θ)  fv (s  θ)"
  "fvsstp (insert⟨t,s sstp θ) = fv (t  θ)  fv (s  θ)"
  "fvsstp (delete⟨t,s sstp θ) = fv (t  θ)  fv (s  θ)"
  "fvsstp (c: t  s sstp θ) = fv (t  θ)  fv (s  θ)"
  "fvsstp (X⟨∨≠: F ∨∉: G sstp θ) =
    fvpairs (F pairs rm_vars (set X) θ)  fvpairs (G pairs rm_vars (set X) θ) - set X"
by simp_all

lemma varssstp_cases[simp]:
  "varssstp (send⟨ts) = fvset (set ts)"
  "varssstp (receive⟨ts) = fvset (set ts)"
  "varssstp (c: t  s) = fv t  fv s"
  "varssstp (insert⟨t,s) = fv t  fv s"
  "varssstp (delete⟨t,s) = fv t  fv s"
  "varssstp (c: t  s) = fv t  fv s"
  "varssstp (X⟨∨≠: F ∨∉: G) = fvpairs F  fvpairs G  set X" (is ?A)
  "varssstp (X⟨∨≠: [(t,s)] ∨∉: []) = fv t  fv s  set X" (is ?B)
  "varssstp (X⟨∨≠: [] ∨∉: [(t,s)]) = fv t  fv s  set X" (is ?C)
proof
  show ?A ?B ?C by auto
qed simp_all

lemma varssstp_subst_cases[simp]:
  "varssstp (send⟨ts sstp θ) = fvset (set ts set θ)"
  "varssstp (receive⟨ts sstp θ) = fvset (set ts set θ)"
  "varssstp (c: t  s sstp θ) = fv (t  θ)  fv (s  θ)"
  "varssstp (insert⟨t,s sstp θ) = fv (t  θ)  fv (s  θ)"
  "varssstp (delete⟨t,s sstp θ) = fv (t  θ)  fv (s  θ)"
  "varssstp (c: t  s sstp θ) = fv (t  θ)  fv (s  θ)"
  "varssstp (X⟨∨≠: F ∨∉: G sstp θ) =
    fvpairs (F pairs rm_vars (set X) θ)  fvpairs (G pairs rm_vars (set X) θ)  set X" (is ?A)
  "varssstp (X⟨∨≠: [(t,s)] ∨∉: [] sstp θ) =
    fv (t  rm_vars (set X) θ)  fv (s  rm_vars (set X) θ)  set X" (is ?B)
  "varssstp (X⟨∨≠: [] ∨∉: [(t,s)] sstp θ) =
    fv (t  rm_vars (set X) θ)  fv (s  rm_vars (set X) θ)  set X" (is ?C)
proof
  show ?A ?B ?C by auto
qed simp_all

lemma bvarssst_cons_subset: "bvarssst A  bvarssst (a#A)"
by auto

lemma bvarssstp_subst: "bvarssstp (a sstp δ) = bvarssstp a"
by (cases a) auto

lemma bvarssst_subst: "bvarssst (A sst δ) = bvarssst A"
using bvarssstp_subst[of _ δ]
by (induct A) (simp_all add: subst_apply_stateful_strand_def)

lemma bvarssstp_set_cases[simp]:
  "set (bvarssstp (send⟨ts)) = {}"
  "set (bvarssstp (receive⟨ts)) = {}"
  "set (bvarssstp (c: t  s)) = {}"
  "set (bvarssstp (insert⟨t,s)) = {}"
  "set (bvarssstp (delete⟨t,s)) = {}"
  "set (bvarssstp (c: t  s)) = {}"
  "set (bvarssstp (X⟨∨≠: F ∨∉: G)) = set X"
by simp_all

lemma bvarssstp_NegChecks: "¬is_NegChecks a  bvarssstp a = []"
by (cases a) simp_all

lemma bvarssst_NegChecks: "bvarssst A = bvarssst (filter is_NegChecks A)" 
proof (induction A)
  case (Cons a A) thus ?case by (cases a) fastforce+
qed simp

lemma varssst_append[simp]: "varssst (A@B) = varssst A  varssst B"
by (simp add: varssst_def)

lemma varssst_Nil[simp]: "varssst [] = {}"
by (simp add: varssst_def)

lemma varssst_Cons: "varssst (a#A) = varssstp a  varssst A"
by (simp add: varssst_def)

lemma fvsst_Cons: "fvsst (a#A) = fvsstp a  fvsst A"
unfolding fvsst_def by simp

lemma bvarssst_Cons: "bvarssst (a#A) = set (bvarssstp a)  bvarssst A"
unfolding bvarssst_def by auto

lemma varssst_Cons'[simp]:
  "varssst (send⟨ts#A) = varssstp (send⟨ts)  varssst A"
  "varssst (receive⟨ts#A) = varssstp (receive⟨ts)  varssst A"
  "varssst (a: t  s#A) = varssstp (a: t  s)  varssst A"
  "varssst (insert⟨t,s#A) = varssstp (insert⟨t,s)  varssst A"
  "varssst (delete⟨t,s#A) = varssstp (delete⟨t,s)  varssst A"
  "varssst (a: t  s#A) = varssstp (a: t  s)  varssst A"
  "varssst (X⟨∨≠: F ∨∉: G#A) = varssstp (X⟨∨≠: F ∨∉: G)  varssst A"
by (simp_all add: varssst_def)

lemma fvsstp_subst_if_no_bvars:
  assumes a: "bvarssstp a = []"
  shows "fvsstp (a sstp θ) = fvset (θ ` fvsstp a)"
proof (cases a)
  case (NegChecks X F G)
  hence "set X = {}" using a by fastforce
  thus ?thesis using fvpairs_subst[of _ θ] unfolding NegChecks by simp
qed (auto simp add: subst_list_set_fv subst_apply_fv_unfold)

lemma fvsst_subst_if_no_bvars:
  assumes A: "bvarssst A = {}"
  shows "fvsst (A sst θ) = fvset (θ ` fvsst A)"
using assms
proof (induction A)
  case (Cons a A) thus ?case
    using fvsstp_subst_if_no_bvars[of a θ] fvsst_Cons[of a A] bvarssst_Cons[of a A]
          subst_sst_cons[of a A θ]
    by simp
qed simp

lemma varssstp_is_fvsstp_bvarssstp:
  fixes x::"('a,'b) stateful_strand_step"
  shows "varssstp x = fvsstp x  set (bvarssstp x)"
proof (cases x)
  case (NegChecks X F G) thus ?thesis by (induct F) force+
qed simp_all

lemma varssst_is_fvsst_bvarssst:
  fixes S::"('a,'b) stateful_strand"
  shows "varssst S = fvsst S  bvarssst S"
proof (induction S)
  case (Cons x S) thus ?case
    using varssstp_is_fvsstp_bvarssstp[of x]
    by (auto simp add: varssst_def)
qed simp

lemma varssstp_NegCheck[simp]:
  "varssstp (X⟨∨≠: F ∨∉: G) = set X  fvpairs F  fvpairs G"
by (simp_all add: sup_commute sup_left_commute varssstp_is_fvsstp_bvarssstp)

lemma bvarssstp_NegCheck[simp]:
  "bvarssstp (X⟨∨≠: F ∨∉: G) = X"
  "set (bvarssstp ([]⟨∨≠: F ∨∉: G)) = {}"
by simp_all

lemma fvsstp_NegCheck[simp]:
  "fvsstp (X⟨∨≠: F ∨∉: G) = fvpairs F  fvpairs G - set X"
  "fvsstp ([]⟨∨≠: F ∨∉: G) = fvpairs F  fvpairs G"
  "fvsstp (t != s) = fv t  fv s"
  "fvsstp (t not in s) = fv t  fv s"
by simp_all

lemma fvsst_append[simp]: "fvsst (A@B) = fvsst A  fvsst B"
by simp

lemma bvarssst_append[simp]: "bvarssst (A@B) = bvarssst A  bvarssst B"
by auto

lemma fvsst_mono: "set A  set B  fvsst A  fvsst B"
by auto

lemma fvsstp_is_subterm_trmssstp:
  assumes "x  fvsstp a"
  shows "Var x  subtermsset (trmssstp a)"
using assms var_is_subterm
proof (cases a)
  case (NegChecks X F F')
  hence "x  fvpairs F  fvpairs F' - set X" using assms by simp
  thus ?thesis using NegChecks var_is_subterm by fastforce
qed force+

lemma fvsst_is_subterm_trmssst: "x  fvsst A  Var x  subtermsset (trmssst A)"
proof (induction A)
  case (Cons a A) thus ?case using fvsstp_is_subterm_trmssstp by (cases "x  fvsst A") auto
qed simp

lemma var_subterm_trmssstp_is_varssstp:
  assumes "Var x  subtermsset (trmssstp a)"
  shows "x  varssstp a"
using assms vars_iff_subtermeq
proof (cases a)
  case (NegChecks X F F')
  hence "Var x  subtermsset (trmspairs F  trmspairs F')" using assms by simp
  thus ?thesis using NegChecks vars_iff_subtermeq by force
qed force+

lemma var_subterm_trmssst_is_varssst: "Var x  subtermsset (trmssst A)  x  varssst A"
proof (induction A)
  case (Cons a A)
  show ?case
  proof (cases "Var x  subtermsset (trmssst A)")
    case True thus ?thesis using Cons.IH by (simp add: varssst_def)
  next
    case False thus ?thesis
      using Cons.prems var_subterm_trmssstp_is_varssstp
      by (fastforce simp add: varssst_def)
  qed
qed simp

lemma var_trmssst_is_varssst: "Var x  trmssst A  x  varssst A"
by (meson var_subterm_trmssst_is_varssst UN_I term.order_refl)

lemma iksst_trmssst_subset: "iksst A  trmssst A"
by (force simp add: iksst_def)

lemma var_subterm_iksst_is_varssst: "Var x  subtermsset (iksst A)  x  varssst A"
using var_subterm_trmssst_is_varssst iksst_trmssst_subset by fast

lemma var_subterm_iksst_is_fvsst:
  assumes "Var x  subtermsset (iksst A)"
  shows "x  fvsst A"
proof -
  obtain ts where ts: "Receive ts  set A" "Var x set set ts"
    using assms unfolding iksst_def by atomize_elim auto
  hence "fvset (set ts)  fvsst A" unfolding fvsst_def by force
  thus ?thesis using ts(2) subterm_is_var by fastforce
qed

lemma fv_iksst_is_fvsst:
  assumes "x  fvset (iksst A)"
  shows "x  fvsst A"
using var_subterm_iksst_is_fvsst assms var_is_subterm by fastforce

lemma fv_trmssst_subset:
  "fvset (trmssst S)  varssst S"
  "fvsst S  fvset (trmssst S)"
proof (induction S)
  case (Cons x S)
  have *: "fvset (trmssst (x#S)) = fvset (trmssstp x)  fvset (trmssst S)"
          "fvsst (x#S) = fvsstp x  fvsst S" "varssst (x#S) = varssstp x  varssst S"
    unfolding trmssst_def fvsst_def varssst_def
    by auto

  { case 1
    show ?case using Cons.IH(1)
    proof (cases x)
      case (NegChecks X F G)
      hence "trmssstp x = trmspairs F  trmspairs G"
            "varssstp x = fvpairs F  fvpairs G  set X"
        by (simp, meson varssstp_cases(7))
      hence "fvset (trmssstp x)  varssstp x"
        using fv_trmspairs_is_fvpairs[of F] fv_trmspairs_is_fvpairs[of G]
        by auto
      thus ?thesis
        using Cons.IH(1) *(1,3)
        by blast
    qed auto
  }

  { case 2
    show ?case using Cons.IH(2)
    proof (cases x)
      case (NegChecks X F G)
      hence "trmssstp x = trmspairs F  trmspairs G"
            "fvsstp x = (fvpairs F  fvpairs G) - set X"
        by auto
      hence "fvsstp x  fvset (trmssstp x)"
        using fv_trmspairs_is_fvpairs[of F] fv_trmspairs_is_fvpairs[of G]
        by auto
      thus ?thesis
        using Cons.IH(2) *(1,2)
        by blast
    qed auto
  }
qed simp_all

lemma fv_ik_subset_fv_sst'[simp]: "fvset (iksst S)  fvsst S"
unfolding iksst_def by (induct S) auto

lemma fv_ik_subset_vars_sst'[simp]: "fvset (iksst S)  varssst S"
using fv_ik_subset_fv_sst' fv_trmssst_subset by fast

lemma iksst_var_is_fv: "Var x  subtermsset (iksst A)  x  fvsst A"
by (meson fv_ik_subset_fv_sst'[of A] fv_subset_subterms subsetCE term.set_intros(3))

lemma varssstp_subst_cases':
  assumes x: "x  varssstp (s sstp θ)"
  shows "x  varssstp s  x  fvset (θ ` varssstp s)"
using x vars_term_subst[of _ θ] varssstp_cases(1,2,3,4,5,6) varssstp_subst_cases(1,2)[of _ θ]
      varssstp_subst_cases(3,6)[of _ _ _ θ] varssstp_subst_cases(4,5)[of _ _ θ]
proof (cases s)
  case (NegChecks X F G)
  let ?θ' = "rm_vars (set X) θ"
  have "x  fvpairs (F pairs ?θ')  x  fvpairs (G pairs ?θ')  x  set X"
    using varssstp_subst_cases(7)[of X F G θ] x NegChecks by simp
  hence "x  fvset (?θ' ` fvpairs F)  x  fvset (?θ' ` fvpairs G)  x  set X"
    using fvpairs_subst[of _ ?θ'] by blast
  hence "x  fvset (θ ` fvpairs F)  x  fvset (θ ` fvpairs G)  x  set X"
    using rm_vars_fvset_subst by fast
  thus ?thesis
    using NegChecks varssstp_cases(7)[of X F G]
    by auto
qed simp_all

lemma varssst_subst_cases:
  assumes "x  varssst (S sst θ)"
  shows "x  varssst S  x  fvset (θ ` varssst S)"
  using assms
proof (induction S)
  case (Cons s S) thus ?case
  proof (cases "x  varssst (S sst θ)")
    case False
    note * = subst_sst_cons[of s S θ] varssst_Cons[of "s sstp θ" "S sst θ"] varssst_Cons[of s S]
    have **: "x  varssstp (s sstp θ)" using Cons.prems False * by simp
    show ?thesis using varssstp_subst_cases'[OF **] * by auto
  qed (auto simp add: varssst_def)
qed simp

lemma subset_subst_pairs_diff_exists:
  fixes ::"('a,'b) subst" and D D'::"('a,'b) dbstate"
  shows "Di. Di  D  Di pset  = (D pset ) - D'"
by (metis (no_types, lifting) Diff_subset subset_image_iff)

lemma subset_subst_pairs_diff_exists':
  fixes ::"('a,'b) subst" and D::"('a,'b) dbstate"
  assumes "finite D"
  shows "Di. Di  D  Di pset   {d p }  d p   (D - Di) pset "
using assms
proof (induction D rule: finite_induct)
  case (insert d' D)
  then obtain Di where IH: "Di  D" "Di pset   {d p }" "d p   (D - Di) pset " by atomize_elim auto
  show ?case
  proof (cases "d' p  = d p ")
    case True
    hence "insert d' Di  insert d' D" "insert d' Di pset   {d p }"
          "d p   (insert d' D - insert d' Di) pset " 
      using IH by auto
    thus ?thesis by metis
  next
    case False
    hence "Di  insert d' D" "Di pset   {d p }"
          "d p   (insert d' D - Di) pset " 
      using IH by auto
    thus ?thesis by metis
  qed
qed simp

lemma stateful_strand_step_subst_inI[intro]:
  "send⟨ts  set A  send⟨ts list θ  set (A sst θ)"
  "receive⟨ts  set A  receive⟨ts list θ  set (A sst θ)"
  "c: t  s  set A  c: (t  θ)  (s  θ)  set (A sst θ)"
  "insert⟨t, s  set A  insert⟨t  θ, s  θ  set (A sst θ)"
  "delete⟨t, s  set A  delete⟨t  θ, s  θ  set (A sst θ)"
  "c: t  s  set A  c: (t  θ)  (s  θ)  set (A sst θ)"
  "X⟨∨≠: F ∨∉: G  set A 
     X⟨∨≠: (F pairs rm_vars (set X) θ) ∨∉: (G pairs rm_vars (set X) θ)  set (A sst θ)"
  "t != s  set A  t  θ != s  θ  set (A sst θ)"
  "t not in s  set A  t  θ not in s  θ  set (A sst θ)"
proof (induction A)
  case (Cons a A)
  note * = subst_sst_cons[of a A θ]
  { case 1 thus ?case using Cons.IH(1) * by (cases a) auto }
  { case 2 thus ?case using Cons.IH(2) * by (cases a) auto }
  { case 3 thus ?case using Cons.IH(3) * by (cases a) auto }
  { case 4 thus ?case using Cons.IH(4) * by (cases a) auto }
  { case 5 thus ?case using Cons.IH(5) * by (cases a) auto }
  { case 6 thus ?case using Cons.IH(6) * by (cases a) auto }
  { case 7 thus ?case using Cons.IH(7) * by (cases a) auto }
  { case 8 thus ?case using Cons.IH(8) * by (cases a) auto }
  { case 9 thus ?case using Cons.IH(9) * by (cases a) auto }
qed simp_all

lemma stateful_strand_step_cases_subst:
  "is_Send a = is_Send (a sstp θ)"
  "is_Receive a = is_Receive (a sstp θ)"
  "is_Equality a = is_Equality (a sstp θ)"
  "is_Insert a = is_Insert (a sstp θ)"
  "is_Delete a = is_Delete (a sstp θ)"
  "is_InSet a = is_InSet (a sstp θ)"
  "is_NegChecks a = is_NegChecks (a sstp θ)"
  "is_Assignment a = is_Assignment (a sstp θ)"
  "is_Check a = is_Check (a sstp θ)"
  "is_Update a = is_Update (a sstp θ)"
  "is_Check_or_Assignment a = is_Check_or_Assignment (a sstp θ)"
by (cases a; simp_all)+

lemma stateful_strand_step_substD:
  "a sstp σ = send⟨ts  ts'. ts = ts' list σ  a = send⟨ts'"
  "a sstp σ = receive⟨ts  ts'. ts = ts' list σ  a = receive⟨ts'"
  "a sstp σ = c: t  s  t' s'. t = t'  σ  s = s'  σ  a = c: t'  s'"
  "a sstp σ = insert⟨t,s  t' s'. t = t'  σ  s = s'  σ  a = insert⟨t',s'"
  "a sstp σ = delete⟨t,s  t' s'. t = t'  σ  s = s'  σ  a = delete⟨t',s'"
  "a sstp σ = c: t  s  t' s'. t = t'  σ  s = s'  σ  a = c: t'  s'"
  "a sstp σ = X⟨∨≠: F ∨∉: G 
    F' G'. F = F' pairs rm_vars (set X) σ  G = G' pairs rm_vars (set X) σ 
            a = X⟨∨≠: F' ∨∉: G'"
  "a sstp σ = t != s  t' s'. t = t'  σ  s = s'  σ  a = t' != s'"
  "a sstp σ = t not in s  t' s'. t = t'  σ  s = s'  σ  a = t' not in s'"
by (cases a; auto simp add: subst_apply_pairs_def; fail)+

lemma stateful_strand_step_mem_substD:
  "send⟨ts  set (S sst σ)  ts'. ts = ts' list σ  send⟨ts'  set S"
  "receive⟨ts  set (S sst σ)  ts'. ts = ts' list σ  receive⟨ts'  set S"
  "c: t  s  set (S sst σ)  t' s'. t = t'  σ  s = s'  σ  c: t'  s'  set S"
  "insert⟨t,s  set (S sst σ)  t' s'. t = t'  σ  s = s'  σ  insert⟨t',s'  set S"
  "delete⟨t,s  set (S sst σ)  t' s'. t = t'  σ  s = s'  σ  delete⟨t',s'  set S"
  "c: t  s  set (S sst σ)  t' s'. t = t'  σ  s = s'  σ  c: t'  s'  set S"
  "X⟨∨≠: F ∨∉: G  set (S sst σ) 
    F' G'. F = F' pairs rm_vars (set X) σ  G = G' pairs rm_vars (set X) σ 
            X⟨∨≠: F' ∨∉: G'  set S"
  "t != s  set (S sst σ)  t' s'. t = t'  σ  s = s'  σ  t' != s'  set S"
  "t not in s  set (S sst σ)  t' s'. t = t'  σ  s = s'  σ  t' not in s'  set S"
proof (induction S)
  case (Cons a S)
  have *: "x  set (S sst σ)"
    when "x  set (a#S sst σ)" "x  a sstp σ" for x
    using that by (simp add: subst_apply_stateful_strand_def)

  { case 1 thus ?case using Cons.IH(1)[OF *] by (cases a) auto }
  { case 2 thus ?case using Cons.IH(2)[OF *] by (cases a) auto }
  { case 3 thus ?case using Cons.IH(3)[OF *] by (cases a) auto }
  { case 4 thus ?case using Cons.IH(4)[OF *] by (cases a) auto }
  { case 5 thus ?case using Cons.IH(5)[OF *] by (cases a) auto }
  { case 6 thus ?case using Cons.IH(6)[OF *] by (cases a) auto }
  { case 7 thus ?case using Cons.IH(7)[OF *] by (cases a) auto }
  { case 8 show ?case
    proof (cases a)
      case (NegChecks Y F' G') thus ?thesis
      proof (cases "t != s = a sstp σ")
        case True thus ?thesis using NegChecks stateful_strand_step_substD(8)[of a σ t s] by force
      qed (use 8 Cons.IH(8)[OF *] in auto)
    qed (use 8 Cons.IH(8)[OF *] in simp_all)
  }
  { case 9 show ?case
    proof (cases a)
      case (NegChecks Y F' G') thus ?thesis
      proof (cases "t not in s = a sstp σ")
        case True thus ?thesis using NegChecks stateful_strand_step_substD(9)[of a σ t s] by force
      qed (use 9 Cons.IH(9)[OF *] in auto)
    qed (use 9 Cons.IH(9)[OF *] in simp_all)
  }
qed simp_all

lemma stateful_strand_step_fv_subset_cases:
  "send⟨ts  set S  fvset (set ts)  fvsst S"
  "receive⟨ts  set S  fvset (set ts)  fvsst S"
  "c: t  s  set S  fv t  fv s  fvsst S"
  "insert⟨t,s  set S  fv t  fv s  fvsst S"
  "delete⟨t,s  set S  fv t  fv s  fvsst S"
  "c: t  s  set S  fv t  fv s  fvsst S"
  "X⟨∨≠: F ∨∉: G  set S  fvpairs F  fvpairs G - set X  fvsst S"
  "t != s  set S  fv t  fv s  fvsst S"
  "t not in s  set S  fv t  fv s  fvsst S"
proof (induction S)
  case (Cons a S)
  { case 1 thus ?case using Cons.IH(1) by auto }
  { case 2 thus ?case using Cons.IH(2) by auto }
  { case 3 thus ?case using Cons.IH(3) by auto }
  { case 4 thus ?case using Cons.IH(4) by auto }
  { case 5 thus ?case using Cons.IH(5) by auto }
  { case 6 thus ?case using Cons.IH(6) by auto }
  { case 7 thus ?case using Cons.IH(7) by fastforce }
  { case 8 thus ?case using Cons.IH(8) by fastforce }
  { case 9 thus ?case using Cons.IH(9) by fastforce }
qed simp_all

lemma trmssst_nil[simp]:
  "trmssst [] = {}"
unfolding trmssst_def by simp

lemma trmssst_mono:
  "set M  set N  trmssst M  trmssst N"
by auto

lemma trmssst_memI[intro?]:
  "send⟨ts  set S  t  set ts  t  trmssst S"
  "receive⟨ts  set S  t  set ts  t  trmssst S"
  "ac: t  s  set S  t  trmssst S"
  "ac: t  s  set S  s  trmssst S"
  "insert⟨t,s  set S  t  trmssst S"
  "insert⟨t,s  set S  s  trmssst S"
  "delete⟨t,s  set S  t  trmssst S"
  "delete⟨t,s  set S  s  trmssst S"
  "X⟨∨≠: F ∨∉: G  set S  t  trmspairs F  t  trmssst S"
  "X⟨∨≠: F ∨∉: G  set S  t  trmspairs G  t  trmssst S"
unfolding trmssst_def by fastforce+

lemma trmssst_in:
  assumes "t  trmssst S"
  shows "a  set S. t  trmssstp a"
using assms unfolding trmssst_def by simp

lemma trmssst_cons: "trmssst (a#A) = trmssstp a  trmssst A"
unfolding trmssst_def by force

lemma trmssst_append[simp]: "trmssst (A@B) = trmssst A  trmssst B"
unfolding trmssst_def by force

lemma trmssstp_subst:
  assumes "set (bvarssstp a)  subst_domain θ = {}"
  shows "trmssstp (a sstp θ) = trmssstp a set θ"
proof (cases a)
  case (NegChecks X F G)
  hence "rm_vars (set X) θ = θ" using assms rm_vars_apply'[of θ "set X"] by auto
  hence "trmssstp (a sstp θ) = trmspairs (F pairs θ)  trmspairs (G pairs θ)"
        "trmssstp a set θ = (trmspairs F set θ)  (trmspairs G set θ)"
    using NegChecks image_Un by simp_all
  thus ?thesis by (metis trmspairs_subst)
qed simp_all

lemma trmssstp_subst':
  assumes "¬is_NegChecks a"
  shows "trmssstp (a sstp θ) = trmssstp a set θ"
using assms by (cases a) simp_all

lemma trmssstp_subst'':
  fixes t::"('a,'b) term" and δ::"('a,'b) subst"
  assumes "t  trmssstp (b sstp δ)"
  shows "s  trmssstp b. t = s  rm_vars (set (bvarssstp b)) δ"
proof (cases "is_NegChecks b")
  case True
  then obtain X F G where *: "b = NegChecks X F G" by (cases b) auto
  thus ?thesis using assms trmspairs_subst[of _ "rm_vars (set X) δ"] by auto
next
  case False
  hence "trmssstp (b sstp δ) = trmssstp b set rm_vars (set (bvarssstp b)) δ"
    using trmssstp_subst' bvarssstp_NegChecks
    by fastforce
  thus ?thesis using assms by fast
qed

lemma trmssstp_subst''':
  fixes t::"('a,'b) term" and δ θ::"('a,'b) subst"
  assumes "t  trmssstp (b sstp δ) set θ"
  shows "s  trmssstp b. t = s  rm_vars (set (bvarssstp b)) δ s θ"
proof -
  obtain s where s: "s  trmssstp (b sstp δ)" "t = s  θ" using assms by atomize_elim auto
  show ?thesis using trmssstp_subst''[OF s(1)] s(2) by auto
qed

lemma trmssst_subst:
  assumes "bvarssst S  subst_domain θ = {}"
  shows "trmssst (S sst θ) = trmssst S set θ"
using assms
proof (induction S)
  case (Cons a S)
  hence IH: "trmssst (S sst θ) = trmssst S set θ" and *: "set (bvarssstp a)  subst_domain θ = {}"
    by auto
  show ?case using trmssstp_subst[OF *] IH by (auto simp add: subst_apply_stateful_strand_def)
qed simp

lemma trmssst_subst_cons:
  "trmssst (a#A sst δ) = trmssstp (a sstp δ)  trmssst (A sst δ)"
using subst_sst_cons[of a A δ] trmssst_cons[of a A] trmssst_append by simp

lemma (in intruder_model) wftrms_trmssstp_subst:
  assumes "wftrms (trmssstp a set δ)"
  shows "wftrms (trmssstp (a sstp δ))"
  using assms
proof (cases a)
  case (NegChecks X F G)
  hence *: "trmssstp (a sstp δ) =
              (trmspairs (F pairs rm_vars (set X) δ))  (trmspairs (G pairs rm_vars (set X) δ))"
    by simp

  have "trmssstp a set δ = (trmspairs F set δ)  (trmspairs G set δ)"
    using NegChecks image_Un by simp
  hence "wftrms (trmspairs F set δ)" "wftrms (trmspairs G set δ)" using * assms by auto
  hence "wftrms (trmspairs F set rm_vars (set X) δ)"
        "wftrms (trmspairs G set rm_vars (set X) δ)"
    using wf_trms_subst_rm_vars[of δ "trmspairs F" "set X"]
          wf_trms_subst_rm_vars[of δ "trmspairs G" "set X"]
    by fast+
  thus ?thesis
    using * trmspairs_subst[of _ "rm_vars (set X) δ"]
    by auto
qed auto

lemma trmssst_fv_varssst_subset: "t  trmssst A  fv t  varssst A" 
proof (induction A)
  case (Cons a A) thus ?case by (cases a) auto
qed simp

lemma trmssst_fv_subst_subset:
  assumes "t  trmssst S" "subst_domain θ  bvarssst S = {}"
  shows "fv (t  θ)  varssst (S sst θ)"
using assms
proof (induction S)
  case (Cons s S) show ?case
  proof (cases "t  trmssst S")
    case True
    hence "fv (t  θ)  varssst (S sst θ)" using Cons.IH Cons.prems by auto
    thus ?thesis using subst_sst_cons[of s S θ] unfolding varssst_def by auto
  next
    case False
    hence *: "t  trmssstp s" "subst_domain θ  set (bvarssstp s) = {}" using Cons.prems by auto
    hence "fv (t  θ)  varssstp (s sstp θ)"
    proof (cases s)
      case (NegChecks X F G)
      hence **: "t  trmspairs F  t  trmspairs G" using *(1) by auto
      have ***: "rm_vars (set X) θ = θ" using *(2) NegChecks rm_vars_apply' by auto
      have "fv (t  θ)  fvpairs (F pairs rm_vars (set X) θ)  fvpairs (G pairs rm_vars (set X) θ)"
        using ** *** trmspairs_fv_subst_subset[of t _ θ] by auto
      thus ?thesis using *(2) using NegChecks varssstp_subst_cases(7)[of X F G θ] by blast
    qed auto
    thus ?thesis using subst_sst_cons[of s S θ] unfolding varssst_def by auto
  qed
qed simp

lemma trmssst_fv_subst_subset':
  assumes "t  subtermsset (trmssst S)" "fv t  bvarssst S = {}" "fv (t  θ)  bvarssst S = {}"
  shows "fv (t  θ)  fvsst (S sst θ)"
using assms
proof (induction S)
  case (Cons s S) show ?case
  proof (cases "t  subtermsset (trmssst S)")
    case True
    hence "fv (t  θ)  fvsst (S sst θ)" using Cons.IH Cons.prems by auto
    thus ?thesis using subst_sst_cons[of s S θ] unfolding varssst_def by auto
  next
    case False
    hence 0: "t  subtermsset (trmssstp s)" "fv t  set (bvarssstp s) = {}"
             "fv (t  θ)  set (bvarssstp s) = {}"
      using Cons.prems by auto

    note 1 = UN_Un UN_insert fvset.simps subst_apply_fv_subset subst_apply_fv_unfold
             subst_apply_term_empty sup_bot.comm_neutral fv_subterms_set fv_subset[OF 0(1)]

    note 2 = subst_apply_fv_union

    have "fv (t  θ)  fvsstp (s sstp θ)"
    proof (cases s)
      case (Send ts)
      have "fv t  fvset (set ts)" using fv_subset[OF 0(1)] unfolding Send fv_subterms_set by simp
      hence "fv (t  θ)  fvset (set ts set θ)"
        by (metis subst_apply_fv_subset subst_apply_fv_unfold_set)
      thus ?thesis using Send by simp
    next
      case (Receive ts)
      have "fv t  fvset (set ts)" using fv_subset[OF 0(1)] unfolding Receive fv_subterms_set by simp
      hence "fv (t  θ)  fvset (set ts set θ)"
        by (metis subst_apply_fv_subset subst_apply_fv_unfold_set)
      thus ?thesis using Receive by simp
    next
      case (NegChecks X F G)
      hence 3: "t  subtermsset (trmspairs F)  t  subtermsset (trmspairs G)" using 0(1) by auto
      have "t  rm_vars (set X) θ = t  θ" using 0(2) NegChecks rm_vars_ident[of t] by auto
      hence "fv (t  θ)  fvpairs (F pairs rm_vars (set X) θ)  fvpairs (G pairs rm_vars (set X) θ)"
        using 3 trmspairs_fv_subst_subset'[of t _ "rm_vars (set X) θ"] by fastforce
      thus ?thesis using 0(2,3) NegChecks fvsstp_subst_cases(7)[of X F G θ] by auto
    qed (metis (no_types, lifting) 1 2 trmssstp.simps(3) fvsstp_subst_cases(3),
         metis (no_types, lifting) 1 2 trmssstp.simps(4) fvsstp_subst_cases(4),
         metis (no_types, lifting) 1 2 trmssstp.simps(5) fvsstp_subst_cases(5),
         metis (no_types, lifting) 1 2 trmssstp.simps(6) fvsstp_subst_cases(6))
    thus ?thesis using subst_sst_cons[of s S θ] unfolding fvsst_def by auto
  qed
qed simp

lemma trmssstp_funs_term_cases:
  assumes "t  trmssstp (s sstp θ)" "f  funs_term t"
  shows "(u  trmssstp s. f  funs_term u)  (x  fvsstp s. f  funs_term (θ x))"
  using assms
proof (cases s)
  case (NegChecks X F G)
  hence "t  trmspairs (F pairs rm_vars (set X) θ)  t  trmspairs (G pairs rm_vars (set X) θ)"
    using assms(1) by auto
  hence "(utrmspairs F. f  funs_term u)  (xfvpairs F. f  funs_term (rm_vars (set X) θ x)) 
         (utrmspairs G. f  funs_term u)  (xfvpairs G. f  funs_term (rm_vars (set X) θ x))"
    using trmspairs_funs_term_cases[OF _ assms(2), of _ "rm_vars (set X) θ"] by meson
  hence "(u  trmspairs F  trmspairs G. f  funs_term u) 
         (x  fvpairs F  fvpairs G. f  funs_term (rm_vars (set X) θ x))"
    by blast
  thus ?thesis
  proof
    assume "x  fvpairs F  fvpairs G. f  funs_term (rm_vars (set X) θ x)"
    then obtain x where x: "x  fvpairs F  fvpairs G" "f  funs_term (rm_vars (set X) θ x)"
      by auto
    hence "x  set X" "rm_vars (set X) θ x = θ x" by auto
    thus ?thesis using x by (auto simp add: assms NegChecks)
  qed (auto simp add: assms NegChecks)
qed (use assms funs_term_subst[of _ θ] in auto)

lemma trmssst_funs_term_cases:
  assumes "t  trmssst (S sst θ)" "f  funs_term t"
  shows "(u  trmssst S. f  funs_term u)  (x  fvsst S. f  funs_term (θ x))"
using assms(1)
proof (induction S)
  case (Cons s S) thus ?case
  proof (cases "t  trmssst (S sst θ)")
    case False
    hence "t  trmssstp (s sstp θ)" using Cons.prems(1) subst_sst_cons[of s S θ] trmssst_cons by auto
    thus ?thesis using trmssstp_funs_term_cases[OF _ assms(2)] by fastforce
  qed auto
qed simp

lemma fvsst_is_subterm_trmssst_subst:
  assumes "x  fvsst T"
    and "bvarssst T  subst_domain θ = {}"
  shows "θ x  subtermsset (trmssst (T sst θ))"
using trmssst_subst[OF assms(2)] subterms_subst_subset'[of θ "trmssst T"]
      fvsst_is_subterm_trmssst[OF assms(1)]
by (metis (no_types, lifting) image_iff subset_iff eval_term.simps(1))

lemma fvsst_subst_fv_subset:
  assumes "x  fvsst S" "x  bvarssst S" "fv (θ x)  bvarssst S = {}"
  shows "fv (θ x)  fvsst (S sst θ)"
using assms
proof (induction S)
  case (Cons a S)
  note 1 = fv_subst_subset[of _ _ θ]
  note 2 = subst_apply_fv_union subst_apply_fv_unfold[of _ θ] fv_subset image_eqI
  note 3 = fvsstp_subst_cases
  note 4 = fvsstp.simps
  from Cons show ?case
  proof (cases "x  fvsst S")
    case False
    hence 5: "x  fvsstp a" " fv (θ x)  set (bvarssstp a) = {}" "x  set (bvarssstp a)"
      using Cons.prems by auto
    hence "fv (θ x)  fvsstp (a sstp θ)"
    proof (cases a)
      case (Send ts) thus ?thesis using 1 5(1) 3(1) 4(1) by auto
    next
      case (Receive ts) thus ?thesis using 1 5(1) 3(2) 4(2) by auto
    next
      case (NegChecks X F G)
      let  = "rm_vars (set X) θ"
      have *: "x  fvpairs F  fvpairs G" using NegChecks 5(1) by auto
      have **: "fv (θ x)  set X = {}" using NegChecks 5(2) by simp
      have ***: "θ x =  x" using NegChecks 5(3) by auto
      have "fv (θ x)  fvpairs (F pairs )  fvpairs (G pairs )"
        using fvpairs_subst_fv_subset[of x _ ] * *** by auto
      thus ?thesis using NegChecks ** by auto
    qed (metis (full_types) 2 5(1) 3(3) 4(3), metis (full_types) 2 5(1) 3(4) 4(4),
         metis (full_types) 2 5(1) 3(5) 4(5), metis (full_types) 2 5(1) 3(6) 4(6))
    thus ?thesis by (auto simp add: subst_sst_cons[of a S θ])
  qed (auto simp add: subst_sst_cons[of a S θ])
qed simp

lemma (in intruder_model) wftrms_trmssst_subst:
  assumes "wftrms (trmssst A set δ)"
  shows "wftrms (trmssst (A sst δ))"
  using assms
proof (induction A)
  case (Cons a A)
  hence IH: "wftrms (trmssst (A sst δ))" and *: "wftrms (trmssstp a set δ)" by auto
  have "wftrms (trmssstp (a sstp δ))" by (rule wftrms_trmssstp_subst[OF *])
  thus ?case using IH trmssst_subst_cons[of a A δ] by blast
qed simp

lemma fvsst_subst_obtain_var:
  assumes "x  fvsst (S sst δ)"
  shows "y  fvsst S. x  fv (δ y)"
  using assms
proof (induction S)
  case (Cons s S)
  hence "x  fvsst (S sst δ)  y  fvsst S. x  fv (δ y)"
    using bvarssst_cons_subset[of S s]
    by blast
  thus ?case
  proof (cases "x  fvsst (S sst δ)")
    case False
    hence *: "x  fvsstp (s sstp δ)"
      using Cons.prems(1) subst_sst_cons[of s S δ]
      by fastforce

    have "y  fvsstp s. x  fv (δ y)"
    proof (cases s)
      case (NegChecks X F G)
      hence "x  fvpairs (F pairs rm_vars (set X) δ)  x  fvpairs (G pairs rm_vars (set X) δ)"
          and **: "x  set X"
        using * by simp_all
      then obtain y where y: "y  fvpairs F  y  fvpairs G" "x  fv ((rm_vars (set X) δ) y)"
        using fvpairs_subst_obtain_var[of _ _ "rm_vars (set X) δ"]
        by blast
      have "y  set X"
      proof
        assume y_in: "y  set X"
        hence "(rm_vars (set X) δ) y = Var y" by auto
        hence "x = y" using y(2) by simp
        thus False using ** y_in by metis
      qed
      thus ?thesis using NegChecks y by auto
    qed (use * fv_subst_obtain_var in force)+
    thus ?thesis by auto
  qed auto
qed simp

lemma fvsst_subst_subset_range_vars_if_subset_domain:
  assumes "fvsst S  subst_domain σ"
  shows "fvsst (S sst σ)  range_vars σ"
using assms fvsst_subst_obtain_var[of _ S σ] subst_dom_vars_in_subst[of _ σ] subst_fv_imgI[of σ]
by (metis (no_types) in_mono subsetI)

lemma fvsst_in_fv_trmssst: "x  fvsst S  x  fvset (trmssst S)"
proof (induction S)
  case (Cons s S) thus ?case
  proof (cases "x  fvsst S")
    case False
    hence *: "x  fvsstp s" using Cons.prems by simp
    hence "x  fvset (trmssstp s)"
    proof (cases s)
      case (NegChecks X F G)
      hence "x  fvpairs F  x  fvpairs G" using * by simp_all
      thus ?thesis using * fvpairs_in_fv_trmspairs[of x] NegChecks by auto
    qed auto
    thus ?thesis by simp
  qed simp
qed simp

lemma fvsstp_ground_subst_compose:
  assumes "subst_domain δ = subst_domain σ"
    and "range_vars δ = {}" "range_vars σ = {}"
  shows "fvsstp (a sstp δ s θ) = fvsstp (a sstp σ s θ)"
proof -
  note 0 = fv_ground_subst_compose

  have 1: "range_vars δ  set (bvarssstp a) = {}" "range_vars σ  set (bvarssstp a) = {}"
    using assms(2,3) by (blast,blast)

  note 2 = 0[OF assms, of _ θ]

  show ?thesis
  proof (cases a)
    case (NegChecks X F G)
    have 3: "range_vars δ  set X = {}" "range_vars (rm_vars (set X) δ) = {}"
            "range_vars σ  set X = {}" "range_vars (rm_vars (set X) σ) = {}"
      using assms(2,3) rm_vars_img_fv_subset[of "set X"] by auto

    have 4: "subst_domain (rm_vars (set X) δ) = subst_domain (rm_vars (set X) σ)"
      using assms(1) rm_vars_dom[of "set X"] by blast

    have 5: "fv (t  rm_vars (set X) (δ s θ)) = fv (t  rm_vars (set X) (σ s θ))" for t
      using 2[of t] rm_vars_comp[OF 3(1), of t θ] rm_vars_comp[OF 3(3), of t θ]
            0[OF 4 3(2,4), of t "rm_vars (set X) θ"]
      by argo

    have 6: "fvpairs (H pairs rm_vars (set X) (δ s θ)) = fvpairs (H pairs rm_vars (set X) (σ s θ))"
      for H
    proof -
      have "fvpair (h p rm_vars (set X) (δ s θ)) = fvpair (h p rm_vars (set X) (σ s θ))" for h
      proof -
        obtain s t where h: "h = (s,t)" by (metis surj_pair)
        show ?thesis using 5[of s] 5[of t] unfolding h by fast
      qed
      thus ?thesis unfolding subst_apply_pairs_def by auto
    qed

    show ?thesis using 5 6 unfolding NegChecks by simp
  qed (use 2 in auto)
qed

lemma fvsst_ground_subst_compose:
  assumes "subst_domain δ = subst_domain σ"
    and "range_vars δ = {}" "range_vars σ = {}"
  shows "fvsst (S sst δ s θ) = fvsst (S sst σ s θ)"
by (induct S) (auto simp add: fvsstp_ground_subst_compose[OF assms] fvsst_Cons subst_sst_cons)

lemma stateful_strand_step_subst_comp:
  assumes "range_vars δ  set (bvarssstp x) = {}"
  shows "x sstp δ s θ = (x sstp δ) sstp θ"
proof (cases x)
  case (NegChecks X F G)
  hence *: "range_vars δ  set X = {}" using assms by simp
  have "H pairs rm_vars (set X) (δ s θ) = (H pairs rm_vars (set X) δ) pairs rm_vars (set X) θ" for H
    using pairs_subst_comp rm_vars_comp[OF *] by (induct H) (auto simp add: subst_apply_pairs_def)
  thus ?thesis using NegChecks by simp
qed simp_all

lemma stateful_strand_subst_comp:
  assumes "range_vars δ  bvarssst S = {}"
  shows "S sst δ s θ = (S sst δ) sst θ"
using assms
proof (induction S)
  case (Cons s S)
  hence IH: "S sst δ s θ = (S sst δ) sst θ" using Cons by auto

  have "s sstp δ s θ = (s sstp δ) sstp θ"
    using Cons.prems stateful_strand_step_subst_comp[of δ s θ]
    unfolding range_vars_alt_def by auto
  thus ?case using IH by (simp add: subst_apply_stateful_strand_def)
qed simp

lemma subst_apply_bvars_disj_NegChecks:
  assumes "set X  subst_domain θ = {}"
  shows "NegChecks X F G sstp θ = NegChecks X (F pairs θ) (G pairs θ)"
proof -
  have "rm_vars (set X) θ = θ" using assms rm_vars_apply'[of θ "set X"] by auto
  thus ?thesis by simp
qed

lemma subst_apply_NegChecks_no_bvars[simp]:
  "[]⟨∨≠: F ∨∉: F' sstp θ = []⟨∨≠: (F pairs θ) ∨∉: (F' pairs θ)"
  "[]⟨∨≠: [] ∨∉: F' sstp θ = []⟨∨≠: [] ∨∉: (F' pairs θ)"
  "[]⟨∨≠: F ∨∉: [] sstp θ = []⟨∨≠: (F pairs θ) ∨∉: []"
  "[]⟨∨≠: [] ∨∉: [(t,s)] sstp θ = []⟨∨≠: [] ∨∉: ([(t  θ,s  θ)])"
  "[]⟨∨≠: [(t,s)] ∨∉: [] sstp θ = []⟨∨≠: ([(t  θ,s  θ)]) ∨∉: []"
by simp_all

lemma setopssst_mono:
  "set M  set N  setopssst M  setopssst N"
by (auto simp add: setopssst_def)

lemma setopssst_nil[simp]: "setopssst [] = {}"
by (simp add: setopssst_def)

lemma setopssst_cons[simp]: "setopssst (a#A) = setopssstp a  setopssst A"
by (simp add: setopssst_def)

lemma setopssst_cons_subset[simp]: "setopssst A  setopssst (a#A)"
using setopssst_cons[of a A] by blast

lemma setopssst_append: "setopssst (A@B) = setopssst A  setopssst B"
proof (induction A)
  case (Cons a A) thus ?case by (cases a) (auto simp add: setopssst_def)
qed (simp add: setopssst_def)

lemma setopssstp_member_iff:
  "(t,s)  setopssstp x 
    (x = Insert t s  x = Delete t s  (ac. x = InSet ac t s) 
     (X F F'. x = NegChecks X F F'  (t,s)  set F'))"
by (cases x) auto

lemma setopssst_member_iff:
  "(t,s)  setopssst A 
    (Insert t s  set A  Delete t s  set A  (ac. InSet ac t s  set A) 
     (X F F'. NegChecks X F F'  set A  (t,s)  set F'))"
  (is "?P  ?Q")
proof (induction A)
  case (Cons a A) thus ?case
  proof (cases "(t, s)  setopssstp a")
    case True thus ?thesis using setopssstp_member_iff[of t s a] by auto
  qed auto
qed simp

lemma setopssstp_subst:
  assumes "set (bvarssstp a)  subst_domain θ = {}"
  shows "setopssstp (a sstp θ) = setopssstp a pset θ"
proof (cases a)
  case (NegChecks X F G)
  hence "rm_vars (set X) θ = θ" using assms rm_vars_apply'[of θ "set X"] by auto
  hence "setopssstp (a sstp θ) = set (G pairs θ)"
        "setopssstp a pset θ = set G pset θ"
    using NegChecks image_Un by simp_all
  thus ?thesis by (simp add: subst_apply_pairs_def) 
qed simp_all

lemma setopssstp_subst':
  assumes "¬is_NegChecks a"
  shows "setopssstp (a sstp θ) = setopssstp a pset θ"
using assms by (cases a) auto

lemma setopssstp_subst'':
  fixes t::"('a,'b) term × ('a,'b) term" and δ::"('a,'b) subst"
  assumes t: "t  setopssstp (b sstp δ)"
  shows "s  setopssstp b. t = s p rm_vars (set (bvarssstp b)) δ"
proof (cases "is_NegChecks b")
  case True
  then obtain X F G where b: "b = NegChecks X F G" by (cases b) auto
  hence "setopssstp b = set G" "setopssstp (b sstp δ) = set (G pairs rm_vars (set (bvarssstp b)) δ)"
    by simp_all
  thus ?thesis using t subst_apply_pairs_pset_subst[of G] by blast
next
  case False
  hence "setopssstp (b sstp δ) = setopssstp b pset rm_vars (set (bvarssstp b)) δ"
    using setopssstp_subst' bvarssstp_NegChecks by fastforce
  thus ?thesis using t by blast
qed

lemma setopssst_subst:
  assumes "bvarssst S  subst_domain θ = {}"
  shows "setopssst (S sst θ) = setopssst S pset θ"
using assms
proof (induction S)
  case (Cons a S)
  have "bvarssst S  subst_domain θ = {}" and *: "set (bvarssstp a)  subst_domain θ = {}"
    using Cons.prems by auto
  hence IH: "setopssst (S sst θ) = setopssst S pset θ"
    using Cons.IH by auto
  show ?case
    using setopssstp_subst[OF *] IH unfolding setopssst_def
    by (auto simp add: subst_apply_stateful_strand_def)
qed (simp add: setopssst_def)

lemma setopssst_subst':
  fixes p::"('a,'b) term × ('a,'b) term" and δ::"('a,'b) subst"
  assumes "p  setopssst (S sst δ)"
  shows "s  setopssst S. X. set X  bvarssst S  p = s p rm_vars (set X) δ"
using assms
proof (induction S)
  case (Cons a S)
  note 0 = setopssst_cons[of a S] bvarssst_Cons[of a S]
  note 1 = setopssst_cons[of "a sstp δ" "S sst δ"] subst_sst_cons[of a S δ]
  have "p  setopssst (S sst δ)  p  setopssstp (a sstp δ)" using Cons.prems 1 by auto
  thus ?case
  proof
    assume *: "p  setopssstp (a sstp δ)"
    show ?thesis using setopssstp_subst''[OF *] 0 by blast
  next
    assume *: "p  setopssst (S sst δ)"
    show ?thesis using Cons.IH[OF *] 0 by blast
  qed
qed simp


subsection ‹Stateful Constraint Semantics›
context intruder_model
begin

definition negchecks_model where
  "negchecks_model (::('a,'b) subst) (D::('a,'b) dbstate) X F G 
      (δ. subst_domain δ = set X  ground (subst_range δ)  
              ((t,s)  set F. t  δ s   s  δ s ) 
              ((t,s)  set G. (t,s) p δ s   D))"

fun strand_sem_stateful::
  "('fun,'var) terms  ('fun,'var) dbstate  ('fun,'var) stateful_strand  ('fun,'var) subst  bool"
  (_; _; _s)
where
  "M; D; []s = (λ. True)"
| "M; D; Send ts#Ss = (λ. (t  set ts. M  t  )  M; D; Ss )"
| "M; D; Receive ts#Ss = (λ. (set ts set )  M; D; Ss )"
| "M; D; Equality _ t t'#Ss = (λ. t   = t'    M; D; Ss )"
| "M; D; Insert t s#Ss = (λ. M; insert ((t,s) p ) D; Ss )"
| "M; D; Delete t s#Ss = (λ. M; D - {(t,s) p }; Ss )"
| "M; D; InSet _ t s#Ss = (λ. (t,s) p   D  M; D; Ss )"
| "M; D; NegChecks X F F'#Ss = (λ. negchecks_model  D X F F'  M; D; Ss )"


lemmas strand_sem_stateful_induct =
  strand_sem_stateful.induct[case_names Nil ConsSnd ConsRcv ConsEq
                                        ConsIns ConsDel ConsIn ConsNegChecks]

abbreviation constr_sem_stateful (infix s 91) where " s A  {}; {}; As "

lemma stateful_strand_sem_NegChecks_no_bvars:
  "M; D; [t not in s]s   (t  , s  )  D"
  "M; D; [t != s]s   t    s  "
by (simp_all add: negchecks_model_def empty_dom_iff_empty_subst)

lemma strand_sem_ik_mono_stateful:
  "M; D; As   M  M'; D; As "
proof (induction A arbitrary: M M' D rule: strand_sem_stateful.induct)
  case (2 M D ts S)
  hence "t  set ts. M  t  " "M  M'; D; Ss " by auto
  thus ?case
    using ideduct_mono[of M _ "M  M'"] strand_sem_stateful.simps(2)[of "M  M'" D ts S]
    by fastforce
next
  case (3 M D ts S)
  hence "((set ts set )  M)  M'; D; Ss " by simp
  hence "(set ts set )  (M  M'); D; Ss " by (metis Un_assoc)
  thus ?case by simp
qed simp_all

lemma strand_sem_append_stateful:
  "M; D; A@Bs   M; D; As   M  (iksst A set ); dbupdsst A  D; Bs "
  (is "?P  ?Q  ?R")
proof -
  have 1: "?P  ?Q" by (induct A rule: strand_sem_stateful.induct) auto

  have 2: "?P  ?R"
  proof (induction A arbitrary: M D B)
    case (Cons a A) thus ?case
    proof (cases a)
      case (Receive ts)
      have "(set ts set )  (M  (iksst A set )) = M  (iksst (a#A) set )"
           "dbupdsst A  D = dbupdsst (a#A)  D"
        using Receive by (auto simp add: iksst_def)
      thus ?thesis
        using Cons Receive
        by (metis (no_types, lifting) Un_assoc append_Cons strand_sem_stateful.simps(3))
    qed (auto simp add: iksst_def)
  qed (simp add: iksst_def)

  have 3: "?Q  ?R  ?P"
  proof (induction A arbitrary: M D)
    case (Cons a A) thus ?case
    proof (cases a)
      case (Receive ts)
      have "(set ts set )  (M  (iksst A set )) = M  (iksst (a#A) set )"
           "dbupdsst A  D = dbupdsst (a#A)  D"
        using Receive by (auto simp add: iksst_def)
      thus ?thesis
        using Cons Receive
        by (metis (no_types, lifting) Un_assoc append_Cons strand_sem_stateful.simps(3))
    qed (auto simp add: iksst_def)
  qed (simp add: iksst_def)

  show ?thesis by (metis 1 2 3)
qed

lemma negchecks_model_db_subset:
  fixes F F'::"(('a,'b) term × ('a,'b) term) list"
  assumes "D'  D"
  and "negchecks_model  D X F F'"
  shows "negchecks_model  D' X F F'"
proof -
  have "(s,t)  set F'. (s,t) p δ s   D'"
    when "(s,t)  set F'. (s,t) p δ s   D"
    for δ::"('a,'b) subst"
    using that assms(1) by blast
  thus ?thesis using assms(2) unfolding negchecks_model_def by meson
qed

lemma negchecks_model_db_supset:
  fixes F F'::"(('a,'b) term × ('a,'b) term) list"
  assumes "D'  D"
    and "f  set F'. δ. subst_domain δ = set X  ground (subst_range δ)  f p (δ s )  D - D'"
    and "negchecks_model  D' X F F'"
  shows "negchecks_model  D X F F'"
proof -
  have "(s,t)  set F'. (s,t) p δ s   D"
    when "(s,t)  set F'. (s,t) p δ s   D'" "subst_domain δ = set X  ground (subst_range δ)"
    for δ::"('a,'b) subst"
    using that assms(1,2) by blast
  thus ?thesis using assms(3) unfolding negchecks_model_def by meson
qed

lemma negchecks_model_subst:
  fixes F F'::"(('a,'b) term × ('a,'b) term) list"
  assumes "(subst_domain δ  range_vars δ)  set X = {}"
  shows "negchecks_model (δ s θ) D X F F'  negchecks_model θ D X (F pairs δ) (F' pairs δ)"
proof -
  have 0: "σ s (δ s θ) = δ s (σ s θ)"
    when σ: "subst_domain σ = set X" "ground (subst_range σ)" for σ
    by (metis (no_types, lifting) σ subst_compose_assoc assms(1) inf_sup_aci(1)
            subst_comp_eq_if_disjoint_vars sup_inf_absorb range_vars_alt_def)

  { fix σ::"('a,'b) subst" and t t'
    assume σ: "subst_domain σ = set X" "ground (subst_range σ)"
        and *: "(s,t)  set F. s  (σ s (δ s θ))  t  (σ s (δ s θ))"
    obtain f where f: "f  set F" "fst f  σ s (δ s θ)  snd f  σ s (δ s θ)"
      using * unfolding case_prod_unfold by (induct F) auto
    hence "(fst f  δ)  σ s θ  (snd f  δ)  σ s θ" using 0[OF σ] by simp
    moreover have "(fst f  δ, snd f  δ)  set (F pairs δ)"
      using f(1) by (auto simp add: subst_apply_pairs_def)
    ultimately have "(s,t)  set (F pairs δ). s  (σ s θ)  t  (σ s θ)"
      using f(1) by fastforce
  } moreover {
    fix σ::"('a,'b) subst" and t t'
    assume σ: "subst_domain σ = set X" "ground (subst_range σ)"
        and *: "(s,t)  set F'. (s,t) p σ s (δ s θ)  D"
    obtain f where f: "f  set F'" "f p σ s (δ s θ)  D"
      using * by (induct F') auto
    hence "f p δ p σ s θ  D" using 0[OF σ] by (metis subst_pair_compose)
    moreover have "f p δ  set (F' pairs δ)"
      using f(1) by (auto simp add: subst_apply_pairs_def)
    ultimately have "(s,t)  set (F' pairs δ). (s,t) p σ s θ  D"
      using f(1) by (metis (no_types, lifting) case_prodI2)
  } moreover {
    fix σ::"('a,'b) subst" and t t'
    assume σ: "subst_domain σ = set X" "ground (subst_range σ)"
        and *: "(s,t)  set (F pairs δ). s  (σ s θ)  t  (σ s θ)"
    obtain f where f: "f  set (F pairs δ)" "fst f  σ s θ  snd f  σ s θ"
      using * by (induct F) auto
    then obtain g where g: "g  set F" "f = g p δ" by (auto simp add: subst_apply_pairs_def)
    have "fst g  σ s (δ s θ)  snd g  σ s (δ s θ)"
      using f(2) g 0[OF σ] by (simp add: prod.case_eq_if)
    hence "(s,t)  set F. s  (σ s (δ s θ))  t  (σ s (δ s θ))"
      using g by fastforce
  } moreover {
    fix σ::"('a,'b) subst" and t t'
    assume σ: "subst_domain σ = set X" "ground (subst_range σ)"
        and *: "(s,t)  set (F' pairs δ). (s,t) p (σ s θ)  D"
    obtain f where f: "f  set (F' pairs δ)" "f p σ s θ  D"
      using * by (induct F') auto
    then obtain g where g: "g  set F'" "f = g p δ" by (auto simp add: subst_apply_pairs_def)
    have "g p σ s (δ s θ)  D"
      using f(2) g 0[OF σ] by (simp add: prod.case_eq_if)
    hence "(s,t)  set F'. (s,t) p (σ s (δ s θ))  D"
      using g by (metis (no_types, lifting) case_prodI2)
  } ultimately show ?thesis
      using assms unfolding negchecks_model_def by meson
qed

lemma strand_sem_subst_stateful:
  fixes δ::"('fun,'var) subst"
  assumes "(subst_domain δ  range_vars δ)  bvarssst S = {}"
  shows "M; D; Ss (δ s θ)  M; D; S sst δs θ"
proof
  note [simp] = subst_sst_cons[of _ _ δ] subst_subst_compose[of _ δ θ]

  have "(subst_domain δ  range_vars δ)  (subst_domain γ  range_vars γ) = {}"
    when δ: "(subst_domain δ  range_vars δ)  set X = {}"
      and γ: "subst_domain γ = set X" "ground (subst_range γ)"
    for X and γ::"('fun,'var) subst"
    using δ γ unfolding range_vars_alt_def by auto
  hence 0: "γ s δ = δ s γ"
    when δ: "(subst_domain δ  range_vars δ)  set X = {}"
      and γ: "subst_domain γ = set X" "ground (subst_range γ)"
    for γ X
    by (metis δ γ subst_comp_eq_if_disjoint_vars)

  show "M; D; Ss (δ s θ)  M; D; S sst δs θ" using assms
  proof (induction S arbitrary: M D rule: strand_sem_stateful_induct)
    case (ConsSnd M D ts S)
    hence "t  set ts. M  t  δ  θ" and IH: "M; D; S sst δs θ" by auto
    hence "t  set (ts list δ). M  t  θ" by simp
    thus ?case using IH by simp
  next
    case (ConsRcv M D ts S)
    hence "(set ts set δ s θ)  M; D; S sst δs θ" by simp
    hence "(set (ts list δ) set θ)  M; D; S sst δs θ" by (metis list.set_map subst_comp_all) 
    thus ?case by simp
  next
    case (ConsNegChecks M D X F F' S)
    hence *: "M; D; S sst δs θ" and **: "(subst_domain δ  range_vars δ)  set X = {}"
      unfolding bvarssst_def negchecks_model_def by (force, auto)
    have "negchecks_model (δ s θ) D X F F'" using ConsNegChecks by auto
    hence "negchecks_model θ D X (F pairs δ) (F' pairs δ)"
      using 0[OF **] negchecks_model_subst[OF **] by blast
    moreover have "rm_vars (set X) δ = δ" using ConsNegChecks.prems(2) by force
    ultimately show ?case using * by auto
  qed simp_all

  show "M; D; S sst δs θ  M; D; Ss (δ s θ)" using assms
  proof (induction S arbitrary: M D rule: strand_sem_stateful_induct)
    case (ConsSnd M D ts S)
    hence "t  set (ts list δ). M  t  θ" and IH: "M; D; Ss (δ s θ)" by auto
    hence "t  set ts. M  t  δ  θ" by simp
    thus ?case using IH by simp
  next
    case (ConsRcv M D ts S)
    hence "(set (ts list δ) set θ)  M; D; Ss (δ s θ)" by simp
    hence "(set ts set δ s θ)  M; D; Ss (δ s θ)" by (metis list.set_map subst_comp_all) 
    thus ?case by simp
  next
    case (ConsNegChecks M D X F F' S)
    have δ: "rm_vars (set X) δ = δ" using ConsNegChecks.prems(2) by force
    hence *: "M; D; Ss (δ s θ)" and **: "(subst_domain δ  range_vars δ)  set X = {}"
      using ConsNegChecks unfolding bvarssst_def negchecks_model_def by auto
    have "negchecks_model θ D X (F pairs δ) (F' pairs δ)"
      using ConsNegChecks.prems(1) δ by (auto simp add: subst_compose_assoc negchecks_model_def)
    hence "negchecks_model (δ s θ) D X F F'"
      using 0[OF **] negchecks_model_subst[OF **] by blast
    thus ?case using * by auto
  qed simp_all
qed

lemma strand_sem_receive_prepend_stateful:
  assumes "M; D; Ss θ"
    and "list_all is_Receive S'"
  shows "M; D; S@S's θ"
using assms(2)
proof (induction S' rule: List.rev_induct)
  case (snoc x S')
  hence "t. x = receive⟨t" "M; D; S@S's θ"
    unfolding list_all_iff is_Receive_def by auto
  thus ?case using strand_sem_append_stateful[of M D "S@S'" "[x]" θ] by auto
qed (simp add: assms(1))

lemma negchecks_model_model_swap:
  fixes I J::"('a,'b) subst"
  assumes "x  (fvpairs F  fvpairs G) - set X. I x = J x"
    and "negchecks_model I D X F G"
  shows "negchecks_model J D X F G"
proof -
  have 0: "x  (fvpairs F  fvpairs G). (δ s I) x = (δ s J) x"
    when "subst_domain δ = set X" "ground (subst_range δ)"
    for δ::"('a,'b) subst"
    using that assms(1)
    by (metis DiffI ground_subst_range_empty_fv subst_comp_notin_dom_eq
              subst_ground_ident_compose(1))

  have 1: "s  (δ s J)  t  (δ s J)"
    when "s  (δ s I)  t  (δ s I)" "(s,t)  set F"
         "subst_domain δ = set X" "ground (subst_range δ)"
    for δ::"('a,'b) subst" and s t
    using that(1,2) 0[OF that(3,4)] term_subst_eq_conv[of _ "δ s I" "δ s J"]
          UnCI fvpairs_inI(4,5)[OF that(2)]
    by metis

  have 2: "(s,t) p (δ s J)  D"
    when "(s,t) p (δ s I)  D" "(s,t)  set G"
         "subst_domain δ = set X" "ground (subst_range δ)"
    for δ::"('a,'b) subst" and s t
    using that(1,2) 0[OF that(3,4)] fvpairs_inI(4,5)[of s t G]
          term_subst_eq_conv[of s "δ s I" "δ s J"]
          term_subst_eq_conv[of t "δ s I" "δ s J"]
    by simp

  have 3: "((s,t)  set F. s  δ s J  t  δ s J)  ((s,t)  set G. (s, t) p δ s J  D)"
    when "subst_domain δ = set X" "ground (subst_range δ)"
         "((s,t)  set F. s  δ s I  t  δ s I)  ((s,t)  set G. (s, t) p δ s I  D)"
    for δ::"('a,'b) subst"
    using 1[OF _ _ that(1,2)] 2[OF _ _ that(1,2)] that(3) by blast
  thus ?thesis
    using assms(2) unfolding negchecks_model_def by simp
qed

lemma strand_sem_model_swap:
  assumes "x  fvsst S. I x = J x"
    and "M; D; Ss I"
  shows "M; D; Ss J"
using assms(2,1)
proof (induction S arbitrary: M D rule: strand_sem_stateful_induct)
  case (ConsSnd M D ts S)
  hence *: "M; D; Ss J" "t  set ts. M  t  I" "x  fvset (set ts). I x = J x"
    by (fastforce, fastforce, fastforce)

  have "t  set ts. M  t  J"
    using *(2,3) term_subst_eq_conv[of _ I J]
    by (metis fv_subset subsetD)
  thus ?case using *(1) by auto
next
  case (ConsRcv M D ts S)
  hence *: "(set ts set I)  M; D; Ss J" "x  fvset (set ts). I x = J x"
    by (fastforce, fastforce)

  have "set ts set I = set ts set J"
    using *(2) term_subst_eq_conv[of _ I J]
    by (meson fv_subset image_cong subsetD)
  thus ?case using *(1) by simp
next
  case (ConsEq M D ac t t' S) thus ?case
    using term_subst_eq_conv[of t I J] term_subst_eq_conv[of t' I J] by force
next
  case (ConsIns M D t s S) thus ?case
    using term_subst_eq_conv[of t I J] term_subst_eq_conv[of s I J] by force
next
  case (ConsDel M D t s S) thus ?case
    using term_subst_eq_conv[of t I J] term_subst_eq_conv[of s I J] by force
next
  case (ConsIn M D uv t s S) thus ?case
    using term_subst_eq_conv[of t I J] term_subst_eq_conv[of s I J] by force
next
  case (ConsNegChecks M D X F F' S)
  hence "M; D; Ss J" "negchecks_model I D X F F'" "xfvpairs F  fvpairs F' - set X. I x = J x"
    by (fastforce, fastforce, fastforce)
  thus ?case using negchecks_model_model_swap[of F F' X I J D] by fastforce
qed simp

lemma strand_sem_receive_send_append:
  assumes A: "M; D; As I"
  shows "M; D; A@[receive⟨[t],send⟨[t]]s I"
proof -
  have "M  (iksst (A@[receive⟨[t]]) set I)  t  I"
    using in_iksst_iff[of t "A@[receive⟨[t]]"] by auto
  thus ?thesis
    using A strand_sem_append_stateful[of M D A "[receive⟨[t]]" I]
          strand_sem_append_stateful[of M D "A@[receive⟨[t]]" "[send⟨[t]]" I]
    by force
qed

lemma strand_sem_stateful_if_no_send_or_check:
  assumes A: "list_all (λa. ¬is_Send a  ¬is_Check_or_Assignment a) A"
  shows "M; D; As I"
using A
proof (induction A rule: List.rev_induct)
  case (snoc a A)
  hence IH: "M; D; As I" and a: "¬is_Send a" "¬is_Check_or_Assignment a" by auto
  from a have "M; D; [a]s I" for M D by (cases a) auto
  thus ?case using IH strand_sem_append_stateful[of M D A "[a]" I] by blast
qed simp

lemma strand_sem_stateful_if_sends_deduct:
  assumes "list_all is_Send A"
    and "ts. send⟨ts  set A  (t  set ts. M  t  I)"
  shows "M; D; As I"
using assms
proof (induction A rule: List.rev_induct)
  case (snoc a A)
  hence IH: "M; D; As I" by auto
  obtain ts where a: "a = send⟨ts" "t  set ts. M  t  I" using snoc.prems by (cases a) auto

  have "M  (iksst A set I); D; [a]s I" for D
    using ideduct_mono[of M _ "M  (iksst A set I)"] a by auto
  thus ?case using IH strand_sem_append_stateful[of M D A "[a]" I] by blast
qed simp

lemma strand_sem_stateful_if_checks:
  assumes "list_all is_Check_or_Assignment A"
    and "ac t s. ac: t  s  set A  t  I = s  I"
    and "ac t s. ac: t  s  set A  (t  I, s  I)  D"
    and "X F G. X⟨∨≠: F ∨∉: G  set A  negchecks_model I D X F G"
  shows "M; D; As I"
using assms
proof (induction A rule: List.rev_induct)
  case (snoc a A)
  hence IH: "M; D; As I" and a: "is_Check_or_Assignment a" by auto

  have 0: "dbupdsst A I D = D"
    using snoc.prems(1) dbupdsst_no_upd[of A I D] unfolding list_all_iff by auto

  have "M; D; [a]s I" for M using a snoc.prems(2,3,4) by (cases a) auto
  thus ?case using IH strand_sem_append_stateful[of M D A "[a]" I] unfolding 0 by blast
qed simp

lemma strand_sem_stateful_sends_deduct:
  assumes A: "M; D; As I"
    and ts: "send⟨ts  set A"
    and t: "t  set ts"
  shows "M  (iksst A set I)  t  I"
using A ts
proof (induction A arbitrary: M D rule: List.rev_induct)
  case (snoc a A)
  have 0: "M; D; As I"
    using strand_sem_append_stateful snoc.prems(1) by fast

  have 1: "M  (iksst A set I)  M  (iksst (A@[a]) set I)"
    by auto

  have 2: "M  (iksst (A@[send⟨ts]) set I) = M  (iksst A set I)"
    using in_iksst_iff[of _ A] in_iksst_iff[of _ "A@[send⟨ts]"] by fastforce

  show ?case
  proof (cases "send⟨ts  set A")
    case True show ?thesis by (rule ideduct_mono[OF snoc.IH[OF 0 True] 1])
  next
    case False
    hence a: "a = send⟨ts" using snoc.prems(2) by force
    show ?thesis
      using strand_sem_append_stateful[of M D A "[a]" I] snoc.prems(1) t
      unfolding a 2 by auto
  qed
qed simp

end


subsection ‹Well-Formedness Lemmata›
lemma wfvarsoccsst_subset_wfrestrictedvarssst[simp]:
  "wfvarsoccssst S  wfrestrictedvarssst S"
by (induction S)
   (auto simp add: wfrestrictedvarssst_def wfvarsoccssst_def
         split: stateful_strand_step.split poscheckvariant.split)

lemma wfvarsoccssst_append: "wfvarsoccssst (S@S') = wfvarsoccssst S  wfvarsoccssst S'"
by (simp add: wfvarsoccssst_def)

lemma wfrestrictedvarssst_union[simp]:
  "wfrestrictedvarssst (S@T) = wfrestrictedvarssst S  wfrestrictedvarssst T"
by (simp add: wfrestrictedvarssst_def)

lemma wfrestrictedvarssst_singleton:
  "wfrestrictedvarssst [s] = wfrestrictedvarssstp s"
by (simp add: wfrestrictedvarssst_def)

lemma iksst_fv_subset_wfrestrictedvarssst[simp]:
  "fvset (iksst S)  wfrestrictedvarssst S"
using in_iksst_iff[of _ S] unfolding wfrestrictedvarssst_def by force

lemma wfsst_prefix[dest]: "wf'sst V (S@S')  wf'sst V S"
by (induct S rule: wf'sst.induct) auto

lemma wfsst_vars_mono: "wf'sst V S  wf'sst (V  W) S"
proof (induction S arbitrary: V)
  case (Cons x S) thus ?case
  proof (cases x)
    case (Send ts)
    have "wf'sst (V  W  fvset (set ts)) S"
      using Cons.prems(1) Cons.IH Send by (metis sup_assoc sup_commute wf'sst.simps(3))
    thus ?thesis by (simp add: Send)
  next
    case (Equality a t t')
    show ?thesis
    proof (cases a)
      case Assign
      hence "wf'sst (V  fv t  W) S" "fv t'  V  W" using Equality Cons.prems(1) Cons.IH by auto
      thus ?thesis using Equality Assign by (simp add: sup_commute sup_left_commute)
    next
      case Check thus ?thesis using Equality Cons by auto
    qed
  next
    case (InSet a t t')
    show ?thesis
    proof (cases a)
      case Assign
      hence "wf'sst (V  fv t  fv t'  W) S" using InSet Cons.prems(1) Cons.IH by auto
      thus ?thesis using InSet Assign by (simp add: sup_commute sup_left_commute)
    next
      case Check thus ?thesis using InSet Cons by auto
    qed
  qed auto
qed simp

lemma wfsstI[intro]: "wfrestrictedvarssst S  V  wf'sst V S"
proof (induction S)
  case (Cons x S) thus ?case
  proof (cases x)
    case (Send ts)
    hence "wf'sst V S" "V  fvset (set ts) = V"
      using Cons
      unfolding wfrestrictedvarssst_def
      by auto
    thus ?thesis using Send by simp
  next
    case (Equality a t t')
    show ?thesis
    proof (cases a)
      case Assign
      hence "wf'sst V S" "fv t'  V"
        using Equality Cons 
        unfolding wfrestrictedvarssst_def
        by auto
      thus ?thesis using wfsst_vars_mono Equality Assign by simp
    next
      case Check
      thus ?thesis
        using Equality Cons
        unfolding wfrestrictedvarssst_def
        by auto
    qed
  next
    case (InSet a t t')
    show ?thesis
    proof (cases a)
      case Assign
      hence "wf'sst V S" "fv t  fv t'  V"
        using InSet Cons
        unfolding wfrestrictedvarssst_def
        by auto
      thus ?thesis using wfsst_vars_mono InSet Assign by (simp add: Un_assoc) 
    next
      case Check
      thus ?thesis
        using InSet Cons
        unfolding wfrestrictedvarssst_def
        by auto
    qed
  qed (simp_all add: wfrestrictedvarssst_def)
qed (simp add: wfrestrictedvarssst_def)

lemma wfsstI'[intro]:
  assumes "((λx. case x of
            Receive ts  fvset (set ts)
          | Equality Assign _ t'  fv t'
          | Insert t t'  fv t  fv t'
          | _  {}) ` set S)  V"
  shows "wf'sst V S"
using assms
proof (induction S)
  case (Cons x S) thus ?case
  proof (cases x)
    case (Equality a t t')
    thus ?thesis using Cons by (cases a) (auto simp add: wfsst_vars_mono)
  next
    case (InSet a t t')
    thus ?thesis using Cons by (cases a) (auto simp add: wfsst_vars_mono Un_assoc)
  qed (simp_all add: wfsst_vars_mono)
qed simp

lemma wfsst_append_exec: "wf'sst V (S@S')  wf'sst (V  wfvarsoccssst S) S'"
proof (induction S arbitrary: V)
  case (Cons x S V) thus ?case
  proof (cases x)
    case (Send ts)
    hence "wf'sst (V  fvset (set ts)  wfvarsoccssst S) S'" using Cons.prems Cons.IH by simp
    thus ?thesis using Send unfolding wfvarsoccssst_def by (auto simp add: sup_assoc)
  next
    case (Equality a t t') show ?thesis
    proof (cases a)
      case Assign
      hence "wf'sst (V  fv t  wfvarsoccssst S) S'" using Equality Cons.prems Cons.IH by auto
      thus ?thesis using Equality Assign unfolding wfvarsoccssst_def by (auto simp add: sup_assoc)
    next
      case Check
      hence "wf'sst (V  wfvarsoccssst S) S'" using Equality Cons.prems Cons.IH by auto
      thus ?thesis using Equality Check unfolding wfvarsoccssst_def by (auto simp add: sup_assoc)
    qed
  next
    case (InSet a t t') show ?thesis
    proof (cases a)
      case Assign
      hence "wf'sst (V  fv t  fv t'  wfvarsoccssst S) S'" using InSet Cons.prems Cons.IH by auto
      thus ?thesis using InSet Assign unfolding wfvarsoccssst_def by (auto simp add: sup_assoc)
    next
      case Check
      hence "wf'sst (V  wfvarsoccssst S) S'" using InSet Cons.prems Cons.IH by auto
      thus ?thesis using InSet Check unfolding wfvarsoccssst_def by (auto simp add: sup_assoc)
    qed
  qed (auto simp add: wfvarsoccssst_def)
qed (simp add: wfvarsoccssst_def)

lemma wfsst_append:
  "wf'sst X S  wf'sst Y T  wf'sst (X  Y) (S@T)"
proof (induction X S rule: wf'sst.induct)
  case 1 thus ?case by (metis wfsst_vars_mono Un_commute append_Nil)
next
  case 3 thus ?case by (metis append_Cons Un_commute Un_assoc wf'sst.simps(3))
next
  case (4 V t t' S)
  hence *: "fv t'  V" and "wf'sst (V  fv t  Y) (S @ T)" by simp_all
  hence "wf'sst (V  Y  fv t) (S @ T)" by (metis Un_commute Un_assoc)
  thus ?case using * by auto
next
  case (8 V t t' S)
  hence "wf'sst (V  fv t  fv t'  Y) (S @ T)" by simp_all
  hence "wf'sst (V  Y  fv t  fv t') (S @ T)" by (metis Un_commute Un_assoc)
  thus ?case by auto
qed auto

lemma wfsst_append_suffix:
  "wf'sst V S  wfrestrictedvarssst S'  wfrestrictedvarssst S  V  wf'sst V (S@S')"
proof (induction V S rule: wf'sst.induct)
  case (2 V ts S)
  hence *: "fvset (set ts)  V" "wf'sst V S" by simp_all
  hence "wfrestrictedvarssst S'  wfrestrictedvarssst S  V"
    using "2.prems"(2) unfolding wfrestrictedvarssst_def by fastforce
  thus ?case using "2.IH" * by simp
next
  case (3 V ts S)
  hence *: "wf'sst (V  fvset (set ts)) S" by simp_all
  hence "wfrestrictedvarssst S'  wfrestrictedvarssst S  (V  fvset (set ts))"
    using "3.prems"(2) unfolding wfrestrictedvarssst_def by auto
  thus ?case using "3.IH" * by simp
next
  case (4 V t t' S)
  hence *: "fv t'  V" "wf'sst (V  fv t) S" by simp_all
  moreover have "varssstp (t := t') = fv t  fv t'"
    by simp
  moreover have "wfrestrictedvarssst (t := t'#S) = fv t  fv t'  wfrestrictedvarssst S"
    unfolding wfrestrictedvarssst_def by auto
  ultimately have "wfrestrictedvarssst S'  wfrestrictedvarssst S  (V  fv t)"
    using "4.prems"(2) by blast
  thus ?case using "4.IH" * by simp
next
  case (6 V t t' S)
  hence *: "fv t  fv t'  V" "wf'sst V S" by simp_all
  moreover have "varssstp (insert⟨t,t') = fv t  fv t'"
    by simp
  moreover have "wfrestrictedvarssst (insert⟨t,t'#S) = fv t  fv t'  wfrestrictedvarssst S"
    unfolding wfrestrictedvarssst_def by auto
  ultimately have "wfrestrictedvarssst S'  wfrestrictedvarssst S  V"
    using "6.prems"(2) by blast
  thus ?case using "6.IH" * by simp
next
  case (8 V t t' S)
  hence *: "wf'sst (V  fv t  fv t') S" by simp_all
  moreover have "varssstp (select⟨t,t') = fv t  fv t'"
    by simp
  moreover have "wfrestrictedvarssst (select⟨t,t'#S) = fv t  fv t'  wfrestrictedvarssst S"
    unfolding wfrestrictedvarssst_def by auto
  ultimately have "wfrestrictedvarssst S'  wfrestrictedvarssst S  (V  fv t  fv t')"
    using "8.prems"(2) by blast
  thus ?case using "8.IH" * by simp
qed (simp_all add: wfsstI wfrestrictedvarssst_def)

lemma wfsst_append_suffix':
  assumes "wf'sst V S"
    and "((λx. case x of
            Receive ts  fvset (set ts)
          | Equality Assign _ t'  fv t'
          | Insert t t'  fv t  fv t'
          | _  {}) ` set S')  wfvarsoccssst S  V"
  shows "wf'sst V (S@S')"
using assms
by (induction V S rule: wf'sst.induct)
   (auto simp add: wfsstI' wfsst_vars_mono wfvarsoccssst_def)

lemma wfsst_subst_apply:
  "wf'sst V S  wf'sst (fvset (δ ` V)) (S sst δ)"
proof (induction S arbitrary: V rule: wf'sst.induct)
  case (2 V ts S)
  hence "wf'sst V S" "fvset (set ts)  V" by simp_all
  hence "wf'sst (fvset (δ ` V)) (S sst δ)" "fvset (set ts set δ)  fvset (δ ` V)"
    using "2.IH" subst_apply_fv_subset by (simp, force)
  thus ?case by (simp add: subst_apply_stateful_strand_def)
next
  case (3 V ts S)
  hence "wf'sst (V  fvset (set ts)) S" by simp
  hence "wf'sst (fvset (δ ` (V  fvset (set ts)))) (S sst δ)" using "3.IH" by metis
  hence "wf'sst (fvset (δ ` V)  fvset (set ts set δ)) (S sst δ)"
    using  subst_apply_fv_unfold_set[of δ ts] by fastforce
  thus ?case by (simp add: subst_apply_stateful_strand_def)
next
  case (4 V t t' S)
  hence "wf'sst (V  fv t) S" "fv t'  V" by auto
  hence "wf'sst (fvset (δ ` (V  fv t))) (S sst δ)" and *: "fv (t'  δ)  fvset (δ ` V)"
    using "4.IH" subst_apply_fv_subset by force+
  hence "wf'sst (fvset (δ ` V)  fv (t  δ)) (S sst δ)" by (metis subst_apply_fv_union)
  thus ?case using * by (simp add: subst_apply_stateful_strand_def)
next
  case (6 V t t' S)
  hence "wf'sst V S" "fv t  fv t'  V" by auto
  hence "wf'sst (fvset (δ ` V)) (S sst δ)" "fv (t  δ)  fvset (δ ` V)" "fv (t'  δ)  fvset (δ ` V)"
    using "6.IH" subst_apply_fv_subset by force+
  thus ?case by (simp add: sup_assoc subst_apply_stateful_strand_def)
next
  case (8 V t t' S)
  hence "wf'sst (V  fv t  fv t') S" by auto
  hence "wf'sst (fvset (δ ` (V  fv t  fv t'))) (S sst δ)"
    using "8.IH" subst_apply_fv_subset by force
  hence "wf'sst (fvset (δ ` V)  fv (t  δ)  fv (t'  δ)) (S sst δ)" by (metis subst_apply_fv_union)
  thus ?case by (simp add: subst_apply_stateful_strand_def)
qed (auto simp add: subst_apply_stateful_strand_def)

lemma wfsst_induct[consumes 1,
                  case_names Nil ConsSnd ConsRcv ConsEq ConsEq2 ConsIn ConsIns ConsDel
                             ConsNegChecks]:
  fixes S::"('a,'b) stateful_strand"
  assumes "wf'sst V S"
          "P []"
          "ts S. wf'sst V S; P S  P (S@[Send ts])"
          "ts S. wf'sst V S; P S; fvset (set ts)  V  wfvarsoccssst S  P (S@[Receive ts])"
          "t t' S. wf'sst V S; P S; fv t'  V  wfvarsoccssst S  P (S@[Equality Assign t t'])"
          "t t' S. wf'sst V S; P S  P (S@[Equality Check t t'])"
          "ac t t' S. wf'sst V S; P S  P (S@[InSet ac t t'])"
          "t t' S. wf'sst V S; P S; fv t  fv t'  V  wfvarsoccssst S  P (S@[Insert t t'])"
          "t t' S. wf'sst V S; P S  P (S@[Delete t t'])"
          "X F G S. wf'sst V S; P S  P (S@[NegChecks X F G])"
  shows "P S"
using assms
proof (induction S rule: List.rev_induct)
  case (snoc x S)
  hence *: "wf'sst V S" "wf'sst (V  wfvarsoccssst S) [x]"
    by (metis wfsst_prefix, metis wfsst_append_exec)
  have IH: "P S" using snoc.IH[OF *(1)] snoc.prems by auto
  note ** = snoc.prems(3-)[OF *(1) IH] *(2)
  show ?case
  proof (cases x)
    case (Send ts) thus ?thesis using **(1) by blast
  next
    case (Receive ts) thus ?thesis using **(2,9) by simp
  next
    case (Equality ac t t') thus ?thesis using **(3,4,9) by (cases ac) auto
  next
    case (Insert t t') thus ?thesis using **(6,9) by force
  next
    case (Delete t t') thus ?thesis using **(7) by presburger
  next
    case (NegChecks X F G) thus ?thesis using **(8) by presburger
  next
    case (InSet ac t t') thus ?thesis using **(5) by (cases ac) auto
  qed
qed simp

lemma wfsst_strand_first_Send_var_split:
  assumes "wf'sst {} S" "v  wfrestrictedvarssst S. t     v"
  shows "Spre Ssuf. ¬(w  wfrestrictedvarssst Spre. t     w)  (
                    (ts. S = Spre@send⟨ts#Ssuf  t   set set ts set ) 
                    (s u. S = Spre@assign: s  u#Ssuf  t    s    ¬(t   set  ` fv u)) 
                    (s u. S = Spre@assign: s  u#Ssuf  (t    s    t    u  )))"
    (is "Spre Ssuf. ?P Spre  ?Q S Spre Ssuf")
using assms
proof (induction S rule: wfsst_induct)
  case (ConsSnd ts' S) show ?case
  proof (cases "w  wfrestrictedvarssst S. t     w")
    case True
    then obtain Spre Ssuf where "?P Spre" "?Q S Spre Ssuf"
      using ConsSnd.IH by atomize_elim auto
    thus ?thesis by fastforce
  next
    case False
    then obtain v where v: "v  fvset (set ts')" "t     v"
      using ConsSnd.prems unfolding wfrestrictedvarssst_def by auto
    then obtain t' where t': "t'  set ts'" "v  fv t'" by auto
    have "t    t'  "
      using v(2) t'(2) subst_mono[of "Var v" t' ] vars_iff_subtermeq[of v] term.order_trans
      by auto
    hence "t   set set ts' set " using v(1) t'(1) by blast
    thus ?thesis using False v by blast
  qed
next
  case (ConsRcv t' S)
  have "fvset (set t')  wfrestrictedvarssst S"
    using ConsRcv.hyps wfvarsoccsst_subset_wfrestrictedvarssst[of S] by blast
  hence "v  wfrestrictedvarssst S. t     v"
    using ConsRcv.prems unfolding wfrestrictedvarssst_def by fastforce
  then obtain Spre Ssuf where "?P Spre" "?Q S Spre Ssuf"
    using ConsRcv.IH by atomize_elim auto
  thus ?case by fastforce
next
  case (ConsEq s s' S)
  have *: "fv s'  wfrestrictedvarssst S"
    using ConsEq.hyps wfvarsoccsst_subset_wfrestrictedvarssst[of S] by blast
  show ?case
  proof (cases "v  wfrestrictedvarssst S. t     v")
    case True
    then obtain Spre Ssuf where "?P Spre" "?Q S Spre Ssuf"
      using ConsEq.IH by atomize_elim auto
    thus ?thesis by fastforce
  next
    case False
    then obtain v where "v  fv s" "t     v" and **: "fv s'  wfrestrictedvarssst S"
      using ConsEq.prems * unfolding wfrestrictedvarssst_def by auto
    hence "t    s  "
      using vars_iff_subtermeq[of v s] subst_mono[of "Var v" s ] term.order_trans by auto
    thus ?thesis using False ** by fastforce
  qed
next
  case (ConsEq2 s s' S)
  have "wfrestrictedvarssst (S@[Equality Check s s']) = wfrestrictedvarssst S"
    unfolding wfrestrictedvarssst_def by auto
  hence "v  wfrestrictedvarssst S. t     v" using ConsEq2.prems by metis
  then obtain Spre Ssuf where "?P Spre" "?Q S Spre Ssuf" using ConsEq2.IH by atomize_elim auto
  thus ?case by fastforce
next
  case (ConsNegChecks X F G S)
  hence "v  wfrestrictedvarssst S. t     v" unfolding wfrestrictedvarssst_def by simp
  then obtain Spre Ssuf where "?P Spre" "?Q S Spre Ssuf" using ConsNegChecks.IH by atomize_elim auto
  thus ?case by fastforce
next
  case (ConsIn ac s s' S)
  show ?case
  proof (cases "v  wfrestrictedvarssst S. t     v")
    case True
    then obtain Spre Ssuf where "?P Spre" "?Q S Spre Ssuf"
      using ConsIn.IH by atomize_elim auto
    thus ?thesis by fastforce
  next
    case False
    hence ac: "ac = assign" using ConsIn.prems unfolding wfrestrictedvarssst_def by (cases ac) auto
    obtain v where "v  fv s  fv s'" "t     v"
        using ConsIn.prems False unfolding wfrestrictedvarssst_def ac by auto
    hence *: "t    s    t    s'  "
      using vars_iff_subtermeq[of v s] vars_iff_subtermeq[of v s']
            subst_mono[of "Var v" s ] subst_mono[of "Var v" s' ] term.order_trans
      by auto
    show ?thesis using * False unfolding ac by fast
  qed
next
  case (ConsIns s s' S)
  have "fv s  fv s'  wfrestrictedvarssst S"
    using ConsIns.hyps wfvarsoccsst_subset_wfrestrictedvarssst[of S] by blast
  hence "v  wfrestrictedvarssst S. t     v"
      using ConsIns.prems unfolding wfrestrictedvarssst_def by fastforce
  then obtain Spre Ssuf where "?P Spre" "?Q S Spre Ssuf" using ConsIns.IH by atomize_elim auto
  thus ?case by fastforce
next
  case (ConsDel s s' S)
  hence "v  wfrestrictedvarssst S. t     v" unfolding wfrestrictedvarssst_def by simp
  then obtain Spre Ssuf where "?P Spre" "?Q S Spre Ssuf" using ConsDel.IH by atomize_elim auto
  thus ?case by fastforce
qed (simp add: wfrestrictedvarssst_def)

lemma wfsst_vars_mono': "wf'sst V S  V  W  wf'sst W S"
by (metis Diff_partition wfsst_vars_mono)

lemma wfrestrictedvarssst_receives_only_eq:
  assumes "list_all is_Receive S"
  shows "wfrestrictedvarssst S = fvsst S"
using assms
proof (induction S)
  case (Cons a A)
  obtain ts where a: "a = receive⟨ts" using Cons.prems by (cases a) auto
  have IH: "wfrestrictedvarssst A = fvsst A" using Cons.prems Cons.IH by simp
  show ?case using IH unfolding a wfrestrictedvarssst_def by simp
qed (simp add: wfrestrictedvarssst_def)

lemma wfvarsoccssst_receives_only_empty:
  assumes "list_all is_Receive S"
  shows "wfvarsoccssst S = {}"
using assms
proof (induction S)
  case (Cons a A)
  obtain ts where a: "a = receive⟨ts" using Cons.prems by (cases a) auto
  have IH: "wfvarsoccssst A = {}" using Cons.prems Cons.IH by simp
  show ?case using IH unfolding a wfvarsoccssst_def by simp
qed (simp add: wfvarsoccssst_def)

lemma wfsst_sends_only:
  assumes "list_all is_Send S"
  shows "wf'sst V S"
using assms
proof (induction S arbitrary: V)
  case (Cons s S) thus ?case by (cases s) auto
qed simp

lemma wfsst_sends_only_prepend:
  assumes "wf'sst V S"
    and "list_all is_Send S'"
  shows "wf'sst V (S'@S)"
using wfsst_append[OF wfsst_sends_only[OF assms(2), of "{}"] assms(1)] by simp

lemma wfsst_receives_only_fv_subset:
  assumes "wf'sst V S"
    and "list_all is_Receive S"
  shows "fvsst S  V"
using assms
proof (induction rule: wfsst_induct)
  case (ConsRcv ts S) thus ?case using wfvarsoccssst_receives_only_empty[of S] by auto
qed auto

lemma wfsst_append_suffix'':
  assumes "wf'sst V S"
    and "wfrestrictedvarssst S'  wfvarsoccssst S  V"
  shows "wf'sst V (S@S')"
using assms
by (induction V S rule: wf'sst.induct)
   (auto simp add: wfsstI' wfsst_vars_mono wfvarsoccssst_def)


end