(* week-03_induction-over-binary-trees.v *)
(* LPP 2023 - CS3234 2023-2024, Sem2 *)
(* Olivier Danvy <danvy@yale-nus.edu.sg> *)
(* Version of 01 Feb 2024 *)

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

Inductive binary_tree (V : Type) : Type :=
  Leaf : V -> binary_tree V
| Node : binary_tree V -> binary_tree V -> binary_tree V.

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

Check binary_tree_ind.

Definition specification_of_mirror (mirror : forall V : Type, binary_tree V -> binary_tree V) : Prop :=
  (forall (V : Type)
          (v : V),
      mirror V (Leaf V v) =
      Leaf V v)
  /\
  (forall (V : Type)
          (t1 t2 : binary_tree V),
      mirror V (Node V t1 t2) =
      Node V (mirror V t2) (mirror V t1)).

Definition specification_of_number_of_nodes (number_of_nodes : forall V: Type, binary_tree V -> nat) : Prop :=
  (forall (V : Type) (v: V),
  number_of_nodes V (Leaf V v) = 0)
    /\
    (forall (V : Type) (t1 t2 : binary_tree V),
    number_of_nodes V (Node V t1 t2) =
    S (number_of_nodes V t1 + number_of_nodes V t2)).

Definition specification_of_number_of_leaves (number_of_leaves : forall V: Type, binary_tree V -> nat) : Prop :=
  (forall (V : Type) (v: V),
  number_of_leaves V (Leaf V v) = 1)
    /\
    (forall (V : Type) (t1 t2 : binary_tree V),
    number_of_leaves V (Node V t1 t2) =
    number_of_leaves V t1 + number_of_leaves V t2).
(* ***** *)

(* Exercise 09a *)

Proposition there_is_at_most_one_mirror_function :
  forall mirror1 mirror2 : forall V : Type, binary_tree V -> binary_tree V,
    specification_of_mirror mirror1 ->
    specification_of_mirror mirror2 ->
    forall (V : Type)
           (t : binary_tree V),
      mirror1 V t = mirror2 V t.
Proof.
  intros mirror1 mirror2.
  unfold specification_of_mirror.
  intros [S_Leaf1 S_Node1].
  intros [S_Leaf2 S_Node2].
  intros V t.
  induction t as [| t1 IHt1 t2 IHt2].
  - rewrite -> (S_Leaf1 V v).
    rewrite -> (S_Leaf2 V v).
    reflexivity.
  - rewrite -> (S_Node1 V t1 t2).
    rewrite -> (S_Node2 V t1 t2).
    rewrite -> IHt1.
    rewrite -> IHt2.
    reflexivity.
Qed.

(* Exercise 09b *)
Proposition there_is_at_most_one_number_of_nodes_function :
  forall number_of_nodes1 number_of_nodes2 : forall V : Type, binary_tree V -> nat,
    specification_of_number_of_nodes number_of_nodes1 ->
    specification_of_number_of_nodes number_of_nodes2 ->
    forall (V : Type)
           (t : binary_tree V),
      number_of_nodes1 V t = number_of_nodes2 V t.
Proof.
  intros number_of_nodes1 number_of_nodes2.
  unfold specification_of_number_of_nodes.
  intros [S_Leaf1 S_Node1].
  intros [S_Leaf2 S_Node2].
  intros V t.
  induction t as [| t1 IHt1 t2 IHt2].
  - rewrite -> (S_Leaf1 V v).
    rewrite -> (S_Leaf2 V v).
    reflexivity.
  - rewrite -> (S_Node1 V t1 t2).
    rewrite -> (S_Node2 V t1 t2).
    rewrite -> IHt1.
    rewrite -> IHt2.
    reflexivity.

Qed.

(* Exercise 09c *)
Proposition there_is_at_most_one_number_of_leaves_function :
  forall number_of_leaves1 number_of_leaves2 : forall V : Type, binary_tree V -> nat,
    specification_of_number_of_leaves number_of_leaves1 ->
    specification_of_number_of_leaves number_of_leaves2 ->
    forall (V : Type)
           (t : binary_tree V),
      number_of_leaves1 V t = number_of_leaves2 V t.
Proof.
  intros number_of_leaves1 number_of_leaves2.
  unfold specification_of_number_of_leaves.
  intros [S_Leaf1 S_Node1].
  intros [S_Leaf2 S_Node2].
  intros V t.
  induction t as [v | t1 IHt1 t2 IHt2].
  - rewrite -> (S_Leaf1 V v).
    rewrite -> (S_Leaf2 V v).
    reflexivity.
  - rewrite -> (S_Node1 V t1 t2).
    rewrite -> (S_Node2 V t1 t2).
    rewrite -> IHt1.
    rewrite -> IHt2.
    reflexivity.
Qed.


(* ***** *)

Theorem mirror_is_involutory :
  forall mirror : forall V : Type, binary_tree V -> binary_tree V,
    specification_of_mirror mirror ->
    forall (V : Type)
           (t : binary_tree V),
      mirror V (mirror V t) = t.
Proof.
  intro mirror.
  unfold specification_of_mirror.
  intros [S_Leaf S_Node].
  intros V t.
  induction t as [v | t1 IHt1 t2 IHt2].
  - revert V v.
    intros V v.
    Check (S_Leaf V v).
    rewrite ->  (S_Leaf V v).
    Check (S_Leaf V v).
(*
    exact (S_Leaf V v).
*)
    rewrite -> (S_Leaf V v).
    reflexivity.
  - revert IHt2.
    revert t2.
    revert IHt1.
    revert t1.
    intros t1 IHt1 t2 IHt2.
    Check (S_Node V t1 t2).
    rewrite -> (S_Node V t1 t2).
    Check (S_Node V (mirror V t2) (mirror V t1)).
    rewrite -> (S_Node V (mirror V t2) (mirror V t1)).
    rewrite -> IHt1.
    rewrite -> IHt2.
    reflexivity.

  Restart. (* now for a less verbose proof: *)

  intros mirror S_mirror V t.
  induction t as [ v | t1 IHt1 t2 IHt2].
  - unfold specification_of_mirror in S_mirror.
    destruct S_mirror as [S_Leaf _].
    Check (S_Leaf V v).
    rewrite ->  (S_Leaf V v).
    Check (S_Leaf V v).
    exact (S_Leaf V v).
  - unfold specification_of_mirror in S_mirror.
    destruct S_mirror as [_ S_Node].
    Check (S_Node V t1 t2).
    rewrite -> (S_Node V t1 t2).
    Check (S_Node V (mirror V t2) (mirror V t1)).
    rewrite -> (S_Node V (mirror V t2) (mirror V t1)).
    rewrite -> IHt1.
    rewrite -> IHt2.
    reflexivity.
Qed.

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

(* Exercise 10 *)
Proposition mirror_reverses_itself :
  forall mirror : forall V : Type, binary_tree V -> binary_tree V,
    specification_of_mirror mirror ->
    forall (V : Type)
           (t t' : binary_tree V),
      mirror V t = t' ->
      mirror V t' = t.
Proof.
  intros mirror S_mirror.
  assert (S_tmp :=S_mirror).
  unfold specification_of_mirror in S_tmp.
  destruct S_tmp as [S_Leaf S_Node].
  intros V t t' M_t_t'.
  induction t' as [v | t1 IHt1 t2 IHt2].
  - rewrite <- M_t_t'.
    rewrite ->  (mirror_is_involutory mirror S_mirror V t).
    reflexivity.
  - rewrite <- M_t_t'.
    rewrite -> (mirror_is_involutory mirror S_mirror V t).
    reflexivity.

  Restart.
  intros mirror S_mirror V t t' M_t_t'.

  assert (f_equal :
            forall t1 t2 : binary_tree V,
              t1 = t2 ->
              mirror V t1 = mirror V t2).
  { intros t1 t2 eq_t1_t2.
    rewrite -> eq_t1_t2.
    reflexivity. }
  Check (f_equal (mirror V t) t').
  Check (f_equal (mirror V t) t' M_t_t').
  assert (H_tmp := f_equal (mirror V t) t' M_t_t').

  rewrite <- H_tmp.
  Check (mirror_is_involutory mirror S_mirror V t).
  rewrite -> (mirror_is_involutory mirror S_mirror V t).
  reflexivity.
Qed.

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

(* Exercise 09c *)

Theorem about_the_relative_number_of_leaves_and_nodes_in_a_tree :
  forall number_of_leaves : forall V : Type, binary_tree V -> nat,
    specification_of_number_of_leaves number_of_leaves ->
    forall number_of_nodes : forall V : Type, binary_tree V -> nat,
      specification_of_number_of_nodes number_of_nodes ->
      forall (V : Type)
             (t : binary_tree V),
        number_of_leaves V t = S (number_of_nodes V t).
Proof.
  intro number_of_leaves.
  unfold specification_of_number_of_leaves.
  intros [S_nol_Leaf S_nol_Node].
  intro number_of_nodes.
  unfold specification_of_number_of_nodes.
  intros [S_non_Leaf S_non_Node].
  
  Restart. (* with less clutter among the assumptions *)

  intros nol S_nol non S_non V t.
  induction t as [v | t1 IHt1 t2 IHt2].
  - unfold specification_of_number_of_leaves in S_nol.
    destruct S_nol as [S_nol_Leaf _].
    unfold specification_of_number_of_nodes in S_non.
    destruct S_non as [S_non_Leaf _].
    Check (S_non_Leaf V v).
    rewrite ->  (S_non_Leaf V v).
    Check (S_nol_Leaf V v).
    exact  (S_nol_Leaf V v).
  - unfold specification_of_number_of_leaves in S_nol.
    destruct S_nol as [_ S_nol_Node].
    unfold specification_of_number_of_nodes in S_non.
    destruct S_non as [_ S_non_Node].
    Check (S_nol_Node V t1 t2).
    rewrite -> (S_nol_Node V t1 t2).
    Check (S_non_Node V t1 t2).
    rewrite -> (S_non_Node V t1 t2).
    rewrite -> IHt1.
    rewrite -> IHt2.
    Search (S _ +  _).
    Check (plus_Sn_m (non V t1) (S (non V t2))).
    rewrite ->  (plus_Sn_m (non V t1) (S (non V t2))).
    Search (_ +  S _ = _).
    Search (_ = _ +  S _).
    Check (plus_n_Sm (non V t1) (non V t2)).
    rewrite <- (plus_n_Sm (non V t1) (non V t2)).
    reflexivity.

  Restart.

  intros nol S_nol non S_non.
  Check binary_tree_ind.
  intro V.
  Check (binary_tree_ind
           V).
  Check (binary_tree_ind
           V
           (fun t : binary_tree V => nol V t = S (non V t))).
  assert (wanted_Leaf :
            forall v : V, nol V (Leaf V v) = S (non V (Leaf V v))).
  { intro v.
    unfold specification_of_number_of_leaves in S_nol.
    destruct S_nol as [S_nol_Leaf _].
    unfold specification_of_number_of_nodes in S_non.
    destruct S_non as [S_non_Leaf _].
    Check (S_non_Leaf V v).
    rewrite ->  (S_non_Leaf V v).
    Check (S_nol_Leaf V v).
    exact  (S_nol_Leaf V v). }
  Check (binary_tree_ind
           V
           (fun t : binary_tree V => nol V t = S (non V t))
           wanted_Leaf).
  apply (binary_tree_ind
           V
           (fun t : binary_tree V => nol V t = S (non V t))
           wanted_Leaf).
  intros t1 IHt1 t2 IHt2.
  unfold specification_of_number_of_leaves in S_nol.
  destruct S_nol as [_ S_nol_Node].
  unfold specification_of_number_of_nodes in S_non.
  destruct S_non as [_ S_non_Node].
  Check (S_nol_Node V t1 t2).
  rewrite -> (S_nol_Node V t1 t2).
  Check (S_non_Node V t1 t2).
  rewrite -> (S_non_Node V t1 t2).
  rewrite -> IHt1.
  rewrite -> IHt2.
  Search (S _ +  _).
  Check (plus_Sn_m (non V t1) (S (non V t2))).
  rewrite ->  (plus_Sn_m (non V t1) (S (non V t2))).
  Search (_ +  S _ = _).
  Search (_ = _ +  S _).
  Check (plus_n_Sm (non V t1) (non V t2)).
  rewrite <- (plus_n_Sm (non V t1) (non V t2)).
  reflexivity.
Qed.

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

(* end of week-03_induction-over-binary-trees.v *)
