(* week-04_more-about-soundness-and-completeness.v *)
(* LPP 2023 - CS3234 2023-2024, Sem2 *)
(* Olivier Danvy <danvy@yale-nus.edu.sg> *)
(* Version of 09 Feb 2024 *)

(* ********** *)

(* Paraphernalia: *)

Require Import Arith.

(* ********** *)

Definition recursive_specification_of_addition (add : nat -> nat -> nat) :=
  (forall y : nat,
      add O y = y)
  /\
  (forall x' y : nat,
      add (S x') y = S (add x' y)).

(* ********** *)

Lemma about_a_recursive_addition :
  forall add : nat -> nat -> nat,
    recursive_specification_of_addition add ->
    forall i j : nat,
      add i j = i + j.
Proof.
  intro add.
  unfold recursive_specification_of_addition.
  intros [S_add_O S_add_S] i.
  induction i as [ | i' IHi'].
  - intro j.
    rewrite -> (Nat.add_0_l j).
    exact (S_add_O j).
  - intro j.
    rewrite -> (S_add_S i' j).
    Check (plus_Sn_m i' j).
    rewrite -> (plus_Sn_m i' j).
    rewrite -> (IHi' j).
    reflexivity.
Qed.

(* ********** *)

Proposition soundness_of_recursive_addition :
  forall add : nat -> nat -> nat,
    recursive_specification_of_addition add ->
    forall i j k : nat,
      add i j = k ->
      i + j = k.
Proof.
  intros add S_add i j k H_k.
  symmetry.
  rewrite <- H_k.
  exact (about_a_recursive_addition add S_add i j).
Qed.

(* ********** *)

Proposition completeness_of_recursive_addition :
  forall add : nat -> nat -> nat,
    recursive_specification_of_addition add ->
    forall i j k : nat,
      i + j = k ->
      add i j = k.
Proof.
  intros add S_add i j k H_k.
  rewrite <- H_k.
  exact (about_a_recursive_addition add S_add i j).
Qed.

(* ********** *)

Lemma about_decomposing_a_pair_using_the_injection_tactic :
  forall i j : nat,
    (i, j) = (0, 1) ->
    i = 0 /\ j = 1.
Proof.
  intros i j H_ij.
  injection H_ij.
  
  Restart.

  intros i j H_ij.
  injection H_ij as H_i H_j.
  exact (conj H_i H_j).
Qed.

Lemma about_decomposing_a_pair_using_the_injection_tactic' :
  forall i j : nat,
    (i, 1) = (0, j) ->
    i = 0 /\ j = 1.
Proof.
  intros i j H_ij.
  injection H_ij as H_i H_j.
  symmetry in H_j.
  exact (conj H_i H_j).
Qed.

Lemma about_decomposing_a_pair_of_pairs_using_the_injection_tactic :
  forall a b c d: nat,
    ((a, 1), (2, d)) = ((0, b), (c, 3)) ->
    a = 0 /\ b = 1 /\ c = 2 /\ d = 3.
Proof.
  intros a b c d H_abcd.
  injection H_abcd as H_a H_b H_c H_d.
  symmetry in H_b.
  symmetry in H_c.
  exact (conj H_a (conj H_b (conj H_c H_d))).
Qed.

(* ********** *)

Proposition true_is_not_false :
  true <> false.
Proof.
  unfold not.
  intro H_absurd.
  discriminate H_absurd.
Qed.

(* ********** *)

Proposition now_what :
  (forall n : nat, n = S n) <-> 0 = 1.
Proof.
  split.

  - intro H_n_Sn.
    Check (H_n_Sn 0).
    exact (H_n_Sn 0).
    
  - intro H_absurd.
    discriminate H_absurd.

  Restart.

  split.

  - intro H_n_Sn.
    Check (H_n_Sn 42).
    discriminate (H_n_Sn 42).

  - intro H_absurd.
    discriminate H_absurd.
Qed.

Proposition what_now :
  forall n : nat,
    n = S n <-> 0 = 1.
Proof.
  intro n.
  split.

  - intro H_n.
    Search (_ <> S _).
    Check (n_Sn n).
    assert (H_tmp := n_Sn n).
    unfold not in H_tmp.
    Check (H_tmp H_n).
    contradiction (H_tmp H_n).

  - intro H_absurd.
    discriminate H_absurd.
Qed.

(* ********** *)

Proposition soundness_of_recursive_addition_revisited :
  forall add : nat -> nat -> nat,
    recursive_specification_of_addition add ->
    forall i j k : nat,
      add i j = k ->
      i + j = k.
Proof.
  intro add.
  unfold recursive_specification_of_addition.
  intros [S_add_O S_add_S] i.
  induction i as [ | i' IHi'].
  - intros j k H_add.
    rewrite -> (S_add_O j) in H_add.
    rewrite -> H_add.
    exact (Nat.add_0_l k).
  - intros j k H_add.
    rewrite -> (S_add_S i' j) in H_add.
    case k as [ | k'].
    + discriminate H_add.
    + injection H_add as H_add.
      Check (IHi' j k').
      Check (IHi' j k' H_add).
      rewrite <- (IHi' j k' H_add).
      Check plus_Sn_m.
      exact (plus_Sn_m i' j).
Qed.

(* ********** *)

Proposition completeness_of_recursive_addition_revisited :
  forall add : nat -> nat -> nat,
    recursive_specification_of_addition add ->
    forall i j k : nat,
      i + j = k ->
      add i j = k.
Proof.
  intro add.
  unfold recursive_specification_of_addition.
  intros [S_add_O S_add_S] i j.
  induction i as [ | i' IHi'].
  - intros k H_k.
    rewrite -> (Nat.add_0_l j) in H_k.
    rewrite <- H_k.
    exact (S_add_O j).
  - intros k H_k.
    rewrite -> (S_add_S i' j).
    rewrite -> (plus_Sn_m i' j) in H_k.
    case k as [ | k'].
    + discriminate H_k.
    + injection H_k as H_k.
      Check (IHi' k').
      Check (IHi' k' H_k).
      rewrite <- (IHi' k' H_k).
      reflexivity.

  Restart.

  intro add.
  unfold recursive_specification_of_addition.
  intros [S_add_O S_add_S] i j.
  induction i as [ | i' IHi'].
  - intros k H_k.
    rewrite -> (Nat.add_0_l j) in H_k.
    rewrite <- H_k.
    exact (S_add_O j).
  - intros k H_k.
    rewrite -> (S_add_S i' j).
    rewrite -> (plus_Sn_m i' j) in H_k.
    Check (IHi' (i' + j)).
    Check (IHi' (i' + j) (eq_refl (i' + j))).
    rewrite -> (IHi' (i' + j) (eq_refl (i' + j))).
    exact H_k.
Qed.

(* ********** *)

Check Nat.add_assoc.

Corollary associativity_of_recursive_addition :
  forall add : nat -> nat -> nat,
    recursive_specification_of_addition add ->
    forall i j k : nat,
      add i (add j k) = add (add i j) k.
Proof.
  intros add S_add i j k.
  rewrite -> (about_a_recursive_addition add S_add i (add j k)).
  rewrite -> (about_a_recursive_addition add S_add j k).
  rewrite -> (about_a_recursive_addition add S_add i j).
  rewrite -> (about_a_recursive_addition add S_add (i + j) k).
  exact (Nat.add_assoc i j k).

  Restart.

  intros add S_add i j k.
  Check (soundness_of_recursive_addition add S_add i j (add i j) (eq_refl (add i j))).
  rewrite <- (soundness_of_recursive_addition add S_add j k (add j k) (eq_refl (add j k))).
  rewrite <- (soundness_of_recursive_addition add S_add i (j + k) (add i (j + k)) (eq_refl (add i (j + k)))).
  Check (soundness_of_recursive_addition add S_add i j (add i j) (eq_refl (add i j))).
  rewrite <- (soundness_of_recursive_addition add S_add i j (add i j) (eq_refl (add i j))).
  Check (soundness_of_recursive_addition add S_add (i + j) k (add (i + j) k) (eq_refl (add (i + j) k))).
  rewrite <- (soundness_of_recursive_addition add S_add (i + j) k (add (i + j) k) (eq_refl (add (i + j) k))).
  exact (Nat.add_assoc i j k).
Qed.

(* ********** *)

Check Nat.add_comm.

Lemma commutativity_of_recursive_addition :
  forall add : nat -> nat -> nat,
    recursive_specification_of_addition add ->
    forall i j : nat,
      add i j = add j i.
Proof.
Admitted. (* for the sake of the argument below *)

Corollary commutativity_of_Nat_dot_add :
  forall add : nat -> nat -> nat,
    recursive_specification_of_addition add ->
    forall i j : nat,
      i + j = j + i.
Proof.
  intros add S_add i j.
  Check (about_a_recursive_addition add S_add i j).
  rewrite <- (about_a_recursive_addition add S_add i j).
  rewrite <- (about_a_recursive_addition add S_add j i).
  Check (commutativity_of_recursive_addition add S_add i j).
  exact (commutativity_of_recursive_addition add S_add i j).

  Restart.

  intros add S_add i j.
  Check (completeness_of_recursive_addition add S_add i j (i + j) (eq_refl (i + j))).
  rewrite <- (completeness_of_recursive_addition add S_add i j (i + j) (eq_refl (i + j))).
  rewrite <- (completeness_of_recursive_addition add S_add j i (j + i) (eq_refl (j + i))).
  exact (commutativity_of_recursive_addition add S_add i j).
Qed.  

(* ********** *)

(* Exercise 07 *)

Definition tail_recursive_specification_of_addition (add : nat -> nat -> nat) :=
  (forall y : nat,
      add O y = y)
  /\
  (forall x' y : nat,
      add (S x') y = add x' (S y)).

(* ***** *)

Lemma about_a_tail_recursive_addition :
  forall add : nat -> nat -> nat,
    tail_recursive_specification_of_addition add ->
    forall i j : nat,
      add i j = i + j.
Proof.
Abort.

(* ***** *)

Proposition soundness_of_tail_recursive_addition :
  forall add : nat -> nat -> nat,
    tail_recursive_specification_of_addition add ->
    forall i j k : nat,
      add i j = k ->
      i + j = k.
Proof.
  (* first, as a corollary of about_a_tail_recursive_addition *)

  Restart.

  (* and second, by induction *)
Abort.

(* ***** *)

Proposition completeness_of_tail_recursive_addition :
  forall add : nat -> nat -> nat,
    tail_recursive_specification_of_addition add ->
    forall i j k : nat,
      i + j = k ->
      add i j = k.
Proof.
  (* first, as a corollary of about_a_tail_recursive_addition *)

  Restart.

  (* and second, by induction *)
Abort.

(* ********** *)

(* end of week-04_more-about-soundness-and-completeness.v *)
