Theory Rewriting_Pterm_Elim

section ‹Higher-order term rewriting with explicit pattern matching›

theory Rewriting_Pterm_Elim
imports
  Rewriting_Nterm
  "../Terms/Pterm"
begin

subsection ‹Intermediate rule sets›

type_synonym irules = "(term list × pterm) fset"
type_synonym irule_set = "(name × irules) fset"

locale pre_irules = constants C_info "fst |`| rs" for C_info and rs :: "irule_set"

locale irules = pre_irules +
  assumes fmap: "is_fmap rs"
  assumes nonempty: "rs  {||}"
  assumes inner:
    "fBall rs (λ(_, irs).
      arity_compatibles irs 
      is_fmap irs 
      patterns_compatibles irs 
      irs  {||} 
      fBall irs (λ(pats, rhs).
        linears pats 
        abs_ish pats rhs 
        closed_except rhs (freess pats) 
        fdisjnt (freess pats) all_consts 
        wellformed rhs 
        ¬ shadows_consts rhs 
        welldefined rhs))"

lemma (in pre_irules) irulesI:
  assumes "name irs. (name, irs) |∈| rs  arity_compatibles irs"
  assumes "name irs. (name, irs) |∈| rs  is_fmap irs"
  assumes "name irs. (name, irs) |∈| rs  patterns_compatibles irs"
  assumes "name irs. (name, irs) |∈| rs  irs  {||}"
  assumes "name irs pats rhs. (name, irs) |∈| rs  (pats, rhs) |∈| irs  linears pats"
  assumes "name irs pats rhs. (name, irs) |∈| rs  (pats, rhs) |∈| irs  abs_ish pats rhs"
  assumes "name irs pats rhs. (name, irs) |∈| rs  (pats, rhs) |∈| irs  fdisjnt (freess pats) all_consts"
  assumes "name irs pats rhs. (name, irs) |∈| rs  (pats, rhs) |∈| irs  closed_except rhs (freess pats)"
  assumes "name irs pats rhs. (name, irs) |∈| rs  (pats, rhs) |∈| irs  wellformed rhs"
  assumes "name irs pats rhs. (name, irs) |∈| rs  (pats, rhs) |∈| irs  ¬ shadows_consts rhs"
  assumes "name irs pats rhs. (name, irs) |∈| rs  (pats, rhs) |∈| irs  welldefined rhs"
  assumes "is_fmap rs" "rs  {||}"
  shows "irules C_info rs"
proof unfold_locales
  show "is_fmap rs"
    using assms(12) .
next
  show "rs  {||}"
    using assms(13) .
next
  show "fBall rs (λ(_, irs). Rewriting_Nterm.arity_compatibles irs  is_fmap irs 
    patterns_compatibles irs  irs  {||} 
    fBall irs (λ(pats, rhs). linears pats  abs_ish pats rhs  closed_except rhs (freess pats) 
      fdisjnt (freess pats) all_consts  pre_strong_term_class.wellformed rhs 
      ¬ shadows_consts rhs  consts rhs |⊆| all_consts))"
    using assms(1-11)
    by (intro prod_BallI conjI) metis+
qed

lemmas irulesI[intro!] = pre_irules.irulesI[unfolded pre_irules_def]

subsubsection ‹Translation from @{typ nterm} to @{typ pterm}

fun nterm_to_pterm :: "nterm  pterm" where
"nterm_to_pterm (Nvar s) = Pvar s" |
"nterm_to_pterm (Nconst s) = Pconst s" |
"nterm_to_pterm (t1 $n t2) = nterm_to_pterm t1 $p nterm_to_pterm t2" |
"nterm_to_pterm (Λn x. t) = (Λp x. nterm_to_pterm t)"

lemma nterm_to_pterm_inj: "nterm_to_pterm x = nterm_to_pterm y  x = y"
by (induction y arbitrary: x) (auto elim: nterm_to_pterm.elims)

lemma nterm_to_pterm:
  assumes "no_abs t"
  shows "nterm_to_pterm t = convert_term t"
using assms
apply induction
apply auto
by (auto simp: free_nterm_def free_pterm_def const_nterm_def const_pterm_def app_nterm_def app_pterm_def)

lemma nterm_to_pterm_frees[simp]: "frees (nterm_to_pterm t) = frees t"
by (induct t) auto

lemma closed_nterm_to_pterm[intro]: "closed_except (nterm_to_pterm t) (frees t)"
unfolding closed_except_def by simp

lemma (in constants) shadows_nterm_to_pterm[simp]: "shadows_consts (nterm_to_pterm t) = shadows_consts t"
by (induct t) (auto simp: shadows_consts_def fdisjnt_alt_def)

lemma wellformed_nterm_to_pterm[intro]: "wellformed (nterm_to_pterm t)"
by (induct t) auto

lemma consts_nterm_to_pterm[simp]: "consts (nterm_to_pterm t) = consts t"
by (induct t) auto

subsubsection ‹Translation from @{typ crule_set} to @{typ irule_set}

definition translate_crules :: "crules  irules" where
"translate_crules = fimage (map_prod id nterm_to_pterm)"

definition compile :: "crule_set  irule_set" where
"compile = fimage (map_prod id translate_crules)"

lemma compile_heads: "fst |`| compile rs = fst |`| rs"
unfolding compile_def by simp

lemma (in crules) compile_rules: "irules C_info (compile rs)"
proof
  have "is_fmap rs"
    using fmap by simp
  thus "is_fmap (compile rs)"
    unfolding compile_def map_prod_def id_apply by (rule is_fmap_image)

  show "compile rs  {||}"
    using nonempty unfolding compile_def by auto

  show "constants C_info (fst |`| compile rs)"
    proof
      show "fdisjnt (fst |`| compile rs) C"
        using disjnt unfolding compile_def
        by force
    next
      show "distinct all_constructors"
        by (fact distinct_ctr)
    qed

  fix name irs
  assume irs: "(name, irs) |∈| compile rs"
  then obtain irs' where "(name, irs') |∈| rs" "irs = translate_crules irs'"
    unfolding compile_def by force
  hence "arity_compatibles irs'"
    using inner
    by (metis (no_types, lifting) case_prodD)
  thus "arity_compatibles irs"
    unfolding irs = translate_crules irs' translate_crules_def
    by (force dest: fpairwiseD)

  have "patterns_compatibles irs'"
    using (name, irs') |∈| rs inner
    by (blast dest: fpairwiseD)
  thus "patterns_compatibles irs"
    unfolding irs = _ translate_crules_def
    by (auto dest: fpairwiseD)

  have "is_fmap irs'"
    using (name, irs') |∈| rs inner by auto
  thus "is_fmap irs"
    unfolding irs = translate_crules irs' translate_crules_def map_prod_def id_apply
    by (rule is_fmap_image)

  have "irs'  {||}"
    using (name, irs') |∈| rs inner by auto
  thus "irs  {||}"
    unfolding irs = translate_crules irs' translate_crules_def by simp

  fix pats rhs
  assume "(pats, rhs) |∈| irs"
  then obtain rhs' where "(pats, rhs') |∈| irs'" "rhs = nterm_to_pterm rhs'"
    unfolding irs = translate_crules irs' translate_crules_def by force
  hence "linears pats" "pats  []" "frees rhs' |⊆| freess pats" "¬ shadows_consts rhs'"
    using fbspec[OF inner (name, irs') |∈| rs]
    by blast+

  show "linears pats" by fact
  show "closed_except rhs (freess pats)"
    unfolding rhs = nterm_to_pterm rhs'
    using frees rhs' |⊆| freess pats
    by (metis dual_order.trans closed_nterm_to_pterm closed_except_def)

  show "wellformed rhs"
    unfolding rhs = nterm_to_pterm rhs' by auto

  have "fdisjnt (freess pats) all_consts"
    using (pats, rhs') |∈| irs' (name, irs') |∈| rs inner
    by auto
  thus "fdisjnt (freess pats) (pre_constants.all_consts C_info (fst |`| compile rs))"
    unfolding compile_def by simp

  have "¬ shadows_consts rhs"
    unfolding rhs = _ using ¬ shadows_consts _ by simp
  thus "¬ pre_constants.shadows_consts C_info (fst |`| compile rs) rhs"
    unfolding compile_heads .

  show "abs_ish pats rhs"
    using pats  [] unfolding abs_ish_def by simp

  have "welldefined rhs'"
    using fbspec[OF inner (name, irs') |∈| rs, simplified]
    using (pats, rhs') |∈| irs'
    by blast

  thus "pre_constants.welldefined C_info (fst |`| compile rs) rhs"
    unfolding compile_def rhs = _
    by simp
qed

sublocale crules  crules_as_irules: irules C_info "compile rs"
by (fact compile_rules)

subsubsection ‹Transformation of @{typ irule_set}

definition transform_irules :: "irules  irules" where
"transform_irules rs = (
  if arity rs = 0 then rs
  else map_prod id Pabs |`| fgroup_by (λ(pats, rhs). (butlast pats, (last pats, rhs))) rs)"

lemma arity_compatibles_transform_irules:
  assumes "arity_compatibles rs"
  shows "arity_compatibles (transform_irules rs)"
proof (cases "arity rs = 0")
  case True
  thus ?thesis
    unfolding transform_irules_def using assms by simp
next
  case False
  let ?rs' = "transform_irules rs"
  let ?f = "λ(pats, rhs). (butlast pats, (last pats, rhs))"
  let ?grp = "fgroup_by ?f rs"
  have rs': "?rs' = map_prod id Pabs |`| ?grp"
    using False unfolding transform_irules_def by simp
  show ?thesis
    proof safe
      fix pats1 rhs1 pats2 rhs2
      assume "(pats1, rhs1) |∈| ?rs'" "(pats2, rhs2) |∈| ?rs'"
      then obtain rhs1' rhs2' where "(pats1, rhs1') |∈| ?grp" "(pats2, rhs2') |∈| ?grp"
        unfolding rs' by auto
      then obtain pats1' pats2' x y ― ‹dummies›
        where "fst (?f (pats1', x)) = pats1" "(pats1', x) |∈| rs"
          and "fst (?f (pats2', y)) = pats2" "(pats2', y) |∈| rs"
        by (fastforce simp: split_beta elim: fgroup_byE2)
      hence "pats1 = butlast pats1'" "pats2 = butlast pats2'" "length pats1' = length pats2'"
        using assms by (force dest: fpairwiseD)+
      thus "length pats1 = length pats2"
        by auto
    qed
qed

lemma arity_transform_irules:
  assumes "arity_compatibles rs" "rs  {||}"
  shows "arity (transform_irules rs) = (if arity rs = 0 then 0 else arity rs - 1)"
proof (cases "arity rs = 0")
  case True
  thus ?thesis
    unfolding transform_irules_def by simp
next
  case False
  let ?f = "λ(pats, rhs). (butlast pats, (last pats, rhs))"
  let ?grp = "fgroup_by ?f rs"
  let ?rs' = "map_prod id Pabs |`| ?grp"

  have "arity ?rs' = arity rs - 1"
    proof (rule arityI)
      show "fBall ?rs' (λ(pats, _). length pats = arity rs - 1)"
        proof (rule prod_fBallI)
          fix pats rhs
          assume "(pats, rhs) |∈| ?rs'"
          then obtain cs where "(pats, cs) |∈| ?grp" "rhs = Pabs cs"
            by force
          then obtain pats' x ― ‹dummy›
            where "pats = butlast pats'" "(pats', x) |∈| rs"
            by (fastforce simp: split_beta elim: fgroup_byE2)
          hence "length pats' = arity rs"
            using assms by (metis arity_compatible_length)
          thus "length pats = arity rs - 1"
            unfolding pats = butlast pats' using False by simp
        qed
    next
      show "?rs'  {||}"
        using assms by (simp add: fgroup_by_nonempty)
    qed

  with False show ?thesis
    unfolding transform_irules_def by simp
qed

definition transform_irule_set :: "irule_set  irule_set" where
"transform_irule_set = fimage (map_prod id transform_irules)"

lemma transform_irule_set_heads: "fst |`| transform_irule_set rs = fst |`| rs"
unfolding transform_irule_set_def by simp

lemma (in irules) rules_transform: "irules C_info (transform_irule_set rs)"
proof
  have "is_fmap rs"
    using fmap by simp
  thus "is_fmap (transform_irule_set rs)"
    unfolding transform_irule_set_def map_prod_def id_apply by (rule is_fmap_image)

  show "transform_irule_set rs  {||}"
    using nonempty unfolding transform_irule_set_def by auto

  show "constants C_info (fst |`| transform_irule_set rs)"
    proof
      show "fdisjnt (fst |`| transform_irule_set rs) C"
        using disjnt unfolding transform_irule_set_def
        by force
    next
      show "distinct all_constructors"
        by (fact distinct_ctr)
    qed

  fix name irs
  assume irs: "(name, irs) |∈| transform_irule_set rs"
  then obtain irs' where "(name, irs') |∈| rs" "irs = transform_irules irs'"
    unfolding transform_irule_set_def by force
  hence "arity_compatibles irs'"
    using inner
    by (metis (no_types, lifting) case_prodD)
  thus "arity_compatibles irs"
    unfolding irs = transform_irules irs' by (rule arity_compatibles_transform_irules)

  have "irs'  {||}"
    using (name, irs') |∈| rs inner by blast
  thus "irs  {||}"
    unfolding irs = transform_irules irs' transform_irules_def
    by (simp add: fgroup_by_nonempty)

  let ?f = "λ(pats, rhs). (butlast pats, (last pats, rhs))"
  let ?grp = "fgroup_by ?f irs'"

  have "patterns_compatibles irs'"
    using (name, irs') |∈| rs inner
    by (blast dest: fpairwiseD)
  show "patterns_compatibles irs"
    proof (cases "arity irs' = 0")
      case True
      thus ?thesis
        unfolding irs = transform_irules irs' transform_irules_def
        using patterns_compatibles irs' by simp
    next
      case False
      hence irs': "irs = map_prod id Pabs |`| ?grp"
        unfolding irs = transform_irules irs' transform_irules_def by simp

      show ?thesis
        proof safe
          fix pats1 rhs1 pats2 rhs2
          assume "(pats1, rhs1) |∈| irs" "(pats2, rhs2) |∈| irs"
          with irs' obtain cs1 cs2 where "(pats1, cs1) |∈| ?grp" "(pats2, cs2) |∈| ?grp"
            by force
          then obtain pats1' pats2' and x y ― ‹dummies›
            where "(pats1', x) |∈| irs'" "(pats2', y) |∈| irs'"
              and "pats1 = butlast pats1'" "pats2 = butlast pats2'"
            unfolding irs'
            by (fastforce elim: fgroup_byE2)
          hence "patterns_compatible pats1' pats2'"
            using patterns_compatibles irs' by (auto dest: fpairwiseD)
          thus "patterns_compatible pats1 pats2"
            unfolding pats1 = _ pats2 = _
            by auto
        qed
    qed

  have "is_fmap irs'"
    using (name, irs') |∈| rs inner by blast
  show "is_fmap irs"
    proof (cases "arity irs' = 0")
      case True
      thus ?thesis
        unfolding irs = transform_irules irs' transform_irules_def
        using is_fmap irs' by simp
    next
      case False
      hence irs': "irs = map_prod id Pabs |`| ?grp"
        unfolding irs = transform_irules irs' transform_irules_def by simp

      show ?thesis
        proof
          fix pats rhs1 rhs2
          assume "(pats, rhs1) |∈| irs" "(pats, rhs2) |∈| irs"
          with irs' obtain cs1 cs2
            where "(pats, cs1) |∈| ?grp" "rhs1 = Pabs cs1"
              and "(pats, cs2) |∈| ?grp" "rhs2 = Pabs cs2"
            by force
          moreover have "is_fmap ?grp"
            by auto
          ultimately show "rhs1 = rhs2"
            by (auto dest: is_fmapD)
        qed
    qed

  fix pats rhs
  assume "(pats, rhs) |∈| irs"

  show "linears pats"
    proof (cases "arity irs' = 0")
      case True
      thus ?thesis
        using (pats, rhs) |∈| irs (name, irs') |∈| rs inner
        unfolding irs = transform_irules irs' transform_irules_def
        by auto
    next
      case False
      hence irs': "irs = map_prod id Pabs |`| ?grp"
        unfolding irs = transform_irules irs' transform_irules_def by simp
      then obtain cs where "(pats, cs) |∈| ?grp"
        using (pats, rhs) |∈| irs by force
      then obtain pats' x ― ‹dummy›
        where "fst (?f (pats', x)) = pats" "(pats', x) |∈| irs'"
        by (fastforce simp: split_beta elim: fgroup_byE2)
      hence "pats = butlast pats'"
        by simp
      moreover have "linears pats'"
        using (pats', x) |∈| irs' (name, irs') |∈| rs inner
        by auto
      ultimately show ?thesis
        by auto
    qed

  have "fdisjnt (freess pats) all_consts"
    proof (cases "arity irs' = 0")
      case True
      thus ?thesis
        using (pats, rhs) |∈| irs (name, irs') |∈| rs inner
        unfolding irs = transform_irules irs' transform_irules_def
        by auto
    next
      case False
      hence irs': "irs = map_prod id Pabs |`| ?grp"
        unfolding irs = transform_irules irs' transform_irules_def by simp
      then obtain cs where "(pats, cs) |∈| ?grp"
        using (pats, rhs) |∈| irs by force
      then obtain pats' x ― ‹dummy›
        where "fst (?f (pats', x)) = pats" "(pats', x) |∈| irs'"
        by (fastforce simp: split_beta elim: fgroup_byE2)
      hence "pats = butlast pats'"
        by simp
      moreover have "fdisjnt (freess pats') all_consts"
        using (pats', x) |∈| irs' (name, irs') |∈| rs inner by auto
      ultimately show ?thesis
        by (metis subsetI in_set_butlastD freess_subset fdisjnt_subset_left)
    qed

  thus "fdisjnt (freess pats) (pre_constants.all_consts C_info (fst |`| transform_irule_set rs))"
    unfolding transform_irule_set_def by simp

  show "closed_except rhs (freess pats)"
    proof (cases "arity irs' = 0")
      case True
      thus ?thesis
        using (pats, rhs) |∈| irs (name, irs') |∈| rs inner
        unfolding irs = transform_irules irs' transform_irules_def
        by auto
    next
      case False
      hence irs': "irs = map_prod id Pabs |`| ?grp"
        unfolding irs = transform_irules irs' transform_irules_def by simp
      then obtain cs where "(pats, cs) |∈| ?grp" "rhs = Pabs cs"
        using (pats, rhs) |∈| irs by force

      show ?thesis
        unfolding rhs = Pabs cs closed_except_simps
        proof safe
          fix pat t
          assume "(pat, t) |∈| cs"
          then obtain pats' where "(pats', t) |∈| irs'" "?f (pats', t) = (pats, (pat, t))"
            using (pats, cs) |∈| ?grp by auto
          hence "closed_except t (freess pats')"
            using (name, irs') |∈| rs inner by auto

          have "pats'  []"
            using arity_compatibles irs' (pats', t) |∈| irs' False
            by (metis list.size(3) arity_compatible_length)
          hence "pats' = pats @ [pat]"
            using ?f (pats', t) = (pats, (pat, t))
            by (fastforce simp: split_beta snoc_eq_iff_butlast)
          hence "freess pats |∪| frees pat = freess pats'"
            unfolding freess_def by auto

          thus "closed_except t (freess pats |∪| frees pat)"
            using closed_except t (freess pats') by simp
        qed
    qed

  show "wellformed rhs"
    proof (cases "arity irs' = 0")
      case True
      thus ?thesis
        using (pats, rhs) |∈| irs (name, irs') |∈| rs inner
        unfolding irs = transform_irules irs' transform_irules_def
        by auto
    next
      case False
      hence irs': "irs = map_prod id Pabs |`| ?grp"
        unfolding irs = transform_irules irs' transform_irules_def by simp
      then obtain cs where "(pats, cs) |∈| ?grp" "rhs = Pabs cs"
        using (pats, rhs) |∈| irs by force

      show ?thesis
        unfolding rhs = Pabs cs
        proof (rule wellformed_PabsI)
          show "cs  {||}"
            using (pats, cs) |∈| ?grp irs'  {||}
            by (meson femptyE fgroup_by_nonempty_inner)
        next
          show "is_fmap cs"
            proof
              fix pat t1 t2
              assume "(pat, t1) |∈| cs" "(pat, t2) |∈| cs"
              then obtain pats1' pats2'
                where "(pats1', t1) |∈| irs'" "?f (pats1', t1) = (pats, (pat, t1))"
                  and "(pats2', t2) |∈| irs'" "?f (pats2', t2) = (pats, (pat, t2))"
                using (pats, cs) |∈| ?grp by force
              moreover hence "pats1'  []" "pats2'  []"
                using arity_compatibles irs' False
                unfolding prod.case
                by (metis list.size(3) arity_compatible_length)+
              ultimately have "pats1' = pats @ [pat]" "pats2' = pats @ [pat]"
                unfolding split_beta fst_conv snd_conv
                by (metis prod.inject snoc_eq_iff_butlast)+
              with is_fmap irs' show "t1 = t2"
                using (pats1', t1) |∈| irs' (pats2', t2) |∈| irs'
                by (blast dest: is_fmapD)
            qed
        next
          show "pattern_compatibles cs"
            proof safe
              fix pat1 rhs1 pat2 rhs2
              assume "(pat1, rhs1) |∈| cs" "(pat2, rhs2) |∈| cs"
              then obtain pats1' pats2'
                where "(pats1', rhs1) |∈| irs'" "?f (pats1', rhs1) = (pats, (pat1, rhs1))"
                  and "(pats2', rhs2) |∈| irs'" "?f (pats2', rhs2) = (pats, (pat2, rhs2))"
                using (pats, cs) |∈| ?grp
                by force
              moreover hence "pats1'  []" "pats2'  []"
                using arity_compatibles irs' False
                unfolding prod.case
                by (metis list.size(3) arity_compatible_length)+
              ultimately have "pats1' = pats @ [pat1]" "pats2' = pats @ [pat2]"
                unfolding split_beta fst_conv snd_conv
                by (metis prod.inject snoc_eq_iff_butlast)+
              moreover have "patterns_compatible pats1' pats2'"
                using (pats1', rhs1) |∈| irs' (pats2', rhs2) |∈| irs' patterns_compatibles irs'
                by (auto dest: fpairwiseD)
              ultimately show "pattern_compatible pat1 pat2"
                by (auto elim: rev_accum_rel_snoc_eqE)
            qed
        next
          fix pat t
          assume "(pat, t) |∈| cs"
          then obtain pats' where "(pats', t) |∈| irs'" "pat = last pats'"
            using (pats, cs) |∈| ?grp by auto
          moreover hence "pats'  []"
            using arity_compatibles irs' False
            by (metis list.size(3) arity_compatible_length)
          ultimately have "pat  set pats'"
            by auto

          moreover have "linears pats'"
            using (pats', t) |∈| irs' (name, irs') |∈| rs inner by auto
          ultimately show "linear pat"
            by (metis linears_linear)

          show "wellformed t"
            using (pats', t) |∈| irs' (name, irs') |∈| rs inner by auto
        qed
    qed

  have "¬ shadows_consts rhs"
    proof (cases "arity irs' = 0")
      case True
      thus ?thesis
        using (pats, rhs) |∈| irs (name, irs') |∈| rs inner
        unfolding irs = transform_irules irs' transform_irules_def
        by auto
    next
      case False
      hence irs': "irs = map_prod id Pabs |`| ?grp"
        unfolding irs = transform_irules irs' transform_irules_def by simp
      then obtain cs where "(pats, cs) |∈| ?grp" "rhs = Pabs cs"
        using (pats, rhs) |∈| irs by force

      show ?thesis
        unfolding rhs = _
        proof
          assume "shadows_consts (Pabs cs)"
          then obtain pat t where "(pat, t) |∈| cs" "shadows_consts t  shadows_consts pat"
            by force
          then obtain pats' where "(pats', t) |∈| irs'" "pat = last pats'"
            using (pats, cs) |∈| ?grp by auto
          moreover hence "pats'  []"
            using arity_compatibles irs' False
            by (metis list.size(3) arity_compatible_length)
          ultimately have "pat  set pats'"
            by auto

          show False
            using shadows_consts t  shadows_consts pat
            proof
              assume "shadows_consts t"
              thus False
                using (name, irs') |∈| rs (pats', t) |∈| irs' inner by auto
            next
              assume "shadows_consts pat"

              have "fdisjnt (freess pats') all_consts"
                using (name, irs') |∈| rs (pats', t) |∈| irs' inner by auto
              have "fdisjnt (frees pat) all_consts"
                apply (rule fdisjnt_subset_left)
                 apply (subst freess_single[symmetric])
                 apply (rule freess_subset)
                 apply simp
                 apply fact+
                done

              thus False
                using shadows_consts pat
                unfolding shadows_consts_def fdisjnt_alt_def by auto
          qed
        qed
    qed
  thus "¬ pre_constants.shadows_consts C_info (fst |`| transform_irule_set rs) rhs"
    by (simp add: transform_irule_set_heads)

  show "abs_ish pats rhs"
    proof (cases "arity irs' = 0")
      case True
      thus ?thesis
        using (pats, rhs) |∈| irs (name, irs') |∈| rs inner
        unfolding irs = transform_irules irs' transform_irules_def
        by auto
    next
      case False
      hence irs': "irs = map_prod id Pabs |`| ?grp"
        unfolding irs = transform_irules irs' transform_irules_def by simp
      then obtain cs where "(pats, cs) |∈| ?grp" "rhs = Pabs cs"
        using (pats, rhs) |∈| irs by force
      thus ?thesis
        unfolding abs_ish_def by (simp add: is_abs_def term_cases_def)
    qed

  have "welldefined rhs"
    proof (cases "arity irs' = 0")
      case True
      hence (pats, rhs) |∈| irs'
        using (pats, rhs) |∈| irs (name, irs') |∈| rs inner
        unfolding irs = transform_irules irs' transform_irules_def
        by (smt fBallE split_conv)
      thus ?thesis
        unfolding transform_irule_set_def
        using fbspec[OF inner (name, irs') |∈| rs, simplified]
        by force
    next
      case False
      hence irs': "irs = map_prod id Pabs |`| ?grp"
        unfolding irs = transform_irules irs' transform_irules_def by simp
      then obtain cs where "(pats, cs) |∈| ?grp" "rhs = Pabs cs"
        using (pats, rhs) |∈| irs by force
      show ?thesis
        unfolding rhs = _
        apply simp
        apply (rule ffUnion_least)
        unfolding ball_simps
        apply rule
        apply (rename_tac x, case_tac x, hypsubst_thin)
        apply simp
        subgoal premises prems for pat t
          proof -
            from prems obtain pats' where "(pats', t) |∈| irs'"
              using (pats, cs) |∈| ?grp by auto
            hence "welldefined t"
              using fbspec[OF inner (name, irs') |∈| rs, simplified]
              by blast
            thus ?thesis
              unfolding transform_irule_set_def
              by simp
          qed
        done
    qed
  thus "pre_constants.welldefined C_info (fst |`| transform_irule_set rs) rhs"
    unfolding transform_irule_set_heads .
qed


subsubsection ‹Matching and rewriting›

definition irewrite_step :: "name  term list  pterm  pterm  pterm option" where
"irewrite_step name pats rhs t = map_option (subst rhs) (match (name $$ pats) t)"

abbreviation irewrite_step' :: "name  term list  pterm  pterm  pterm  bool" (‹_, _, _ i/ _ / _› [50,0,50] 50) where
"name, pats, rhs i t  u  irewrite_step name pats rhs t = Some u"

lemma irewrite_stepI:
  assumes "match (name $$ pats) t = Some env" "subst rhs env = u"
  shows "name, pats, rhs i t  u"
using assms unfolding irewrite_step_def by simp

inductive irewrite :: "irule_set  pterm  pterm  bool" (‹_/ i/ _ / _› [50,0,50] 50) for irs where
step: " (name, rs) |∈| irs; (pats, rhs) |∈| rs; name, pats, rhs i t  t'   irs i t  t'" |
beta: " c |∈| cs; c  t  t'   irs i Pabs cs $p t  t'" |
"fun": "irs i t  t'  irs i t $p u  t' $p u" |
arg: "irs i u  u'  irs i t $p u  t $p u'"

global_interpretation irewrite: rewriting "irewrite rs" for rs
by standard (auto intro: irewrite.intros simp: app_pterm_def)+

abbreviation irewrite_rt :: "irule_set  pterm  pterm  bool" (‹_/ i/ _ ⟶*/ _› [50,0,50] 50) where
"irewrite_rt rs  (irewrite rs)**"

lemma (in irules) irewrite_closed:
  assumes "rs i t  u" "closed t"
  shows "closed u"
using assms proof induction
  case (step name rs pats rhs t t')
  then obtain env where "match (name $$ pats) t = Some env" "t' = subst rhs env"
    unfolding irewrite_step_def by auto
  hence "closed_env env"
    using step by (auto intro: closed.match)

  show ?case
    unfolding t' = _
    apply (subst closed_except_def)
    apply (subst subst_frees)
     apply fact
    apply (subst match_dom)
     apply fact
    apply (subst frees_list_comb)
    apply simp
    apply (subst closed_except_def[symmetric])
    using inner step by fastforce
next
  case (beta c cs t t')
  then obtain pat rhs where "c = (pat, rhs)"
    by (cases c) auto
  with beta obtain env where "match pat t = Some env" "t' = subst rhs env"
    by auto
  moreover have "closed t"
    using beta unfolding closed_except_def by simp
  ultimately have "closed_env env"
    using beta by (auto intro: closed.match)

  show ?case
    unfolding t' = subst rhs env
    apply (subst closed_except_def)
    apply (subst subst_frees)
     apply fact
    apply (subst match_dom)
     apply fact
    apply simp
    apply (subst closed_except_def[symmetric])
    using inner beta c = _ by (auto simp: closed_except_simps)
qed (auto simp: closed_except_def)

corollary (in irules) irewrite_rt_closed:
  assumes "rs i t ⟶* u" "closed t"
  shows "closed u"
using assms by induction (auto intro: irewrite_closed)

subsubsection ‹Correctness of translation›

abbreviation irelated :: "nterm  pterm  bool" (‹_ i _› [0,50] 50) where
"n i p  nterm_to_pterm n = p"

global_interpretation irelated: term_struct_rel_strong irelated
by standard
   (auto simp: app_pterm_def app_nterm_def const_pterm_def const_nterm_def elim: nterm_to_pterm.elims)

lemma irelated_vars: "t i u  frees t = frees u"
by auto

lemma irelated_no_abs:
  assumes "t i u"
  shows "no_abs t  no_abs u"
using assms
apply (induction arbitrary: t)
 apply (auto elim!: nterm_to_pterm.elims)
   apply (fold const_nterm_def const_pterm_def free_nterm_def free_pterm_def app_pterm_def app_nterm_def)
by auto

lemma irelated_subst:
  assumes "t i u" "irelated.P_env nenv penv"
  shows "subst t nenv i subst u penv"
using assms proof (induction arbitrary: nenv penv u rule: nterm_to_pterm.induct)
  case (1 s)
  then show ?case
    by (auto elim!: fmrel_cases[where x = s])
next
  case 4
  from 4(2)[symmetric] show ?case
    apply simp
    apply (rule 4)
     apply simp
    using 4(3)
    by (simp add: fmrel_drop)
qed auto

lemma related_irewrite_step:
  assumes "name, pats, nterm_to_pterm rhs i u  u'" "t i u"
  obtains t' where "unsplit_rule (name, pats, rhs)  t  t'" "t' i u'"
proof -
  let ?rhs' = "nterm_to_pterm rhs"
  let ?x = "name $$ pats"

  from assms obtain env where "match ?x u = Some env" "u' = subst ?rhs' env"
    unfolding irewrite_step_def by blast
  then obtain nenv where "match ?x t = Some nenv" "irelated.P_env nenv env"
    using assms
    by (metis Option.is_none_def not_None_eq option.rel_distinct(1) option.sel rel_option_unfold irelated.match_rel)

  show thesis
    proof
      show "unsplit_rule (name, pats, rhs)  t  subst rhs nenv"
        using match ?x t = _ by auto
    next
      show "subst rhs nenv i u'"
        unfolding u' = _ using irelated.P_env nenv env
        by (auto intro: irelated_subst)
    qed
qed

theorem (in nrules) compile_correct:
  assumes "compile (consts_of rs) i u  u'" "t i u" "closed t"
  obtains t' where "rs n t  t'" "t' i u'"
using assms(1-3) proof (induction arbitrary: t thesis rule: irewrite.induct)
  case (step name irs pats rhs u u')
  then obtain crs where "irs = translate_crules crs" "(name, crs) |∈| consts_of rs"
    unfolding compile_def by force
  moreover with step obtain rhs' where "rhs = nterm_to_pterm rhs'" "(pats, rhs') |∈| crs"
    unfolding translate_crules_def by force
  ultimately obtain rule where "split_rule rule = (name, (pats, rhs'))" "rule |∈| rs"
    unfolding consts_of_def by blast
  hence "nrule rule"
    using all_rules by blast

  obtain t' where "unsplit_rule (name, pats, rhs')  t  t'" "t' i u'"
    using name, pats, rhs i u  u' t i u unfolding rhs = nterm_to_pterm rhs'
    by (elim related_irewrite_step)
  hence "rule  t  t'"
    using nrule rule split_rule rule = (name, (pats, rhs'))
    by (metis unsplit_split)

  show ?case
    proof (rule step.prems)
      show "rs n t  t'"
        apply (rule nrewrite.step)
         apply fact
        apply fact
        done
    next
      show "t' i u'"
        by fact
    qed
next
  case (beta c cs u u')
  then obtain pat rhs where "c = (pat, rhs)" "(pat, rhs) |∈| cs"
    by (cases c) auto
  obtain v w where "t = v $n w" "v i Pabs cs" "w i u"
    using t i Pabs cs $p u by (auto elim: nterm_to_pterm.elims)
  obtain x nrhs irhs where "v = (Λn x. nrhs)" "cs = {| (Free x, irhs) |}" "nrhs i irhs"
    using v i Pabs cs by (auto elim: nterm_to_pterm.elims)
  hence "t = (Λn x. nrhs) $n w" "Λn x. nrhs i Λp x. irhs"
    unfolding t = v $n w using v i Pabs cs by auto

  have "pat = Free x" "rhs = irhs"
    using cs = {| (Free x, irhs) |} (pat, rhs) |∈| cs by auto
  hence "(Free x, irhs)  u  u'"
    using beta c = _ by simp
  hence "u' = subst irhs (fmap_of_list [(x, u)])"
    by simp

  show ?case
    proof (rule beta.prems)
      show "rs n t  subst nrhs (fmap_of_list [(x, w)])"
        unfolding t = (Λn x. nrhs) $n w
        by (rule nrewrite.beta)
    next
      show "subst nrhs (fmap_of_list [(x, w)]) i u'"
        unfolding u' = subst irhs _
        apply (rule irelated_subst)
         apply fact
        apply simp
        apply rule
         apply rule
        apply fact
        done
    qed
next
  case ("fun" v v' u)
  obtain w x where "t = w $n x" "w i v" "x i u"
    using t i v $p u by (auto elim: nterm_to_pterm.elims)
  with "fun" obtain w' where "rs n w  w'" "w' i v'"
    unfolding closed_except_def by auto

  show ?case
    proof (rule fun.prems)
      show "rs n t  w' $n x"
        unfolding t = w $n x
        by (rule nrewrite.fun) fact
    next
      show "w' $n x i v' $p u"
        by auto fact+
    qed
next
  case (arg u u' v)
  obtain w x where "t = w $n x" "w i v" "x i u"
    using t i v $p u by (auto elim: nterm_to_pterm.elims)
  with arg obtain x' where "rs n x  x'" "x' i u'"
    unfolding closed_except_def by auto

  show ?case
    proof (rule arg.prems)
      show "rs n t  w $n x'"
        unfolding t = w $n x
        by (rule nrewrite.arg) fact
    next
      show "w $n x' i v $p u'"
        by auto fact+
    qed
qed

corollary (in nrules) compile_correct_rt:
  assumes "compile (consts_of rs) i u ⟶* u'" "t i u" "closed t"
  obtains t' where "rs n t ⟶* t'" "t' i u'"
using assms proof (induction arbitrary: thesis t) (* FIXME clone of transform_correct_rt, maybe locale? *)
  case (step u' u'')

  obtain t' where "rs n t ⟶* t'" "t' i u'"
    using step by blast

  obtain t'' where "rs n t' ⟶* t''" "t'' i u''"
    proof (rule compile_correct)
      show "compile (consts_of rs) i u'  u''" "t' i u'"
        by fact+
    next
      show "closed t'"
        using rs n t ⟶* t' closed t
        by (rule nrewrite_rt_closed)
    qed blast

  show ?case
    proof (rule step.prems)
      show "rs n t ⟶* t''"
        using rs n t ⟶* t' rs n t' ⟶* t'' by auto
    qed fact
qed blast

subsubsection ‹Completeness of translation›

lemma (in nrules) compile_complete:
  assumes "rs n t  t'" "closed t"
  shows "compile (consts_of rs) i nterm_to_pterm t  nterm_to_pterm t'"
  using assms proof induction
  case (step r t t')
  then obtain pat rhs' where "r = (pat, rhs')"
    by force
  then have "(pat, rhs') |∈| rs" "(pat, rhs')  t  t'"
    using step by blast+
  then have "nrule (pat, rhs')"
    using all_rules by blast
  then obtain name pats where "(name, (pats, rhs')) = split_rule r" "pat = name $$ pats"
    unfolding split_rule_def r = _
    apply atomize_elim
    by (auto simp: split_beta)

  obtain crs where "(name, crs) |∈| consts_of rs" "(pats, rhs') |∈| crs"
    using step _ = split_rule r r = _
    by (metis consts_of_def fgroup_by_complete fst_conv snd_conv)
  then obtain irs where "irs = translate_crules crs"
    by blast
  then have "(name, irs) |∈| compile (consts_of rs)"
    unfolding compile_def
    using (name, _) |∈| _
    by (metis fimageI id_def map_prod_simp)
  obtain rhs where "rhs = nterm_to_pterm rhs'" "(pats, rhs) |∈| irs"
    using irs = _ _ |∈| crs
    unfolding translate_crules_def
    by (metis fimageI id_def map_prod_simp)

  from step obtain env' where "match pat t = Some env'" "t' = subst rhs' env'"
    unfolding r = _ using rewrite_step.simps
    by force
  then obtain env where "match pat (nterm_to_pterm t) = Some env" "irelated.P_env env' env"
    by (metis irelated.match_rel option_rel_Some1)
  then have "subst rhs env = nterm_to_pterm t'"
    unfolding t' = _
    apply -
    apply (rule sym)
    apply (rule irelated_subst)
    unfolding rhs = _
    by auto

  have "name, pats, rhs i nterm_to_pterm t  nterm_to_pterm t'"
    apply (rule irewrite_stepI)
    using match _ _ = Some env unfolding pat = _
     apply assumption
    by fact

  show ?case
    by rule fact+
next
  case (beta x t t')
  obtain c where "c = (Free x, nterm_to_pterm t)"
    by blast
  from beta have "closed (nterm_to_pterm t')"
    using closed_nterm_to_pterm[where t = t']
    unfolding closed_except_def
    by auto
  show ?case
    apply simp
    apply rule
    using c = _
    by (fastforce intro: irelated_subst[THEN sym])+
next
  case ("fun" t t' u)
  show ?case
    apply simp
    apply rule
    apply (rule "fun")
    using "fun"
    unfolding closed_except_def
    apply simp
    done
next
  case (arg u u' t)
  show ?case
    apply simp
    apply rule
    apply (rule arg)
    using arg
    unfolding closed_except_def
    by simp
qed


subsubsection ‹Correctness of transformation›

abbreviation irules_deferred_matches :: "pterm list  irules  (term × pterm) fset" where
"irules_deferred_matches args  fselect
  (λ(pats, rhs). map_option (λenv. (last pats, subst rhs env)) (matchs (butlast pats) args))"

context irules begin

inductive prelated :: "pterm  pterm  bool" (‹_ p _› [0,50] 50) where
const: "Pconst x p Pconst x" |
var: "Pvar x p Pvar x" |
app: "t1 p u1  t2 p u2  t1 $p t2 p u1 $p u2" |
pat: "rel_fset (rel_prod (=) prelated) cs1 cs2  Pabs cs1 p Pabs cs2" |
"defer":
  "(name, rsi) |∈| rs  0 < arity rsi 
   rel_fset (rel_prod (=) prelated) (irules_deferred_matches args rsi) cs 
   list_all closed args 
   name $$ args p Pabs cs"

inductive_cases prelated_absE[consumes 1, case_names pat "defer"]: "t p Pabs cs"

lemma prelated_refl[intro!]: "t p t"
proof (induction t)
  case Pabs
  thus ?case
    by (auto simp: snds.simps intro!: prelated.pat rel_fset_refl_strong rel_prod.intros)
qed (auto intro: prelated.intros)

sublocale prelated: term_struct_rel prelated
by standard (auto simp: const_pterm_def app_pterm_def intro: prelated.intros elim: prelated.cases)

lemma prelated_pvars:
  assumes "t p u"
  shows "frees t = frees u"
using assms proof (induction rule: prelated.induct)
  case (pat cs1 cs2)
  show ?case
    apply simp
    apply (rule arg_cong[where f = ffUnion])
    apply (rule rel_fset_image_eq)
     apply fact
    apply auto
    done
next
  case ("defer" name rsi args cs)

  {
    fix pat t
    assume "(pat, t) |∈| cs"
    with "defer" obtain t'
      where "(pat, t') |∈| irules_deferred_matches args rsi" "frees t = frees t'"
      by (auto elim: rel_fsetE2)
    then obtain pats rhs env
      where "pat = last pats" "(pats, rhs) |∈| rsi"
        and "matchs (butlast pats) args = Some env" "t' = subst rhs env"
      by auto

    have "closed_except rhs (freess pats)" "linears pats"
      using (pats, rhs) |∈| rsi (name, rsi) |∈| rs inner by auto

    have "arity_compatibles rsi"
      using "defer" inner
      by (metis (no_types, lifting) case_prodD)
    have "length pats > 0"
      by (subst arity_compatible_length) fact+
    hence "pats = butlast pats @ [last pats]"
      by simp

    note frees t = frees t'
    also have "frees t' = frees rhs - fmdom env"
      unfolding t' = _
      apply (rule subst_frees)
      apply (rule closed.matchs)
       apply fact+
      done
    also have " = frees rhs - freess (butlast pats)"
      using matchs _ _ = _ by (metis matchs_dom)
    also have " |⊆| freess pats - freess (butlast pats)"
      using closed_except _ _
      by (auto simp: closed_except_def)
    also have " = frees (last pats) |-| freess (butlast pats)"
      by (subst pats = _) (simp add: funion_fminus)
    also have " = frees (last pats)"
      proof (rule fminus_triv)
        have "fdisjnt (freess (butlast pats)) (freess [last pats])"
          using linears pats pats = _
          by (metis linears_appendD)
        thus "frees (last pats) |∩| freess (butlast pats) = {||}"
          by (fastforce simp: fdisjnt_alt_def)
      qed
    also have " = frees pat" unfolding pat = _ ..
    finally have "frees t |⊆| frees pat" .
  }
  hence "closed (Pabs cs)"
    unfolding closed_except_simps
    by (auto simp: closed_except_def)
  moreover have "closed (name $$ args)"
    unfolding closed_list_comb by fact
  ultimately show ?case
    unfolding closed_except_def by simp
qed auto

corollary prelated_closed: "t p u  closed t  closed u"
unfolding closed_except_def
by (auto simp: prelated_pvars)

lemma prelated_no_abs_right:
  assumes "t p u" "no_abs u"
  shows "t = u"
using assms
apply (induction rule: prelated.induct)
    apply auto
 apply (fold app_pterm_def)
 apply auto
done

corollary env_prelated_refl[intro!]: "prelated.P_env env env"
by (auto intro: fmap.rel_refl)

text ‹
  The following, more general statement does not hold:
    @{prop "t p u  rel_option prelated.P_env (match x t) (match x u)"}
  If @{text t} and @{text u} are related because of the @{thm [source=true] prelated.defer} rule,
  they have completely different shapes.
  Establishing @{prop "is_abs t  is_abs u"} as a precondition would rule out this case, but at
  the same time be too restrictive.

  Instead, we use @{thm prelated.related_match}.
›

lemma prelated_subst:
  assumes "t1 p t2" "prelated.P_env env1 env2"
  shows "subst t1 env1 p subst t2 env2"
using assms proof (induction arbitrary: env1 env2 rule: prelated.induct)
  case (var x)
  thus ?case
    proof (cases rule: fmrel_cases[where x = x])
      case none
      thus ?thesis
        by (auto intro: prelated.var)
    next
      case (some t u)
      thus ?thesis
        by simp
    qed
next
  case (pat cs1 cs2)
  let ?drop = "λenv. λ(pat::term). fmdrop_fset (frees pat) env"
  from pat have "prelated.P_env (?drop env1 pat) (?drop env2 pat)" for pat
    by blast
  with pat show ?case
    by (auto intro!: prelated.pat rel_fset_image)
next
  case ("defer" name rsi args cs)
  have "name $$ args p Pabs cs"
    apply (rule prelated.defer)
       apply fact+
     apply (rule fset.rel_mono_strong)
      apply fact
     apply force
    apply fact
    done
  moreover have "closed (name $$ args)"
    unfolding closed_list_comb by fact
  ultimately have "closed (Pabs cs)"
    by (metis prelated_closed)

  let ?drop = "λenv. λpat. fmdrop_fset (frees pat) env"
  let ?f = "λenv. (λ(pat, rhs). (pat, subst rhs (?drop env pat)))"

  have "name $$ args p Pabs (?f env2 |`| cs)"
    proof (rule prelated.defer)
      show "(name, rsi) |∈| rs" "0 < arity rsi" "list_all closed args"
        using "defer" by auto
    next
      {
        fix pat1 rhs1
        fix pat2 rhs2
        assume "(pat2, rhs2) |∈| cs"
        assume "pat1 = pat2" "rhs1 p rhs2"
        have "rhs1 p subst rhs2 (fmdrop_fset (frees pat2) env2)"
          by (subst subst_closed_pabs) fact+
      }
      hence "rel_fset (rel_prod (=) prelated) (id |`| irules_deferred_matches args rsi) (?f env2 |`| cs)"
        by (force intro!: rel_fset_image[OF rel_fset _ _ _])

      thus "rel_fset (rel_prod (=) prelated) (irules_deferred_matches args rsi) (?f env2 |`| cs)"
        by simp
    qed

  moreover have "map (λt. subst t env1) args = args"
    apply (rule map_idI)
    apply (rule subst_closed_id)
    using "defer" by (simp add: list_all_iff)

  ultimately show ?case
    by (simp add: subst_list_comb)
qed (auto intro: prelated.intros)

lemma prelated_step:
  assumes "name, pats, rhs i u  u'" "t p u"
  obtains t' where "name, pats, rhs i t  t'" "t' p u'"
proof -
  let ?lhs = "name $$ pats"
  from assms obtain env where "match ?lhs u = Some env" "u' = subst rhs env"
    unfolding irewrite_step_def by blast
  then obtain env' where "match ?lhs t = Some env'" "prelated.P_env env' env"
    using assms by (auto elim: prelated.related_match)
  hence "subst rhs env' p subst rhs env"
    using assms by (auto intro: prelated_subst)

  show thesis
    proof
      show "name, pats, rhs i t  subst rhs env'"
        unfolding irewrite_step_def using match ?lhs t = Some env'
        by simp
    next
      show "subst rhs env' p u'"
        unfolding u' = subst rhs env
        by fact
    qed
qed

(* FIXME write using relators *)
lemma prelated_beta: ― ‹same problem as @{thm [source=true] prelated.related_match}
  assumes "(pat, rhs2)  t2  u2" "rhs1 p rhs2" "t1 p t2"
  obtains u1 where "(pat, rhs1)  t1  u1" "u1 p u2"
proof -
  from assms obtain env2 where "match pat t2 = Some env2" "u2 = subst rhs2 env2"
    by auto
  with assms obtain env1 where "match pat t1 = Some env1" "prelated.P_env env1 env2"
    by (auto elim: prelated.related_match)
  with assms have "subst rhs1 env1 p subst rhs2 env2"
    by (auto intro: prelated_subst)

  show thesis
    proof
      show "(pat, rhs1)  t1  subst rhs1 env1"
        using match pat t1 = _ by simp
    next
      show "subst rhs1 env1 p u2"
        unfolding u2 = _ by fact
    qed
qed

theorem transform_correct:
  assumes "transform_irule_set rs i u  u'" "t p u" "closed t"
  obtains t' where "rs i t ⟶* t'" ― ‹zero or one step› and "t' p u'"
using assms(1-3) proof (induction arbitrary: t thesis rule: irewrite.induct)
  case (beta c cs2 u2 x2')
  obtain v u1 where "t = v $p u1" "v p Pabs cs2" "u1 p u2"
    using t p Pabs cs2 $p u2 by cases
  with beta have "closed u1"
    by (simp add: closed_except_def)

  obtain pat rhs2 where "c = (pat, rhs2)" by (cases c) auto

  from v p Pabs cs2 show ?case
    proof (cases rule: prelated_absE)
      case (pat cs1)
      with beta c = _ obtain rhs1 where "(pat, rhs1) |∈| cs1" "rhs1 p rhs2"
        by (auto elim: rel_fsetE2)
      with beta obtain x1' where "(pat, rhs1)  u1  x1'" "x1' p x2'"
        using u1 p u2 assms c = _
        by (auto elim: prelated_beta simp del: rewrite_step.simps)

      show ?thesis
        proof (rule beta.prems)
          show "rs i t ⟶* x1'"
            unfolding t = _ v = _
            by (intro r_into_rtranclp irewrite.beta) fact+
        next
          show "x1' p x2'"
            by fact
      qed
    next
      case ("defer" name rsi args)
      with beta c = _ obtain rhs1' where "(pat, rhs1') |∈| irules_deferred_matches args rsi" "rhs1' p rhs2"
        by (auto elim: rel_fsetE2)
      then obtain enva rhs1 pats
        where "matchs (butlast pats) args = Some enva" "pat = last pats" "rhs1' = subst rhs1 enva"
          and "(pats, rhs1) |∈| rsi"
        by auto
      hence "linears pats"
        using (name, rsi) |∈| rs inner unfolding irules_def by auto

      have "arity_compatibles rsi"
        using "defer" inner
        by (metis (no_types, lifting) case_prodD)
      have "length pats > 0"
        by (subst arity_compatible_length) fact+
      hence "pats = butlast pats @ [pat]"
        unfolding pat = _ by simp

      from beta c = _ obtain envb where "match pat u2 = Some envb" "x2' = subst rhs2 envb"
        by fastforce
      with u1 p u2 obtain envb' where "match pat u1 = Some envb'" "prelated.P_env envb' envb"
        by (metis prelated.related_match)

      have "closed_env enva"
        by (rule closed.matchs) fact+
      have "closed_env envb'"
        apply (rule closed.matchs[where pats = "[pat]" and ts = "[u1]"])
         apply simp
         apply fact
        apply simp
        apply fact
        done

      have "fmdom enva = freess (butlast pats)"
        by (rule matchs_dom) fact
      moreover have "fmdom envb' = frees pat"
        by (rule match_dom) fact
      moreover have "fdisjnt (freess (butlast pats)) (frees pat)"
        using pats = _ linears pats
        by (metis freess_single linears_appendD(3))
      ultimately have "fdisjnt (fmdom enva) (fmdom envb')"
        by simp

      show ?thesis
        proof (rule beta.prems)
          have "rs i name $$ args $p u1  subst rhs1' envb'"
            proof (rule irewrite.step)
              show "(name, rsi) |∈| rs" "(pats, rhs1) |∈| rsi"
                by fact+
            next
              show "name, pats, rhs1 i name $$ args $p u1  subst rhs1' envb'"
                apply (rule irewrite_stepI)
                 apply (fold app_pterm_def)
                 apply (subst list_comb_snoc)
                 apply (subst matchs_match_list_comb)
                 apply (subst pats = _)
                 apply (rule matchs_appI)
                  apply fact
                 apply simp
                 apply fact
                unfolding rhs1' = _
                apply (rule subst_indep')
                 apply fact+
                done
            qed
          thus "rs i t ⟶* subst rhs1' envb'"
            unfolding t = _ v = _
            by (rule r_into_rtranclp)
        next
          show "subst rhs1' envb' p x2'"
            unfolding x2' = _
            by (rule prelated_subst) fact+
        qed
    qed
next
  case (step name rs2 pats rhs u u')
  then obtain rs1 where "rs2 = transform_irules rs1" "(name, rs1) |∈| rs"
    unfolding transform_irule_set_def by force
  hence "arity_compatibles rs1"
    using inner
    by (metis (no_types, lifting) case_prodD)

  show ?case
    proof (cases "arity rs1 = 0")
      case True
      hence "rs2 = rs1"
        unfolding rs2 = _ transform_irules_def by simp
      with step have "(pats, rhs) |∈| rs1"
        by simp
      from step obtain t' where "name, pats, rhs i t  t'" "t' p u'"
        using assms
        by (auto elim: prelated_step)

      show ?thesis
        proof (rule step.prems)
          show "rs i t ⟶* t'"
            by (intro conjI exI r_into_rtranclp irewrite.step) fact+
        qed fact
    next
      let ?f = "λ(pats, rhs). (butlast pats, last pats, rhs)"
      let ?grp = "fgroup_by ?f rs1"

      case False
      hence "rs2 = map_prod id Pabs |`| ?grp"
        unfolding rs2 = _ transform_irules_def by simp
      with step obtain cs where "rhs = Pabs cs" "(pats, cs) |∈| ?grp"
        by force

      from step obtain env2 where "match (name $$ pats) u = Some env2" "u' = subst rhs env2"
        unfolding irewrite_step_def by auto
      then obtain args2 where "u = name $$ args2" "matchs pats args2 = Some env2"
        by (auto elim: match_list_combE)
      with step obtain args1 where "t = name $$ args1" "list_all2 prelated args1 args2"
        by (auto elim: prelated.list_combE)

      then obtain env1 where "matchs pats args1 = Some env1" "prelated.P_env env1 env2"
        using matchs pats args2 = _ by (metis prelated.related_matchs)
      hence "fmdom env1 = freess pats"
        by (auto simp: matchs_dom)

      obtain cs' where "u' = Pabs cs'"
        unfolding u' = _ rhs = _ by auto

      hence "cs' = (λ(pat, rhs). (pat, subst rhs (fmdrop_fset (frees pat) env2 ))) |`| cs"
        using u' = subst rhs env2 unfolding rhs = _
        by simp

      show ?thesis
        proof (rule step.prems)
          show "rs i t ⟶* t"
            by (rule rtranclp.rtrancl_refl)
        next
          show "t p u'"
            unfolding u' = Pabs cs' t = _
            proof (intro prelated.defer rel_fsetI; safe?)
              show "(name, rs1) |∈| rs"
                by fact
            next
              show "0 < arity rs1"
                using False by simp
            next
              show "list_all closed args1"
                using closed t unfolding t = _ closed_list_comb .
            next
              fix pat rhs'
              assume "(pat, rhs') |∈| irules_deferred_matches args1 rs1"
              then obtain pats' rhs env
                where "(pats', rhs) |∈| rs1"
                  and "matchs (butlast pats') args1 = Some env" "pat = last pats'" "rhs' = subst rhs env"
                by auto
              with False have "pats'  []"
                using arity_compatibles rs1
                by (metis list.size(3) arity_compatible_length)
              hence "butlast pats' @ [last pats'] = pats'"
                by simp

              from (pats, cs) |∈| ?grp obtain patse rhse
                where "(patse, rhse) |∈| rs1" "pats = butlast patse"
                by (auto elim: fgroup_byE2)

              have "patterns_compatible (butlast pats') pats"
                unfolding pats = _
                apply (rule rev_accum_rel_butlast)
                using (pats', rhs) |∈| rs1 (patse, rhse) |∈| rs1 (name, rs1) |∈| rs inner
                by (blast dest: fpairwiseD)

              interpret irules': irules C_info "transform_irule_set rs" by (rule rules_transform)

              have "butlast pats' = pats" "env = env1"
                 apply (rule matchs_compatible_eq)
                subgoal by fact
                subgoal
                  apply (rule linears_butlastI)
                  using (pats', rhs) |∈| rs1 (name, rs1) |∈| rs inner by auto
                subgoal
                  using (pats, _) |∈| rs2 (name, rs2) |∈| transform_irule_set rs
                  using irules'.inner by auto
                  apply fact+
                subgoal
                  apply (rule matchs_compatible_eq)
                      apply fact
                     apply (rule linears_butlastI)
                  using (pats', rhs) |∈| rs1 (name, rs1) |∈| rs inner
                  apply auto []
                  using (pats, _) |∈| rs2 (name, rs2) |∈| transform_irule_set rs
                  using irules'.inner apply auto[]
                  by fact+
                done

              let ?rhs_subst = "λenv. subst rhs (fmdrop_fset (frees pat) env)"

              have "fmdom env2 = freess pats"
                using match (_ $$ _) _ = Some env2
                by (simp add: match_dom)

              show "fBex cs' (rel_prod (=) prelated (pat, rhs'))"
                unfolding rhs' = _
                proof (rule fBexI, rule rel_prod.intros)
                  have "fdisjnt (freess (butlast pats')) (frees (last pats'))"
                    apply (subst freess_single[symmetric])
                    apply (rule linears_appendD)
                    apply (subst butlast pats' @ [last pats'] = pats')
                    using (pats', rhs) |∈| rs1 (name, rs1) |∈| rs inner
                    by auto

                  show "subst rhs env p ?rhs_subst env2"
                    apply (rule prelated_subst)
                     apply (rule prelated_refl)
                    unfolding fmfilter_alt_defs
                    apply (subst fmfilter_true)
                    subgoal premises prems for x y
                      using fmdomI[OF prems]
                      unfolding pat = _ fmdom env2 = _
                      apply (subst (asm) butlast pats' = pats[symmetric])
                      using fdisjnt (freess (butlast pats')) (frees (last pats'))
                      by (auto simp: fdisjnt_alt_def)
                    subgoal
                      unfolding env = _
                      by fact
                    done
                next
                  have "(pat, rhs) |∈| cs"
                    unfolding pat = _
                    apply (rule fgroup_byD[where a = "(x, y)" for x y])
                      apply fact
                     apply simp
                     apply (intro conjI)
                       apply fact
                      apply (rule refl)+
                    apply fact
                    done
                  thus "(pat, ?rhs_subst env2) |∈| cs'"
                    unfolding cs' = _ by force
                qed simp
            next
              fix pat rhs'
              assume "(pat, rhs') |∈| cs'"
              then obtain rhs
                where "(pat, rhs) |∈| cs"
                  and "rhs' = subst rhs (fmdrop_fset (frees pat) env2 )"
                unfolding cs' = _ by auto
              with (pats, cs) |∈| ?grp obtain pats'
                where "(pats', rhs) |∈| rs1" "pats = butlast pats'" "pat = last pats'"
                by auto
              with False have "length pats'  0"
                using arity_compatibles _ by (metis arity_compatible_length)
              hence "pats' = pats @ [pat]"
                unfolding pats = _ pat = _ by auto
              moreover have "linears pats'"
                using (pats', rhs) |∈| rs1 (name, rs1) |∈| _ inner by auto
              ultimately have "fdisjnt (fmdom env1) (frees pat)"
                unfolding fmdom env1 = _
                by (auto dest: linears_appendD)

              let ?rhs_subst = "λenv. subst rhs (fmdrop_fset (frees pat) env)"

              show "fBex (irules_deferred_matches args1 rs1) (λe. rel_prod (=) prelated e (pat, rhs'))"
                unfolding rhs' = _
                proof (rule fBexI, rule rel_prod.intros)
                  show "?rhs_subst env1 p ?rhs_subst env2"
                    using prelated.P_env env1 env2 inner
                    by (auto intro: prelated_subst)
                next
                  have "matchs (butlast pats') args1 = Some env1"
                    using matchs pats args1 = _ pats = _ by simp
                  moreover have "subst rhs env1 = ?rhs_subst env1"
                    apply (rule arg_cong[where f = "subst rhs"])
                    unfolding fmfilter_alt_defs
                    apply (rule fmfilter_true[symmetric])
                    using fdisjnt (fmdom env1) _
                    by (auto simp: fdisjnt_alt_def intro: fmdomI)
                  ultimately show "(pat, ?rhs_subst env1) |∈| irules_deferred_matches args1 rs1"
                    using (pats', rhs) |∈| rs1 pat = last pats'
                    by auto
                qed simp
            qed
        qed
    qed
next
  case ("fun" v v' u)
  obtain w x where "t = w $p x" "w p v" "x p u" "closed w"
    using t p v $p u closed t by cases (auto simp: closed_except_def)
  with "fun" obtain w' where "rs i w ⟶* w'" "w' p v'"
    by blast

  show ?case
    proof (rule fun.prems)
      show "rs i t ⟶* w' $p x"
        unfolding t = _
        by (intro irewrite.rt_comb[unfolded app_pterm_def] rtranclp.rtrancl_refl) fact
    next
      show "w' $p x p v' $p u"
        by (rule prelated.app) fact+
    qed
next
  case (arg u u' v)
  obtain w x where "t = w $p x" "w p v" "x p u" "closed x"
    using t p v $p u closed t by cases (auto simp: closed_except_def)
  with arg obtain x' where "rs i x ⟶* x'" "x' p u'"
    by blast

  show ?case
    proof (rule arg.prems)
      show "rs i t ⟶* w $p x'"
        unfolding t = w $p x
        by (intro irewrite.rt_comb[unfolded app_pterm_def] rtranclp.rtrancl_refl) fact
    next
      show "w $p x' p v $p u'"
        by (rule prelated.app) fact+
    qed
qed

end

subsubsection ‹Completeness of transformation›

lemma (in irules) transform_completeness:
  assumes "rs i t  t'" "closed t"
  shows "transform_irule_set rs i t ⟶* t'"
using assms proof induction
  case (step name irs' pats' rhs' t t')
  then obtain irs where "irs = transform_irules irs'" "(name, irs) |∈| transform_irule_set rs"
    unfolding transform_irule_set_def
    by (metis fimageI id_apply map_prod_simp)
  show ?case
  proof (cases "arity irs' = 0")
    case True
    hence "irs = irs'"
      unfolding irs = _
      unfolding transform_irules_def
      by simp
    with step have "(pats', rhs') |∈| irs" "name, pats', rhs' i t  t'"
      by blast+
    have "transform_irule_set rs i t ⟶* t'"
      apply (rule r_into_rtranclp)
      apply rule
      by fact+

    show ?thesis by fact
  next
    let ?f = "λ(pats, rhs). (butlast pats, last pats, rhs)"
    let ?grp = "fgroup_by ?f irs'"
    note closed_except_def [simp add]
    case False
    then have "irs = map_prod id Pabs |`| ?grp"
      unfolding irs = _
      unfolding transform_irules_def
      by simp
    with False have "irs = transform_irules irs'"
      unfolding transform_irules_def
      by simp
    obtain pat pats where "pat = last pats'" "pats = butlast pats'"
      by blast
    from step False have "length pats'  0"
      using arity_compatible_length inner
      by (smt (verit, ccfv_threshold) case_prodD)
    then have "pats' = pats @ [pat]"
      unfolding pat = _ pats = _
      by simp
    from step have "linears pats'"
      using inner by auto
    then have "fdisjnt (freess pats) (frees pat)"
      unfolding pats' = _
      using linears_appendD(3) freess_single
      by force

    from step obtain cs where "(pats, cs) |∈| ?grp"
      unfolding pats = _
      by (metis (no_types, lifting) fgroup_by_complete fst_conv prod.simps(2))
    with step have "(pat, rhs') |∈| cs"
      unfolding pat = _ pats = _
      by (meson fgroup_byD old.prod.case)
    have "(pats, Pabs cs) |∈| irs"
      using irs = map_prod id Pabs |`| ?grp (pats, cs) |∈| _
      by (metis (no_types, lifting) eq_snd_iff fst_conv fst_map_prod id_def rev_fimage_eqI snd_map_prod)
    from step obtain env' where "match (name $$ pats') t = Some env'" "subst rhs' env' = t'"
      using irewrite_step_def by auto
    have "name $$ pats' = (name $$ pats) $ pat"
      unfolding pats' = _
      by (simp add: app_term_def)
    then obtain t0 t1 env0 env1 where "t = t0 $p t1" "match (name $$ pats) t0 = Some env0" "match pat t1 = Some env1" "env' = env0 ++f env1"
      using match_appE_split[OF match (name $$ pats') _ = _[unfolded name $$ pats' = _], unfolded app_pterm_def]
      by blast
    with step have "closed t0" "closed t1"
      by auto
    then have "closed_env env0" "closed_env env1"
      using match_vars[OF match _ t0 = _] match_vars[OF match _ t1 = _]
      unfolding closed_except_def
      by auto
    obtain t0' where "subst (Pabs cs) env0 = t0'"
      by blast
    then obtain cs' where "t0' = Pabs cs'" "cs' = ((λ(pat, rhs). (pat, subst rhs (fmdrop_fset (frees pat) env0))) |`| cs)"
      using subst_pterm.simps(3) by blast
    obtain rhs where "subst rhs' (fmdrop_fset (frees pat) env0) = rhs"
      by blast
    then have "(pat, rhs) |∈| cs'"
      unfolding cs' = _
      using _ |∈| cs
      by (metis (mono_tags, lifting) old.prod.case rev_fimage_eqI)
    have "env0 ++f env1 = (fmdrop_fset (frees pat) env0) ++f env1"
      apply (subst fmadd_drop_left_dom[symmetric])
      using match pat _ = _ match_dom
      by metis
    have "fdisjnt (fmdom env0) (fmdom env1)"
      using match_dom
      using match pat _ = _ match (name $$ pats) _ = _
      using fdisjnt _ _
      unfolding fdisjnt_alt_def
      by (metis matchs_dom match_list_combE)
    have "subst rhs env1 = t'"
      unfolding _ = rhs[symmetric]
      unfolding _ = t'[symmetric]
      unfolding env' = _
      unfolding env0 ++f _ = _
      apply (subst subst_indep')
      using closed_env env0
        apply blast
      using fdisjnt (fmdom _) _
      unfolding fdisjnt_alt_def
      by auto

    show ?thesis
      unfolding t = _
      apply rule
       apply (rule r_into_rtranclp)
       apply (rule irewrite.intros(3))
       apply rule
         apply fact+
       apply (rule irewrite_stepI)
        apply fact+
      unfolding t0' = _
      apply rule
       apply fact
      using match pat t1 = _ subst rhs _ = _
      by force
  qed
qed (auto intro: irewrite.rt_comb[unfolded app_pterm_def] intro!: irewrite.intros simp: closed_except_def)


subsubsection ‹Computability›

export_code
  compile transform_irules
  checking Scala SML

end