import LeanFoundations._MyTactics

Propositional Logic #

Now, let's see how to study logic within the logic of Lean.

namespace PropLogic
inductive Proposition : Type where
  | var (name: String): Proposition
  | top : Proposition
  | and (p q : Proposition) : Proposition
  | imp (p q : Proposition) : Proposition
  | bot : Proposition
  | or (p q : Proposition) : Proposition

abbrev Proposition.not (p : Proposition) : Proposition := p.imp .bot

abbrev Context := List Proposition

abbrev Context.add (p : Proposition) (Γ : Context) : Context := p :: Γ

Classical Logic #

Semantics #

def Eval := String -> Bool

def Eval.eval (e: Eval): Proposition -> Bool
  | .var x => e x
  | .top => true
  | .and p q => (e.eval p).and (e.eval q)
  | .imp p q => (e.eval p).not.or (e.eval q)
  | .bot => false
  | .or p q => (e.eval p).or (e.eval q)

def Eval.satisfies (e: Eval) (Γ: Context) := forall p: Proposition, p ∈ Γ -> e.eval p = true

@[simp]
theorem Eval.satisfies.empty {e: Eval}:
  e.satisfies []
:= by
  intro p I
  simp at I

theorem Eval.satisfies.add {e: Eval} {Γ: Context} {p: Proposition}:
  e.satisfies Γ ->  e.eval p = true -> e.satisfies (Γ.add p)
:= by
  intro S H
  intro c I
  simp [Context.add] at I
  cases I with
  | inl =>
    simp_all
  | _ =>
    apply S
    assumption

theorem Eval.satisfies.split_add {e: Eval} {Γ: Context} {p: Proposition}:
  e.satisfies (Γ.add p) -> e.satisfies Γ ∧ e.eval p = true
:= by
  intro S
  and_intros
  . intro c I
    apply S
    simp_all
  . apply S
    simp

def Context.entailsC (Γ: Context) (p: Proposition): Prop := forall e: Eval, e.satisfies Γ -> e.eval p = true

theorem entailsC_imp_iff {Γ: Context} {p q: Proposition}:
  (Γ.add p).entailsC q <-> Γ.entailsC (p.imp q)
:= by
  apply Iff.intro
  . intro H
    intro e S
    simp [Eval.eval]
    cases E: e.eval p with
    | false =>
      left
      eq_refl
    | true =>
      right
      apply H
      apply Eval.satisfies.add
      . exact S
      . exact E
  . intro H
    intro e S
    rcases S.split_add with ⟨S, K⟩
    specialize H e S
    simp [Eval.eval] at H
    simp_all

Syntax #

inductive Context.provesC: Context -> Proposition -> Prop where
  | ax {Γ: Context} {p} : p ∈ Γ -> Γ.provesC p
  | topI {Γ: Context}: Γ.provesC .top
  | andI {Γ: Context} {p q} : Γ.provesC p -> Γ.provesC q -> Γ.provesC (Proposition.and p q)
  | andE1 {Γ: Context} {p q} : Γ.provesC (Proposition.and p q) -> Γ.provesC p
  | andE2 {Γ: Context} {p q} : Γ.provesC (Proposition.and p q) -> Γ.provesC q
  | impI {Γ: Context} {p q} : (Γ.add p).provesC q -> Γ.provesC (Proposition.imp p q)
  | impE {Γ: Context} {p q: Proposition}: Γ.provesC (p.imp q) -> Γ.provesC p -> Γ.provesC q
  | botE {Γ: Context} {p} : Γ.provesC Proposition.bot -> Γ.provesC p
  | orI1 {Γ: Context} {p q} : Γ.provesC p -> Γ.provesC (Proposition.or p q)
  | orI2 {Γ: Context} {p q} : Γ.provesC q -> Γ.provesC (Proposition.or p q)
  | orE {Γ: Context} {p q r} : Γ.provesC (Proposition.or p q) -> (Γ.add p).provesC r -> (Γ.add q).provesC r -> Γ.provesC r
  | em {Γ: Context} {p: Proposition} : Γ.provesC (p.or p.not)

@[simp]
theorem Context.provesC.add {Γ: Context} {c}:
  (Γ.add c).provesC c
:= by
  apply Context.provesC.ax
  simp [Context.add]

theorem Context.provesC.permute {Γ Γ': Context} {p}:
  Context.provesC Γ p ->
  List.Perm Γ Γ' ->
  Context.provesC Γ' p
:= by
  intro H P
  induction H generalizing Γ' <;> try grind [Context.provesC]
  case impI Γ p q H IH =>
    apply Context.provesC.impI
    simp [Context.add] at *
    apply IH
    simp
    exact P
  case orE Γ p q r H H1 H2 IH IH1 IH2 =>
    apply Context.provesC.orE
    . apply IH P
    . apply IH1
      simp
      exact P
    . apply IH2
      simp
      exact P

theorem Context.provesC.weaken {Γ Δ: Context} {p}:
  Γ.provesC p -> Context.provesC (Δ ++ Γ) p
:= by
  intro H
  induction H <;> try grind [Context.provesC]
  case impI Γ p q H IH =>
    apply Context.provesC.impI
    simp [Context.add] at *
    apply IH.permute
    simp
  case orE Γ p q r H H1 H2 IH IH1 IH2 =>
    apply IH.orE
    . apply IH1.permute
      simp
    . apply IH2.permute
      simp

theorem Context.provesC.weaken_add {Γ: Context} c {p}:
  Γ.provesC p -> (Γ.add c).provesC p
:= by
  intro H
  replace H := H.weaken (Δ := [c])
  exact H

theorem Context.provesC.weaken' {Γ Δ: Context} {p}:
  Γ.provesC p -> Context.provesC (Γ ++ Δ) p
:= by
  intro H
  replace H := H.weaken (Δ := Δ)
  apply H.permute
  apply List.perm_append_comm

theorem Context.provesC.weaken_add' {Γ: Context} c {p}:
  Context.provesC [c] p -> (Γ.add c).provesC p
:= by
  intro H
  replace H := H.weaken' (Δ := Γ)
  exact H

def Proposition.leC (p q: Proposition): Prop := Context.provesC [p] q

@[simp]
theorem Proposition.leC.refl {p: Proposition}:
  Proposition.leC p p
:= by
  apply Context.provesC.ax
  simp

theorem Proposition.leC.trans {p q r: Proposition}:
  Proposition.leC p q -> Proposition.leC q r -> Proposition.leC p r
:= by
  intro H1 H2
  replace H2 := H2.impI
  replace H2 := H2.weaken_add p
  simp [Context.add] at H2
  apply Context.provesC.impE
  . exact H2
  . exact H1

@[simp]
theorem Proposition.leC.top {p: Proposition}:
  p.leC .top
:= by
  apply Context.provesC.topI

@[simp]
theorem Proposition.leC.bot {p: Proposition}:
  Proposition.bot.leC p
:= by
  apply Context.provesC.botE
  apply Context.provesC.ax
  simp

def Proposition.eqC (p q: Proposition): Prop := Proposition.leC p q ∧ Proposition.leC q p

@[simp]
theorem Proposition.eqC.refl {p: Proposition}:
  p.eqC p
:= by
  simp [eqC]

theorem Proposition.eqC.symm {p q: Proposition}:
  p.eqC q -> q.eqC p
:= by
  intros
  simp_all [eqC]

theorem Proposition.eqC.trans {p q r: Proposition}:
  p.eqC q -> q.eqC r -> p.eqC r
:= by
  intros
  grind [leC.trans, eqC]

theorem Proposition.eqC.imp_not_or {p q: Proposition}:
  (p.imp q).eqC (p.not.or q)
:= by
  simp [eqC, leC]
  and_intros
  . apply Context.provesC.orE
    . apply Context.provesC.em (p := p)
    . apply Context.provesC.orI2
      apply Context.provesC.impE (p := p)
      . apply Context.provesC.ax
        simp
      . apply Context.provesC.ax
        simp
    . apply Context.provesC.orI1
      apply Context.provesC.ax
      simp
  . apply Context.provesC.orE (p := p.not) (q := q)
    . apply Context.provesC.ax
      simp
    . apply Context.provesC.impI
      simp [Context.add]
      apply Context.provesC.botE
      apply Context.provesC.impE (p := p)
      . apply Context.provesC.ax
        simp
      . apply Context.provesC.ax
        simp
    . apply Context.provesC.impI
      apply Context.provesC.ax
      simp

theorem Proposition.eqC.dne {p: Proposition}:
  p.eqC p.not.not
:= by
  simp [eqC, leC]
  and_intros
  . apply Context.provesC.impI
    simp [Context.add]
    apply Context.provesC.impE (p := p)
    . apply Context.provesC.ax
      simp
    . apply Context.provesC.ax
      simp
  . apply Context.provesC.orE
    . apply Context.provesC.em (p := p)
    . apply Context.provesC.ax
      simp
    . apply Context.provesC.botE
      apply Context.provesC.impE (p := p.not)
      . apply Context.provesC.ax
        simp
      . apply Context.provesC.ax
        simp

theorem Proposition.eqC.provesC {Γ: Context} {p p': Proposition}:
  p.eqC p' -> Context.provesC Γ p -> Context.provesC Γ p'
:= by
  intro E H
  apply Context.provesC.impE (p := p)
  . have K: Context.provesC [] (p.imp p') := by
      apply Context.provesC.impI
      apply E.left
    replace K := K.weaken (Δ := Γ)
    simp at K
    exact K
  . exact H

theorem Proposition.eqC.or {p p' q q': Proposition}:
  p.eqC p' -> q.eqC q' -> (p.or q).eqC (p'.or q')
:= by
  intros Hp Hq
  simp [eqC, leC]
  and_intros
  . apply Context.provesC.orE
    . apply Context.provesC.ax (p := p.or q)
      simp
    . apply Context.provesC.orI1
      apply Context.provesC.weaken_add'
      exact Hp.left
    . apply Context.provesC.orI2
      apply Context.provesC.weaken_add'
      exact Hq.left
  . apply Context.provesC.orE
    . apply Context.provesC.ax (p := p'.or q')
      simp
    . apply Context.provesC.orI1
      apply Context.provesC.weaken_add'
      exact Hp.right
    . apply Context.provesC.orI2
      apply Context.provesC.weaken_add'
      exact Hq.right

theorem Proposition.eqC.or1 {p p' q: Proposition}:
  p.eqC p' -> (p.or q).eqC (p'.or q)
:= by
  intro H
  apply Proposition.eqC.or
  . exact H
  . apply Proposition.eqC.refl

theorem Proposition.eqC.or2 {p q q': Proposition}:
  q.eqC q' -> (p.or q).eqC (p.or q')
:= by
  intro H
  apply Proposition.eqC.or
  . apply Proposition.eqC.refl
  . exact H

theorem Proposition.eqC.and {p p' q q': Proposition}:
  p.eqC p' -> q.eqC q' -> (p.and q).eqC (p'.and q')
:= by
  intros Hp Hq
  simp [eqC, leC]
  and_intros
  . apply Context.provesC.andI
    . apply Hp.provesC
      apply Context.provesC.andE1 (q := q)
      simp
    . apply Hq.provesC
      apply Context.provesC.andE2 (p := p)
      simp
  . apply Context.provesC.andI
    . apply Hp.symm.provesC
      apply Context.provesC.andE1 (q := q')
      simp
    . apply Hq.symm.provesC
      apply Context.provesC.andE2 (p := p')
      simp

theorem Proposition.eqC.imp {p p' q q': Proposition}:
  p.eqC p' -> q.eqC q' -> (p.imp q).eqC (p'.imp q')
:= by
  intros Hp Hq
  simp [eqC, leC]
  and_intros
  . apply Context.provesC.impI
    apply Hq.provesC
    apply Context.provesC.impE (p := p) (q := q)
    . apply Context.provesC.ax
      simp [Context.add]
    . apply Hp.symm.provesC
      apply Context.provesC.ax
      simp [Context.add]
  . apply Context.provesC.impI
    apply Hq.symm.provesC
    apply Context.provesC.impE (p := p') (q := q')
    . apply Context.provesC.ax
      simp [Context.add]
    . apply Hp.provesC
      apply Context.provesC.ax
      simp [Context.add]

theorem Proposition.eqC.not {p p': Proposition}:
  p.eqC p' -> p.not.eqC p'.not
:= by
  intro Hp
  apply Proposition.eqC.imp
  . exact Hp
  . simp

inductive Context.eqC: Context -> Context -> Prop where
  | nil: Context.eqC [] []
  | cons {p p': Proposition} {Γ Γ': Context}:
    p.eqC p' -> Context.eqC Γ Γ' -> Context.eqC (Γ.add p) (Γ'.add p')

theorem Context.provesC.eqC {Γ Γ': Context} {p: Proposition}:
  Context.provesC Γ p -> Context.eqC Γ Γ' -> Γ'.provesC p
:= by
  intro H E
  induction Γ generalizing Γ' p
  case nil =>
    cases E
    exact H
  case cons x xs IH =>
    cases E; case cons y ys E1 E2 =>
    replace H := H.impI
    specialize IH H E2
    sorry

theorem Proposition.eqC.and_idempotent {p: Proposition}:
  (p.and p).eqC p
:= by
  simp [eqC, leC]
  and_intros
  . apply Context.provesC.andE1 (q := p)
    apply Context.provesC.ax
    simp
  . apply Context.provesC.andI
    . apply Context.provesC.ax
      simp
    . apply Context.provesC.ax
      simp

theorem Proposition.eqC.or_comm {p q: Proposition}:
  (p.or q).eqC (q.or p)
:= by
  and_intros
  . apply Context.provesC.orE (p := p) (q := q)
    . apply Context.provesC.ax
      simp
    . apply Context.provesC.orI2
      apply Context.provesC.ax
      simp
    . apply Context.provesC.orI1
      apply Context.provesC.ax
      simp
  . apply Context.provesC.orE (p := q) (q := p)
    . apply Context.provesC.ax
      simp
    . apply Context.provesC.orI2
      apply Context.provesC.ax
      simp
    . apply Context.provesC.orI1
      apply Context.provesC.ax
      simp

theorem Proposition.eqC.or_bot {p: Proposition}:
  (p.or .bot).eqC p
:= by
  and_intros
  . apply Context.provesC.orE (p := p) (q := .bot)
    . apply Context.provesC.ax
      simp
    . apply Context.provesC.ax
      simp
    . apply Context.provesC.botE
      apply Context.provesC.ax
      simp
  . apply Context.provesC.orI1
    simp

theorem Proposition.eqC.bot_or {p: Proposition}:
  (Proposition.bot.or p).eqC p
:= by
  apply Proposition.eqC.trans
  . apply Proposition.eqC.or_comm
  apply Proposition.eqC.or_bot

theorem Proposition.eqC.or_top {p: Proposition}:
  (p.or .top).eqC .top
:= by
  and_intros
  . apply Context.provesC.topI
  . apply Context.provesC.orI2
    apply Context.provesC.topI

theorem Proposition.eqC.top_or {p: Proposition}:
  (Proposition.top.or p).eqC .top
:= by
  apply Proposition.eqC.trans
  . apply Proposition.eqC.or_comm
  apply Proposition.eqC.or_top

theorem Proposition.eqC.or_assoc {p q r: Proposition}:
  (p.or (q.or r)).eqC ((p.or q).or r)
:= by
  and_intros
  . apply Context.provesC.orE (p := p) (q := q.or r)
    . simp
    . apply Context.provesC.orI1
      apply Context.provesC.orI1
      apply Context.provesC.ax
      simp
    . apply Context.provesC.orE (p := q) (q := r)
      . simp
      . apply Context.provesC.orI1
        apply Context.provesC.orI2
        apply Context.provesC.ax
        simp
      . apply Context.provesC.orI2
        apply Context.provesC.ax
        simp
  . unfold leC
    apply Context.provesC.orE (p := p.or q) (q := r)
    . simp
    . apply Context.provesC.orE (p := p) (q := q)
      . simp
      . apply Context.provesC.orI1
        apply Context.provesC.ax
        simp
      . apply Context.provesC.orI2
        apply Context.provesC.orI1
        apply Context.provesC.ax
        simp
    . apply Context.provesC.orI2
      apply Context.provesC.orI2
      apply Context.provesC.ax
      simp

theorem Proposition.eqC.or_and_distrib {p q r: Proposition}:
  (p.or (q.and r)).eqC ((p.or q).and (p.or r))
:= by
  simp [eqC, leC]
  and_intros
  . sorry
  . apply Context.provesC.orE (p := p) (q := q)
    . apply Context.provesC.andE1 (q := p.or r)
      simp
    . apply Context.provesC.orI1
      simp
    . apply Context.provesC.orE (p := p) (q := r) <;> simp [Context.add]
      . apply Context.provesC.andE2 (p := p.or q)
        apply Context.provesC.ax
        simp
      . apply Context.provesC.orI1
        simp
      . apply Context.provesC.orI2
        apply Context.provesC.andI
        . apply Context.provesC.ax
          simp
        . apply Context.provesC.ax
          simp

theorem Proposition.eqC.or_and_distrib' {p q r: Proposition}:
  ((p.and q).or r).eqC ((p.or r).and (q.or r))
:= by
  apply Proposition.eqC.trans
  . apply Proposition.eqC.or_comm
  -- Target is changed to `(r.or (p.and q)).eqC ((p.or r).and (q.or r))`.
  -- We can then trigger the previous theorem.
  apply Proposition.eqC.trans
  . apply Proposition.eqC.or_and_distrib
  -- Now, it is `((r.or p).and (r.or q)).eqC ((p.or r).and (q.or r))`
  apply Proposition.eqC.and
  . sorry
  . sorry

theorem Proposition.eqC.not_top:
  Proposition.top.not.eqC Proposition.bot
:= by
  and_intros
  . apply Context.provesC.impE (p := .top)
    . simp
    . apply Context.provesC.topI
  . apply Context.provesC.botE
    simp

theorem Proposition.eqC.not_bot:
  Proposition.bot.not.eqC Proposition.top
:= by
  and_intros
  . apply Context.provesC.topI
  . apply Context.provesC.impI
    apply Context.provesC.ax
    simp

theorem Proposition.eqC.em {p: Proposition}:
  (p.or p.not).eqC .top
:= by
  and_intros
  . apply Context.provesC.topI
  . apply Context.provesC.em

theorem Proposition.eqC.em' {p: Proposition}:
  (p.not.or p).eqC .top
:= by
  apply Proposition.eqC.trans
  . apply Proposition.eqC.or_comm
  . apply Proposition.eqC.em

theorem Proposition.eqC.deMorgan_or {p q: Proposition}:
  (p.or q).not.eqC (p.not.and q.not)
:= by
  and_intros
  . apply Context.provesC.andI
    . apply Context.provesC.impI
      apply Context.provesC.impE (p := p.or q)
      . apply Context.provesC.ax
        simp
      . apply Context.provesC.orI1
        simp
    . sorry
  . apply Context.provesC.impI
    apply Context.provesC.orE (p := p) (q := q)
    . simp
    . apply Context.provesC.impE (p := p)
      . apply Context.provesC.andE1 (q := q.not)
        apply Context.provesC.ax
        simp
      . simp
    . apply Context.provesC.impE (p := q)
      . apply Context.provesC.andE2 (p := p.not)
        apply Context.provesC.ax
        simp
      . simp

theorem Proposition.eqC.deMorgan_and {p q: Proposition}:
  (p.and q).not.eqC (p.not.or q.not)
:= by
  and_intros
  . apply Proposition.eqC.provesC
    . apply Proposition.eqC.imp_not_or
    . apply Context.provesC.impI
      apply Context.provesC.impI
      simp [Context.add]
      sorry
  . apply Context.provesC.impI
    simp [Context.add]
    apply Context.provesC.orE (p := p.not) (q := q.not)
    . apply Context.provesC.ax
      simp
    . apply Context.provesC.impE (p := p)
      . apply Context.provesC.ax
        simp
      . apply Context.provesC.andE1 (q := q)
        apply Context.provesC.ax
        simp
    . apply Context.provesC.impE (p := q)
      . apply Context.provesC.ax
        simp
      . apply Context.provesC.andE2 (p := p)
        apply Context.provesC.ax
        simp

Soundness #

theorem soundnessC {Γ: Context} {p}:
  Γ.provesC p -> Γ.entailsC p
:= by
  intro H
  intro e S
  induction H generalizing e
    <;> try grind [Eval.eval]
  case ax I =>
    apply S
    exact I
  case impI | orE =>
    grind [Eval.eval, Eval.satisfies.add]

theorem weakSoundnessC {p}:
  Context.provesC [] p -> Context.entailsC [] p
:= by
  apply soundnessC

theorem Proposition.leC.eval {p q: Proposition}:
  p.leC q -> forall e: Eval, e.eval (p.imp q) = true
:= by
  unfold Proposition.leC
  intro H e
  replace H := H.impI
  replace H := weakSoundnessC H
  specialize H e
  simp at H
  exact H

theorem Proposition.eqC.eval {p q: Proposition}:
  p.eqC q -> forall e: Eval, e.eval p = e.eval q
:= by
  rintro ⟨H1, H2⟩ e
  replace H1 := H1.eval e
  replace H2 := H2.eval e
  simp [Eval.eval] at H1 H2
  cases H1
  . cases H2
    . simp_all
    . simp_all
  . cases H2
    . simp_all
    . simp_all

Completeness via Conjunctive Normal Form #

namespace DNF
inductive Literal: Proposition -> Prop where
  | var (x: String): Literal (.var x)
  | not (x: String): Literal (Proposition.var x).not

inductive Disj: Proposition -> Prop where
  | bot: Disj .bot
  | single {p: Proposition}: Literal p -> Disj p
  | or {p q: Proposition}: Disj p -> Disj q -> Disj (p.or q)

inductive Conj: Proposition -> Prop where
  | top: Conj .top
  | single {p: Proposition}: Disj p -> Conj p
  | and {p q: Proposition}: Conj p -> Conj q -> Conj (p.and q)


def disj_or_conj (p q: Proposition): Proposition :=
  match q with
  | .and q1 q2 => (disj_or_conj p q1).and (disj_or_conj p q2)
  | .top => .top
  | _ => p.or q

theorem disj_or_conj_eqC {p q: Proposition}:
  (disj_or_conj p q).eqC (p.or q)
:= by
  induction q <;> try simp [disj_or_conj]
  case and q1 q2 IH1 IH2 =>
    apply Proposition.eqC.trans
    . apply Proposition.eqC.and
      . exact IH1
      . exact IH2
    . exact Proposition.eqC.or_and_distrib.symm
  case top =>
    simp [Proposition.eqC, Proposition.leC]
    and_intros
    . apply Context.provesC.orI2
      apply Context.provesC.topI
    . apply Context.provesC.topI

theorem disj_or_conj_conj {p q: Proposition}:
  Disj p -> Conj q -> Conj (disj_or_conj p q)
:= by
  intro Hp Hq
  induction Hq
  case top =>
    simp [disj_or_conj]
    apply Conj.top
  case single q Hq =>
    unfold disj_or_conj
    split
    . cases Hq
      contradiction
    . apply Conj.top
    . apply Conj.single
      apply Disj.or
      . exact Hp
      . exact Hq
  case and q1 q2 H1 H2 IH1 IH2 =>
    simp [disj_or_conj]
    apply Conj.and
    . exact IH1
    . exact IH2


def conj_or_conj (p q: Proposition): Proposition :=
  match p with
  | .and p1 p2 => (conj_or_conj p1 q).and (conj_or_conj p2 q)
  | .top => .top
  | p => disj_or_conj p q

theorem conj_or_conj_eqC {p q: Proposition}:
  (conj_or_conj p q).eqC (p.or q)
:= by
  induction p <;> try (simp [conj_or_conj]; apply disj_or_conj_eqC)
  case and p1 p2 IH1 IH2=>
    simp [conj_or_conj]
    apply Proposition.eqC.trans
    . apply Proposition.eqC.and
      . exact IH1
      . exact IH2
    . exact Proposition.eqC.or_and_distrib'.symm
  case top =>
    simp [conj_or_conj]
    and_intros
    . apply Context.provesC.orI1
      simp
    . apply Context.provesC.topI

theorem conj_or_conj_conj {p q: Proposition}:
  Conj p -> Conj q -> Conj (conj_or_conj p q)
:= by
  intro Hp Hq
  induction Hp
  case top =>
    simp [conj_or_conj]
    apply Conj.top
  case single p Hp =>
    unfold conj_or_conj
    split
    . cases Hp
      contradiction
    . apply Conj.top
    . apply disj_or_conj_conj
      . exact Hp
      . exact Hq
  case and p1 p2 H1 H2 IH1 IH2 =>
    simp [conj_or_conj]
    apply Conj.and
    . exact IH1
    . exact IH2


def conj_not: Proposition -> Proposition
  | .var x => (Proposition.var x).not
  | .not (.var x) => .var x
  | .and p1 p2 => conj_or_conj (conj_not p1) (conj_not p2)
  | .or p1 p2 => (conj_not p1).and (conj_not p2)
  | .top => .bot
  | .bot => .top
  | p => p.not

theorem conj_not_eqC {p: Proposition}:
  (conj_not p).eqC p.not
:= by
  induction p
  case var x =>
    simp [conj_not]
  case top =>
    simp [conj_not]
    exact Proposition.eqC.not_top.symm
  case bot =>
    simp [conj_not]
    exact Proposition.eqC.not_bot.symm
  case imp p1 p2 IH1 IH2 =>
    unfold conj_not
    split <;> try simp_all  -- contradiction or reflexivity
    -- the only case is some `¬¬x`, which is proved by `Proposition.eqC.dne`
    apply Proposition.eqC.dne
  case and p1 p2 IH1 IH2 =>
    simp [conj_not]
    apply Proposition.eqC.trans
    . apply conj_or_conj_eqC
    apply Proposition.eqC.trans
    . apply Proposition.eqC.or
      . exact IH1
      . exact IH2
    exact Proposition.eqC.deMorgan_and.symm
  case or p1 p2 IH1 IH2 =>
    simp [conj_not]
    apply Proposition.eqC.trans
    . apply Proposition.eqC.and
      . exact IH1
      . exact IH2
    exact Proposition.eqC.deMorgan_or.symm

theorem conj_not_disj_conj {p: Proposition}:
  Disj p -> Conj (conj_not p)
:= by
  intro H
  induction H
  case bot =>
    simp [conj_not]
    apply Conj.top
  case single p H =>
    cases H <;>
    . simp [conj_not]
      apply Conj.single
      apply Disj.single
      constructor
  case or p1 p2 H1 H2 IH1 IH2 =>
    simp [conj_not]
    apply Conj.and
    . exact IH1
    . exact IH2

theorem conj_not_conj_conj {p: Proposition}:
  Conj p -> Conj (conj_not p)
:= by
  intro H
  induction H
  case top =>
    simp [conj_not]
    apply Conj.single
    apply Disj.bot
  case single p H =>
    apply conj_not_disj_conj
    exact H
  case and p1 p2 H1 H2 IH1 IH2 =>
    simp [conj_not]
    apply conj_or_conj_conj
    . exact IH1
    . exact IH2

theorem CNF_exists (p: Proposition):
  exists q: Proposition, Conj q ∧ p.eqC q
:= by
  induction p
  case var x =>
    exists .var x
    and_intros
    . apply Conj.single
      apply Disj.single
      apply Literal.var
    . simp
    . simp
  case bot =>
    exists .bot
    and_intros
    . apply Conj.single
      apply Disj.bot
    . simp
    . simp
  case top =>
    exists .top
    and_intros
    . apply Conj.top
    . simp
    . simp
  case and p1 p2 IH1 IH2 =>
    rcases IH1 with ⟨q1, IH1, E1⟩
    rcases IH2 with ⟨q2, IH2, E2⟩
    exists (q1.and q2)
    apply And.intro
    . apply Conj.and
      . exact IH1
      . exact IH2
    . apply Proposition.eqC.and
      . exact E1
      . exact E2
  case or p1 p2 IH1 IH2 =>
    rcases IH1 with ⟨q1, H1, E1⟩
    rcases IH2 with ⟨q2, H2, E2⟩
    exists (conj_or_conj q1 q2)
    apply And.intro
    . apply conj_or_conj_conj
      . exact H1
      . exact H2
    . apply Proposition.eqC.trans
      . apply Proposition.eqC.or
        . exact E1
        . exact E2
      exact conj_or_conj_eqC.symm
  case imp p1 p2 IH1 IH2 =>
    rcases IH1 with ⟨q1, H1, E1⟩
    rcases IH2 with ⟨q2, H2, E2⟩
    exists conj_or_conj (conj_not q1) q2
    apply And.intro
    . apply conj_or_conj_conj
      . apply conj_not_conj_conj
        exact H1
      . exact H2
    . sorry

def disj_split: Proposition -> List (String × Bool)
  | .var x => [(x, true)]
  | .not (.var x) => [(x, false)]
  | .or p q => disj_split p ++ disj_split q
  | _ => []

def disj_join: List (String × Bool) -> Proposition
  | [] => .bot
  | (x, b)::xs =>
    let p := if b then Proposition.var x else (Proposition.var x).not
    p.or (disj_join xs)

theorem disj_join_append {xs ys: List (String × Bool)}:
  (disj_join (xs ++ ys)).eqC ((disj_join xs).or (disj_join ys))
:= by
  induction xs
  case nil =>
    simp [disj_join]
    apply Proposition.eqC.bot_or.symm
  case cons x xs IH =>
    simp [disj_join]
    split <;>
    . apply Proposition.eqC.trans
      . apply IH.or2
      . apply Proposition.eqC.or_assoc

theorem disj_join_split {p: Proposition}:
  Disj p -> (disj_join (disj_split p)).eqC p
:= by
  intro H
  induction H
  case bot =>
    simp [disj_split, disj_join]
  case single p H =>
    cases H <;>
    . simp [disj_split, disj_join]
      apply Proposition.eqC.or_bot
  case or p1 p2 H1 H2 IH1 IH2 =>
    simp [disj_split]
    apply Proposition.eqC.trans
    . apply disj_join_append
    apply Proposition.eqC.or
    . exact IH1
    . exact IH2

theorem disj_join_perm {l1 l2: List (String × Bool)}:
   l1.Perm l2 -> (disj_join l1).eqC (disj_join l2)
:= by
  intro H
  induction H
  case nil =>
    simp [disj_join]
  case cons x xs ys H IH =>
    simp [disj_join]
    split <;>
    . apply Proposition.eqC.or2
      exact IH
  case swap x y l =>
    simp [disj_join]
    split <;>
    . split <;>
      . apply Proposition.eqC.or_assoc.trans
        apply Proposition.eqC.symm
        apply Proposition.eqC.or_assoc.trans
        apply Proposition.eqC.or1
        apply Proposition.eqC.or_comm
  case trans l1 l2 l3 P12 P23 IH12 IH23 =>
    apply Proposition.eqC.trans
    . exact IH12
    . exact IH23

def disj_eval (l: List (String × Bool)) (e: Eval): Bool :=
  match l with
  | [] => false
  | (x, b) :: xs =>
    let v := e x
    if b then v || disj_eval xs e else (!v) || disj_eval xs e

theorem disj_eval_perm {l1 l2: List (String × Bool)}:
  l1.Perm l2 ->
  forall e: Eval, disj_eval l1 e = disj_eval l2 e
:= by
  intro H e
  induction H
  case nil =>
    simp [disj_eval]
  case cons x xs ys H IH =>
    simp [disj_eval]
    simp [IH]
  case swap x y l =>
    simp [disj_eval]
    grind
  case trans l1 l2 l3 P12 P23 IH12 IH23 =>
    simp_all

theorem disj_eval_join_eq {l: List (String × Bool)}:
  forall e: Eval, disj_eval l e = e.eval (disj_join l)
:= by
  intro e
  induction l
  case nil =>
    simp [disj_join, disj_eval, Eval.eval]
  case cons x xs IH =>
    simp [disj_join, disj_eval, Eval.eval]
    rewrite [IH]
    rcases x with ⟨x, b⟩
    split
    . simp [Eval.eval]
    . simp [Eval.eval]

def split_at (l: List (String × Bool)) (x: String) (b: Bool):
  Option (List (String × Bool) × List (String × Bool))
:= match l with
  | [] => none
  | (y, c) :: ys =>
    if x == y && b == c then some ([], ys)
    else
      match split_at ys x b with
      | none => none
      | some (l1, l2) => some ((y, c) :: l1, l2)

theorem split_at_some {l l1 l2: List (String × Bool)} {x: String} {b: Bool}:
  split_at l x b = some (l1, l2) -> l = l1 ++ (x, b) :: l2
:= by
  intro H
  induction l generalizing l1 l2
  case nil =>
    simp [split_at] at H
  case cons y ys IH =>
    simp [split_at] at H
    split at H
    . cases H
      simp_all
    . split at H
      . -- none
        cases H
      . cases H
        simp
        apply IH
        assumption

theorem split_at_none {l: List (String × Bool)} {x: String} {b: Bool}:
  split_at l x b = none ->
  split_at l x (!b) = none ->
  forall b' e, disj_eval l e = disj_eval l (fun y => if y == x then b' else e y)
:= by
  intro H1 H2 b' e
  induction l
  case nil =>
    simp [disj_eval]
  case cons y ys IH =>
    simp [split_at] at H1 H2
    split at H1 <;> try contradiction
    split at H1 <;> try contradiction
    rename_i E1
    split at H2 <;> try contradiction
    split at H2 <;> try contradiction
    rename_i E2
    specialize IH E1 E2
    simp [disj_eval]
    simp [IH]
    grind

theorem disj_split_tautology {l: List (String × Bool)}:
  (forall e: Eval, disj_eval l e = true) -> (disj_join l).eqC .top
:= by
  intro H
  induction l
  case nil =>
    specialize H (fun _ => false)
    simp only [disj_eval] at H
    cases H
  case cons x xs IH =>
    rcases x with ⟨x, b⟩
    cases E: split_at xs x (!b)
    case some l =>
      rcases l with ⟨l1, l2⟩
      replace E := split_at_some E
      let xs' := (x, !b) :: (l1 ++ l2)
      have P: List.Perm ((x, b)::xs) ((x, b)::xs') := by
        grind
      apply (disj_join_perm P).trans
      simp [disj_join, xs']
      split <;>
      . simp_all
        apply Proposition.eqC.trans
        . apply Proposition.eqC.or_assoc
        apply Proposition.eqC.trans
        . apply Proposition.eqC.or1
          first | apply Proposition.eqC.em | apply Proposition.eqC.em'
        apply Proposition.eqC.top_or
    case none =>
      -- We must have `disj_eval xs e = true` for all `e`.
      have H: forall e: Eval, disj_eval xs e = true := by
        intro e
        cases I: disj_eval xs e
        case true =>
          eq_refl
        case false =>
          let e' := fun y => if y == x then !b else e y
          have I': disj_eval xs e' = false := by
            rewrite [<-I]
            symm
            apply split_at_none _ E
            cases E': split_at xs x b
            case none =>
              eq_refl
            case some l =>
              specialize H e
              simp [disj_eval, I] at H
              rcases l with ⟨l1, l2⟩
              replace E' := split_at_some E'
              let xs' := (x, b) :: (l1 ++ l2)
              have P: List.Perm xs xs' := by grind
              rewrite [disj_eval_perm P] at I
              simp [disj_eval, xs'] at I
              split at H
              . simp_all
              . simp_all
          let H' := H e'
          simp only [disj_eval] at H'
          simp [I'] at H'
          -- note that we choose `e' x = !b`
          simp [e'] at H'
      specialize IH H
      simp [disj_join]
      apply Proposition.eqC.trans
      . apply Proposition.eqC.or2
        exact IH
      . apply Proposition.eqC.or_top

theorem tautology_disj_top {p: Proposition}:
  Context.entailsC [] p -> Disj p -> p.eqC .top
:= by
  intro T D
  let l := disj_split p
  have E: (disj_join l).eqC p := disj_join_split D
  apply E.symm.trans
  apply disj_split_tautology
  intro e
  rewrite [disj_eval_join_eq]
  rewrite [E.eval]
  apply T
  simp

theorem tautology_conj_top {p: Proposition}:
  Context.entailsC [] p -> Conj p -> p.eqC .top
:= by
  intro H C
  induction C
  case top =>
    simp
  case and p1 p2 C1 C2 IH1 IH2 =>
    have H1: Context.entailsC [] p1 := by
        intro e S
        specialize H e S
        simp_all [Eval.eval]
    have H2: Context.entailsC [] p2 := by
      intro e S
      specialize H e S
      simp_all [Eval.eval]
    specialize IH1 H1
    specialize IH2 H2
    have H: (p1.and p2).eqC (Proposition.top.and .top) := by
      apply Proposition.eqC.and
      . exact IH1
      . exact IH2
    apply H.trans
    apply Proposition.eqC.and_idempotent
  case single p D =>
    cases D
    case bot =>
      specialize H (fun _ => false)
      simp [Eval.eval, Eval.satisfies] at H
    case single L =>
      cases L with
      | var x =>
        specialize H (fun _ => false)
        simp [Eval.eval, Eval.satisfies] at H
      | not x =>
        specialize H (fun _ => true)
        simp [Eval.eval, Eval.satisfies] at H
    case or p1 p2 D1 D2 =>
      apply tautology_disj_top
      . exact H
      . apply Disj.or
        . exact D1
        . exact D2

theorem tautology_eqC_top {p: Proposition}:
  Context.entailsC [] p -> p.eqC .top
:= by
  intro H
  rcases CNF_exists p with ⟨q, C, E⟩
  have H: Context.entailsC [] q := by
    intro e S
    rewrite [<-E.eval]
    apply H
    exact S
  replace H := tautology_conj_top H C
  apply E.trans
  exact H

end DNF
theorem weakCompletenessC {p: Proposition}:
  Context.entailsC [] p -> Context.provesC [] p
:= by
  intro H
  replace H := DNF.tautology_eqC_top H
  apply H.symm.provesC
  apply Context.provesC.topI

theorem completenessC {Γ: Context} {p}:
  Γ.entailsC p -> Γ.provesC p
:= by
  induction Γ generalizing p
  case nil =>
    apply weakCompletenessC
  case cons x xs IH =>
    intros H
    replace H := entailsC_imp_iff.mp H
    specialize IH H
    apply Context.provesC.impE
    . apply IH.weaken_add
    . simp

Intuitionistic Logic #

Syntax #

inductive Context.proves: Context -> Proposition -> Prop where
  | ax {Γ: Context} {p} : p ∈ Γ -> Γ.proves p
  | topI {Γ: Context}: Γ.proves .top
  | andI {Γ: Context} {p q} : Γ.proves p -> Γ.proves q -> Γ.proves (Proposition.and p q)
  | andE1 {Γ: Context} {p q} : Γ.proves (Proposition.and p q) -> Γ.proves p
  | andE2 {Γ: Context} {p q} : Γ.proves (Proposition.and p q) -> Γ.proves q
  | impI {Γ: Context} {p q} : (Γ.add p).proves q -> Γ.proves (Proposition.imp p q)
  | impE {Γ: Context} {p q: Proposition}: Γ.proves (p.imp q) -> Γ.proves p -> Γ.proves q
  | botE {Γ: Context} {p} : Γ.proves Proposition.bot -> Γ.proves p
  | orI1 {Γ: Context} {p q} : Γ.proves p -> Γ.proves (Proposition.or p q)
  | orI2 {Γ: Context} {p q} : Γ.proves q -> Γ.proves (Proposition.or p q)
  | orE {Γ: Context} {p q r} : Γ.proves (Proposition.or p q) -> (Γ.add p).proves r -> (Γ.add q).proves r -> Γ.proves r

theorem Context.proves.permute {Γ Γ': Context} {p}:
  Γ.proves p ->
  Γ.Perm Γ' ->
  Context.proves Γ' p
:= by
  intro H P
  induction H generalizing Γ'
    <;> try grind [Context.proves]
  case impI Γ p q H IH =>
    apply Context.proves.impI
    simp [Context.add] at *
    apply IH
    simp
    exact P
  case orE Γ p q r H H1 H2 IH IH1 IH2 =>
    apply Context.proves.orE
    . apply IH P
    . apply IH1
      simp
      exact P
    . apply IH2
      simp
      exact P

theorem Context.proves.weaken {Γ Γ': Context} {p}:
  Γ.proves p ->
  Γ.Sublist Γ' ->
  Context.proves (Γ') p
:= by
  intro H R
  induction H generalizing Γ'
    <;> grind [Context.proves]

Semantics #

We use Kripke models for semantics of intuitionistic logic. In classical logic, we use the type Bool, which is a special case of Boolean algebra. Readers familiar with logic may expect Heyting algebra as the semantic model for intuitionistic logic. It turns out that Kripke models are just special cases of Heyting algebra in some presheaves category.

structure Kripke where
  W: Type
  R: W -> W -> Prop
  R_refl: forall {w: W}, R w w
  R_trans: forall {w1 w2 w3: W}, R w1 w2 -> R w2 w3 -> R w1 w3

  Cover: List W -> W -> Prop
  Cover_nonempty: forall {w}, ¬ Cover [] w
  Cover_R: forall {C: List W} {w: W}, Cover C w -> forall (w': W), w' ∈ C -> R w w'
  Cover_self: forall {w: W}, Cover [w] w
  /-
  Stability as in Mac Lane's _Sheaves in Geometry and Logic_.
  -/
  Cover_pullback: forall {C w u},
    Cover C w ->
    R w u ->
    exists D, Cover D u ∧ (forall u', u' ∈ D -> exists w', w' ∈ C ∧ R w' u')
  Cover_trans: forall {C w} {P: W -> Prop},
    Cover C w ->
    (f: forall w', w' ∈ C -> exists D, Cover D w' ∧ forall w'', w'' ∈ D -> P w'') ->
    exists D, Cover D w ∧ (forall w'', w'' ∈ D -> P w'')

  F: W -> String -> Prop
  F_monotone: forall {w1 w2 x}, R w1 w2 -> F w1 x -> F w2 x
  F_cover: forall {C w x}, Cover C w -> (forall w', w' ∈ C -> F w' x) -> F w x

def Kripke.forces (K: Kripke) (w: K.W) (p: Proposition): Prop :=
  match p with
  | .var x => K.F w x
  | .top => True
  | .bot => False
  | .and p q => K.forces w p ∧ K.forces w q
  | .or p q => exists C: List K.W, K.Cover C w ∧ (forall w', w' ∈ C -> K.forces w' p ∨ K.forces w' q)
  | .imp p q => forall w', K.R w w' -> K.forces w' p -> K.forces w' q

theorem Kripke.forces_weak {K: Kripke} {w1 w2: K.W} {p: Proposition}:
  K.R w1 w2 -> K.forces w1 p -> K.forces w2 p
:= by
  intro R F
  induction p generalizing w1 w2
  case var x =>
    apply K.F_monotone
    . exact R
    . exact F
  case top =>
    simp [Kripke.forces]
  case bot =>
    simp [Kripke.forces] at F
  case and p1 p2 IH1 IH2 =>
    simp [Kripke.forces] at *
    grind
  case imp p1 p2 IH1 IH2 =>
    intro w2' R' F'
    have R'': K.R w1 w2' := K.R_trans R R'
    apply F
    . apply K.R_trans R R'
    . exact F'
  case or p1 p2 IH1 IH2 =>
    simp [Kripke.forces] at F
    rcases F with ⟨C, HC, H⟩
    rcases K.Cover_pullback HC R with ⟨D, HD, S⟩
    exists D
    and_intros
    . exact HD
    . intro w' I
      rcases S w' I with ⟨w'', I'', R'⟩
      specialize H w'' I''
      cases H with
      | inl H =>
        left
        apply IH1 R'
        exact H
      | inr H =>
        right
        apply IH2 R'
        exact H

theorem Kripke.forces_cover {K: Kripke} {C: List K.W} {w: K.W} {p: Proposition}:
  K.Cover C w -> (forall w', w' ∈ C -> K.forces w' p) -> K.forces w p
:= by
  intro HC H
  induction p generalizing C w
  case var x =>
    apply K.F_cover HC
    exact H
  case top =>
    simp [Kripke.forces]
  case bot =>
    cases C
    case nil =>
      exfalso
      apply K.Cover_nonempty HC
    case cons w' C =>
      specialize H w' (by simp)
      simp [Kripke.forces] at H
  case and p1 p2 IH1 IH2 =>
    simp [Kripke.forces] at *
    sorry
  case imp p1 p2 IH1 IH2 =>
    intro w' R F
    rcases K.Cover_pullback HC R with ⟨D, HD, S⟩
    apply IH2 HD
    intro w'' I
    rcases S w'' I with ⟨w''', I', R'⟩
    specialize H w''' I'
    apply H
    . exact R'
    . apply Kripke.forces_weak _ F
      apply K.Cover_R
      . exact HD
      . exact I
  case or p1 p2 IH1 IH2 =>
    let P := fun w: K.W => K.forces w p1 ∨ K.forces w p2
    have f: ∀ (w' : K.W), w' ∈ C → ∃ D, K.Cover D w' ∧ forall w'', w'' ∈ D -> P w'' := by
      intro w' I
      simp [P]
      specialize H w' I
      rcases H with ⟨D, HD, H⟩
      exists D
    rcases K.Cover_trans HC f with ⟨D, HD, T⟩
    exists D

def Context.entails (Γ: Context) (p: Proposition): Prop :=
  forall K: Kripke, forall w: K.W, (forall q, q ∈ Γ -> K.forces w q) -> K.forces w p

Soundness #

theorem soundness {Γ: Context} {p}:
  Γ.proves p -> Γ.entails p
:= by
  intro H K w S
  induction H generalizing K w
  case ax I =>
    apply S
    exact I
  case topI =>
    simp [Kripke.forces]
  case botE Γ p H IH =>
    specialize IH K w S
    simp [Kripke.forces] at IH
  case andI Γ p q H1 H2 IH1 IH2 =>
    simp [Kripke.forces]
    and_intros
    . apply IH1
      exact S
    . apply IH2
      exact S
  case andE1 Γ p q H IH | andE2 Γ p q H IH =>
    specialize IH K w S
    simp [Kripke.forces] at IH
    simp_all
  case impI Γ p q H IH =>
    simp [Kripke.forces]
    intro w' R F
    apply IH
    intro r I
    simp at I
    cases I with
    | inl I =>
      simp_all
    | inr I =>
      apply Kripke.forces_weak R
      apply S
      exact I
  case impE Γ p q H1 H2 IH1 IH2 =>
    specialize IH1 K w S w
    apply IH1
    . apply K.R_refl
    . apply IH2
      exact S
  case orI1 Γ p q H IH | orI2 Γ p q H IH =>
    simp [Kripke.forces]
    specialize IH K w S
    exists [w]
    and_intros
    . apply K.Cover_self
    . intro w' I
      simp_all
  case orE Γ p q r H1 H2 H3 IH1 IH2 IH3 =>
    specialize IH1 K w S
    simp [Kripke.forces] at IH1
    rcases IH1 with ⟨C, HC, H⟩
    apply Kripke.forces_cover HC
    intro w' I
    specialize H w' I
    have R: K.R w w' := K.Cover_R HC _ I
    cases H with
    | inl H =>
      apply IH2
      intro a I'
      simp at I'
      cases I' with
      | inl I' =>
        simp_all
      | inr I' =>
        apply Kripke.forces_weak R
        apply S
        exact I'
    | inr H =>
      apply IH3
      intro a I'
      simp at I'
      cases I' with
      | inl I' =>
        simp_all
      | inr I' =>
        apply Kripke.forces_weak R
        apply S
        exact I'

Completeness #

axiom Context.decide (Γ: Context) p: Decidable (Γ.proves p)

namespace UniversalModel
def World: Type := {Γ: Context // ¬ Γ.proves .bot}

def Cover (C: List World) (w: World): Prop :=
  (¬ C = []) ∧
  (forall w', w' ∈ C -> w.val.Sublist w'.val) ∧
  forall {p}, (forall w, w ∈ C -> w.val.proves p) -> w.val.proves p

theorem Cover.exists {C: List World} {w: World}:
  Cover C w ->
  exists w', w' ∈ C ∧ w.val.Sublist w'.val
:= by
  intro HC
  cases C
  case nil =>
    simp [Cover] at HC
  case cons w' C =>
    exists w'
    simp_all [Cover]

theorem Cover.sub {C: List World} {w: World}:
  Cover C w ->
  forall w', w' ∈ C -> w.val.Sublist w'.val
:= by
  simp_all [Cover]

theorem Cover.proves {C: List World} {w: World}:
  Cover C w ->
  forall {p}, (forall w, w ∈ C -> w.val.proves p) -> w.val.proves p
:= by
  simp_all [Cover]

def mapWithIn {A B} (l: List A) (f: (x: A) -> (x ∈ l) -> B): List B :=
  match l with
  | [] => []
  | x :: xs =>
    let b := f x (by simp)
    let bs := mapWithIn xs (fun y I => f y (by simp [I]))
    b :: bs

theorem mapWithIn_spec {A B} {l: List A} {f: (x: A) -> (x ∈ l) -> B}:
  forall {y}, y ∈ mapWithIn l f <-> exists x, exists I: x ∈ l, y = f x I
:= by
  induction l
  case nil =>
    simp [mapWithIn]
  case cons x xs IH =>
    simp [mapWithIn]
    grind

def multiImp (Γ: Context) (p: Proposition): Proposition :=
  match Γ with
  | [] => p
  | q :: qs => q.imp (multiImp qs p)

theorem multiImp_iff {Δ Γ: Context} {p: Proposition}:
  (Δ ++ Γ).proves p <-> Γ.proves (multiImp Δ p)
:= by
  induction Δ generalizing Γ p
  case nil =>
    simp [multiImp]
  case cons q Δ IH =>
    simp [multiImp]
    apply Iff.intro
    . intro H
      apply Context.proves.impI
      apply IH.mp
      apply H.permute
      simp [Context.add]
      apply List.perm_comm.mp
      simp
    . intro H
      replace H: Context.proves (q :: Γ) (multiImp Δ p) := by
        apply Context.proves.impE
        . apply H.weaken
          simp
        . apply Context.proves.ax
          simp
      replace H := IH.mpr H
      apply H.permute
      simp

theorem weak_append {Γ Γ': Context} {p}:
  Γ.Sublist Γ' ->
  Context.proves (Γ' ++ Γ) p ->
  Γ'.proves p
:= by
  intro R H
  replace H: (Γ ++ Γ').proves p := by
    apply H.permute
    apply List.perm_append_comm
  induction Γ generalizing Γ' p
  case nil =>
    simp_all
  case cons q Γ IH =>
    replace H := H.impI
    specialize IH (by grind) H
    apply IH.impE
    apply Context.proves.ax
    grind

def Model: Kripke where
  W := World
  R := fun w1 w2 => w1.val.Sublist w2.val
  R_refl := by simp
  R_trans := by
    intro w1 w2 w3
    apply List.Sublist.trans

  Cover := Cover
  Cover_nonempty := by
    simp [Cover]
  Cover_R := by
    intro C w HC
    simp [Cover] at HC
    replace HC := HC.right.left
    apply HC
  Cover_self := by
    intro w
    unfold Cover
    grind
  Cover_pullback := by
    intro C w u HC R
    have H: exists L1 L2: List Context,
      (forall w', w' ∈ C <-> w'.val ∈ L1 ∨ w'.val ∈ L2) ∧
      (forall Γ: Context, Γ ∈ L1 -> ¬ (u.val ++ Γ).proves .bot) ∧
      (forall Γ: Context, Γ ∈ L2 -> (u.val ++ Γ).proves .bot)
    := by
      clear HC
      induction C
      case nil =>
        exists [], []
        simp
      case cons w' C IH =>
        rcases IH with ⟨L1, L2, I, H1, H2⟩
        cases (u.val ++ w'.val).decide .bot
        case isTrue =>
          exists L1, (w'.val :: L2)
          grind [Subtype.ext]
        case isFalse =>
          exists (w'.val :: L1), L2
          grind [Subtype.ext]
    rcases H with ⟨L1, L2, I, H1, H2⟩
    rcases HC with ⟨NE, S, HC⟩
    let f: (x: Context) -> (x ∈ L1) -> World := (fun Γ I => ⟨u.val ++ Γ, H1 Γ I⟩)
    let D: List World := mapWithIn L1 f
    exists D
    and_intros
    . -- D is not empty
      intro E
      -- Since `D = []`, we must also have `L1 = []`.
      cases L1
      case cons => -- This case is impossible, since `D` is forced non-empty.
        simp [D, mapWithIn] at E
      -- Now, forall `w'`, `w' ∈ C` iff `w'.val ∈ L2`.
      simp at I  -- We get that simply by `simp` at `I`.
      -- This means `u.val ++ w'.val` is inconsistent, i.e. `w'.val ⊢ ¬ u.val`.
      have H: forall w', w' ∈ C -> w'.val.proves (multiImp u.val .bot) := by
        intro w' I'
        apply multiImp_iff.mp
        apply H2
        apply (I w').mp
        exact I'
      -- Since `C` covers `w`, we can conclude that `w ⊢ ¬ u.val`,
      specialize HC H
      -- which makes `u.val ++ w.val` inconsistent,
      replace HC := multiImp_iff.mpr HC
      -- which further implies `u` is inconsistent -- a contradiction.
      replace HC := weak_append R HC
      apply u.property
      exact HC
    . -- each u is a sublist of all w' in D
      intro u' H
      replace H := mapWithIn_spec.mp H
      grind
    . -- if each w in D proves p then u proves p, i.e., the transitivity of covering
      intro p HD
      apply weak_append R
      apply multiImp_iff.mpr
      apply HC
      intro w'' I'
      replace I' := (I w'').mp I'
      rcases I' with I' | I'
      . let y: World := ⟨u.val ++ w''.val, H1 w''.val I'⟩
        replace I': y ∈ D := by
          apply mapWithIn_spec.mpr
          exists w''.val, I'
        specialize HD y I'
        simp [y] at HD
        apply multiImp_iff.mp
        exact HD
      . apply multiImp_iff.mp
        specialize H2 _ I'
        apply H2.botE
    . -- forall u' in d, there exists w' in C such that w' is a sublist of u'
      intro u' H
      replace H := mapWithIn_spec.mp H
      rcases H with ⟨Γ, I', E⟩
      have _: ¬ Γ.proves .bot := by
        intro contra
        apply H1 _ I'
        apply contra.weaken
        simp
      let w': World := ⟨Γ, by assumption⟩
      exists w'
      simp_all [w', f]
      apply (I _).mpr
      left
      exact I'
  Cover_trans := by
    intro C w P HC f
    have HD: exists D: List World,
      forall x,
        x ∈ D <->
        exists w', exists (I: w' ∈ C),
          x ∈ (f w' I).choose
    := by
      clear HC
      induction C
      case nil =>
        exists []
        -- `x ∈ []` is impossilbe, while `I: w' ∈ []` is also impossible.
        simp
      case cons w' ws' IH =>
        -- We first get the cover `D` of `w`.
        have I': w' ∈ w' :: ws' := by simp
        let T := f w' I'
        let D := T.choose
        -- Then, we get the accumulated result `D'` of `ws'` from `IH`.
        have f': ∀ (w' : World),
            w' ∈ ws' →
            ∃ D, Cover D w' ∧ ∀ (w'' : World), w'' ∈ D → P w''
        := by
          intros w' I'
          apply f
          simp_all
        rcases IH f' with ⟨D', HD'⟩
        exists (D ++ D')
        intro x
        apply Iff.intro
        . grind
        . rintro ⟨w'', I'', H''⟩
          simp at I''
          rcases I'' with E | I''
          . let F := f w'' I''
            simp only [E] at H''
            replace H'': x ∈ D := by
              apply H''
            simp [H'']
          . grind
    rcases HD with ⟨D, HD⟩
    exists D
    and_intros
    . intro E
      rcases HC.exists with ⟨w', I', _⟩
      -- `C` is not `[]`
      let T := f w' I'
      let C' := T.choose
      let HC' := T.choose_spec.left
      rcases HC'.exists with ⟨w'', I'', _⟩
      replace HD := (HD w'').mpr ⟨w', I', I''⟩
      simp [E] at HD
    . intro w' I'
      replace HD := (HD w').mp I'
      rcases HD with ⟨w'', I'', HD⟩
      let H := (f w'' I'').choose_spec.left
      apply List.Sublist.trans
      . apply HC.sub
        exact I''
      . apply H.sub
        exact HD
    . intro p H
      apply HC.proves
      intro w' I'
      let T := f w' I'
      let C' := T.choose
      let HC' := T.choose_spec.left
      apply HC'.proves
      intro w'' I''
      apply H
      apply (HD w'').mpr
      exists w', I'
    . intro w'' I''
      -- can be proved by `grind`
      replace HD := (HD w'').mp I''
      rcases HD with ⟨w', I', HD⟩
      let C' := (f w' I').choose
      let HC' := (f w' I').choose_spec.right
      apply HC'
      exact HD
  F w x := w.val.proves (Proposition.var x)
  F_monotone := by
    intro w1 w2 x
    intro R H
    apply H.weaken R
  F_cover {C w x} := by
    intro HC P
    apply HC.right.right
    exact P

theorem forces_iff_proves {w: World} {p: Proposition}:
  Model.forces w p <-> w.val.proves p
:= by
  induction p generalizing w
  case var x =>
    simp [Kripke.forces, Model]
  case top =>
    simp [Kripke.forces]
    apply Context.proves.topI
  case bot =>
    simp [Kripke.forces]
    exact w.property
  case and p1 p2 IH1 IH2 =>
    simp [Kripke.forces]
    apply Iff.intro
    . intro H
      rcases H with ⟨H1, H2⟩
      replace IH1 := IH1.mp H1
      replace IH2 := IH2.mp H2
      exact Context.proves.andI IH1 IH2
    . intro H
      have H1 := Context.proves.andE1 H
      have H2 := Context.proves.andE2 H
      replace IH1 := IH1.mpr H1
      replace IH2 := IH2.mpr H2
      exact ⟨IH1, IH2⟩
  case or p1 p2 IH1 IH2 =>
    apply Iff.intro
    . intro H
      rcases H with ⟨C, HC, H⟩
      simp [Model] at HC
      rcases HC with ⟨N, S, P⟩
      apply P
      intro w I
      specialize H w I
      rcases H with H | H
      . replace IH1 := IH1.mp H
        exact IH1.orI1
      . replace IH2 := IH2.mp H
        exact IH2.orI2
    . intro H
      rcases w with ⟨Γ, C⟩
      simp at H
      let Γ1 := Γ.add p1
      let Γ2 := Γ.add p2
      rcases Γ1.decide .bot with C1 | I1
      case isTrue => -- If p1 :: Γ is inconsistent, then Γ ⊢ p1.
        replace H: Γ.proves p2 := by
          apply H.orE
          . apply I1.botE
          . apply Context.proves.ax
            simp
        -- Since Γ covers itself, we can conclude that Γ ⊩ p1 ∨ p2.
        exists [⟨Γ, C⟩]
        apply And.intro
        . apply Model.Cover_self
        . intro w' I
          cases I <;> try contradiction
          right
          apply IH2.mpr
          simp
          exact H
      -- Now we have Γ1 is consistent.
      -- similarly, we discuss whether p2 :: Γ is inconsistent.
      rcases Γ2.decide .bot with C2 | I2
      case isTrue => -- We simply prove it by a `grind`.
        exists [⟨Γ, C⟩]
        grind [Model.Cover_self, Context.proves]
      -- Now that both p1 :: Γ and p2 :: Γ are consistent, we can construct a cover.
      let w1: World := ⟨Γ1, C1⟩
      let w2: World := ⟨Γ2, C2⟩
      exists [w1, w2]
      have K: forall {w}, w ∈ [w1, w2] -> w = w1 ∨ w = w2 := by
        grind
      and_intros
      . simp
      . intro w I
        rcases K I with I | I
        . simp_all [w1, Γ1]
        . simp_all [w2, Γ2]
      . intro p HC
        apply H.orE
        . apply HC w1
          apply List.Mem.head
        . apply HC w2
          apply List.Mem.tail
          apply List.Mem.head
      . intro w' I
        rcases K I with I | I
        . simp_all [w1, Γ1]
          left
          apply IH1.mpr
          apply Context.proves.ax
          simp
        . simp_all [w2, Γ2]
          right
          apply IH2.mpr
          apply Context.proves.ax
          simp
  case imp p1 p2 IH1 IH2 =>
    apply Iff.intro
    . intro H
      let Γ := w.val.add p1
      rcases Γ.decide .bot with C | I
      case isTrue =>
        apply Context.proves.impI
        apply I.botE
      case isFalse =>
        let w': World := ⟨Γ, C⟩
        have F: Model.forces w' p1 := by
          apply IH1.mpr
          simp [w', Γ]
          apply Context.proves.ax
          simp
        specialize H w' (by simp [Model, w', Γ]) F
        replace IH2 := IH2.mp H
        apply Context.proves.impI
        exact IH2
    . intro H
      simp [Kripke.forces]
      intro w' R F
      replace H: w'.val.proves (p1.imp p2) := by
        simp [Model] at R
        apply H.weaken
        exact R
      apply IH2.mpr
      apply Context.proves.impE
      . exact H
      . apply IH1.mp
        exact F

end UniversalModel
theorem completeness {Γ: Context} {p}:
  Γ.entails p -> Γ.proves p
:= by
  intro H
  rcases Γ.decide .bot with C | I
  case isTrue => -- If Γ is inconsistent, this is obvious.
    apply I.botE
  -- We then use the universal model for the consistent case.
  have K: forall p, p ∈ Γ -> UniversalModel.Model.forces ⟨Γ, C⟩ p := by
    intro p I
    apply UniversalModel.forces_iff_proves.mpr
    apply Context.proves.ax
    exact I
  specialize H UniversalModel.Model ⟨Γ, C⟩ K
  replace H := UniversalModel.forces_iff_proves.mp H
  exact H

λ-Calculus and Curry-Howard Correspondence #

Glivenko's theorem #

end PropLogic