Theory Proof_Term_Utils

section‹Preliminaries›

theory Proof_Term_Utils
imports 
  First_Order_Terms.Matching
  First_Order_Rewriting.Term_Impl
begin

subsection‹Utilities for Lists›

lemma obtain_list_with_property:
  assumes "x  set xs. a. P a x"
  shows "as. length as = length xs  (i < length xs. P (as!i) (xs!i))"
  using assms proof(induct xs)
  case (Cons a xs)
  then show ?case
    by (metis length_map nth_map nth_mem)
qed simp

lemma card_Union_Sum:
  assumes "is_partition (map f [0..<length xs])" 
    and "i < length xs. finite (f i)"
  shows "card (i<length xs. f i) = (i<length xs. card (f i))"
proof-
  from assms(1) have disj:"pairwise (λs t. disjnt (f s) (f t)) {..<length xs}" 
    unfolding pairwise_def is_partition_alt is_partition_alt_def disjnt_def by simp
  then have "pairwise disjnt (f ` {..<length xs})" 
    by (metis (mono_tags, lifting) pairwiseD pairwise_imageI) 
  then have "card (i<length xs. f i) = sum card (f ` {..<length xs})" 
    using assms(2) card_Union_disjoint by (metis (mono_tags, lifting) imageE lessThan_iff) 
  with disj show ?thesis 
    using sum_card_image by (metis finite_lessThan)
qed

lemma sum_sum_concat: "(i<length xs. xf (xs!i). g x) = (xconcat (map f xs). g x)"
proof(induct xs)
  case (Cons a xs)
  then show ?case unfolding list.map concat.simps map_append sum_list_append
    by (metis (mono_tags, lifting) length_nth_simps(2) nth_Cons_0 nth_Cons_Suc sum.cong sum.lessThan_Suc_shift)
qed simp

lemma concat_map2_zip:
  assumes "length xs = length ys" 
    and "i < length xs. length (xs!i) = length (ys!i)"
  shows "concat (map2 zip xs ys) = zip (concat xs) (concat ys)"
  using assms proof(induct xs arbitrary:ys rule:rev_induct)
  case (snoc x xs)
  from snoc(2) obtain y ys' where y:"ys = ys'@[y]"
    by (metis append_is_Nil_conv length_0_conv neq_Nil_conv rev_exhaust) 
  moreover with snoc(2) have l:"length xs = length ys'" by simp
  moreover with snoc(3) have l':"i < length xs. length (xs!i) = length (ys'!i)"
    unfolding y by (metis (no_types, lifting) Ex_less_Suc add_Suc_right append.right_neutral append_Cons_nth_left length_Cons length_append order_less_trans) 
  ultimately have IH:"concat (map2 zip xs ys') = zip (concat xs) (concat ys')"
    using snoc(1) by presburger
  have *:"concat (map2 zip (xs @ [x]) ys) = concat (map2 zip xs ys') @ (zip x y)" 
    unfolding y zip_append[OF l] by simp
  have "length (concat xs) = length (concat ys')" 
    using l l' eq_length_concat_nth by blast 
  then show ?case 
    unfolding * IH unfolding y concat_append using zip_append by simp   
qed simp

lemma sum_list_less:
  assumes less:"i < j" 
    and i'j':"i' < length xs" "j' < length xs" 
    and j'':"j'' < length (xs!j')" 
    and sums:"i = sum_list (map length (take i' xs)) + i''" "j = sum_list (map length (take j' xs)) + j''"
  shows "i'  j'" 
proof(rule ccontr)
  assume *:"¬ i'  j'" 
  then have subsums:"sum_list (map length (take i' xs)) = sum_list (map length (take j' xs)) + sum_list (map length (take (i'-j') (drop j' xs)))"
    by (metis le_add_diff_inverse map_append nat_le_linear sum_list_append take_add) 
  from * have "take (i' - j') (drop j' xs) = xs!j' # (take (i' - (Suc j')) (drop (Suc j') xs))"  
    using i'j' by (metis Cons_nth_drop_Suc Suc_diff_Suc linorder_le_less_linear take_Suc_Cons) 
  with j'' have "j'' < sum_list (map length (take (i'-j') (drop j' xs)))" by simp
  then show False
    using sums subsums less by linarith
qed

lemma zip_symm: "(x, y)  set (zip xs ys)  (y, x)  set (zip ys xs)"
  by (induct xs ys rule:list_induct2') auto

lemma sum_list_elem:
  "(x[y]. f x) = f y"
  by simp

lemma sum_list_zero:
  assumes "i < length xs. f (xs!i) = 0"
  shows "(xxs. f x) = 0"
  by (metis assms map_eq_conv' monoid_add_class.sum_list_0)

lemma distinct_is_partition:
  assumes "distinct (concat ts)"
  shows "is_partition (map set ts)" 
  using assms proof(induct ts)
  case Nil
  then show ?case
    using is_partition_Nil by auto
next
  case (Cons t ts)
  {fix i j assume j:"j < length (t#ts)" and ij:"i < j" 
    have "(map set (t#ts))!i  (map set (t#ts))!j = {}" proof(cases i)
      case 0
      show ?thesis using Cons(2) unfolding 0
        using ij j by force
    next
      case (Suc n)
      from Cons have "is_partition (map set ts)" by simp
      then show ?thesis 
        unfolding Suc is_partition_def using j ij using Suc Suc_less_eq2 by fastforce
    qed
  }
  then show ?case unfolding is_partition_def by simp
qed

lemma filter_ex_index:
  assumes "x = filter f xs ! i" "i < length (filter f xs)" 
  shows "j. j < length xs  x = xs!j"
  using assms proof(induct "xs" arbitrary:i)
  case (Cons y ys)
  show ?case proof(cases "f y")
    case True
    then have filter:"filter f (y#ys) = y#(filter f ys)" by simp 
    show ?thesis proof(cases i)
      case 0
      from Cons(2) show ?thesis unfolding filter 0 by auto
    next
      case (Suc n)
      from Cons(2) have "x = filter f ys ! n" 
        unfolding Suc filter by simp
      moreover from Cons(3) have "n < length (filter f ys)" 
        unfolding Suc filter by simp
      ultimately obtain j where "j<length ys" and "x = ys ! j" 
        using Cons(1) by blast
      then show ?thesis by auto 
    qed
  next
    case False
    then have filter:"filter f (y#ys) = filter f ys" by simp
    from Cons obtain j where "j<length ys" and "x = ys ! j" 
      unfolding filter by blast 
    then show ?thesis by auto
  qed
qed simp

lemma filter_index_neq':
  assumes "i < j" "j < length (filter f xs)"
  shows " i' j'. i' < length xs  j' < length xs  i' < j'  xs ! i' = (filter f xs) ! i  xs ! j' = (filter f xs) ! j" 
  using assms proof(induct xs arbitrary: i j)
  case (Cons x xs)
  then show ?case proof(cases "f x")
    case True
    show ?thesis proof(cases i)
      case 0
      then have i0:"filter f (x#xs) ! i = (x#xs) ! 0" 
        using f x by simp 
      from Cons(2) obtain j' where "j = Suc j'" 
        unfolding 0 using gr0_implies_Suc by blast
      with Cons(3) have "j' < length (filter f xs)" 
        unfolding filter.simps using f x by simp  
      then obtain j'' where j'':"j'' < length xs" "filter f xs ! j' = xs ! j''"
        by (meson filter_ex_index) 
      then have "filter f (x#xs) ! j = (x#xs) ! (Suc j'')" 
        using f x j = Suc j' by simp
      with i0 j''(1) show ?thesis
        by (metis length_nth_simps(2) not_less_eq zero_less_Suc)
    next
      case (Suc i')
      from Cons(2) obtain j' where j:"j = Suc j'" 
        unfolding Suc using Suc_lessE by auto 
      from Cons(1)[of i' j'] Cons(2,3) obtain i'' j'' where "i'' < length xs" "j'' < length xs" "i'' < j''" "xs ! i'' = filter f xs ! i'" "xs ! j'' = filter f xs ! j'"
        using Suc True j by auto 
      then show ?thesis
        by (smt (verit) Suc Suc_less_eq True filter.simps(2) j length_nth_simps(2) nth_Cons_Suc)
    qed
  next
    case False
    then have "filter f (x#xs) = filter f xs" by simp
    with Cons show ?thesis
      by (metis Suc_less_eq length_nth_simps(2) nth_Cons_Suc)
  qed
qed simp

lemma filter_index_neq:
  assumes "i  j" "i < length (filter f xs)" "j < length (filter f xs)"
  shows " i' j'. i' < length xs  j' < length xs  i'  j'  xs ! i' = (filter f xs) ! i  xs ! j' = (filter f xs) ! j"
using assms filter_index_neq' proof(cases "i < j")
  case False
  then have *:"j < i" using assms(1) by simp
  then show ?thesis using filter_index_neq'[OF * assms(2)] by blast
qed blast

lemma nth_drop_equal: 
  assumes "length xs = length ys"
    and "j < length xs. j  i  xs!j = ys!j"
  shows "drop i xs = drop i ys"
using assms proof (induct i arbitrary: xs ys)
  case 0
  then show ?case
    using nth_equalityI by blast
next
  case (Suc i)
  then show ?case proof(cases "Suc i < length xs")
    case True
    then obtain x xs' where x:"xs = x # xs'"
      by (metis Suc_length_conv Suc_lessE) 
    with Suc(2) obtain y ys' where y:"ys = y # ys'"
      by (metis length_greater_0_conv nth_drop_0)
    from Suc(1)[of xs' ys'] have "drop i xs' = drop i ys'"
      using Suc(2,3) unfolding x y
      by (metis Suc_le_mono length_nth_simps(2) linorder_not_le nat.inject nth_Cons_Suc)
    then show ?thesis unfolding x y by simp
  qed simp
qed

lemma union_take_drop_list:
  assumes "i < length xs"
  shows "(set (take i xs))  (set (drop (Suc i) xs)) = {xs!j | j. j < length xs  j  i}"
proof-
  from assms have i:"i  length xs" by simp
  have set1:"set (take i xs) = {xs ! j |j. j < i}" 
    using nth_image[OF i] unfolding image_def by fastforce
  from assms have i:"Suc i  length xs" by simp
  have set2:"set (drop (Suc i) xs) = {xs !j |j. i < j  j < length xs}" proof
    {fix x assume "x  set (drop (Suc i) xs)" 
      then have "x  {xs !j |j. i < j  j < length xs}" 
        unfolding set_conv_nth nth_drop[OF i] length_drop by auto 
    }
    then show "set (drop (Suc i) xs)  {xs ! j |j. i < j  j < length xs}" by auto
    {fix x assume "x  {xs !j |j. i < j  j < length xs}" 
      then have "x  set (drop (Suc i) xs)" 
        unfolding set_conv_nth nth_drop[OF i] length_drop
        by (smt (verit, best) Suc_leI add_diff_inverse_nat i mem_Collect_eq nat_add_left_cancel_less not_less_eq_eq)
    }
    then show "{xs ! j |j. i < j  j < length xs}  set (drop (Suc i) xs)" by auto
  qed
  {fix x assume "x  set (take i xs)  set (drop (Suc i) xs)"
    then consider "x  set (take i xs)" | "x  set (drop (Suc i) xs)" by blast
    then have "x  {xs ! j |j. j < length xs  j  i}" proof(cases)
      case 1
      with set1 show ?thesis using in_set_idx by fastforce
    next
      case 2
      with set2 show ?thesis using in_set_idx by fastforce
    qed
  }moreover
  {fix x assume "x  {xs ! j |j. j < length xs  j  i}"
    then obtain j where "x = xs!j" and j:"j < length xs" "j  i"
      by blast
    then have "x  set (take i xs)  set (drop (Suc i) xs)" 
      using set1 set2 using nat_neq_iff by auto
  }
  ultimately show ?thesis by auto
qed

lemma list_tl_eq:
  assumes "length xs = length ys" "xs  []"
    and "i < length xs. i > 0  xs!i = ys!i"
  shows "tl xs = tl ys"
  by (metis Suc_le_lessD assms(1) assms(3) length_greater_0_conv list.sel(3) nth_drop_0 nth_drop_equal) 

subsubsection‹Lists of @{type option}

lemma length_those:
  assumes "those xs = Some ys"
  shows "length xs = length ys"
  using assms proof(induction xs arbitrary:ys)
  case Nil
  then show ?case by simp
next
  case (Cons a xs)
  from Cons(2) obtain ys' where ys':"those xs = Some ys'"
    by (smt not_None_eq option.case_eq_if option.simps(8) those.simps(2)) 
  from Cons(2) obtain y where y:"Some y = a"
    by (metis option.case_eq_if option.exhaust_sel option.simps(3) those.simps(2)) 
  from y ys' have "those (Cons a xs) = Some (Cons y ys')"
    by auto
  then show ?case using Cons ys'
    by auto
qed

lemma those_not_none_x: "those xs = Some ys  x  set xs  x  None"
proof (induction xs arbitrary: x ys)
  case (Cons a xs)
  from Cons(2) have "a  None" using option.simps(4) by fastforce 
  from this Cons(2) have "those xs  None" by auto
  then show ?case using Cons(1,3) a  None by auto
qed (simp)

lemma those_not_none_xs: "list_all (λx. x  None) xs  those xs  None"
  by (induction xs) auto

lemma those_some:
  assumes "length xs = length ys" "i < length xs. xs!i = Some (ys!i)" 
  shows "those xs = Some ys"
  using assms by (induct rule:list_induct2) (simp, force)

lemma those_some2:
  assumes "those xs = Some ys"
  shows "i < length xs. xs!i = Some (ys!i)"
proof-
  from assms have "length xs = length ys" by (simp add: length_those)
  then show ?thesis using assms proof(induction xs ys rule:list_induct2)
    case (Cons x xs y ys)
    from Cons(3) have "x  None" by (metis list.set_intros(1) those_not_none_x)
    with Cons(3) have *:"x = Some y" by force
    with Cons(3) have "those xs = Some ys" by force
    with * Cons(2) show ?case by (simp add: nth_Cons') 
  qed simp
qed

lemma exists_some_list:
  assumes "i < length xs. (y. xs!i = Some y)"
  shows " ys. (i < length xs. xs!i = Some (ys!i))  length ys = length xs"
  by (metis assms length_map nth_map option.sel)

subsection‹Results About Linear Terms›

lemma linear_term_var_vars_term_list:
  assumes "linear_term t"
  shows "vars_term_list t = vars_distinct t"
  using assms linear_term_distinct_vars
  by (metis comp_apply distinct_rev remdups_id_iff_distinct rev_rev_ident)

lemma linear_term_unique_vars:
  assumes "linear_term s"
    and "p  poss s" and "s|_p = Var x"
    and "q  poss s" and "s|_q = Var x"
  shows "p = q"
proof(rule ccontr)
  assume "p  q"
  with assms(2-) obtain i j where ij:"i < length (var_poss_list s)" "j < length (var_poss_list s)" "i  j" 
    "var_poss_list s ! i = p" "var_poss_list s ! j = q"
    by (metis in_set_idx var_poss_iff var_poss_list_sound) 
  with assms(3,5) have "vars_term_list s ! i = vars_term_list s ! j"
    by (metis length_var_poss_list term.inject(1) vars_term_list_var_poss_list) 
  moreover from assms(1) have "distinct (vars_term_list s)"
    by (metis distinct_remdups distinct_rev linear_term_var_vars_term_list o_apply) 
  ultimately show False using ij(1,2,3)
    by (metis distinct_Ex1 length_var_poss_list nth_mem) 
qed

lemma linear_term_ctxt:
  assumes "linear_term t"
    and "p  poss t"
  shows "vars_ctxt (ctxt_of_pos_term p t)  vars_term (t|_p) = {}"
  using assms proof(induct p arbitrary:t)
  case (Cons i p)
  from Cons(3) obtain f ts where t:"t = Fun f ts" "i < length ts" "p  poss (ts!i)"
    using args_poss by blast 
  with Cons(1,2) have IH:"vars_ctxt (ctxt_of_pos_term p (ts!i))  vars_term ((ts!i) |_ p) = {}"
    by simp 
  {fix j assume j:"j < length ts" "j  i"
    with Cons(2) have "vars_term (ts!j)  vars_term (ts!i |_p) = {}"
      unfolding t using var_in_linear_args t(2,3) by (metis (no_types, opaque_lifting) Int_Un_distrib disjoint_iff sup_bot.neutr_eq_iff vars_ctxt_pos_term) 
  }
  then have " {vars_term (ts ! j) |j. j < length ts  j  i}  vars_term (ts!i |_p) = {}"
    by blast 
  moreover have "( (vars_term ` set (take i ts))   (vars_term ` set (drop (Suc i) ts))) = 
                 {vars_term (ts ! j) |j. j < length ts  j  i}" 
    unfolding set_map[symmetric] take_map[symmetric] drop_map[symmetric] Union_Un_distrib[symmetric]
    using union_take_drop_list[where xs="(map vars_term ts)"] unfolding length_map using t(2) by auto
  ultimately show ?case unfolding t ctxt_of_pos_term.simps subt_at.simps using IH
    by (metis (no_types, lifting) bot_eq_sup_iff inf_sup_distrib2 vars_ctxt.simps(2))      
qed simp

lemma linear_term_obtain_subst:
  assumes "linear_term (Fun f ts)" and l:"length ts = length ss"
    and substs: "i< length ts. (σ. ts!i  σ = ss!i)" 
  shows "σ. Fun f ts  σ = Fun f ss" 
  using assms proof(induct ts arbitrary: ss)
  case (Cons t ts)
  from Cons(3) obtain s ss' where ss:"ss = s#ss'"
    by (metis length_Suc_conv)
  from Cons(2) have lin:"linear_term (Fun f ts)" 
    unfolding linear_term.simps by (simp add: is_partition_Cons) 
  from Cons(4) have "i<length ts. σ. ts ! i  σ = ss' ! i"
    unfolding ss by (metis length_nth_simps(2) not_less_eq nth_Cons_Suc) 
  then obtain σ where σ:"Fun f ts  σ = Fun f ss'" 
    using Cons(1)[OF lin, of ss'] using Cons.prems(2) ss by auto 
  from Cons(4) obtain σ1 where σ1:"t  σ1 = s"
    using ss by auto 
  let ="λx. if x  vars_term t then σ1 x else σ x" 
  have t:"t   = s"
    by (simp add: σ1 term_subst_eq) 
  {fix i assume "i < length ts" 
    then have "ts!i   = ss'!i"
      by (smt (verit, ccfv_SIG) Cons.prems(1) Cons.prems(2) Suc_inject Suc_leI σ σ1 eval_term.simps(2) le_imp_less_Suc length_nth_simps(2) map_nth_eq_conv nth_Cons_0 nth_Cons_Suc ss term.sel(4) term_subst_eq var_in_linear_args zero_less_Suc)
  }
  with t have "Fun f (t#ts)   = Fun f ss"
    using Cons.prems(2) map_nth_eq_conv ss by auto 
  then show ?case by blast
qed simp

lemma linear_ctxt_of_pos_term:
  assumes "linear_term t" and lin_s:"linear_term s" and p:"p  poss t"
    and "vars_term t  vars_term s = {}" 
  shows "linear_term (replace_at t p s)" 
using assms proof(induct t arbitrary:p)
  case (Var x)
  with p have "p = []" by simp
  with lin_s show ?case by simp
next
  case (Fun f ts)
  from lin_s show ?case proof(cases p)
    case (Cons i p')
    with Fun(4) have i:"i < length ts" by simp
    with Fun(4) have p':"p'  poss (ts!i)" unfolding Cons by simp
    {fix n assume n:"n < length ts" "n  i"
      with Fun(2) have "vars_term (ts!n)  vars_term (ts!i) = {}"
        by (metis disjoint_iff i var_in_linear_args) 
      then have "vars_term (ts!n)  vars_ctxt (ctxt_of_pos_term p' (ts!i)) = {}"
        using p' vars_ctxt_pos_term by fastforce 
      moreover from n Fun(5) have "vars_term (ts!n)  vars_term s = {}"
        by (meson disjoint_iff nth_mem term.set_intros(4))
      ultimately have "vars_term (ts!n)  vars_term ((ctxt_of_pos_term p' (ts ! i))s) = {}"
        unfolding vars_term_ctxt_apply by blast
    }
    with Fun(2) have "is_partition (map vars_term (take i ts @ (ctxt_of_pos_term p' (ts ! i))s # drop (Suc i) ts))"
      unfolding linear_term.simps is_partition_def by (smt (z3) Int_commute append_Cons_nth_not_middle i id_take_nth_drop 
          length_append length_map length_nth_simps(2) linorder_neq_iff nth_append_take nth_map order.strict_implies_order order.strict_trans) 
    moreover have "linear_term ((ctxt_of_pos_term p' (ts ! i))s)" 
      using Fun p' by (meson disjoint_iff i linear_term.simps(2) nth_mem term.set_intros(4)) 
    ultimately show ?thesis 
      using Fun(2) unfolding Cons ctxt_of_pos_term.simps intp_actxt.simps linear_term.simps
      by (metis Un_iff in_set_dropD in_set_takeD set_ConsD set_append)
  qed simp
qed

lemma distinct_vars:
  assumes "p q x y. p  q  p  poss t  q  poss t  t|_p = Var x  t|_q = Var y  x  y"
  shows "distinct (vars_term_list t)" 
proof-
  {fix i j assume ij:"i  j" and i:"i < length (vars_term_list t)" and j:"j < length (vars_term_list t)" 
    let ?p="var_poss_list t ! i" and ?q="var_poss_list t ! j" 
    let ?x="vars_term_list t ! i" and ?y="vars_term_list t ! j" 
    from ij i j have pq:"?p  ?q"
      by (simp add: distinct_var_poss_list length_var_poss_list nth_eq_iff_index_eq) 
    have p:"?p  poss t"
      by (metis i length_var_poss_list nth_mem var_poss_imp_poss var_poss_list_sound) 
    have q:"?q  poss t"
      by (metis j length_var_poss_list nth_mem var_poss_imp_poss var_poss_list_sound) 
    have "?x  ?y" 
      using assms[OF pq p q] i j by (simp add: vars_term_list_var_poss_list) 
  }
  then show ?thesis by (meson distinct_conv_nth) 
qed

lemma distinct_vars_linear_term:
  assumes "distinct (vars_term_list t)"
  shows "linear_term t" 
  using assms proof(induct t)
  case (Fun f ts)
  {fix t assume t:"t  set ts" 
    with Fun(2) have "distinct (vars_term_list t)" 
      unfolding vars_term_list.simps by (simp add: distinct_concat_iff) 
    with t Fun(1) have "linear_term t"
      by auto
  }
  moreover have "is_partition (map vars_term ts)" 
    using Fun(2) unfolding vars_term_list.simps using distinct_is_partition set_vars_term_list
    by (metis (mono_tags, lifting) length_map map_nth_eq_conv)
  ultimately show ?case by simp
qed simp

lemma distinct_vars_eq_linear: "linear_term t = distinct (vars_term_list t)"
  using distinct_vars_linear_term linear_term_distinct_vars by blast 

subsection‹Results About Substitutions and Contexts›

lemma ctxt_apply_term_subst:
  assumes "linear_term t" and "i < length (vars_term_list t)"
    and "p = (var_poss_list t)!i"
  shows "(ctxt_of_pos_term p (t  σ))s = t  σ((vars_term_list t)!i := s)"
proof-
  from assms(2,3) have "t|_p = Var ((vars_term_list t)!i)"
    by (metis vars_term_list_var_poss_list)
  with assms show ?thesis
    by (smt (verit, ccfv_threshold) filter_cong fun_upd_other fun_upd_same length_var_poss_list linear_term_replace_in_subst nth_mem var_poss_imp_poss var_poss_list_sound)
qed

lemma ctxt_subst_apply:
  assumes "p  poss t" and "t|_p = Var x"
    and "linear_term t"
  shows "((ctxt_of_pos_term p t) c σ)s = t  σ(x := s)"
  unfolding ctxt_of_pos_term_subst[symmetric, OF assms(1)]
  using assms 
  by (smt (verit) fun_upd_apply linear_term_replace_in_subst)

lemma ctxt_of_pos_term_hole_subst:
  assumes "linear_term t" 
    and "i < length (var_poss_list t)" and "p = var_poss_list t ! i"
    and "x  vars_term t. x  vars_term_list t !i  σ x = τ x"
  shows "ctxt_of_pos_term p (t  σ) = ctxt_of_pos_term p (t  τ)"
  using assms proof(induct p arbitrary: t i)
  case (Cons j p)
  from Cons(3,4) have "j#p  var_poss t"
    using nth_mem by force 
  then obtain f ts where ts:"j < length ts" "t = Fun f ts" "p  var_poss (ts!j)"
    by (metis args_poss subt_at.simps(2) var_poss_iff) 
  then obtain i' where i':"i' < length (var_poss_list (ts!j))" "p = var_poss_list (ts!j)!i'"
    using var_poss_list_sound by (metis in_set_conv_nth)
  from Cons(3,4) have "Var (vars_term_list t ! i) = t|_(j#p)"
    by (metis length_var_poss_list vars_term_list_var_poss_list) 
  also have "... = (ts!j)|_p" 
    unfolding ts(2) by simp
  also have "... = Var (vars_term_list (ts!j) ! i')" 
    using i' by (simp add: length_var_poss_list vars_term_list_var_poss_list) 
  finally have *:"vars_term_list t ! i = vars_term_list (ts ! j) ! i'" by simp
  with Cons(5) have "xvars_term (ts!j). x  vars_term_list (ts!j) ! i'  σ x = τ x" 
    unfolding ts(2) using ts(1) by auto 
  with Cons(2) i' ts have IH:"ctxt_of_pos_term p ((ts!j)  σ) = ctxt_of_pos_term p ((ts!j)  τ)"  
    using Cons(1)[of "ts!j" i'] by (meson linear_term.simps(2) nth_mem) 
  {fix j' assume j':"j' < length ts" "j'  j"
    with Cons(2) have "vars_term (ts ! j')  vars_term (ts!j) = {}" 
      unfolding ts(2) by (metis disjoint_iff ts(1) var_in_linear_args)
    then have "x  vars_term (ts!j'). σ x = τ x" 
      using Cons(5) j' * by (metis disjoint_iff i'(1) length_var_poss_list nth_mem set_vars_term_list term.set_intros(4) ts(2)) 
    then have "(ts!j')  σ = (ts!j')  τ"
       by (meson term_subst_eq)
   } note t'=this
  with ts(1) have "take j (map (λt. t  σ) ts) = take j (map (λt. t  τ) ts)" 
    using nth_take_lemma[of j "(map (λt. t  σ) ts)" "(map (λt. t  τ) ts)"] by simp
  moreover from t' ts(1) have "(drop (Suc j) (map (λt. t  σ) ts)) = (drop (Suc j) (map (λt. t  τ) ts))"
    using nth_drop_equal[of "(map (λt. t  σ) ts)" "(map (λt. t  τ) ts)" "Suc j"] by auto
  ultimately show ?case 
    unfolding ts(2) eval_term.simps ctxt_of_pos_term.simps using IH by (simp add: ts(1)) 
qed simp

lemma ctxt_apply_ctxt_apply:
  assumes "p  poss t"
  shows "(ctxt_of_pos_term (p@q) ((ctxt_of_pos_term p t) s)) u = (ctxt_of_pos_term p t)(ctxt_of_pos_term q s) u"
  by (metis assms ctxt_ctxt ctxt_of_pos_term_append hole_pos_ctxt_of_pos_term hole_pos_id_ctxt hole_pos_poss replace_at_subt_at)

lemma replace_at_append_subst:
  assumes "linear_term t"
    and "p  poss t" "t|_p = Var x"
  shows "(ctxt_of_pos_term (p@q) (t  σ)) s = t  σ(x := (ctxt_of_pos_term q (σ x)) s)"
  using assms proof(induct p arbitrary:t)
  case (Cons i p)
  then obtain f ts where t:"t = Fun f ts" and i:"i < length ts" and p:"p  poss (ts!i)"
    by (meson args_poss)
  from Cons(4) have x:"(ts!i)|_p = Var x" 
    unfolding t by simp
  from Cons(2) have lin:"linear_term (ts!i)"
    using i t by simp
  have IH:"(ctxt_of_pos_term (p@q) ((ts!i)  σ)) s = (ts!i)  σ(x := (ctxt_of_pos_term q (σ x)) s)"
    using Cons(1)[OF lin p x] .
  let ="σ(x := (ctxt_of_pos_term q (σ x)) s)"
  {fix j assume j:"j < length ts" "j  i"
    from x have "x  vars_term (ts!i)"
      by (metis p subsetD term.set_intros(3) vars_term_subt_at)
    then have "x  vars_term (ts!j)" 
      using j Cons(2) unfolding t by (meson i var_in_linear_args) 
    then have "(ts!j)  σ = (ts!j)  "
      by (simp add: term_subst_eq_conv)
  } note sigma=this
  then have "take i (map (λt. t  σ) ts) = take i (map (λt. t  ) ts)"
    using nth_take_lemma[of i "(map (λt. t  σ) ts)" "(map (λt. t  ) ts)"] i by simp 
  moreover from sigma have "drop (Suc i) (map (λt. t  σ) ts) = drop (Suc i) (map (λt. t  ) ts)"
     using nth_drop_equal[of "(map (λt. t  σ) ts)" "(map (λt. t  ) ts)"] i by simp 
  ultimately show ?case 
    unfolding t append_Cons eval_term.simps ctxt_of_pos_term.simps intp_actxt.simps nth_map[OF i] IH
    by (metis i id_take_nth_drop length_map nth_map)
qed simp

lemma replace_at_fun_poss_not_below:
  assumes "¬ p p q"
    and "p  poss t" and "q  fun_poss (replace_at t p s)"
  shows "q  fun_poss t"
  using assms by (metis ctxt_supt_id fun_poss_ctxt_apply_term hole_pos_ctxt_of_pos_term less_eq_pos_def) 

lemma substitution_subterm_at:
  assumes "j < length (vars_term_list l). σ (vars_term_list l ! j) = s |_ (var_poss_list l ! j)"
    and "τ. l  τ = s"
  shows "l  σ = s"
  using assms proof(induct l arbitrary:s)
  case (Var x)
  then show ?case 
    unfolding vars_term_list.simps poss_list.simps var_poss.simps eval_term.simps by simp 
next
  case (Fun f ts)
  from Fun(3) obtain ss where s:"s = Fun f ss" and l:"length ts = length ss"
    by fastforce 
  {fix i assume i:"i < length ts"
    {fix j assume j:"j<length (vars_term_list (ts!i))" 
      let ?p="var_poss_list (ts!i) ! j"
      let ?x="vars_term_list (ts!i) ! j"
      let ?k="sum_list (map (length  vars_term_list) (take i ts)) + j"
      from i j have x:"?x = vars_term_list (Fun f ts) ! ?k"
        unfolding vars_term_list.simps by (simp add: concat_nth take_map) 
      have p:"var_poss_list (Fun f ts) ! ?k = i # ?p" proof-
        from i have i':"i < length (map2 (λx. map ((#) x)) [0..<length ts] (map var_poss_list ts))" 
          by simp 
        from i j have "j < length ((map var_poss_list ts) ! i)" 
          using length_var_poss_list by (metis (mono_tags, lifting) nth_map) 
        with i have j':"j < length (map2 (λx. map ((#) x)) [0..<length ts] (map var_poss_list ts) ! i)"
          by simp
        {fix l assume "l < length ts"
          then have "(map length (map2 (λx. map ((#) x)) [0..<length ts] (map var_poss_list ts)))!l = (map (length  vars_term_list) ts) ! l" 
            using length_var_poss_list by simp
        }  
        then have "map length (map2 (λx. map ((#) x)) [0..<length ts] (map var_poss_list ts)) = map (length  vars_term_list) ts" 
          using nth_equalityI[where ys="map (length  vars_term_list) ts"] by simp
        with i have k:"sum_list (map length (take i (map2 (λx. map ((#) x)) [0..<length ts] (map var_poss_list ts)))) + j = ?k"
          by (metis take_map)
        then have "var_poss_list (Fun f ts) ! ?k = (map2 (λi. map ((#) i)) [0..<length ts] (map var_poss_list ts))!i !j" 
          unfolding var_poss_list.simps using concat_nth[OF i' j'] by presburger
        also have "... = (map ((#) i) (var_poss_list (ts!i)))!j" using i by simp
        also have "... = i # ?p" using nth_map j length_var_poss_list by metis
        ultimately show ?thesis by simp
      qed
      from i j have k:"?k < length (vars_term_list (Fun f ts))" 
        unfolding vars_term_list.simps by (metis concat_nth_length length_map map_map nth_map take_map) 
      from Fun(2) k have "σ ?x = (ss!i) |_ (var_poss_list (ts!i) ! j)" 
        unfolding x s using p by simp
    }
    then have "j<length (vars_term_list (ts!i)).σ (vars_term_list (ts!i) ! j) = (ss!i) |_ var_poss_list (ts!i) ! j" 
      by simp
    moreover from Fun(3) have "τ. (ts!i)  τ = ss!i" 
      unfolding eval_term.simps s using i l by (metis nth_map term.inject(2))
    ultimately have "(ts!i)  σ = ss!i " 
      using i Fun(1) nth_mem by blast  
  }
  then show ?case unfolding eval_term.simps s 
    using l by (simp add: map_nth_eq_conv)
qed

lemma vars_map_vars_term:
  "map f (vars_term_list t) = vars_term_list (map_vars_term f t)"
unfolding map_vars_term_eq proof(induct t)
  case (Fun g ts)
  then have "map (λxs. map f xs)(map vars_term_list ts) = map vars_term_list (map (λt. t  (Var  f)) ts)"
    by fastforce
  then show ?case unfolding vars_term_list.simps eval_term.simps map_map map_concat
    by presburger
qed (simp add: vars_term_list.simps)

lemma ctxt_apply_subt_at:
  assumes "q  poss s" 
  shows "(ctxt_of_pos_term p (s|_q)) t = (ctxt_of_pos_term (q@p) s) t |_ q"
using assms proof(induct q arbitrary: s)
  case (Cons i q)
  from Cons(2) obtain f ss where i:"i < length ss" and s:"s = Fun f ss"
    by (meson args_poss) 
  from i Cons show ?case unfolding s
    by (metis ctxt_apply_ctxt_apply ctxt_supt_id replace_at_subt_at) 
qed simp

subsubsection‹Utilities for @{const mk_subst}
text‹We consider the special case of applying @{const mk_subst} when the variables involved form a partition.›

lemma mk_subst_same:
  assumes "length xs = length ts" "distinct xs"
  shows "map (mk_subst f (zip xs ts)) xs = ts"
  using assms by (simp add: mk_subst_distinct map_nth_eq_conv)

lemma map2_zip: "set (map fst (concat (map2 zip xs ys)))  set (concat xs)"
proof
  fix x assume x:"x  set (map fst (concat (map2 zip xs ys)))"
  let ?l="min (length xs) (length ys)"
  from x obtain i where i:"i < ?l" "x  set (map fst (zip (xs!i) (ys!i)))"
    by (smt (verit) case_prod_conv in_set_conv_nth length_map length_zip min.strict_boundedE nth_concat_split nth_map nth_zip) 
  then have "x  set (xs!i)"
    by (metis in_set_takeD map_fst_zip_take) 
  then show "x  set (concat xs)"
    using i(1) by (metis concat_nth concat_nth_length in_set_conv_nth min.strict_boundedE) 
qed

lemma mk_subst_partition: 
  fixes xs :: "'a list list"
  assumes l:"length xs = length ss"
    and part:"is_partition (map set xs)"
  shows "i < length xs. x  set (xs!i). (mk_subst f (zip (xs!i) (ss!i))) x = (mk_subst f (concat (map2 zip xs ss))) x"
proof-
  {fix i assume i:"i < length xs" 
    {fix x assume x:"x  set (xs!i)"
      have "concat (map2 zip xs ss) = concat (map2 zip (take i xs) (take i ss)) @ concat (map2 zip (drop i xs) (drop i ss))"
        by (metis append_take_drop_id concat_append drop_map drop_zip take_map take_zip)
      moreover have "concat (map2 zip (drop i xs) (drop i ss)) = concat (zip (xs!i) (ss!i) # (map2 zip (drop (Suc i) xs) (drop (Suc i) ss)))"
        using i l by (smt (verit, del_insts) Cons_nth_drop_Suc list.map(2) prod.simps(2) zip_Cons_Cons) 
      ultimately have cc:"concat (map2 zip xs ss) = concat (map2 zip (take i xs) (take i ss)) @ 
                concat (zip (xs!i) (ss!i) # (map2 zip (drop (Suc i) xs) (drop (Suc i) ss)))" by presburger
      {fix j assume "j < length xs" and "j  i"
        with i x part have "x  set (xs!j)" 
          unfolding is_partition_alt is_partition_alt_def by auto
      } note part=this
      then have "x  set (concat (take i xs))"
        by (smt (verit) in_set_conv_nth length_take less_length_concat min.strict_boundedE nth_take order_less_irrefl)
      then have "x  set (map fst (concat (map2 zip (take i xs) (take i ss))))" 
        using map2_zip in_mono by fastforce 
      then have subst:"(mk_subst f (concat (map2 zip xs ss))) x = (mk_subst f (concat (zip (xs!i) (ss!i) # 
                                                                  (map2 zip (drop (Suc i) xs) (drop (Suc i) ss))))) x"
        unfolding cc using mk_subst_concat by metis
      have "mk_subst f (zip (xs ! i) (ss ! i)) x = (mk_subst f (concat (map2 zip xs ss))) x"
      proof(cases "x  set (map fst (zip (xs!i) (ss!i)))")
        case True
        then show ?thesis 
          using mk_subst_concat_Cons subst by metis
      next
        case False
        {fix j assume j:"j < length (drop (Suc i) xs)"
          then have "(drop (Suc i) xs)!j = xs!(Suc i + j)"
              using Suc_leI i nth_drop by blast 
            moreover from i j have "Suc i + j < length xs"
              by (metis add.commute length_drop less_diff_conv) 
            ultimately have "x  set ((drop (Suc i) xs)!j)" 
              using part by (metis Suc_n_not_le_n le_add1) 
          }        
          then have "x  set (concat (drop (Suc i) xs))" 
            by (smt (verit) in_set_conv_nth length_map length_take less_not_refl2 min.strict_boundedE nth_concat_split nth_map nth_take) 
          then have "x  set (map fst (concat (map2 zip (drop (Suc i) xs) (drop (Suc i) ss))))" 
            using map2_zip in_mono by fastforce 
          with False have "x  set (map fst (concat (zip (xs!i) (ss!i) # (map2 zip (drop (Suc i) xs) (drop (Suc i) ss)))))" 
            unfolding concat.simps by (metis Un_iff map_append set_append)
          with False show ?thesis 
            unfolding subst using mk_subst_not_mem' by metis
        qed
      }                         
    }
  then show ?thesis by simp
qed

text‹The following lemma is used later to show that @{text "A = (to_pterm (lhs α)) ⋅ σ"} implies 
@{text "A = (to_pterm (lhs α)) ⋅ ⟨As⟩α"} for some suitable @{text As}.›
lemma subst_imp_mk_subst:
  assumes "s = t  σ"
  shows "ss. t  σ = t  (mk_subst Var (zip (vars_distinct t) ss))  length ss = length (vars_distinct t)  (i < length ss. σ (vars_distinct t!i) = ss!i)" 
proof-
  let ?ss="map σ (vars_distinct t)"
  let ="(mk_subst Var (zip (vars_distinct t) ?ss))"
  {fix x assume "x  vars_term t"
    then have "σ x =  x" unfolding mk_subst_def
      by (simp add: map_of_zip_map) 
  }
  then have "t  σ = t  "  
    using term_subst_eq by blast 
  then show ?thesis by auto
qed

lemma mk_subst_rename:
  assumes "length (vars_distinct t) = length xs" and "inj f"
  shows "t  (mk_subst Var (zip (vars_distinct t) xs)) = (map_vars_term f t)  (mk_subst Var (zip (vars_distinct (map_vars_term f t)) xs))" 
proof-
  {fix x assume "x  vars_term t" 
    then obtain i where i:"x = (vars_distinct t)!i" "i < length (vars_distinct t)"
      by (metis in_set_conv_nth set_vars_term_list vars_term_list_vars_distinct) 
    with assms have 1:"(mk_subst Var (zip (vars_distinct t) xs)) x = xs!i" 
      using mk_subst_distinct by (metis comp_apply distinct_remdups distinct_rev) 
    have "vars_distinct (map_vars_term f t) = map f (vars_distinct t)" 
      unfolding vars_map_vars_term[symmetric] comp_apply using assms(2)
      by (metis distinct_map distinct_remdups distinct_remdups_id inj_on_inverseI remdups_map_remdups rev_map the_inv_f_f)
    with assms i have 2:"(mk_subst Var (zip (vars_distinct (map_vars_term f t)) xs)) (f x) = xs!i"
      by (metis (mono_tags, lifting) comp_apply distinct_remdups distinct_rev length_map mk_subst_same nth_map) 
    from 1 2 have "(mk_subst Var (zip (vars_distinct t) xs)) x = (mk_subst Var (zip (vars_distinct (map_vars_term f t)) xs)) (f x)"
      by presburger
  }
  then show ?thesis
    by (simp add: apply_subst_map_vars_term term_subst_eq_conv) 
qed

subsection‹Matching Terms›

text‹The goal is showing that @{term "match (t  σ) t = Some σ"} whenever the domain of @{term σ} is a subset of the variables in @{term t}.
For that we need some helper lemmas.›

lemma decompose_fst:
  assumes "decompose (Fun f ss) t = Some us"
  shows "map fst us = ss"
proof-
  from assms obtain ts where t:"t = Fun f ts" 
     by (metis (no_types, lifting) decompose_def option.distinct(1) decompose_Some is_FunE old.prod.case term.case_eq_if)
  with assms have "length ss = length ts"
    by blast 
  with assms(1) t show ?thesis
    by auto 
qed

lemma decompose_vars_term:
  assumes "decompose (Fun f ss) t = Some us"
  shows "vars_term (Fun f ss) = ((a, b)  set us. vars_term a)"
proof-
  have "vars_term (Fun f ss) = ( s  set ss. vars_term s)"
    by (meson Term.term.simps(18))
  also have "... = ( s  set (map fst us). vars_term s)"
    using assms decompose_fst by metis 
  finally show ?thesis
    using image_image by auto 
qed

lemma match_term_list_domain:
  assumes "match_term_list P σ = Some τ"
  shows "x. x  ( (a, b)  set P. vars_term a)  σ x = None   τ x = None"
  using assms proof(induct P σ rule:match_term_list.induct)
  case (2 x t P σ)
  then show ?case
    by (metis (mono_tags, lifting) Sup_insert Un_iff case_prod_conv fun_upd_idem_iff fun_upd_triv fun_upd_twist image_insert list.simps(15) match_term_list.simps(2) option.simps(3) term.set_intros(3))
next
  case (3 f ss g ts P σ)
  from 3(2) obtain us where us:"decompose (Fun f ss) (Fun g ts) = Some us"
    using  match_term_list.simps(3) option.distinct(1) option.simps(4) by fastforce
  with 3(2) have *:"match_term_list (us @ P) σ = Some τ" 
    by auto
  from us have "( (a, b)  set ((Fun f ss, Fun g ts) # P). vars_term a) = ( s  set ss. vars_term s)  ( (a, b)  set P. vars_term a)"
    by simp
  also have "... = ( (a, b)  set (us@P). vars_term a)" 
    using us by (metis (mono_tags, lifting) Term.term.simps(18) UN_Un decompose_vars_term set_append)
  finally show ?case 
    using  3(1) us * by metis
qed simp_all

lemma match_subst_domain:
  assumes "match a b = Some σ"
  shows "subst_domain σ  vars_term b"
proof-
  from assms have "x. x  vars_term b  σ x = Var x"
    unfolding match_def match_list_def subst_of_map_def using match_term_list_domain by fastforce
  then show ?thesis
    using subst_domain_def by fastforce 
qed

lemma match_trivial: 
  assumes "subst_domain σ  vars_term t"
  shows "match (t  σ) t = Some σ"
proof-
  obtain τ where tau:"match (t  σ) t = Some τ" and 1:"(xvars_term t. σ x = τ x)"
    by (meson match_complete') 
  from assms have 2:"x. x  vars_term t  σ x = Var x"
    by (meson notin_subst_domain_imp_Var subset_eq)
  from tau have 3:"x. x  vars_term t  τ x = Var x" 
    using match_subst_domain notin_subst_domain_imp_Var by fastforce  
  from 1 2 3 show ?thesis
    by (metis subst_term_eqI tau term_subst_eq)
qed

end