theory LundfallErfurtEx06
imports QML Main

begin
text \<open>Exercise 1\<close>
consts X :: \<sigma>
consts Y :: \<sigma>
theorem K:
  shows "\<lfloor> \<^bold>\<box>(X \<^bold>\<rightarrow> Y) \<^bold>\<rightarrow> (\<^bold>\<box> X \<^bold>\<rightarrow> \<^bold>\<box> Y)\<rfloor>"
proof simp
qed

consts "mayVote" :: "\<mu>\<Rightarrow>\<sigma>"
consts "bornEqual" :: "\<mu>\<Rightarrow>\<mu>\<Rightarrow>\<sigma>"

theorem "Ex1_a":
  assumes 1: "\<lfloor> \<^bold>\<not> \<^bold>\<diamond> ((\<^bold>\<forall> h1. \<^bold>\<forall> h2. bornEqual h1 h2) \<^bold>\<and> (\<^bold>\<exists> h3. \<^bold>\<not> mayVote(h3)))  \<rfloor>" AND (*Not possible (all humans born equal AND some humans MAY NOT vore)*)
  assumes 2: "\<lfloor> \<^bold>\<box> (\<^bold>\<forall> h1. \<^bold>\<forall> h2. bornEqual h1 h2) \<rfloor>" (*all are born equal*)
  shows "\<lfloor> (\<^bold>\<box> (\<^bold>\<forall> h.(mayVote(h)))) \<rfloor>"
proof -
 from assms have "\<lfloor> \<^bold>\<box> (\<^bold>\<not> ((\<^bold>\<forall> h1. \<^bold>\<forall> h2. bornEqual h1 h2) \<^bold>\<and> (\<^bold>\<exists> h3. \<^bold>\<not> mayVote(h3)))) \<rfloor>" by simp
 then have "\<lfloor> \<^bold>\<box> ((\<^bold>\<forall> h1. \<^bold>\<forall> h2. bornEqual h1 h2) \<^bold>\<rightarrow> (\<^bold>\<not> (\<^bold>\<exists> h3. \<^bold>\<not> mayVote(h3)))) \<rfloor>" by metis
 then have 3: "\<lfloor> (\<^bold>\<box> (\<^bold>\<forall> h1. \<^bold>\<forall> h2. (bornEqual h1 h2))) \<^bold>\<rightarrow> (\<^bold>\<box> \<^bold>\<not> (\<^bold>\<exists> h3. \<^bold>\<not> mayVote(h3))) \<rfloor>" using K by simp
 from 2 3 have "\<lfloor>(\<^bold>\<box> \<^bold>\<not> (\<^bold>\<exists> h3. \<^bold>\<not> mayVote(h3)))\<rfloor>" by simp
 then show ?thesis by simp
qed

consts "isRaining" :: "\<sigma>"
consts "StreetIsWet" :: "\<sigma>"

theorem "Ex1_b":
  assumes 1: "\<lfloor>\<^bold>\<box>(isRaining \<^bold>\<rightarrow> StreetIsWet)\<rfloor>" AND
  assumes 2: "\<lfloor>isRaining\<rfloor>"
  shows "\<lfloor>StreetIsWet\<rfloor>"
nitpick
(*
Nitpick found a counterexample for card i = 2:

  Free variable:
    AND = True
  Skolem constant:
    w = i\<^sub>1
*)
oops

axiomatization where
  T: "\<lfloor>\<^bold>\<box>A \<^bold>\<rightarrow> A \<rfloor>"

theorem "Ex1_c":
  assumes 1: "\<lfloor>\<^bold>\<box>( isRaining \<^bold>\<rightarrow> StreetIsWet)\<rfloor>" AND
  assumes 2: "\<lfloor>isRaining\<rfloor>"
  shows "\<lfloor>StreetIsWet\<rfloor>"
proof -
  from assms have 3: "\<lfloor>(\<^bold>\<box>isRaining) \<^bold>\<rightarrow> (\<^bold>\<box> StreetIsWet)\<rfloor>" by simp
  from 2 have 4: "\<lfloor>\<^bold>\<box>isRaining\<rfloor>" by simp
  from 2 3 have "\<lfloor>\<^bold>\<box> StreetIsWet\<rfloor>" by auto
  then show ?thesis using T by auto
qed

axiomatization where
  5: "\<lfloor>\<^bold>\<diamond>A \<^bold>\<rightarrow> \<^bold>\<box>\<^bold>\<diamond>A \<rfloor>"

text \<open>The intuition behind this axiom is: If something is possible, then it is necessarily possible\<close>

theorem "Ex1_d":
  assumes "\<lfloor>isRaining\<rfloor>"
  shows "\<lfloor>\<^bold>\<box>\<^bold>\<diamond>isRaining\<rfloor>"
proof -
  from assms show ?thesis using 5 by metis
qed

text \<open>Exercise 2\<close>
consts unlimited :: "\<mu>\<Rightarrow>\<sigma>"

definition God where "God G \<equiv> unlimited(G)"

axiomatization where
  B: "\<lfloor>(\<^bold>\<box> (\<^bold>\<exists> x. unlimited(x))) \<^bold>\<or> (\<^bold>\<not>\<^bold>\<diamond>(\<^bold>\<exists> x. unlimited(x)))\<rfloor>"

axiomatization where
  C: "\<lfloor>\<^bold>\<not>\<^bold>\<diamond>(\<^bold>\<exists> x. unlimited(x))\<rfloor>"

theorem d:
  shows "\<lfloor>\<^bold>\<box> (\<^bold>\<exists> x. x \<^bold>= G)\<rfloor>"
proof -
  show ?thesis using B C by auto
qed

text \<open>To prove this ontological argument we do not require axiom K or 5\<close>
end


theory Timed_Game_Automata
  imports TA.Timed_Automata TA_Games_Misc "~/Code/explorer/Guess_Explore"
begin

paragraph \<open>Basic Definitions\<close>

datatype 'a move = Act 'a | Wait

type_synonym ('c, 't, 's) ta_config = "'s \<times> ('c, 't) cval"

\<comment> \<open>We only consider memoryless strategies:\<close>
type_synonym ('a, 'c, 't, 's) strategy =
  "('c, 't, 's) ta_config \<Rightarrow> 'a move set"

locale Timed_Game_Automaton =
  fixes A :: "('a, 'c, 't :: time, 's) ta"
    and controllable :: "'a \<Rightarrow> bool"
    and strategy :: "('a, 'c, 't, 's) strategy"
begin

inductive step where
  action: "step (l, u) (l', u')"
  if "A \<turnstile> \<langle>l, u\<rangle> \<rightarrow>\<^bsub>a\<^esub> \<langle>l', u'\<rangle>" "controllable a" "Act a \<in> strategy (l, u)"
| wait: "step (l, u) (l', u')"
  if "A \<turnstile> \<langle>l, u\<rangle> \<rightarrow>\<^bsup>d\<^esup> \<langle>l',u'\<rangle>" "Wait \<in> strategy (l, u)"
    "\<forall>d' < d. \<exists> l' u'. A \<turnstile> \<langle>l, u\<rangle> \<rightarrow>\<^bsup>d'\<^esup> \<langle>l', u'\<rangle> \<and> Wait \<in> strategy (l', u')"
| uncontrolled: "step (l, u) (l', u')"
  if "A \<turnstile> \<langle>l, u\<rangle> \<rightarrow>\<^bsub>a\<^esub> \<langle>l', u'\<rangle>" "\<not> controllable a"

sublocale Graph_Defs step .

\<comment> \<open>XXX: Q: why are these not automatically activated? Why are there no bundles?\<close>
notation step ("_ \<rightarrow> _" [100, 100] 40)

notation reaches ("_ \<rightarrow>* _" [100, 100] 40)

term "reaches x y"

end

locale Timed_Reachability_Game =
  fixes A :: "('a, 'c, 't :: time, 's) ta"
    and controllable :: "'a \<Rightarrow> bool"
  fixes K :: "('c, 't, 's) ta_config set"
begin

definition
  "lost \<equiv> \<lambda>(l, u). \<forall>a l' u'. A \<turnstile> \<langle>l, u\<rangle> \<rightarrow>\<^bsub>a\<^esub> \<langle>l', u'\<rangle> \<longrightarrow> \<not> controllable a"

definition
  "terminal x \<longleftrightarrow> lost x \<or> x \<in> K"

definition
  "maximal xs \<longleftrightarrow> xs \<noteq> [] \<and> terminal (last xs) \<and> (\<forall>x \<in> set (butlast xs). \<not> terminal x)"

lemma maximal_single_eq_terminal [simp]:
  "maximal [x] \<longleftrightarrow> terminal x"
  unfolding maximal_def by simp

lemma maximal_terminal_startD:
  "xs = []" if "maximal (x # xs)" "terminal x"
  using that unfolding maximal_def by (auto split: if_split_asm)

context
  fixes strategy :: "('a, 'c, 't, 's) strategy"
begin

interpretation Timed_Game_Automaton A controllable strategy .

definition
  "no_runs_from x \<longleftrightarrow> (\<forall>\<omega>. run (x ## \<omega>) \<longrightarrow>
      (\<exists>xs \<omega>'. \<omega> = xs @- \<omega>' \<and> maximal (x # xs)))"

lemma maximal_Cons_iff:
  "maximal (x # xs) \<longleftrightarrow> terminal x \<and> xs = [] \<or> \<not> terminal x \<and> maximal xs"
  unfolding maximal_def by auto

\<comment> \<open>We show: \<open>no_runs_from x \<longleftrightarrow> x \<turnstile> F terminal\<close>.\<close>
lemma no_runs_from_def1:
  "no_runs_from x \<longleftrightarrow> (\<forall>\<omega>. run (x ## \<omega>) \<longrightarrow> ev (holds terminal) (x ## \<omega>))"
proof -
  have "\<exists>xs \<omega>'. \<omega> = xs @- \<omega>' \<and> maximal (x # xs)" if "ev (holds terminal) (x ## \<omega>)" for \<omega>
    using that
  proof (induction "x ## \<omega>" arbitrary: x \<omega> rule: ev_induct_strong)
    case base
    then show ?case
      unfolding holds.simps maximal_def by simp
  next
    case step
    from step(3)[of "shd \<omega>" "stl \<omega>"] obtain xs \<omega>' where
      "maximal (shd \<omega> # xs)" "stl \<omega> = xs @- \<omega>'"
      by auto
    then show ?case
      using stream.collapse[symmetric, of \<omega>] step(2)
      by (inst_existentials "shd \<omega> # xs" \<omega>'; simp add: holds_Stream maximal_Cons_iff)
  qed
  moreover have "ev (holds terminal) (x ## \<omega>)" if "\<omega> = xs @- \<omega>'" "maximal (x # xs)" for xs \<omega> \<omega>'
    using that
    unfolding ev_alt_def maximal_def
    apply (simp split: if_split_asm)
    subgoal
      by (metis holds_Stream shift.simps(1))
    subgoal
      by (metis append_butlast_last_id holds_Stream shift.simps(2) shift_append)
    done
  ultimately show ?thesis
    unfolding no_runs_from_def by auto
qed

lemma no_runs_from_if_start_winning:
  "no_runs_from c\<^sub>0" if "c\<^sub>0 \<in> K"
  using that unfolding no_runs_from_def maximal_def terminal_def by auto

definition
  "wstrat_from c\<^sub>0 \<longleftrightarrow>
    (\<forall>xs. steps (c\<^sub>0 # xs) \<and> maximal (c\<^sub>0 # xs) \<longrightarrow> last (c\<^sub>0 # xs) \<in> K)
  \<and> no_runs_from c\<^sub>0"

lemma wstrat_from_def1:
  "wstrat_from c\<^sub>0 \<longleftrightarrow> c\<^sub>0 \<in> K \<or> \<not> lost c\<^sub>0 \<and>
    (\<forall>xs w. steps (c\<^sub>0 # xs @ [w]) \<and> maximal (c\<^sub>0 # xs @ [w]) \<longrightarrow> w \<in> K)
  \<and> no_runs_from c\<^sub>0"
  unfolding wstrat_from_def
  by (fastforce dest: maximal_terminal_startD elim: snocE no_runs_from_if_start_winning
        simp: terminal_def)

lemma no_runs_from_unfold:
  "no_runs_from c\<^sub>0 \<longleftrightarrow> terminal c\<^sub>0 \<or> (\<forall>x. c\<^sub>0 \<rightarrow> x \<longrightarrow> no_runs_from x)"
  unfolding no_runs_from_def1 by (auto simp: ev_Stream holds.simps elim: run.cases)

lemma wstrat_from_rec:
  "wstrat_from c\<^sub>0 \<longleftrightarrow> c\<^sub>0 \<in> K \<or> \<not> terminal c\<^sub>0 \<and> (\<forall>x. c\<^sub>0 \<rightarrow> x \<longrightarrow> wstrat_from x)"
  unfolding wstrat_from_def
  apply safe
        apply (fastforce simp: terminal_def)
       apply (metis Single stepsI maximal_Cons_iff last.simps list.sel(1))
      apply (subst (asm) no_runs_from_unfold; auto; fail)
     apply (fastforce dest: maximal_terminal_startD simp: terminal_def)
    apply (fastforce elim: no_runs_from_if_start_winning)
   apply (smt (verit, ccfv_threshold) terminal_def last.simps list.sel(1)
            local.steps.simps maximal_Cons_iff maximal_def terminal_def)
  apply (subst no_runs_from_unfold; auto; fail)
  done

end

definition
  "winning c\<^sub>0 \<longleftrightarrow> (\<exists>strategy. wstrat_from strategy c\<^sub>0)"

end

locale Timed_Safety_Game =
  fixes A :: "('a, 'c, 't :: time, 's) ta"
    and controllable :: "'a \<Rightarrow> bool"
  fixes K :: "('c, 't, 's) ta_config set"
begin

\<comment> \<open>We want to show that the enemy cannot win the dual game.\<close>
sublocale dual: Timed_Reachability_Game where
  A = A and
  controllable = "\<lambda>a. \<not> controllable a" and
  K = "- K" .

end

locale Timed_Safety_Game_Strat =
  Timed_Safety_Game A controllable K
  for A :: "('a, 'c, 't :: time, 's) ta" and controllable K +
  fixes strategy :: "('a, 'c, 't, 's) strategy"
begin

sublocale Timed_Game_Automaton A controllable strategy .

definition
  "safe_steps xs \<longleftrightarrow> steps xs \<and> (\<forall>x \<in> set xs. x \<notin> K)"

definition
  "safe_from c\<^sub>0 \<longleftrightarrow> c\<^sub>0 \<notin> K \<and> (\<forall>xs. steps (c\<^sub>0 # xs) \<longrightarrow> (\<forall>x \<in> set xs. x \<notin> K))"

lemma safe_from_alt_def:
  "safe_from c\<^sub>0 \<longleftrightarrow> (\<forall>x. c\<^sub>0 \<rightarrow>* x \<longrightarrow> x \<notin> K)"
  unfolding safe_from_def
  unfolding reaches_steps_iff2
  apply (cases c\<^sub>0)
  apply auto
  subgoal for a\<^sub>0 b\<^sub>0 xs a b
    apply (drule split_list)
    apply clarsimp
    subgoal for ys zs
      \<comment> \<open>Can we automate such goals?\<close>
      \<^cancel>\<open>apply (subgoal_tac "steps ((a\<^sub>0, b\<^sub>0) # ys @ [(a, b)])")
       apply metis\<close>
      by (smt (verit, del_insts) Nil_is_append_conv Single append_self_conv2 hd_append2 list.discI
            list.sel(1) list.sel(3) local.steps.cases stepsI steps_append_single steps_decomp)
    done
  done

lemma safe_from_unfold:
  "safe_from c\<^sub>0 \<longleftrightarrow> c\<^sub>0 \<notin> K \<and> (\<forall>x. c\<^sub>0 \<rightarrow> x \<longrightarrow> safe_from x)"
  unfolding safe_from_alt_def
  including graph_automation_aggressive by (cases c\<^sub>0) (auto elim: converse_rtranclpE)

lemma invariant_safe_fromI:
  assumes "Graph_Invariant_Start step c\<^sub>0 I" "\<forall>s. I s \<longrightarrow> s \<notin> K"
  shows "safe_from c\<^sub>0"
  using assms by (metis Graph_Invariant_Start.invariant_reaches safe_from_alt_def)

end

context Timed_Safety_Game
begin

abbreviation
  "safe_from' \<equiv> Timed_Safety_Game_Strat.safe_from A controllable K"

definition
  "safe c\<^sub>0 \<longleftrightarrow> (\<exists>strategy. safe_from' strategy c\<^sub>0)"

text \<open>Some ways of expressing duality. I am not sure how these are proved.\<close>
lemma
  "safe c\<^sub>0 \<longleftrightarrow> \<not> dual.winning c\<^sub>0"
  oops

lemma
  "safe (l, u) \<longleftrightarrow> (l, u) \<notin> K \<and> (\<forall>l' u'. A \<turnstile> \<langle>l, u\<rangle> \<rightarrow>\<^bsub>a\<^esub> \<langle>l', u'\<rangle> \<and> \<not> controllable a \<longrightarrow> safe (l', u'))"
  apply (subst safe_def)
  apply safe
  subgoal
    unfolding Timed_Safety_Game_Strat.safe_from_def by simp
  subgoal for strategy l' u'
    unfolding safe_def
    apply (rule exI[where x = strategy])
    apply (subst (asm) Timed_Safety_Game_Strat.safe_from_unfold)
    apply (auto intro: Timed_Game_Automaton.step.intros)
    done
  subgoal \<comment> \<open>\<longleftarrow>\<close>
    apply (subst Timed_Safety_Game_Strat.safe_from_unfold)
    \<comment> \<open>Use intersection of strategies for choice in current state\<close>
  oops

end

end


theory LF
imports "../Nominal2"
begin

atom_decl name
atom_decl ident

nominal_datatype lf:
    kind =
      Type
    | KPi "ty" n::"name" k::"kind" binds n in k
and ty =
      TConst "ident"
    | TApp "ty" "trm"
    | TPi "ty" n::"name" ty::"ty"   binds n in ty
and trm =
      Const "ident"
    | Var "name"
    | App "trm" "trm"
    | Lam' "ty" n::"name" t::"trm"  binds n in t

abbreviation
  KPi_syn::"name \<Rightarrow> ty \<Rightarrow> kind \<Rightarrow> kind" ("\<Pi>[_:_]._" [100,100,100] 100)
where
  "\<Pi>[x:A].K \<equiv> KPi A x K"

abbreviation
  TPi_syn::"name \<Rightarrow> ty \<Rightarrow> ty \<Rightarrow> ty" ("\<Pi>[_:_]._" [100,100,100] 100)
where
  "\<Pi>[x:A1].A2 \<equiv> TPi A1 x A2"

abbreviation
  Lam_syn::"name \<Rightarrow> ty \<Rightarrow> trm \<Rightarrow> trm" ("Lam [_:_]._" [100,100,100] 100)
where
  "Lam [x:A].M \<equiv> Lam' A x M"

thm lf.distinct
thm lf.induct
thm lf.inducts
thm lf.exhaust
thm lf.strong_exhaust
thm lf.fv_defs
thm lf.bn_defs
thm lf.perm_simps
thm lf.eq_iff
thm lf.fv_bn_eqvt
thm lf.size_eqvt
thm lf.supports
thm lf.fsupp
thm lf.supp
thm lf.fresh
thm lf.fresh[simplified]

nominal_datatype sig_ass =
    TC_ass "ident" "kind"
  | C_ass "ident" "ty"

type_synonym Sig = "sig_ass list"
type_synonym Ctx = "(name \<times> ty) list"
type_synonym Subst = "(name \<times> trm) list"

inductive
    sig_valid  :: "Sig \<Rightarrow> bool" ("\<turnstile> _ sig" [60] 60)
and ctx_valid  :: "Sig \<Rightarrow> Ctx \<Rightarrow> bool" ("_ \<turnstile> _ ctx" [60,60] 60)
and trm_valid  :: "Sig \<Rightarrow> Ctx \<Rightarrow> trm \<Rightarrow> ty \<Rightarrow> bool" ("_,_ \<turnstile> _ : _" [60,60,60,60] 60)
and ty_valid   :: "Sig \<Rightarrow> Ctx \<Rightarrow> ty \<Rightarrow> kind \<Rightarrow> bool" ("_,_ \<turnstile> _ : _" [60,60,60,60] 60)
and kind_valid :: "Sig \<Rightarrow> Ctx \<Rightarrow> kind \<Rightarrow> bool" ("_,_ \<turnstile> _ : Kind" [60,60,60] 60)
and trm_equiv  :: "Sig \<Rightarrow> Ctx \<Rightarrow> trm \<Rightarrow> trm \<Rightarrow> ty \<Rightarrow> bool" ("_,_ \<turnstile> _ = _ : _" [60,60,60,60,60] 60)
and ty_equiv   :: "Sig \<Rightarrow> Ctx \<Rightarrow> ty \<Rightarrow> ty \<Rightarrow> kind \<Rightarrow> bool" ("_,_ \<turnstile> _ = _ : _" [60,60,60,60,60] 60)
and kind_equiv :: "Sig \<Rightarrow> Ctx \<Rightarrow> kind \<Rightarrow> kind \<Rightarrow> bool" ("_,_ \<turnstile> _ = _ : Kind" [60,60,60,60] 60)
where
(* Signatures *)
  s1: "\<turnstile> [] sig"
| s2: "\<lbrakk>\<turnstile> \<Sigma> sig; \<Sigma>,[] \<turnstile> K : Kind; atom a\<sharp>\<Sigma>\<rbrakk> \<Longrightarrow> \<turnstile> (TC_ass a K)#\<Sigma> sig"
| s3: "\<lbrakk>\<turnstile> \<Sigma> sig; \<Sigma>,[] \<turnstile> A : Type; atom c\<sharp>\<Sigma>\<rbrakk> \<Longrightarrow> \<turnstile> (C_ass c A)#\<Sigma> sig"

(* Contexts *)
| c1: "\<turnstile> \<Sigma> sig \<Longrightarrow> \<Sigma> \<turnstile> [] ctx"
| c2: "\<lbrakk>\<Sigma> \<turnstile> \<Gamma>  ctx; \<Sigma>,\<Gamma> \<turnstile> A : Type; atom x\<sharp>\<Gamma>\<rbrakk> \<Longrightarrow> \<Sigma> \<turnstile> (x,A)#\<Gamma> ctx"

(* Typing Terms *)
| t1: "\<lbrakk>\<Sigma> \<turnstile> \<Gamma>  ctx; (x,A) \<in> set \<Gamma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (Var x) : A"
| t2: "\<lbrakk>\<Sigma> \<turnstile> \<Gamma>  ctx; C_ass c A \<in> set \<Sigma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (Const c) : A"
| t3: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> M1 : \<Pi>[x:A2].A1; \<Sigma>,\<Gamma> \<turnstile> M2 : A2; atom x\<sharp>\<Gamma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (App M1 M2) : A1"
| t4: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A1 : Type; \<Sigma>,(x,A1)#\<Gamma> \<turnstile> M2 : A2; atom x\<sharp>(\<Gamma>,A1)\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (Lam [x:A1].M2) : \<Pi>[x:A1].A2"
| t5: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> M : A; \<Sigma>,\<Gamma> \<turnstile> A = B : Type\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> M : B "

(* Typing Types *)
| f1: "\<lbrakk>\<Sigma> \<turnstile> \<Gamma>  ctx; TC_ass a K \<in> set \<Sigma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (TConst a) : K"
| f2: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A : \<Pi>[x:B].K; \<Sigma>,\<Gamma> \<turnstile> M : B; atom x\<sharp>\<Gamma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (TApp A M) : K"
| f3: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A1 : Type; \<Sigma>,(x,A1)#\<Gamma> \<turnstile> A2 : Type; atom x\<sharp>(\<Gamma>,A1)\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (\<Pi>[x:A1].A2) : Type"
| f4: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A : K; \<Sigma>,\<Gamma> \<turnstile> K = L : Kind\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> A : L"

(* Typing Kinds *)
| k1: "\<Sigma> \<turnstile> \<Gamma> ctx \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> Type : Kind"
| k2: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A : Type; \<Sigma>,(x,A)#\<Gamma> \<turnstile> K : Kind; atom x\<sharp>(\<Gamma>,A)\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (\<Pi>[x:A].K) : Kind"

(* Simultaneous Congruence for Terms *)
| q1: "\<lbrakk>\<Sigma> \<turnstile> \<Gamma>  ctx; (x,A) \<in> set \<Gamma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (Var x) = (Var x) : A"
| q2: "\<lbrakk>\<Sigma> \<turnstile> \<Gamma>  ctx; C_ass c A \<in> set \<Sigma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (Const c) = (Const c): A"
| q3: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> M1 = N1 : \<Pi>[x:A2].A1; \<Sigma>,\<Gamma> \<turnstile> M2 = N2 : A2; atom x\<sharp>\<Gamma>\<rbrakk>
       \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (App M1 M2) = (App N1 N2) : A1"
| q4: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A1' = A1 : Type; \<Sigma>,\<Gamma> \<turnstile> A1'' = A1 : Type; \<Sigma>,\<Gamma> \<turnstile> A1 : Type;
        \<Sigma>,(x,A1)#\<Gamma> \<turnstile> M2 = N2 : A2; atom x\<sharp>\<Gamma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (Lam [x:A1'].M2) = (Lam [x:A1''].N2) : \<Pi>[x:A1].A2"

(* Extensionality *)
| ex: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> M : \<Pi>[x:A1].A2; \<Sigma>,\<Gamma> \<turnstile> N : \<Pi>[x:A1].A2; \<Sigma>,\<Gamma> \<turnstile> A1 : Type;
        \<Sigma>,(x,A1)#\<Gamma> \<turnstile> App M (Var x) = App N (Var x) : A2; atom x\<sharp>\<Gamma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> M = N : \<Pi>[x:A1].A2"

(* Parallel Conversion *)
| pc: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A1 : Type; \<Sigma>,(x,A1)#\<Gamma> \<turnstile> M2 = N2 : A2; \<Sigma>,\<Gamma> \<turnstile> M1 = N1 : A1; atom x\<sharp>\<Gamma>\<rbrakk>
       \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> App (Lam [x:A1].M2) M1 = N2 : A2"

(* Equivalence *)
| e1: "\<Sigma>,\<Gamma> \<turnstile> M = N : A \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> N = M : (A::ty)"
| e2: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> M = N : A; \<Sigma>,\<Gamma> \<turnstile> N = P : A\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> M = P : (A::ty)"
(*| e3: "\<Sigma>,\<Gamma> \<turnstile> M : A \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> M = M : (A::ty)"*)

(* Type conversion *)
| tc: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> M = N : A; \<Sigma>,\<Gamma> \<turnstile> A = B : Type\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> M = N : B"

(* Types Conruence *)
| ft1: "\<lbrakk>\<Sigma> \<turnstile> \<Gamma>  ctx; TC_ass a K \<in> set \<Sigma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (TConst a) = (TConst a) : K"
| ft2: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A = B : \<Pi>[x:C].K; \<Sigma>,\<Gamma> \<turnstile> M = N : C; atom x\<sharp>\<Gamma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> (TApp A M) = (TApp B N) : K"
| ft3: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A1 = B1 : Type; \<Sigma>,\<Gamma> \<turnstile> A1 : Type; \<Sigma>,(x,A1)#\<Gamma> \<turnstile> A2 = B2 : Type; atom x\<sharp>\<Gamma>\<rbrakk>
       \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> \<Pi>[x:A1].A2 = \<Pi>[x:B1].B2 : Type"

(* Types Equivalence *)
| fe1: "\<Sigma>,\<Gamma> \<turnstile> A = (B::ty) : (K::kind) \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> B = A : K"
| fe2: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A = B : K; \<Sigma>,\<Gamma> \<turnstile> B = C : K\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> A = C : (K::kind)"
(*| fe3: "\<Sigma>,\<Gamma> \<turnstile> A : K \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> A = A : (K::kind)"*)

(* Kind Conversion *)
| kc: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A = B : K; \<Sigma>,\<Gamma> \<turnstile> K = L : Kind\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> A = B : (L::kind)"

(* Kind Congruence *)
| kc1: "\<Sigma> \<turnstile> \<Gamma> ctx \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> Type = Type : Kind"
| kc2: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A = B : Type; \<Sigma>,\<Gamma> \<turnstile> A : Type; \<Sigma>,(x,A)#\<Gamma> \<turnstile> K = L : Kind; atom x\<sharp>\<Gamma>\<rbrakk>
        \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> \<Pi>[x:A].K = \<Pi>[x:B].L : Kind"

(* Kind Equivalence *)
| ke1: "\<Sigma>,\<Gamma> \<turnstile> K = L : Kind \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> L = K : Kind"
| ke2: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> K = L : Kind; \<Sigma>,\<Gamma> \<turnstile> L = L' : Kind\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> K = L' : Kind"
(*| ke3: "\<Sigma>,\<Gamma> \<turnstile> K : Kind \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> K = K : Kind"*)

(* type extensionality - needed in order to get the soundness theorem through*)
| tex: "\<lbrakk>\<Sigma>,\<Gamma> \<turnstile> A : \<Pi>[x:C].K; \<Sigma>,\<Gamma> \<turnstile> B : \<Pi>[x:C].K; \<Sigma>,\<Gamma> \<turnstile> C : Type;
         \<Sigma>,(x,C)#\<Gamma> \<turnstile> TApp A (Var x) = TApp B (Var x) : K; atom x\<sharp>\<Gamma>\<rbrakk> \<Longrightarrow> \<Sigma>,\<Gamma> \<turnstile> A = B : \<Pi>[x:C].K"

end