
Require Arith.

(* 1. Quelques preliminaires, utiles pour la suite, mais ce n'est
  pas grave si vous ne comprenez pas les preuves. *)

(* au_plus est une function qui retourne le booleen true si m<=n, false sinon.
  Il s'agit d'un programme, au contraire de la relation le, qui est un predicat
  (de type nat -> nat -> Prop). *)
Fixpoint au_plus [m,n:nat] : bool :=
  Cases m of
      O => true
    | (S m') =>
      Cases n of
          O => false
	| (S n') => (au_plus m' n')
      end
  end.

Lemma choix_bool : (b:bool) b=true \/ b=false.
Proof.
  Induction b. Left . Reflexivity.
  Right . Reflexivity.
Qed.

Lemma au_plus_le_1 : (n,m:nat) (au_plus m n)=true -> (le m n).
Proof.
  Induction n. Induction m. Intro. Apply le_n.
  Simpl. Intros. Discriminate H0.
  Induction m. Intro. Apply le_O_n.
  Simpl. Intros. Apply le_n_S. Apply H. Assumption.
Qed.

Lemma au_plus_le_2 : (n,m:nat) (le m n) -> (au_plus m n)=true.
Proof.
  Induction n. Induction m. Trivial.
  Intros. Elim (le_Sn_O n0 H0).
  Induction m. Trivial.
  Intros. Simpl. Apply H. Apply le_S_n. Assumption.
Qed.

Lemma au_plus_le_3 : (n,m:nat) (au_plus m n)=false -> ~(le m n).
Proof.
  Unfold not. Intros. Rewrite (au_plus_le_2 n m H0) in H. Discriminate H.
Qed.

Lemma au_plus_le_4 : (n,m:nat) ~(le m n) -> (au_plus m n)=false.
Proof.
  Intros. Elim (choix_bool (au_plus m n)). Intro. Elim (H (au_plus_le_1 n m H0)).
  Trivial.
Qed.

Lemma au_plus_refl : (m:nat) (au_plus m m)=true.
Proof.
  Induction m; Trivial.
Qed.

Lemma au_plus_trans : (m,n,k:nat) (au_plus m n)=true -> (au_plus n k)=true ->
                            (au_plus m k)=true.
Proof.
  Intros. Apply au_plus_le_2. (Apply (le_trans m n k); Apply au_plus_le_1; Assumption).
Qed.

Lemma au_plus_total : (m,n:nat) (au_plus m n)=false -> (au_plus n m)=true.
Proof.
  Induction m. Induction n. Trivial.
  Intros. Elim (au_plus_le_3 ? ? H0 (le_O_n (S n0))).
  Induction n0. Trivial.
  Intros. Simpl. Apply H. Exact H1.
Qed.

(* De meme, egal est le programme calculant si les deux entiers en entree sont
  egaux ou non. *)
Fixpoint egal [m,n:nat] : bool :=
  Cases m n of
      O O => true
    | (S m') (S n') => (egal m' n')
    | _ _ => false
  end.

Lemma egal_1 : (m,n:nat) (egal m n)=true -> m=n.
Proof.
  Induction m. Induction n. Trivial.
  Intros. Simpl in H0. Discriminate H0.
  Induction n0. Simpl. Intro. Discriminate H0.
  Simpl. Intros. Cut n=n1. Intro. Rewrite H2. Reflexivity.
  Apply H. Assumption.
Qed.

Lemma egal_refl : (m:nat) (egal m m)=true.
Proof.
  Induction m; Trivial.
Qed.

Lemma egal_imp_au_plus : (m,n:nat) (egal m n)=true -> (au_plus m n)=true.
Proof.
  Intros. Rewrite (egal_1 m n H). Apply au_plus_refl.
Qed.

Lemma non_au_plus_imp_non_egal : (m,n:nat) (au_plus m n)=false -> (egal m n)=false.
Proof.
  Intros. Elim (choix_bool (egal m n)). Intro. Rewrite (egal_imp_au_plus m n H0) in H.
  Discriminate H.
  Trivial.
Qed.



(* 2. Les tas. *)

Inductive pretas : Set := V : pretas | B : pretas -> nat -> pretas -> pretas.

Fixpoint minore [n:nat; t:pretas] : Prop :=
  Cases t of
      V => True
    | (B g m d) => (au_plus n m)=true /\ (minore n g) /\ (minore n d)
  end.

Fixpoint tas [t:pretas] : Prop :=
  Cases t of
      V => True
    | (B g n d) => (minore n g) /\ (tas g) /\ (minore n d) /\ (tas d)
  end.

Fixpoint ajoute [n:nat; t:pretas] : pretas :=
  Cases t of
      V => (B V n V)
    | (B g m d) => if (au_plus n m)
                   then (B (ajoute m d) n g)
                   else (B (ajoute n d) m g)
  end.

Lemma ajoute_minore : (t:pretas) (m,n:nat) (au_plus m n)=true -> (minore m t)
                          -> (minore m (ajoute n t)).
Proof.
  Induction t. Simpl. Intros. (Split; Trivial). (Split; Trivial).
  Simpl. Intros. Elim (choix_bool (au_plus n0 n)). Intro. Rewrite H3. Simpl. Split. Assumption.
  Split. Apply H0. (Apply (au_plus_trans m n0 n); Assumption).
  Elim H2. Intros. Elim H5. Trivial.
  Elim H2. Intros. (Elim H5; Trivial).
  Intro. Rewrite H3. Simpl. Elim H2. Intros. Elim H5. Intros. Split. Assumption.
  Split. Apply H0. Assumption.
  Assumption.
  Assumption.
Qed.

Lemma au_plus_minore : (t:pretas) (m,n:nat) (au_plus m n)=true -> (minore n t)
                           -> (minore m t).
Proof.
  Induction t. Trivial.
  Simpl. Intros. Elim H2. Intros. Elim H4. Intros. Split. Apply (au_plus_trans m n0 n). Assumption.
  Assumption.
  Split. Apply H with n:=n0. Assumption.
  Assumption.
  Apply H0 with n:=n0. Assumption.
  Assumption.
Qed.

Lemma ajoute_preserve_tas : (t:pretas) (tas t) -> (n:nat) (tas (ajoute n t)).
Proof.
  Induction t. Simpl. Intros. Split; Trivial. Split; Trivial. Split; Trivial.
  Intros. Simpl. Elim (choix_bool (au_plus n0 n)). Intro. Rewrite H2. Simpl. Simpl in H1.
  Elim H1. Intros. Elim H4. Intros. Elim H6. Intros. Split. Apply ajoute_minore. Assumption.
  (Apply au_plus_minore with n:=n; Assumption).
  Split. (Apply H0; Assumption).
  Split. (Apply au_plus_minore with n:=n; Assumption).
  Assumption.
  Intro. Rewrite H2. Simpl. Simpl in H1. Elim H1. Intros. Elim H4. Intros. Elim H6. Intros. Split.
  Apply ajoute_minore. Apply au_plus_total. Assumption.
  Assumption.
  Split. (Apply H0; Assumption).
  (Split; Assumption).
Qed.

Inductive liste : Set := nil : liste | cons : nat -> liste -> liste.

Fixpoint conversion [l:liste] : pretas :=
  Cases l of
      nil => V
    | (cons n l') => (ajoute n (conversion l'))
  end.

Fixpoint nocc_l [x:nat; l:liste] : nat :=
  Cases l of
      nil => O
    | (cons m l') =>
      if (egal x m)
      then (S (nocc_l x l'))
      else (nocc_l x l')
  end.

Fixpoint nocc_tas [x:nat; t:pretas] : nat :=
  Cases t of
      V => O
    | (B g n d) =>
      if (egal x n)
      then (S (plus (nocc_tas x g) (nocc_tas x d)))
      else (plus (nocc_tas x g) (nocc_tas x d))
  end.

Definition memes_elements := [l:liste; t:pretas]
  (x:nat) (nocc_l x l)=(nocc_tas x t).

Lemma conversion_produit_tas : (l:liste) (tas (conversion l)).
Proof.
  Induction l. Simpl. Trivial.
  Simpl. Intros. Apply ajoute_preserve_tas. Assumption.
Qed.

Lemma nocc_ajoute : (t:pretas) (x,n:nat)
  ((egal x n)=true  -> (nocc_tas x (ajoute n t))=(S (nocc_tas x t))) /\
  ((egal x n)=false -> (nocc_tas x (ajoute n t))=(nocc_tas x t)).
  (* On ne peut prouver ces deux proprietes qu'ensemble,
  ce qu'on appelle usuellement en mathematiques "par recurrence simultanee". *)
Proof.
  Induction t. Simpl. Intros. Split. Intro. Rewrite H. Reflexivity.
  Intro. Rewrite H. Reflexivity.
  Simpl. Intros. Elim (choix_bool (egal x n0)). Intro. Rewrite H1. Split. Intro.
  Elim (choix_bool (au_plus n0 n)). Intro. Rewrite H3. Simpl. Rewrite H1.Elim (H0 x n). Intros.
  Elim (choix_bool (egal x n)). Intro. Rewrite H6. Rewrite (H4 H6). Simpl. Rewrite plus_sym.
  Reflexivity.
  Intro. Rewrite H6. Rewrite (H5 H6). Rewrite plus_sym. Reflexivity.
  Intro. Rewrite H3. Simpl. Elim (choix_bool (egal x n)). Intro. Rewrite <- (egal_1 x n0 H1) in H3.
  Rewrite (egal_imp_au_plus x n H4) in H3. Discriminate H3.
  Intro. Rewrite H4. Elim (H0 x n0). Intros. Rewrite (H5 H1). Simpl. Rewrite plus_sym. Reflexivity.
  Intro. Discriminate H2.
  Intro. Rewrite H1. Split. Intro. Discriminate H2.
  Intro. Elim (H0 x n). Intros. Elim (choix_bool (au_plus n0 n)). Intro. Rewrite H5. Simpl.
  Rewrite H1. Elim (choix_bool (egal x n)). Intro. Rewrite H6. Rewrite (H3 H6). Simpl.
  Rewrite plus_sym. Reflexivity.
  Intro. Rewrite H6. Rewrite (H4 H6). Apply plus_sym.
  Intro. Rewrite H5. Simpl. Elim (H0 x n0). Intros. Elim (choix_bool (egal x n)). Intro.
  Rewrite H8. Rewrite (H7 H1). Rewrite plus_sym. Reflexivity.
  Intro. Rewrite H8. Rewrite (H7 H1). Apply plus_sym.
Qed.

Lemma nocc_ajoute_1 : (t:pretas) (n:nat) (nocc_tas n (ajoute n t))=(S (nocc_tas n t)).
Proof.
  Intros. Elim (nocc_ajoute t n n). Intros. Apply H. Apply egal_refl.
Qed.

Lemma nocc_ajoute_2 : (t:pretas) (x,n:nat) (egal x n)=false
                         -> (nocc_tas x (ajoute n t))=(nocc_tas x t).
Proof.
  Intros. Elim (nocc_ajoute t x n). Intros. Apply H1. Assumption.
Qed.


Lemma conversion_memes_elements : (l:liste) (memes_elements l (conversion l)).
Proof.
  Induction l. Simpl. Unfold memes_elements. Intro. Reflexivity.
  Unfold memes_elements. Simpl. Intros. Elim (choix_bool (egal x n)). Intro. Rewrite H0.
  Rewrite (egal_1 x n H0). Rewrite (nocc_ajoute_1 (conversion l0) n). Rewrite (H n). Reflexivity.
  Intro. Rewrite H0. Rewrite (nocc_ajoute_2 (conversion l0) x n H0). Apply H.
Qed.
