(* --------------------------------------------------------||
||                                                         ||
||                  NATURAL DEDUCTION                      ||
||                                                         ||
||           Logic and Functional Programming              ||
||                 Florent Schaffhauser                    ||
||                                                         ||
||                 Heidelberg University                   ||
||                   (Summer 2026)                         ||
||                                                         ||
||-------------------------------------------------------- *)

From Coq Require Import Utf8.

Definition ℕ := nat.

(* In this file, we formalise some of the results on natural deduction systems that we have proven in the lecture. We also prove a soundness theorem and we prove that the natural deduction system introduced in the lecture yields the same derivable formulas as the Hilbert system introduced earlier. *)

(* ---------------------||
||     PRELIMINARY      ||
||       RESULTS        ||
||--------------------- *)

From Coq Require Import List.
Import ListNotations.

(* LISTS *)

(* Because we choose to formalize contexts as list of well-formed formulas, we will new a few results on list, which we recall below. 

The first notion we need is that of a member of a list. More precisely, we want a predicate on lists, saying that `a` is a member of the list `L`. Formally, this is presented as family of functions `In a : list A → Prop`, indexed by elements `a : A`. *)

Check List.In.
  (* In : ∀ A : Type, A → list A → Prop *)

(* Given `a : A` and `L : list A`, the proposition `In a L` should represent the property that `a` is a member of the list `L`. In Rocq's `Stdlib`, the predicate `In a : List A → Prop` is encoded as a function that patterns match on `l : List A` and sends `[]` to `False` (since the empty list should not contain anything) and `b :: l'` to the proposition `b = a ∨ In a l'` (since the `In a (b :: l')` should have a proof exactly if `a = b` or if `a` is a member of the list `l'`).  Note that this function is defined by primitive recursion on lists, just as the length function, for instance. *)

Print In.

(* One could also write the definition as follows.

```rocq
Fixpoint In {A : Type} (a : A) (l : list A) : Prop :=
  match l with
  | [] => False
  | b :: l' => b = a ∨ In a l'
  end.
```

Other definitions of the predicate `In a : list A → Prop` are possible, for instance as an inductive predicate. For practical purposes, it is useful to introduce the following notation. *)

Notation "a ∈ l" := (List.In a l) (at level 70, no associativity).

(* The useful results we will need later in this file are the following. The first one is an equivalent characterisation of the proposition `a ∈ l`. Note how we look for such a result by using the `Search` command and the pattern we are looking for. *)

Search (_ ∈ _ ↔ _).

(* Depending on what we are looking for, we may then notice `in_app_iff`, which we will be used for instance in the proof the `soundness` theorem. *)

Check in_app_iff. (* in_app_iff : ∀ (A : Type) (l l' : list A) (a : A), a ∈ l ++ l' ↔ a ∈ l ∨ a ∈ l' *)

(* A much more basic result would be a proof of the fact that, for every `l : list A`, we have `l ++ []` =l` *)

Search (_ ++ [] =_).
Check app_nil_r. (* app_nil_r : ∀ (A : Type) (l : list A), l ++ [] = l *)

(* As you may suspect, there is also an `app_nil_l`, where the empty list now appears to the left. However the proofs are quite different, since `app_nil_l l` holds by reflexivity, while `app_nil_r l` is proven by induction on `l` (a good exercise!). *)

Search ([] ++ _ = _).
Check app_nil_l. (* app_nil_l : ∀ (A : Type) (l : list A), [] ++ l = l *)

(* As an exercise, we prove a basic associativity property, which in practice will be used with the `rewrite` tactic (to replace the left-hand-side by the right-hand-side or vice-versa in a given expression).

Note how the `Search` command is used half-way through the proof to look for a result with the correct pattern we need. Note that the way you enter the pattern matters, and that sometimes you can find results that are no longer recommended to use!

After experimenting with the  two `Search` commands proposed below, uncomment the first line, and see how it closes the goal automatically after the `rewrite` (no need to use `reflexivity`). You can only use `now` as a so-called *terminating tactic*. *)

Theorem append_cons {A : Type} (L L' : list A) (a : A) : L ++ a :: L' = (L ++ [a]) ++ L'.
Proof.
  (* now rewrite <- app_assoc. *)
  Search (_ ++ _ ++ _ = (_ ++ _) ++ _).
  rewrite <- app_assoc.
  (* Search ((_ ++ _) ++ _ = _ ++ _ ++ _). *)
  (* rewrite app_assoc_reverse_deprecated. *)
  simpl.
  reflexivity.
Qed.

(* WELL-FORMED FORMULAS, BOOLEAN INTERPRETATION, ENTAILMENT *)

(* We use a minimal definition of well-formed formulas, with only one binary constructor (implication). For the sake of simplicity in the notation, we also use the set of natural numbers to index our atomic formulas. *)

Inductive Wff :=
| P    : nat → Wff
| Impl : Wff → Wff → Wff.

Notation "F ⇒ F'" := 
  (Impl F F') (at level 99, right associativity).

(* Recall the following notation from Boolean semantics. *)

Notation "b ≼ b'" := (implb b b') (at level 99, right associativity).

Fixpoint eval (v : nat → bool) (F : Wff) : bool :=
match F with
| P i    => v i
| F ⇒ F' => (eval v F) ≼ (eval v F')
end.

(* We also recall the concept of entailment. Since contexts are now lists of well-formed formulas, we are forced to introduce the condition `∀ G : Wff, G ∈ Γ → eval v G = true`, where `Γ : list Wff` and `v : nat → bool`, to replace what was previously `∀ j : J, eval v (Γ j) = true`, when `Γ : J → Wff` was an indexed famil of well-formed formulas. *)

Definition Entails (Γ : list Wff) (F : Wff) :=
  ∀ v : nat → bool, (∀ G : Wff, G ∈ Γ → eval v G = true) → eval v F = true.

Infix "⊨" := Entails (at level 110, right associativity).

(* ---------------------||
||   NATURAL DEDUCTION  ||
||--------------------- *)

(* JUDGMENTS AND INFERENCE RULES *)

(* We introduce contexts as lists of well-formed formulas. Note that the two sets `Ctx` and `list Wff` are definitionally equal and we can use either when declaring theorems or writing proofs. *)

Definition Ctx := list Wff.

Goal (Ctx = list Wff).
Proof.
  reflexivity.
Qed.

(* A term `Γ : list Wff` is meant to represent the premises of a deduction in a formal system. More precisely, given a well-formed formula `F` and a list of well-formed formulas `Γ`, we want to define so-called **sequents**, denoted by `Γ ⊢ F`, which represent the fact that `F` is derivable from the premisses contained in `Γ` (i.e. the members of `Γ`). 

As discussed in the lecture, the property of being a sequent is defined inductively as follows. *)

Inductive IsSequent : Ctx → Wff → Prop :=
| focus (Γ : Ctx) (F : Wff)        : IsSequent (Γ ++ [F]) F
| weaken (Γ : Ctx) (F G : Wff)     : IsSequent Γ F → IsSequent (Γ ++ [G]) F
| impl_intro (Γ : Ctx) (F G : Wff) : IsSequent (Γ ++ [F]) G → IsSequent Γ (F ⇒ G)
| impl_elim (Γ : Ctx) (F G : Wff)  : IsSequent Γ F → IsSequent Γ (F ⇒ G) → IsSequent Γ G.

(* We then introduce the infix notation `Γ`. Also, making certain arguments of certain constructors implicit will be convenient to avoid cluttering the proofs later. *)

Infix "⊢" := IsSequent (at level 110, no associativity).

Arguments focus {Γ} {F}.
Arguments weaken {Γ} {F G}.
Arguments impl_intro {Γ}.
Arguments impl_elim {Γ}.

(* FIRST PROOFS *)

(* Let us prove our first theorem using natural deduction.  We are saying that, given a well-formed formula `A` and an arbitrary context `Γ`, the formula `A ⇒ A` can be derived from `Γ` (or more accurately in our terminology, that the sequent `Γ ⊢ A`, which is a proposition, indeed has a proof). 

Note that the result holds in particular for the empty context! This is exactly what we proved in the lecture on Hilbert systems. *)

Theorem I-ND {Γ : Ctx} (A : Wff) : Γ ⊢ A ⇒ A.
Proof.
  apply impl_intro.
  apply focus.
Qed.

(* Now let us prove the `revert` rule, which is a converse to the introduction rule of implication. *)

Theorem revert {Γ : Ctx} (A B : Wff) : (Γ ⊢ A ⇒ B) → Γ ++ [A] ⊢ B.
Proof.
  intro H.
  apply (impl_elim A).
  - apply focus.
  - apply weaken.
    exact H.
Qed. 

(* Next we prove the transitivity of implication, which we also proved using a Hilbert system. Note how, in this example, we use a theorem that is not one the four basic rules, namely `revert`. *)

Theorem impl_trans' {Γ : Ctx} (A B C: Wff) : (Γ ⊢ A ⇒ B) → (Γ ⊢ B ⇒ C) → Γ ⊢ A ⇒ C.
Proof.
  intros H1 H2.
  apply impl_intro.
  apply (impl_elim B).
  - apply revert.
    exact H1.
  - apply weaken.
    exact H2. 
Qed.

(* Finally, we prove the so-called `cut` rule, which is related to a famous theorem of Gentzen's in *sequent calculus*. *)

Theorem cut {Γ : Ctx} (A B : Wff) : (Γ ⊢ A) → (Γ ++ [A] ⊢ B) → Γ ⊢ B.
Proof.
  intros H1 H2.
  apply (impl_elim A).
  - assumption.
  - apply impl_intro.
    assumption.
Qed.

(*  AXIOMS OF THE HILBERT SYSTEM *)

(* In the Hilbert system presented in the lecture, we had two axioms, `K` and `S`. These were essentially well-formed formulas that are derivable from the empty context. So let us prove that these formulas are also provable using natural deduction. *)

Theorem K-ND {Γ : Ctx} (A B : Wff) : Γ ⊢ A ⇒ B ⇒ A.
Proof.
  repeat apply impl_intro.
  apply weaken.
  apply focus.
Qed.

Theorem S-ND {Γ : Ctx} (A B C : Wff) : Γ ⊢ (A ⇒ B ⇒ C) ⇒ (A ⇒ B) ⇒ (A ⇒ C).
Proof.
  repeat apply impl_intro.
  apply (impl_elim B).
  - apply revert, focus.
  - apply revert.
    apply weaken.
    apply focus. 
Qed.

(* As one last exercise, we also prove that `Γ ⊢ A ⇒ B ⇒ B` holds, which we also checked in the lecture on Hilbert systems. *)

Theorem K'-ND {Γ : Ctx} (A B : Wff) : Γ ⊢ A ⇒ B ⇒ B.
Proof.
  apply impl_intro.
  apply I-ND.
Qed.

(* ---------------------||
||    SOUNDNESS OF      ||
||     THE BOOLEAN      ||
||    INTERPRETATION    ||
||--------------------- *)

(* Now we prove that Boolean semantics are sound for natural deduction. This means that if a well-formed formula is derivable from the context `Γ` (i.e. the sequent `Γ ⊢ F` holds), then for every valuation `v : nat → bool` such that all formulas in `Γ` evaluate to `true`, the formula `F` also evaluates to `true` (i.e. `Γ` entails `F`). As a byproduct of this proof, we see that two a priori different formal systems (natural deduction systems and Hilbert systems) can share at least one common set of semantics.

The proof that if `Γ ⊢ F` holds then `Γ ⊨ F` holds will be done by induction on the proof of `Γ ⊢ F`. This is to be expected, since we have defined sequents inductively, so induction is the priviledged available method to prove that something holds *for every sequent*, just like if you want to prove that something holds for all natural numbers, it is likely that you will use induction to prove it. *)

Theorem soundness : ∀ Γ : Ctx, ∀ F : Wff, (Γ ⊢ F) → Γ ⊨ F.
Proof.
  intros Γ F HF.
  unfold Entails.
  intros v Hv. 
  induction HF.
  + apply (Hv F).
    Search (_ ∈ (_ ++ _)).
    apply in_elt.
  + (* Note the induction hypothesis `IHHF` in this case and the next ones. *)
    apply IHHF.
    intros G0 HG0.
    apply Hv.
    Search (_ ∈ (_ ++ _)).
    apply in_app_iff.
    now left.
      (* `now` can be used with other tactics than `rewrite` and it can apply other closing tactics than `reflexivity`, for instance `assumption` *)
  + simpl.
    revert IHHF.
    destruct (eval v F) eqn: hF, (eval v G).
      (* Note how we keep `eval F = true` in the context by combining the destruct tactic with `eqn : hF`. Without it, `eval F` would be replaced by `true` everywhere, but we would not be able to use this fact to continue our reasoning. *)
    all: simpl; repeat reflexivity.
    intro H0.
    apply H0.
    intros G0 HG0.
    apply in_app_iff in HG0.
    destruct HG0.
    - apply Hv.
      exact H.
    - simpl in H.
      destruct H.
      * rewrite H in hF.
        exact hF.
      * Check False_ind.
        apply False_ind.
        (* exfalso. *)
        (* Here, we are using the principle of explosion, we will comment on this further in a forthcoming lecture. *)
        exact H.
  + pose proof IHHF1 Hv as H.
    pose proof IHHF2 Hv as H'.
    revert H H'.
    simpl.
    destruct (eval v F), (eval v G).
    all: trivial.
Qed.

(* ---------------------||
||  COMPARISON WITH A   ||
||    HILBERT SYSTEM    ||
||--------------------- *)

(* REMINDER ON HILBERT SYSTEMS *)

(* Recall the axioms we introduced in our Hilbert system (there were only two of them). *)

Inductive IsAxiom : Wff → Prop :=
| K (A B : Wff)   : IsAxiom (A ⇒ B ⇒ A)
| S (A B C : Wff) : IsAxiom ((A ⇒ B ⇒ C) ⇒ (A ⇒ B) ⇒ (A ⇒ C)).

(* Now let us redeclare the inductive predicate `IsDerivableFrom` from the lecture on Hilbert systems, this time using the set `Ctx` to represent contexts of assumptions (as opposed to indexed families `J → Wff`). Note that this approach forces us to add the side condition `F ∈ Γ` in the arguments of the `premise` constructor. *)

Inductive IsDerivableFrom (Γ : Ctx) : Wff → Prop :=
| axiom (a : Wff) (ha : IsAxiom a) : IsDerivableFrom Γ a
| premise (F : Wff)                : F ∈ Γ → IsDerivableFrom Γ F
| mp (F F' : Wff)                  : IsDerivableFrom Γ F → IsDerivableFrom Γ (F ⇒ F') → IsDerivableFrom Γ F'.

(* Here too we pass some argmuments as implicit and we introduce an infix notation for `IsDerivableFrom Γ F`. Since `⊢` is already taken by the sequents form natural deduction, we use `Γ ⊢ₕ F` instead, where the `h` stands for Hilbert system. *)

Arguments premise {Γ}.
Arguments mp {Γ}.

Infix "⊢ₕ" := IsDerivableFrom (at level 70, no associativity).

(* As an example (which will be used in the proof of the `deduction` theorem), let us prove `I`, meaning that if `F` is a well-formed formula, then for every context `Γ : Ctx`, the well-formed formula `F ⇒ F` is derivable from `Γ`. Note that this includes the case when `Γ = []`, which we used to call `empty_family` in the lecture on Hilbert systems. *)

Theorem I (F : Wff) : ∀ Γ : Ctx, Γ ⊢ₕ (F ⇒ F).
Proof.
  intro.
  apply (mp (F ⇒ F ⇒ F)).
  - apply axiom. apply K.
  - apply (mp (F ⇒ (F ⇒ F) ⇒ F)).
    + apply axiom. apply K.
    + apply axiom. apply S.
Qed.

(* THE DEDUCTION THEOREM *)

(* We will also need to use the following result, which says that if a formula is derivable (in the sense of Hilbert) from a context `Γ`, then it is also derivable from any context of the form `Γ ++ [G]`.

This will be used in the proof of the `deduction` theorem for Hilbert systems, as well as in our forthcoming proof of the equivalence of derivability in the sense of Hilbert systems and in the sense of natural deduction. *)

Theorem derivable_from_extended_ctx (Γ : Ctx) (F : Wff) : Γ ⊢ₕ F → ∀ G : Wff, (Γ ++ [G]) ⊢ₕ F.
Proof.
  intros HF G.
  induction HF.
  + apply axiom.
    assumption.
  + apply premise.
    apply in_app_iff.
    left; assumption.
  + apply (mp F).
    all: assumption.
Qed.

(* Let us give the proof of the deduction theorem for Hilbert systems. Note that contexts are again represented by lists of well-formed formulas here. *)

Theorem deduction (Γ : Ctx) (F G : Wff) : Γ ⊢ₕ (F ⇒ G) ↔ (Γ ++ [F]) ⊢ₕ G.
Proof.
  split.
  + intro H.
    apply (mp F).
    - apply premise.
      apply in_app_iff.
      right.
      simpl.
      left.
      reflexivity.
    - apply derivable_from_extended_ctx.
      assumption.
  + intro H.
    induction H.
    - apply (mp a).
      * apply axiom; assumption.
      * apply axiom. apply K.
    - apply in_app_iff in H.
      destruct H.
      * apply (mp F0).
        ** apply premise; assumption.
        ** apply axiom. apply K.
      * simpl in H.
        destruct H.
        ** rewrite H.
           apply I.
        ** exfalso; assumption.
    - apply (mp (F ⇒ F0)).
      * assumption.
      * apply (mp (F ⇒ F0 ⇒ F')). 
        ** assumption.
        ** apply axiom. apply S.
Qed.

(* FROM NATURAL DEDUCTION TO HILBERT SYSTEMS *)

(* Now that we have recalled all the resuts we shall need about Hilbert systems, let us prove that if a sequent is derivable in the sense of natural deduction, then its conclusion is also derivable in our Hilbert system, from the same context.

This means that we want to prove the (meta-theoretic) implication `(Γ ⊢ F) → Γ ⊢ₕ F`. The proof of this implication is by induction on the proof of the sequent `Γ ⊢ F`. *)

Theorem nd_sequents_are_hs_derivable (Γ : Ctx) (F : Wff) : (Γ ⊢ F) → Γ ⊢ₕ F.
Proof.
  intro HF.
  induction HF.
  + apply premise.
    apply in_app_iff.
    right.
    simpl.
    left.
    reflexivity.
  + apply derivable_from_extended_ctx.
    assumption.
  + apply deduction.
    assumption.
  + apply (mp F).
    all: assumption.
Qed. 

(* FROM HILBERT SYSTEMS TO NATURAL DEDUCTION *)

(* Next we want to prove the converse, i.e. `Γ ⊢ₕ F → (Γ ⊢ F)`, but for that we need to prove an auxiliary result first, namely that, for all contexts `Γ, Γ'`, if `(Γ ⊢ F)` holds then `Γ ++ Γ' ⊢ F` also holds. This generalises the `weaken` rule from `Γ' = [G]` to any `Γ'`.

We could of course decide that the `weaken` rule should be that more general rule, but this would come at a cost when we prove soundness and other results involving an induction  on the proof of a sequent `Γ ⊢ F`, since the corresponding case now has a greater quantity of subcases (indexed by all lists `Γ'` as opposed to just singletons `[G]`).

The subtle part of the proof of `provable_from_sum_list` is the `revert Γ` at the beginning, before we start the induction on `Γ'` (which is a context, hence a list of well-formed formulas). If you comment the first line, you will see exactly where you get stuck (see also the comment in the proof, after `apply IHΓ'`. )

This often happens in proofs by induction, so one should really learn that technique. One might think that a slightly better approach would be to formalise the statement of `provable_from_sum_list` as follows

```rocq
Theorem provable_from_sum_list (Γ : Ctx) (F : Wff) : (Γ ⊢ F) → ∀ Γ' : Ctx, Γ ++ Γ' ⊢ F.
```

meaning that  we would have to prove that, for every context `Γ`, if `(Γ ⊢ F)` holds then, *for every context `Γ'`, the sequent `Γ ++ Γ' ⊢ F` also holds. But still, one would have to revert `Γ` to the context before starting the induction on `Γ'`. 

A natural question is then: why not do induction on `Γ` instead? And the reason is that `Γ` appears on the left-hand-side of the `++` operation in the expression `Γ ++ Γ' ⊢ F` but in the `weaken` rule, which is the rule we are trying to generalize to arbitrary `Γ'`, the singleton list `[G]` appears on the right-hand-side of the `++` operation in the expression `Γ ++ [G] ⊢ F)`. Try it to see why it fails! *)

Theorem provable_from_sum_list (Γ Γ' : Ctx) (F : Wff) : (Γ ⊢ F) → Γ ++ Γ' ⊢ F.
Proof.
  revert Γ.
  unfold Ctx in Γ'.
    (* To convince oneself that `Γ'` is a list, so it makes sense to reason by induction on it. *)
  induction Γ'.
  + intros. 
    Search (_ ++ [] =_).
    rewrite app_nil_r.
    assumption.
  + intros.
    rewrite append_cons.
    apply IHΓ'.
      (* Here we need to apply the induction hypothesis to the context `(Γ ++ [a]) ++ Γ'`, not just `Γ ++ Γ'`, which we cannot do if we do not have a universal quantifier in said induction hypothesis. *)
    apply weaken.
    assumption.
Qed.

(* Now let us prove that if a formula `F` is derivable from a context `Γ` in our Hilbert system, then the sequent `Γ ⊢ F` is also derivable in the sense natural deduction. This is where we use that the axioms of our Hilbert system, namely `K` and `S`, are derivable from any context in natural deduction, a fact that we have proved as exercises in `K-ND` and `S-ND`. *)

Theorem hs_derivable_wff_are-ND_provable (Γ : Ctx) (F : Wff) : (Γ ⊢ₕ F) → Γ ⊢ F.
Proof.
  intro HF.
  induction HF.
  + induction ha.
    - apply K-ND.
    - apply S-ND.
  + Search (_ ∈ _ → ∃ _, _).
    pose proof in_split _ Γ H.
    destruct H0 as [l1 [l2 h]].
    rewrite append_cons in h.
    rewrite h.
    apply provable_from_sum_list.
    apply focus.
  + apply (impl_elim F).
    all: assumption.
Qed.

(* PROOF OF THE EQUIVALENCE *)

(* To conclude, we can state the equivalence, for all `Γ` and `F`, between `(Γ ⊢ₕ F)` and `(Γ ⊢ F)`. This simply collects the proofs of the two implications into the proof of the equivalence. 

As a final remark, note that, as we add more constructors to our system of well-formed formulas, we also add more introduction and elimination rules to our natural deduction system, and more axioms to our Hilbert system. This will make the proofs of soundness of the Boolean interpretation more complicated, and the same would occur for the proof that these two formal systems are equivalent. 

Instead of doing that, we will next think about what constructors we want for well-formed formulas and what logic we want to implement (these are two very related questions). Then we will pick just one formal system to reason in, namely natural deduction. *)

Theorem hs_derivable_wff_iff-ND_provable (Γ : Ctx) (F : Wff) : (Γ ⊢ₕ F) ↔ (Γ ⊢ F).
Proof.
  split.
  + exact (hs_derivable_wff_are-ND_provable Γ F).
    (* apply hs_derivable_wff_are-ND_provable. *)
      (* the `apply` tactic is quite powerful and can sometimes infer the arguments it needs to close the goal (provided they are in the context!) *)
  + apply nd_sequents_are_hs_derivable.
Qed.
