Theory HIT_Reduction

theory HIT_Reduction
  imports HIT_Syntax
begin

section ‹Reduction and conversion›

text ‹
  There are exactly two computation rules: the usual ‹β›-rule, and the rule
  that an eliminator applied to a constructor hands the arguments to the
  corresponding method.  The second rule has two instances, one for point
  constructors and one for path constructors.  The instance for path
  constructors is the cubical-style computation rule: the method is a term with a
  free dimension variable, namely the reserved index ‹0›, and the reduct first
  instantiates that variable with the dimension of the path constructor and only
  then applies the constructor and recursive arguments.  This order is
  significant under nested eliminators: the bound dimension of a method must not
  capture a dimension occurring freely in an argument supplied to that method.
  There is no boundary rule: applying a path constructor at a constant dimension
  is, by the definition of const‹mkL›, already the corresponding endpoint.
›

definition dclosed0 :: "sigenv ⇒ tm ⇒ bool" where
  "dclosed0 S t ⟷ (∀j. dsubst S 0 (DVar j) t = t)"

lemma dclosed0_list_iff:
  "(∀j. map (dsubst S 0 (DVar j)) xs = xs) ⟷
    list_all (dclosed0 S) xs"
  by (induct xs) (auto simp: dclosed0_def)

lemma dclosed0_Var [simp]: "dclosed0 S (Var i)"
  unfolding dclosed0_def by simp

lemma dclosed0_Lam [simp]:
  "dclosed0 S (Lam A t) ⟷ dclosed0 S t"
  unfolding dclosed0_def by simp

lemma dclosed0_App [simp]:
  "dclosed0 S (App t u) ⟷
    dclosed0 S t ∧ dclosed0 S u"
  unfolding dclosed0_def by auto

lemma dclosed0_PCon [simp]:
  "dclosed0 S (PCon h k as) ⟷
    list_all (dclosed0 S) as"
  unfolding dclosed0_def
  by (induct as) auto

lemma dclosed0_LCon [simp]:
  "dclosed0 S (LCon h k as (DVar i)) ⟷
    i ≠ 0 ∧ list_all (dclosed0 S) as"
proof (cases i)
  case 0
  then show ?thesis
    unfolding dclosed0_def by auto
next
  case (Suc i)
  then show ?thesis
    unfolding dclosed0_def
    by (induct as) auto
qed

lemma dclosed0_HElim [simp]:
  "dclosed0 S (HElim h C pms lms t) ⟷
    list_all (dclosed0 S) pms ∧ dclosed0 S t"
  unfolding dclosed0_def
  by (induct pms) auto

inductive red :: "sigenv ⇒ tm ⇒ tm ⇒ bool" for S :: sigenv where
  beta:
    "dclosed0 S u ⟹
      red S (App (Lam A t) u) (subst 0 u t)"
| elimPoint:
    "k < length (pcons (S h)) ⟹
      red S (HElim h C pms lms (PCon h k as))
        (apps (pms ! k) (elimArgs h C pms lms (pcons (S h) ! k) as))"
| elimPath:
    "k < length (lcons (S h)) ⟹
      red S (HElim h C pms lms (LCon h k as (DVar j)))
        (apps (dsubst S 0 (DVar j) (lms ! k))
          (elimArgs h C pms lms (lcons (S h) ! k) as))"
| appL: "red S t t' ⟹ red S (App t u) (App t' u)"
| appR: "red S u u' ⟹ red S (App t u) (App t u')"
| lam: "red S t t' ⟹ red S (Lam A t) (Lam A t')"
| pconArg:
    "red S a a' ⟹
      red S (PCon h k (xs @ a # ys)) (PCon h k (xs @ a' # ys))"
| lconArg:
    "red S a a' ⟹
      red S (LCon h k (xs @ a # ys) d) (LCon h k (xs @ a' # ys) d)"
| elimPm:
    "red S m m' ⟹
      red S (HElim h C (xs @ m # ys) lms t)
        (HElim h C (xs @ m' # ys) lms t)"
| elimLm:
    "red S m m' ⟹
      red S (HElim h C pms (xs @ m # ys) t)
        (HElim h C pms (xs @ m' # ys) t)"
| elimScrutinee:
    "red S t t' ⟹
      red S (HElim h C pms lms t) (HElim h C pms lms t')"

abbreviation reds :: "sigenv ⇒ tm ⇒ tm ⇒ bool" where
  "reds S ≡ (red S)**"

definition conv :: "sigenv ⇒ tm ⇒ tm ⇒ bool" where
  "conv S = (λt u. (λa b. red S a b ∨ red S b a)** t u)"

lemma conv_refl: "conv S t t"
  by (simp add: conv_def)

lemma conv_sym:
  assumes "conv S t u"
  shows "conv S u t"
proof -
  have "symp (λa b. red S a b ∨ red S b a)"
    by (simp add: symp_def)
  then have "symp ((λa b. red S a b ∨ red S b a)**)"
    by (rule symp_rtranclp)
  with assms show ?thesis
    unfolding conv_def by (simp add: symp_def)
qed

lemma conv_trans:
  "conv S t u ⟹ conv S u v ⟹ conv S t v"
  unfolding conv_def by (rule rtranclp_trans)

lemma red_conv: "red S t u ⟹ conv S t u"
  unfolding conv_def by auto

lemma reds_conv: "reds S t u ⟹ conv S t u"
proof (induct rule: rtranclp_induct)
  case base
  show ?case by (rule conv_refl)
next
  case (step y z)
  from step(3) red_conv[OF step(2)] show ?case by (rule conv_trans)
qed

definition joinable :: "sigenv ⇒ tm ⇒ tm ⇒ bool" where
  "joinable S t u ⟷ (∃v. reds S t v ∧ reds S u v)"

lemma joinable_refl [simp]: "joinable S t t"
  unfolding joinable_def by auto

lemma joinable_sym:
  "joinable S t u ⟹ joinable S u t"
  unfolding joinable_def by blast

lemma joinableI:
  "reds S t v ⟹ reds S u v ⟹ joinable S t u"
  unfolding joinable_def by blast

lemma joinable_conv:
  assumes "joinable S t u"
  shows "conv S t u"
proof -
  from assms obtain v where tv: "reds S t v" and uv: "reds S u v"
    unfolding joinable_def by blast
  show ?thesis
    by (rule conv_trans[OF reds_conv[OF tv] conv_sym[OF reds_conv[OF uv]]])
qed

subsection ‹Normal forms and the strong-normalization predicate›

definition nform :: "sigenv ⇒ tm ⇒ bool" where
  "nform S t ⟷ (∄u. red S t u)"

inductive SN :: "sigenv ⇒ tm ⇒ bool" for S :: sigenv where
  SNI: "(⋀u. red S t u ⟹ SN S u) ⟹ SN S t"

lemma SN_step: "SN S t ⟹ red S t u ⟹ SN S u"
  by (induct rule: SN.induct) blast

lemma SN_reds: "reds S t u ⟹ SN S t ⟹ SN S u"
  by (induct rule: rtranclp_induct) (auto intro: SN_step)

lemma SN_nform: "nform S t ⟹ SN S t"
  unfolding nform_def by (rule SNI) blast

text ‹
  The strong-normalization predicate passes from a one-hole context to the term filling it,
  provided the context is monotone for reduction.
›

lemma SN_context:
  assumes mono: "⋀a b. red S a b ⟹ red S (f a) (f b)"
    and sn: "SN S s"
    and eq: "s = f t"
  shows "SN S t"
  using sn eq
proof (induct arbitrary: t rule: SN.induct)
  case (SNI s)
  show ?case
  proof (rule SN.SNI)
    fix v
    assume "red S t v"
    then have "red S (f t) (f v)" by (rule mono)
    with SNI(3) have "red S s (f v)" by simp
    from SNI(2)[OF this refl] show "SN S v" .
  qed
qed

lemma SN_App1: "SN S (App t u) ⟹ SN S t"
  by (rule SN_context[where f = "λx. App x u"]) (auto intro: red.appL)

lemma SN_App2: "SN S (App t u) ⟹ SN S u"
  by (rule SN_context[where f = "λx. App t x"]) (auto intro: red.appR)

lemma SN_apps_head: "SN S (apps t xs) ⟹ SN S t"
  by (induct xs arbitrary: t) (auto dest: SN_App1)

subsection ‹Reduction inside constructor arguments›

lemma red_PCon_arg:
  assumes "i < length as" and "red S (as ! i) a'"
  shows "red S (PCon h k as) (PCon h k (as[i := a']))"
proof -
  have "as = take i as @ as ! i # drop (Suc i) as"
    using assms(1) by (simp add: id_take_nth_drop)
  moreover have "as[i := a'] = take i as @ a' # drop (Suc i) as"
    using assms(1) by (simp add: upd_conv_take_nth_drop)
  ultimately show ?thesis
    using red.pconArg[OF assms(2), of h k "take i as" "drop (Suc i) as"]
    by simp
qed

lemma SN_PCon_arg:
  assumes sn: "SN S (PCon h k as)" and i: "i < length as"
  shows "SN S (as ! i)"
proof (rule SN_context[where f = "λx. PCon h k (as[i := x])"])
  fix a b
  assume "red S a b"
  moreover have "as[i := a] ! i = a" using i by simp
  ultimately have "red S (PCon h k (as[i := a]))
      (PCon h k ((as[i := a])[i := b]))"
    using i by (metis length_list_update red_PCon_arg)
  then show "red S (PCon h k (as[i := a])) (PCon h k (as[i := b]))"
    by simp
next
  show "SN S (PCon h k as)" by (rule sn)
next
  show "PCon h k as = PCon h k (as[i := as ! i])" by simp
qed

lemma red_LCon_arg:
  assumes "i < length as" and "red S (as ! i) a'"
  shows "red S (LCon h k as d) (LCon h k (as[i := a']) d)"
proof -
  have "as = take i as @ as ! i # drop (Suc i) as"
    using assms(1) by (simp add: id_take_nth_drop)
  moreover have "as[i := a'] = take i as @ a' # drop (Suc i) as"
    using assms(1) by (simp add: upd_conv_take_nth_drop)
  ultimately show ?thesis
    using red.lconArg[OF assms(2), of h k "take i as" "drop (Suc i) as" d]
    by simp
qed

lemma SN_LCon_arg:
  assumes sn: "SN S (LCon h k as d)" and i: "i < length as"
  shows "SN S (as ! i)"
proof (rule SN_context[where f = "λx. LCon h k (as[i := x]) d"])
  fix a b
  assume "red S a b"
  moreover have "as[i := a] ! i = a" using i by simp
  ultimately have "red S (LCon h k (as[i := a]) d)
      (LCon h k ((as[i := a])[i := b]) d)"
    using i by (metis length_list_update red_LCon_arg)
  then show "red S (LCon h k (as[i := a]) d)
      (LCon h k (as[i := b]) d)"
    by simp
next
  show "SN S (LCon h k as d)" by (rule sn)
next
  show "LCon h k as d = LCon h k (as[i := as ! i]) d" by simp
qed

end