Theory Rewriting_Nterm

section ‹Higher-order term rewriting using explicit bound variable names›

theory Rewriting_Nterm
imports
  Rewriting_Term
  Higher_Order_Terms.Term_to_Nterm
  "../Terms/Strong_Term"
begin

subsection ‹Definitions›

type_synonym nrule = "term × nterm"

abbreviation nrule :: "nrule  bool" where
"nrule  basic_rule"

fun (in constants) not_shadowing :: "nrule  bool" where
"not_shadowing (lhs, rhs)  ¬ shadows_consts lhs  ¬ shadows_consts rhs"

locale nrules = constants C_info "heads_of rs" for C_info and rs :: "nrule fset" +
  assumes all_rules: "fBall rs nrule"
  assumes arity: "arity_compatibles rs"
  assumes fmap: "is_fmap rs"
  assumes patterns: "pattern_compatibles rs"
  assumes nonempty: "rs  {||}"
  assumes not_shadows: "fBall rs not_shadowing"
  assumes welldefined_rs: "fBall rs (λ(_, rhs). welldefined rhs)"


subsection ‹Matching and rewriting›

inductive nrewrite :: "nrule fset  nterm  nterm  bool" ("_/ n/ _ / _" [50,0,50] 50) for rs where
step: "r |∈| rs  r  t  u  rs n t  u" |
beta: "rs n ((Λn x. t) $n t')  subst t (fmap_of_list [(x, t')])" |
"fun": "rs n t  t'  rs n t $n u  t' $n u" |
arg: "rs n u  u'  rs n t $n u  t $n u'"

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

abbreviation nrewrite_rt :: "nrule fset  nterm  nterm  bool" ("_/ n/ _ ⟶*/ _" [50,0,50] 50) where
"nrewrite_rt rs  (nrewrite rs)**"

lemma (in nrules) nrewrite_closed:
  assumes "rs n t  t'" "closed t"
  shows "closed t'"
using assms proof induction
  case (step r t u)
  obtain lhs rhs where "r = (lhs, rhs)"
    by force
  with step have "nrule (lhs, rhs)"
    using all_rules by blast
  hence "frees rhs |⊆| frees lhs"
    by simp
  have "(lhs, rhs)  t  u"
    using step unfolding r = _ by simp

  show ?case
    apply (rule rewrite_step_closed)
      apply fact+
    done
next
  case beta
  show ?case
    apply simp
    apply (subst closed_except_def)
    apply (subst subst_frees)
    using beta unfolding closed_except_def by auto
qed (auto simp: closed_except_def)

corollary (in nrules) nrewrite_rt_closed:
  assumes "rs n t ⟶* t'" "closed t"
  shows "closed t'"
using assms
by induction (auto intro: nrewrite_closed)

subsection ‹Translation from @{typ term} to @{typ nterm}

context begin

private lemma term_to_nterm_all_vars0:
  assumes "wellformed' (length Γ) t"
  shows "T. all_frees (fst (run_state (term_to_nterm Γ t) x)) |⊆| fset_of_list Γ |∪| frees t |∪| T  fBall T (λy. y > x)"
using assms proof (induction Γ t arbitrary: x rule: term_to_nterm_induct)
  case (bound Γ i)
  hence "Γ ! i |∈| fset_of_list Γ"
    by (simp add: fset_of_list_elem)
  with bound show ?case
    by (auto simp: State_Monad.return_def)
next
  case (abs Γ t)

  obtain T
    where "all_frees (fst (run_state (term_to_nterm (next x # Γ) t) (next x))) |⊆| fset_of_list (next x # Γ) |∪| frees t |∪| T"
      and "fBall T ((<) (next x))"
    apply atomize_elim
    apply (rule abs(1))
    using abs by auto

  show ?case
    apply (simp add: split_beta create_alt_def)
    apply (rule exI[where x = "finsert (next x) T"])
    apply (intro conjI)
    subgoal by auto
    subgoal using all_frees (fst (run_state _ (next x))) |⊆| _ by simp
    subgoal
      apply simp
      apply (rule conjI)
       apply (rule next_ge)
      using fBall T ((<) (next x))
      by (metis fBallE fBallI fresh_class.next_ge order.strict_trans)
    done
next
  case (app Γ t1 t2 x1)
  obtain t1' x2 where "run_state (term_to_nterm Γ t1) x1 = (t1', x2)"
    by fastforce
  moreover obtain T1
    where "all_frees (fst (run_state (term_to_nterm Γ t1) x1)) |⊆| fset_of_list Γ |∪| frees t1 |∪| T1"
      and "fBall T1 ((<) x1)"
    apply atomize_elim
    apply (rule app(1))
    using app by auto
  ultimately have "all_frees t1' |⊆| fset_of_list Γ |∪| frees t1 |∪| T1"
    by simp

  obtain T2
    where "all_frees (fst (run_state (term_to_nterm Γ t2) x2)) |⊆| fset_of_list Γ |∪| frees t2 |∪| T2"
      and "fBall T2 ((<) x2)"
    apply atomize_elim
    apply (rule app(2))
    using app by auto
  moreover obtain t2' x' where "run_state (term_to_nterm Γ t2) x2 = (t2', x')"
    by fastforce
  ultimately have "all_frees t2' |⊆| fset_of_list Γ |∪| frees t2 |∪| T2"
    by simp

  have "x1  x2"
    apply (rule state_io_relD[OF term_to_nterm_mono])
    apply fact
    done

  show ?case
    apply simp
    unfolding run_state (term_to_nterm Γ t1) x1 = _
    apply simp
    unfolding run_state (term_to_nterm Γ t2) x2 = _
    apply simp
    apply (rule exI[where x = "T1 |∪| T2"])
    apply (intro conjI)
    subgoal using all_frees t1' |⊆| _ by auto
    subgoal using all_frees t2' |⊆| _ by auto
    subgoal
      apply auto
      using fBall T1 ((<) x1) apply auto[]
      using fBall T2 ((<) x2) x1  x2
      by auto
    done
qed auto

lemma term_to_nterm_all_vars:
  assumes "wellformed t" "fdisjnt (frees t) S"
  shows "fdisjnt (all_frees (fresh_frun (term_to_nterm [] t) (T |∪| S))) S"
proof -
  let  = "[]"
  let ?x = "fresh_fNext (T |∪| S)"
  from assms have "wellformed' (length ) t"
    by simp
  then obtain F
    where "all_frees (fst (run_state (term_to_nterm  t) ?x)) |⊆| fset_of_list  |∪| frees t |∪| F"
      and "fBall F (λy. y > ?x)"
    by (metis term_to_nterm_all_vars0)

  have "fdisjnt F (T |∪| S)" if "S  {||}"
    apply (rule fdisjnt_ge_max)
    apply (rule fBall_pred_weaken[OF _ fBall F (λy. y > ?x)])
    apply (rule less_trans)
     apply (rule fNext_ge_max)
    using that by auto
  show ?thesis
    apply (rule fdisjnt_subset_left)
     apply (subst fresh_frun_def)
     apply (subst fresh_fNext_def[symmetric])
     apply fact
    apply simp
    apply (rule fdisjnt_union_left)
     apply fact
    using _  fdisjnt F (T |∪| S) by (auto simp: fdisjnt_alt_def)
qed

end

fun translate_rule :: "name fset  rule  nrule" where
"translate_rule S (lhs, rhs) = (lhs, fresh_frun (term_to_nterm [] rhs) (frees lhs |∪| S))"

lemma translate_rule_alt_def:
  "translate_rule S = (λ(lhs, rhs). (lhs, fresh_frun (term_to_nterm [] rhs) (frees lhs |∪| S)))"
by auto

definition compile' where
"compile' C_info rs = translate_rule (pre_constants.all_consts C_info (heads_of rs)) |`| rs"

context rules begin

definition compile :: "nrule fset" where
"compile = translate_rule all_consts |`| rs"

lemma compile'_compile_eq[simp]: "compile' C_info rs = compile"
unfolding compile'_def compile_def ..

lemma compile_heads: "heads_of compile = heads_of rs"
unfolding compile_def translate_rule_alt_def head_def[abs_def]
by force

lemma compile_rules: "nrules C_info compile"
proof
  have "fBall compile (λ(lhs, rhs). nrule (lhs, rhs))"
    proof safe
      fix lhs rhs
      assume "(lhs, rhs) |∈| compile"
      then obtain rhs'
        where "(lhs, rhs') |∈| rs"
          and rhs: "rhs = fresh_frun (term_to_nterm [] rhs') (frees lhs |∪| all_consts)"
        unfolding compile_def by force
      then have rule: "rule (lhs, rhs')"
        using all_rules by blast

      show "nrule (lhs, rhs)"
        proof
          from rule show "linear lhs" "is_const (fst (strip_comb lhs))" "¬ is_const lhs" by auto

          have "frees rhs |⊆| frees rhs'"
            unfolding rhs using rule
            by (metis rule.simps term_to_nterm_vars)
          also have "frees rhs' |⊆| frees lhs"
            using rule by auto
          finally show "frees rhs |⊆| frees lhs" .
        qed
    qed

  thus "fBall compile nrule"
    by fast
next
  show "arity_compatibles compile"
    proof safe
      fix lhs1 rhs1 lhs2 rhs2
      assume "(lhs1, rhs1) |∈| compile" "(lhs2, rhs2) |∈| compile"
      then obtain rhs1' rhs2' where "(lhs1, rhs1') |∈| rs" "(lhs2, rhs2') |∈| rs"
        unfolding compile_def by force
      thus "arity_compatible lhs1 lhs2"
        using arity by (blast dest: fpairwiseD)
    qed
next
  have "is_fmap rs"
    using fmap by simp
  thus "is_fmap compile"
    unfolding compile_def translate_rule_alt_def
    by (rule is_fmap_image)
next
  have "pattern_compatibles rs"
    using patterns by simp
  thus "pattern_compatibles compile"
    unfolding compile_def translate_rule_alt_def
    by (auto dest: fpairwiseD)
next
  show "fdisjnt (heads_of compile) C"
    using disjnt by (simp add: compile_heads)
next
  have "fBall compile not_shadowing"
    proof safe
      fix lhs rhs
      assume "(lhs, rhs) |∈| compile"
      then obtain rhs'
        where "rhs = fresh_frun (term_to_nterm [] rhs') (frees lhs |∪| all_consts)"
          and "(lhs, rhs') |∈| rs"
        unfolding compile_def translate_rule_alt_def by auto
      hence "rule (lhs, rhs')" "¬ shadows_consts lhs"
        using all_rules not_shadows by blast+
      moreover hence "wellformed rhs'" "frees rhs' |⊆| frees lhs" "fdisjnt all_consts (frees lhs)"
        unfolding shadows_consts_def by simp+

      moreover have "¬ shadows_consts rhs"
        apply (subst shadows_consts_def)
        apply simp
        unfolding rhs = _
        apply (rule fdisjnt_swap)
        apply (rule term_to_nterm_all_vars)
         apply fact
        apply (rule fdisjnt_subset_left)
         apply fact
        apply (rule fdisjnt_swap)
        apply fact
        done

      ultimately show "not_shadowing (lhs, rhs)"
        unfolding compile_heads by simp
    qed
  thus "fBall compile (constants.not_shadowing C_info (heads_of compile))"
    unfolding compile_heads .

  have "fBall compile (λ(_, rhs). welldefined rhs)"
    unfolding compile_heads
    unfolding compile_def ball_simps
    apply (rule fBall_pred_weaken[OF _ welldefined_rs])
    subgoal for x
      apply (cases x)
      apply simp
      apply (subst fresh_frun_def)
      apply (subst term_to_nterm_consts[THEN pred_state_run_state])
      by auto
    done
  thus "fBall compile (λ(_, rhs). consts rhs |⊆| pre_constants.all_consts C_info (heads_of compile))"
    unfolding compile_heads .
next
  show "compile  {||}"
    using nonempty unfolding compile_def by auto
next
  show "distinct all_constructors"
    by (fact distinct_ctr)
qed

sublocale rules_as_nrules: nrules C_info compile
by (fact compile_rules)

end

subsection ‹Correctness of translation›

theorem (in rules) compile_correct:
  assumes "compile n u  u'" "closed u"
  shows "rs  nterm_to_term' u  nterm_to_term' u'"
using assms proof (induction u u')
  case (step r u u')
  moreover obtain pat rhs' where "r = (pat, rhs')"
    by force
  ultimately obtain nenv where "match pat u = Some nenv" "u' = subst rhs' nenv"
    by auto
  then obtain env where "nrelated.P_env [] env nenv" "match pat (nterm_to_term [] u) = Some env"
    by (metis nrelated.match_rel option.exhaust option.rel_distinct(1) option.rel_inject(2))

  have "closed_env nenv"
    using step match pat u = Some nenv by (intro closed.match)

  from step obtain rhs
    where "rhs' = fresh_frun (term_to_nterm [] rhs) (frees pat |∪| all_consts)" "(pat, rhs) |∈| rs"
    unfolding r = _ compile_def by auto
  with assms have "rule (pat, rhs)"
    using all_rules by blast
  hence "rhs = nterm_to_term [] rhs'"
    unfolding rhs' = _
    by (simp add: term_to_nterm_nterm_to_term fresh_frun_def)

  have "compile n u  u'"
    using step by (auto intro: nrewrite.step)
  hence "closed u'"
    by (rule rules_as_nrules.nrewrite_closed) fact

  show ?case
    proof (rule rewrite.step)
      show "(pat, rhs)  nterm_to_term [] u  nterm_to_term [] u'"
        apply (subst nterm_to_term_eq_closed)
         apply fact
        apply simp
        apply (rule exI[where x = env])
        apply (rule conjI)
         apply fact
        unfolding rhs = _
        apply (subst nrelated_subst)
           apply fact
          apply fact
        unfolding fdisjnt_alt_def apply simp
        unfolding u' = subst rhs' nenv by simp
    qed fact
next
  case beta
  show ?case
    apply simp
    apply (subst subst_single_eq[symmetric, simplified])
    apply (subst nterm_to_term_subst_replace_bound[where n = 0])
    subgoal using beta by (simp add: closed_except_def)
    subgoal by simp
    subgoal by simp
    subgoal by simp (rule rewrite.beta)
    done
qed (auto intro: rewrite.intros simp: closed_except_def)

subsection ‹Completeness of translation›

context rules begin

context
  notes [simp] = closed_except_def fdisjnt_alt_def
begin

private lemma compile_complete0:
  assumes "rs  t  t'" "closed t" "wellformed t"
  obtains u' where "compile n fst (run_state (term_to_nterm [] t) s)  u'" "u' α fst (run_state (term_to_nterm [] t') s')"
apply atomize_elim
using assms proof (induction t t' arbitrary: s s')
  case (step r t t')
  let ?tn = "fst (run_state (term_to_nterm [] t) s)"
  let ?tn' = "fst (run_state (term_to_nterm [] t') s')"
  from step have "closed t" "closed ?tn"
    using term_to_nterm_vars0[where Γ = "[]"]
    by force+
  from step have "nterm_to_term' ?tn = t"
    by (auto intro!: term_to_nterm_nterm_to_term0)

  obtain pat rhs' where "r = (pat, rhs')"
    by fastforce
  with step obtain env' where "match pat t = Some env'" "t' = subst rhs' env'"
    by fastforce
  with _ = t have "rel_option (nrelated.P_env []) (match pat t) (match pat (?tn))"
    by (metis nrelated.match_rel)
  with step _ = Some env'
    obtain env where "nrelated.P_env [] env' env" "match pat ?tn = Some env"
    by (metis (no_types, lifting) option_rel_Some1)
  with closed ?tn have "closed_env env"
    by (blast intro: closed.match)

  from step obtain rhs
    where "rhs = fresh_frun (term_to_nterm [] rhs') (frees pat |∪| all_consts)" "(pat, rhs) |∈| compile"
    unfolding r = _ compile_def
    by force
  with step have "rule (pat, rhs')"
    unfolding r = _
    using all_rules by fast
  hence "nterm_to_term' rhs = rhs'"
    unfolding rhs = _
    by (simp add: fresh_frun_def term_to_nterm_nterm_to_term)
  obtain u' where "subst rhs env = u'"
    by simp
  have "t' = nterm_to_term' u'"
    unfolding t' = _
    unfolding _ = rhs'[symmetric]
    apply (subst nrelated_subst)
       apply fact+
    using _ = u'
    by simp+

  have "compile n ?tn  u'"
    apply (rule nrewrite.step)
     apply fact
    apply simp
    apply (intro conjI exI)
     apply fact+
    done
  with closed ?tn have "closed u'"
    by (blast intro:rules_as_nrules.nrewrite_closed)
  with t' = nterm_to_term' _ have "u' α ?tn'"
    by (force intro: nterm_to_term_term_to_nterm[where Γ = "[]" and Γ' = "[]",simplified])

  show ?case
    apply (intro conjI exI)
     apply (rule nrewrite.step)
      apply fact
     apply simp
     apply (intro conjI exI)
      apply fact+
    done
next
  case (beta t t')
  let ?name = "next s"
  let ?tn = "fst (run_state (term_to_nterm [?name] t) (?name))"
  let ?tn' = "fst (run_state (term_to_nterm [] t') (snd (run_state (term_to_nterm [?name] t) (?name))))"

  from beta have "closed t" "closed (t [t']β)"  "closed (Λ t $ t')" "closed  t'"
    using replace_bound_frees
    by fastforce+
  moreover from beta have "wellformed' (Suc 0) t" "wellformed t'"
    by simp+
  ultimately have "t = nterm_to_term [?name] ?tn"
             and "t' = nterm_to_term' ?tn'"
             and *:"frees ?tn = {|?name|}  frees ?tn = fempty"
             and "closed ?tn'"
    using term_to_nterm_vars0[where Γ = "[?name]"]
    using term_to_nterm_vars0[where Γ = "[]"]
    by (force simp: term_to_nterm_nterm_to_term0)+

  hence **:"t [t']β = nterm_to_term' (subst_single ?tn ?name ?tn')"
    by (auto simp: nterm_to_term_subst_replace_bound[where n = 0])

  from closed ?tn' have "closed_env (fmap_of_list [(?name, ?tn')])"
    by auto

  show ?case
    apply (rule exI)
    apply (auto simp: split_beta create_alt_def)
     apply (rule nrewrite.beta)
    apply (subst subst_single_eq[symmetric])
    apply (subst **)
    apply (rule nterm_to_term_term_to_nterm[where Γ = "[]" and Γ' = "[]", simplified])
    apply (subst subst_single_eq)
    apply (subst subst_frees[OF closed_env _])
    using * by force
next
  case ("fun" t t' u)
  hence "closed  t" "closed u" "closed (t $ u)"
       and wellform:"wellformed t" "wellformed u"
    by fastforce+
  from "fun" obtain u'
    where "compile n fst (run_state (term_to_nterm [] t) s)  u'"
          "u' α fst (run_state (term_to_nterm [] t') s')"
    by force
  show ?case
    apply (rule exI)
    apply (auto simp: split_beta create_alt_def)
     apply (rule nrewrite.fun)
     apply fact
    apply rule
     apply fact
    apply (subst term_to_nterm_alpha_equiv[of "[]" "[]", simplified])
    using closed u wellformed u by auto
next
  case (arg u u' t)
  hence "closed  t" "closed u" "closed (t $ u)"
       and wellform:"wellformed t" "wellformed u"
    by fastforce+
  from arg obtain t'
    where "compile n fst (run_state (term_to_nterm [] u) (snd (run_state (term_to_nterm [] t) s)))  t'"
          "t' α  fst (run_state (term_to_nterm [] u') (snd (run_state (term_to_nterm [] t) s')))"
    by force
  show ?case
    apply (rule exI)
    apply (auto simp: split_beta create_alt_def)
     apply rule
     apply fact
    apply rule
     apply (subst term_to_nterm_alpha_equiv[of "[]" "[]", simplified])
    using closed t wellformed t apply force+
    by fact
qed

lemma compile_complete:
  assumes "rs  t  t'" "closed t" "wellformed t"
  obtains u' where "compile n term_to_nterm' t  u'" "u' α term_to_nterm' t'"
unfolding term_to_nterm'_def
using assms by (metis compile_complete0)

end

end

subsection ‹Splitting into constants›

type_synonym crules = "(term list × nterm) fset"
type_synonym crule_set = "(name × crules) fset"

abbreviation arity_compatibles :: "(term list × 'a) fset  bool" where
"arity_compatibles  fpairwise (λ(pats1, _) (pats2, _). length pats1 = length pats2)"

lemma arity_compatible_length:
  assumes "arity_compatibles rs" "(pats, rhs) |∈| rs"
  shows "length pats = arity rs"
proof -
  have "fBall rs (λ(pats1, _). fBall rs (λ(pats2, _). length pats1 = length pats2))"
    using assms unfolding fpairwise_alt_def by blast
  hence "fBall rs (λx. fBall rs (λy. (length  fst) x = (length  fst) y))"
    by force
  hence "(length  fst) (pats, rhs) = arity rs"
    using assms(2) unfolding arity_def fthe_elem'_eq by (rule singleton_fset_holds)
  thus ?thesis
    by simp
qed

locale pre_crules = constants C_info "fst |`| rs" for C_info and rs :: "crule_set"

locale crules = pre_crules +
  assumes fmap: "is_fmap rs"
  assumes nonempty: "rs  {||}"
  assumes inner:
    "fBall rs (λ(_, crs).
      arity_compatibles crs 
      is_fmap crs 
      patterns_compatibles crs 
      crs  {||} 
      fBall crs (λ(pats, rhs).
        linears pats 
        pats  [] 
        fdisjnt (freess pats) all_consts 
        ¬ shadows_consts rhs 
        frees rhs |⊆| freess pats 
        welldefined rhs))"

lemma (in pre_crules) crulesI:
  assumes "name crs. (name, crs) |∈| rs  arity_compatibles crs"
  assumes "name crs. (name, crs) |∈| rs  is_fmap crs"
  assumes "name crs. (name, crs) |∈| rs  patterns_compatibles crs"
  assumes "name crs. (name, crs) |∈| rs  crs  {||}"
  assumes "name crs pats rhs. (name, crs) |∈| rs  (pats, rhs) |∈| crs  linears pats"
  assumes "name crs pats rhs. (name, crs) |∈| rs  (pats, rhs) |∈| crs  pats  []"
  assumes "name crs pats rhs. (name, crs) |∈| rs  (pats, rhs) |∈| crs  fdisjnt (freess pats) all_consts"
  assumes "name crs pats rhs. (name, crs) |∈| rs  (pats, rhs) |∈| crs  ¬ shadows_consts rhs"
  assumes "name crs pats rhs. (name, crs) |∈| rs  (pats, rhs) |∈| crs  frees rhs |⊆| freess pats"
  assumes "name crs pats rhs. (name, crs) |∈| rs  (pats, rhs) |∈| crs  welldefined rhs"
  assumes "is_fmap rs" "rs  {||}"
  shows "crules C_info rs"
proof unfold_locales
  show "is_fmap rs"
    using assms(11) .
next
  show "rs  {||}"
    using assms(12) .
next
  show "fBall rs (λ(_, crs). Rewriting_Nterm.arity_compatibles crs  is_fmap crs 
    patterns_compatibles crs  crs  {||} 
    fBall crs (λ(pats, rhs). linears pats  pats  []  fdisjnt (freess pats) all_consts 
      ¬ shadows_consts rhs  frees rhs |⊆| freess pats  consts rhs |⊆| all_consts))"
    using assms(1-10)
    by (intro prod_BallI conjI) metis+
qed

lemmas crulesI[intro!] = pre_crules.crulesI[unfolded pre_crules_def]

definition "consts_of" :: "nrule fset  crule_set" where
"consts_of = fgroup_by split_rule"

lemma consts_of_heads: "fst |`| consts_of rs = heads_of rs"
unfolding consts_of_def
by (simp add: split_rule_fst comp_def)

lemma (in nrules) consts_rules: "crules C_info (consts_of rs)"
proof
  have "is_fmap rs"
    using fmap by simp
  thus "is_fmap (consts_of rs)"
    unfolding consts_of_def by auto

  show "consts_of rs  {||}"
    using nonempty unfolding consts_of_def
    by (meson fgroup_by_nonempty)

  show "constants C_info (fst |`| consts_of rs)"
    proof
      show "fdisjnt (fst |`| consts_of rs) C"
        using disjnt by (auto simp: consts_of_heads)
    next
      show "distinct all_constructors"
        by (fact distinct_ctr)
    qed

  fix name crs
  assume crs: "(name, crs) |∈| consts_of rs"

  thus "crs  {||}"
    unfolding consts_of_def
    by (meson femptyE fgroup_by_nonempty_inner)

  show "arity_compatibles crs" "patterns_compatibles crs"
    proof safe
      fix pats1 rhs1 pats2 rhs2
      assume "(pats1, rhs1) |∈| crs" "(pats2, rhs2) |∈| crs"
      with crs obtain lhs1 lhs2
        where rs: "(lhs1, rhs1) |∈| rs" "(lhs2, rhs2) |∈| rs" and
              split: "split_rule (lhs1, rhs1) = (name, (pats1, rhs1))"
                     "split_rule (lhs2, rhs2) = (name, (pats2, rhs2))"
        unfolding consts_of_def by (force simp: split_beta)

      hence arity: "arity_compatible lhs1 lhs2"
        using arity by (force dest: fpairwiseD)

      from rs have const: "is_const (fst (strip_comb lhs1))" "is_const (fst (strip_comb lhs2))"
        using all_rules by force+

      have "name = const_name (fst (strip_comb lhs1))" "name = const_name (fst (strip_comb lhs2))"
        using split by (auto simp: split_beta)
      with const have "fst (strip_comb lhs1) = Const name" "fst (strip_comb lhs2) = Const name"
         apply (fold const_term_def)
        subgoal by simp
        subgoal by fastforce
        done
      hence fst: "fst (strip_comb lhs1) = fst (strip_comb lhs2)"
        by simp

      with arity have "length (snd (strip_comb lhs1)) = length (snd (strip_comb lhs2))"
        unfolding arity_compatible_def
        by (simp add: split_beta)

      with split show "length pats1 = length pats2"
        by (auto simp: split_beta)

      have "pattern_compatible lhs1 lhs2"
        using rs patterns by (auto dest: fpairwiseD)
      moreover have "lhs1 = name $$ pats1"
        using split(1) const(1) by (auto simp: split_beta)
      moreover have "lhs2 = name $$ pats2"
        using split(2) const(2) by (auto simp: split_beta)
      ultimately have "pattern_compatible (name $$ pats1) (name $$ pats2)"
        by simp
      thus "patterns_compatible pats1 pats2"
        using length pats1 = _ by (auto dest: pattern_compatible_combD)
    qed

  show "is_fmap crs"
    proof
      fix pats rhs1 rhs2
      assume "(pats, rhs1) |∈| crs" "(pats, rhs2) |∈| crs"
      with crs obtain lhs1 lhs2
        where rs: "(lhs1, rhs1) |∈| rs" "(lhs2, rhs2) |∈| rs" and
              split: "split_rule (lhs1, rhs1) = (name, (pats, rhs1))"
                     "split_rule (lhs2, rhs2) = (name, (pats, rhs2))"
        unfolding consts_of_def by (force simp: split_beta)

      have "lhs1 = lhs2"
        proof (rule ccontr)
          assume "lhs1  lhs2"
          then consider (fst) "fst (strip_comb lhs1)  fst (strip_comb lhs2)"
                      | (snd) "snd (strip_comb lhs1)  snd (strip_comb lhs2)"
            by (metis list_strip_comb)
          thus False
            proof cases
              case fst
              moreover have "is_const (fst (strip_comb lhs1))" "is_const (fst (strip_comb lhs2))"
                using rs all_rules by force+
              ultimately show ?thesis
                using split const_name_simps by (fastforce simp: split_beta)
            next
              case snd
              with split show ?thesis
                by (auto simp: split_beta)
            qed
        qed

      with rs show "rhs1 = rhs2"
        using is_fmap rs by (auto dest: is_fmapD)
    qed

  fix pats rhs
  assume "(pats, rhs) |∈| crs"
  then obtain lhs where "(lhs, rhs) |∈| rs" "pats = snd (strip_comb lhs)"
    using crs unfolding consts_of_def by (force simp: split_beta)
  hence "nrule (lhs, rhs)"
    using all_rules by blast
  hence "linear lhs" "frees rhs |⊆| frees lhs"
    by auto
  thus "linears pats"
    unfolding pats = _ by (intro linears_strip_comb)

  have "¬ is_const lhs" "is_const (fst (strip_comb lhs))"
    using nrule _ by auto
  thus "pats  []"
    unfolding pats = _ using linear lhs
    apply (cases lhs)
        apply (fold app_term_def)
    by (auto split: prod.splits)

  from nrule (lhs, rhs) have "frees (fst (strip_comb lhs)) = {||}"
    by (cases "fst (strip_comb lhs)") (auto simp: is_const_def)
  hence "frees lhs = freess (snd (strip_comb lhs))"
    by (subst frees_strip_comb) auto
  thus "frees rhs |⊆| freess pats"
    unfolding pats = _ using frees rhs |⊆| frees lhs by simp

  have "¬ shadows_consts rhs"
    using (lhs, rhs) |∈| rs not_shadows
    by force
  thus "¬ pre_constants.shadows_consts C_info (fst |`| consts_of rs) rhs"
    by (simp add: consts_of_heads)

  have "fdisjnt all_consts (frees lhs)"
    using (lhs, rhs) |∈| rs not_shadows
    by (force simp: shadows_consts_def)
  moreover have "freess pats |⊆| frees lhs"
    unfolding pats = _ frees lhs = _
    by simp
  ultimately have "fdisjnt (freess pats) all_consts"
    by (metis fdisjnt_subset_right fdisjnt_swap)

  thus "fdisjnt (freess pats) (pre_constants.all_consts C_info (fst |`| consts_of rs))"
    by (simp add: consts_of_heads)

  show "pre_constants.welldefined C_info (fst |`| consts_of rs) rhs"
    using welldefined_rs (lhs, rhs) |∈| rs
    by (force simp: consts_of_heads)
qed

sublocale nrules  nrules_as_crules?: crules C_info "consts_of rs"
by (fact consts_rules)

subsection ‹Computability›

export_code
  translate_rule consts_of arity nterm_to_term
  checking Scala

end