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