(** * ImpParser: Lexing and Parsing in Coq *)

(** The development of the Imp language in [Imp.v] completely ignores
    issues of concrete syntax -- how an ascii string that a programmer
    might write gets translated into abstract syntax trees defined by
    the datatypes [aexp], [bexp], and [com].  In this chapter, we
    illustrate how the rest of the story can be filled in by building
    a simple lexical analyzer and parser using Coq's functional
    programming facilities.

    It is not important to understand all the details here (and
    accordingly, the explanations are fairly terse and there are no
    exercises).  The main point is simply to demonstrate that it can
    be done.  You are invited to look through the code -- most of it
    is not very complicated, though the parser relies on some
    "monadic" programming idioms that may require a little work to
    make out -- but most readers will probably want to just skim down
    to the Examples section at the very end to get the punchline. *)

(* DROP *)

(* ################################################################# *)
(** * Internals *)

Set Warnings "-notation-overridden,-parsing,-deprecated-implicit-arguments".
Require Import Coq.Strings.String.
Require Import Coq.Strings.Ascii.
Require Import Coq.Arith.Arith.
Require Import Coq.Arith.EqNat.
Require Import Coq.Lists.List.
Import ListNotations.
Require Import Maps.
Require Import Imp.

(* ================================================================= *)
(** ** Lexical Analysis *)

Definition isWhite (c : ascii) : bool :=
  let n := nat_of_ascii c in
  orb (orb (beq_nat n 32)   (* space *)
           (beq_nat n 9))   (* tab *)
      (orb (beq_nat n 10)   (* linefeed *)
           (beq_nat n 13)). (* Carriage return. *)

Notation "x '<=?' y" := (leb x y)
  (at level 70, no associativity) : nat_scope.

Definition isLowerAlpha (c : ascii) : bool :=
  let n := nat_of_ascii c in
    andb (97 <=? n) (n <=? 122).

Definition isAlpha (c : ascii) : bool :=
  let n := nat_of_ascii c in
    orb (andb (65 <=? n) (n <=? 90))
        (andb (97 <=? n) (n <=? 122)).

Definition isDigit (c : ascii) : bool :=
  let n := nat_of_ascii c in
     andb (48 <=? n) (n <=? 57).

Inductive chartype := white | alpha | digit | other.

Definition classifyChar (c : ascii) : chartype :=
  if isWhite c then
    white
  else if isAlpha c then
    alpha
  else if isDigit c then
    digit
  else
    other.

Fixpoint list_of_string (s : string) : list ascii :=
  match s with
  | EmptyString => []
  | String c s => c :: (list_of_string s)
  end.

Fixpoint string_of_list (xs : list ascii) : string :=
  fold_right String EmptyString xs.

Definition token := string.

Fixpoint tokenize_helper (cls : chartype) (acc xs : list ascii)
                       : list (list ascii) :=
  let tk := match acc with [] => [] | _::_ => [rev acc] end in
  match xs with
  | [] => tk
  | (x::xs') =>
    match cls, classifyChar x, x with
    | _, _, "("      =>
      tk ++ ["("]::(tokenize_helper other [] xs')
    | _, _, ")"      =>
      tk ++ [")"]::(tokenize_helper other [] xs')
    | _, white, _    =>
      tk ++ (tokenize_helper white [] xs')
    | alpha,alpha,x  =>
      tokenize_helper alpha (x::acc) xs'
    | digit,digit,x  =>
      tokenize_helper digit (x::acc) xs'
    | other,other,x  =>
      tokenize_helper other (x::acc) xs'
    | _,tp,x         =>
      tk ++ (tokenize_helper tp [x] xs')
    end
  end %char.

Definition tokenize (s : string) : list string :=
  map string_of_list (tokenize_helper white [] (list_of_string s)).

Example tokenize_ex1 :
    tokenize "abc12==3  223*(3+(a+c))" %string
  = ["abc"; "12"; "=="; "3"; "223";
       "*"; "("; "3"; "+"; "(";
       "a"; "+"; "c"; ")"; ")"]%string.
Proof. reflexivity. Qed.

(* ================================================================= *)
(** ** Parsing *)

(* ----------------------------------------------------------------- *)
(** *** Options With Errors *)

(** An [option] type with error messages: *)

Inductive optionE (X:Type) : Type :=
  | SomeE : X -> optionE X
  | NoneE : string -> optionE X.

Implicit Arguments SomeE [[X]].
Implicit Arguments NoneE [[X]].

(** Some syntactic sugar to make writing nested match-expressions on
    optionE more convenient. *)

Notation "'DO' ( x , y ) <== e1 ; e2"
   := (match e1 with
         | SomeE (x,y) => e2
         | NoneE err => NoneE err
       end)
   (right associativity, at level 60).

Notation "'DO' ( x , y ) <-- e1 ; e2 'OR' e3"
   := (match e1 with
         | SomeE (x,y) => e2
         | NoneE err => e3
       end)
   (right associativity, at level 60, e2 at next level).

(* ----------------------------------------------------------------- *)
(** *** Generic Combinators for Building Parsers *)

Open Scope string_scope.

Definition parser (T : Type) :=
  list token -> optionE (T * list token).

Fixpoint many_helper {T} (p : parser T) acc steps xs :=
  match steps, p xs with
  | 0, _ =>
      NoneE "Too many recursive calls"
  | _, NoneE _ =>
      SomeE ((rev acc), xs)
  | S steps', SomeE (t, xs') =>
      many_helper p (t::acc) steps' xs'
  end.

(** A (step-indexed) parser that expects zero or more [p]s: *)

Fixpoint many {T} (p : parser T) (steps : nat) : parser (list T) :=
  many_helper p [] steps.

(** A parser that expects a given token, followed by [p]: *)

Definition firstExpect {T} (t : token) (p : parser T)
                     : parser T :=
  fun xs => match xs with
            | x::xs' =>
              if string_dec x t
              then p xs'
              else NoneE ("expected '" ++ t ++ "'.")
            | [] =>
              NoneE ("expected '" ++ t ++ "'.")
            end.

(** A parser that expects a particular token: *)

Definition expect (t : token) : parser unit :=
  firstExpect t (fun xs => SomeE(tt, xs)).

(* ----------------------------------------------------------------- *)
(** *** A Recursive-Descent Parser for Imp *)

(** Identifiers: *)

Definition parseIdentifier (xs : list token)
                         : optionE (id * list token) :=
match xs with
| [] => NoneE "Expected identifier"
| x::xs' =>
    if forallb isLowerAlpha (list_of_string x) then
      SomeE (Id x, xs')
    else
      NoneE ("Illegal identifier:'" ++ x ++ "'")
end.

(** Numbers: *)

Definition parseNumber (xs : list token)
                     : optionE (nat * list token) :=
match xs with
| [] => NoneE "Expected number"
| x::xs' =>
    if forallb isDigit (list_of_string x) then
      SomeE (fold_left
               (fun n d =>
                  10 * n + (nat_of_ascii d -
                            nat_of_ascii "0"%char))
               (list_of_string x)
               0,
             xs')
    else
      NoneE "Expected number"
end.

(** Parse arithmetic expressions *)

Fixpoint parsePrimaryExp (steps:nat)
                         (xs : list token)
                       : optionE (aexp * list token) :=
  match steps with
  | 0 => NoneE "Too many recursive calls"
  | S steps' =>
      DO (i, rest) <-- parseIdentifier xs ;
          SomeE (AId i, rest)
      OR DO (n, rest) <-- parseNumber xs ;
          SomeE (ANum n, rest)
                OR (DO (e, rest) <== firstExpect "("
                       (parseSumExp steps') xs;
          DO (u, rest') <== expect ")" rest ;
          SomeE(e,rest'))
  end

with parseProductExp (steps:nat)
                     (xs : list token) :=
  match steps with
  | 0 => NoneE "Too many recursive calls"
  | S steps' =>
    DO (e, rest) <==
      parsePrimaryExp steps' xs ;
    DO (es, rest') <==
       many (firstExpect "*" (parsePrimaryExp steps'))
            steps' rest;
    SomeE (fold_left AMult es e, rest')
  end

with parseSumExp (steps:nat) (xs : list token)  :=
  match steps with
  | 0 => NoneE "Too many recursive calls"
  | S steps' =>
    DO (e, rest) <==
      parseProductExp steps' xs ;
    DO (es, rest') <==
      many (fun xs =>
        DO (e,rest') <--
           firstExpect "+"
             (parseProductExp steps') xs;
           SomeE ( (true, e), rest')
        OR DO (e,rest') <==
        firstExpect "-"
           (parseProductExp steps') xs;
            SomeE ( (false, e), rest'))
        steps' rest;
      SomeE (fold_left (fun e0 term =>
                          match term with
                            (true,  e) => APlus e0 e
                          | (false, e) => AMinus e0 e
                          end)
                       es e,
             rest')
  end.

Definition parseAExp := parseSumExp.

(** Parsing boolean expressions: *)

Fixpoint parseAtomicExp (steps:nat)
                        (xs : list token)  :=
match steps with
  | 0 => NoneE "Too many recursive calls"
  | S steps' =>
     DO    (u,rest) <-- expect "true" xs;
         SomeE (BTrue,rest)
     OR DO (u,rest) <-- expect "false" xs;
         SomeE (BFalse,rest)
     OR DO (e,rest) <--
            firstExpect "not"
               (parseAtomicExp steps')
               xs;
         SomeE (BNot e, rest)
     OR DO (e,rest) <--
              firstExpect "("
                (parseConjunctionExp steps') xs;
          (DO (u,rest') <== expect ")" rest;
              SomeE (e, rest'))
     OR DO (e, rest) <== parseProductExp steps' xs;
            (DO (e', rest') <--
              firstExpect "=="
                (parseAExp steps') rest;
              SomeE (BEq e e', rest')
             OR DO (e', rest') <--
               firstExpect "<="
                 (parseAExp steps') rest;
               SomeE (BLe e e', rest')
             OR
               NoneE
      "Expected '==' or '<=' after arithmetic expression")
end

with parseConjunctionExp (steps:nat)
                         (xs : list token) :=
  match steps with
  | 0 => NoneE "Too many recursive calls"
  | S steps' =>
    DO (e, rest) <==
      parseAtomicExp steps' xs ;
    DO (es, rest') <==
       many (firstExpect "&&"
               (parseAtomicExp steps'))
            steps' rest;
    SomeE (fold_left BAnd es e, rest')
  end.

Definition parseBExp := parseConjunctionExp.

Check parseConjunctionExp.

Definition testParsing {X : Type}
           (p : nat ->
                list token ->
                optionE (X * list token))
           (s : string) :=
  let t := tokenize s in
  p 100 t.

(*
Eval compute in
  testParsing parseProductExp "x*y*(x*x)*x".

Eval compute in
  testParsing parseConjunctionExp "not((x==x||x*x<=(x*x)*x)&&x==x".
*)

(** Parsing commands: *)

Fixpoint parseSimpleCommand (steps:nat)
                            (xs : list token) :=
  match steps with
  | 0 => NoneE "Too many recursive calls"
  | S steps' =>
    DO (u, rest) <-- expect "SKIP" xs;
      SomeE (SKIP, rest)
    OR DO (e,rest) <--
         firstExpect "IF" (parseBExp steps') xs;
       DO (c,rest')  <==
         firstExpect "THEN"
           (parseSequencedCommand steps') rest;
       DO (c',rest'') <==
         firstExpect "ELSE"
           (parseSequencedCommand steps') rest';
       DO (u,rest''') <==
         expect "END" rest'';
       SomeE(IFB e THEN c ELSE c' FI, rest''')
    OR DO (e,rest) <--
         firstExpect "WHILE"
           (parseBExp steps') xs;
       DO (c,rest') <==
         firstExpect "DO"
           (parseSequencedCommand steps') rest;
       DO (u,rest'') <==
         expect "END" rest';
       SomeE(WHILE e DO c END, rest'')
    OR DO (i, rest) <==
         parseIdentifier xs;
       DO (e, rest') <==
         firstExpect ":=" (parseAExp steps') rest;
       SomeE(i ::= e, rest')
  end

with parseSequencedCommand (steps:nat)
                           (xs : list token) :=
  match steps with
  | 0 => NoneE "Too many recursive calls"
  | S steps' =>
      DO (c, rest) <==
        parseSimpleCommand steps' xs;
      DO (c', rest') <--
        firstExpect ";"
          (parseSequencedCommand steps') rest;
        SomeE(c ;; c', rest')
      OR
        SomeE(c, rest)
  end.

Definition bignumber := 1000.

Definition parse (str : string) : optionE (com * list token) :=
  let tokens := tokenize str in
  parseSequencedCommand bignumber tokens.

(* ################################################################# *)
(** * Examples *)

(*
Compute parse "
  IF x == y + 1 + 2 - y * 6 + 3 THEN
    x := x * 1;;
    y := 0
  ELSE
    SKIP
  END  ".
====>
  SomeE
     (IFB BEq (AId (Id 0))
              (APlus
                 (AMinus (APlus (APlus (AId (Id 1)) (ANum 1)) (ANum 2))
                    (AMult (AId (Id 1)) (ANum 6)))
                 (ANum 3))
      THEN Id 0 ::= AMult (AId (Id 0)) (ANum 1);; Id 1 ::= ANum 0
      ELSE SKIP FI, [])
*)

(*
Compute parse "
  SKIP;;
  z:=x*y*(x*x);;
  WHILE x==x DO
    IF z <= z*z && not x == 2 THEN
      x := z;;
      y := z
    ELSE
      SKIP
    END;;
    SKIP
  END;;
  x:=z  ".
====>
  SomeE
     (SKIP;;
      Id 0 ::= AMult (AMult (AId (Id 1)) (AId (Id 2)))
                     (AMult (AId (Id 1)) (AId (Id 1)));;
      WHILE BEq (AId (Id 1)) (AId (Id 1)) DO
        IFB BAnd (BLe (AId (Id 0)) (AMult (AId (Id 0)) (AId (Id 0))))
                  (BNot (BEq (AId (Id 1)) (ANum 2)))
           THEN Id 1 ::= AId (Id 0);; Id 2 ::= AId (Id 0)
           ELSE SKIP FI;;
        SKIP
      END;;
      Id 1 ::= AId (Id 0),
     [])
*)

(*
Compute parse "
  SKIP;;
  z:=x*y*(x*x);;
  WHILE x==x DO
    IF z <= z*z && not x == 2 THEN
      x := z;;
      y := z
    ELSE
      SKIP
    END;;
    SKIP
  END;;
  x:=z  ".
=====>
  SomeE
     (SKIP;;
      Id 0 ::= AMult (AMult (AId (Id 1)) (AId (Id 2)))
            (AMult (AId (Id 1)) (AId (Id 1)));;
      WHILE BEq (AId (Id 1)) (AId (Id 1)) DO
        IFB BAnd (BLe (AId (Id 0)) (AMult (AId (Id 0)) (AId (Id 0))))
                 (BNot (BEq (AId (Id 1)) (ANum 2)))
          THEN Id 1 ::= AId (Id 0);;
               Id 2 ::= AId (Id 0)
          ELSE SKIP
        FI;;
        SKIP
      END;;
      Id 1 ::= AId (Id 0),
     []).
*)

(* /DROP *)

(** $Date: 2017-04-26 17:33:43 -0400 (Wed, 26 Apr 2017) $ *)


Require Export String.
Open Scope string.

(** Fill in comments (string) here if we have any for the student. It will
appear in feedback file, e.g., *)
(**
[[
Definition Comment := "Your homework does not compile!".
]] *)
Definition Comment := tt.

(** Apply penalty in percentage. It is usually used when the homework does not
compile. The value should be a string, with a number from 0 to 100, e.g., *)
(**
[[
Definition Penalty := "10".
]]
This applies 10% penalty.
*)
Definition Penalty := tt.

(** We list all the manual grade items here. *)
(** The definition could be in one of the following forms:

1. [tt]: this exercise is not graded yet. We do not need to grade every one
manually because (hopefully) most of them can be graded automatically.

2. of type [bool * string]: a boolean grade and a comment. They get full mark if
[true], or zero if [false]. e.g., [(false, "completely wrong")], [(true, "you
did well!")]. You can leave the comment string empty of course. I usually leave
useful comments to point out their mistakes.

3. of type [nat * string]: a numeric grade and a comment. e.g., [(1, "the first
case is correct but the rest is wrong")], [(2, "so close! the last case is
wrong")].

*)
Definition mg_leb := tt.
Definition mg_leb_refl := tt.
Definition mg_leb_succ := tt.
Definition mg_leb_double_statement := tt.
Definition mg_leb_double := tt.
Definition mg_plus_1 := (false, "No comment").

(** DO NOT remove these placeholders, though they are not used in most cases. In
my experience, sometimes we may think an exercise does not need manual grading.
But then for whatever reason, we need to turn it into a manual grade item. For
example, the students might complain and we need to give them partial credits
now. However, because [local.v] will not be overwritten (you don't want to lose
your manual graded scores) when we update the test script and re-prepare the
build, we can not simply add another manual grade item to [local.v]. In that
case, we can use one of these as a stand-in. *)
Definition mg_exc1 := tt.
Definition mg_exc2 := tt.
Definition mg_exc3 := tt.
Definition mg_exc4 := tt.
Definition mg_exc5 := tt.


(** * Preliminaries *)

(** This file contains definitions and proofs from the Base Library for the ICL lecture.
   - Version: 3 October 2016
   - Author: Gert Smolka, Saarland University
   - Acknowlegments: Sigurd Schneider, Dominik Kirst, Yannick Forster
 *)

(** ** Type Theory **)

Require Export Bool Omega List Setoid Morphisms Tactics.

Global Set Implicit Arguments.
Global Unset Strict Implicit.
Global Unset Printing Records.
Global Unset Printing Implicit Defensive.
Global Set Regular Subst Tactic.

Hint Extern 4 => exact _.

(** **** Lists *)
Export ListNotations.
Notation "x 'el' A" := (In x A) (at level 70).
Notation "A <<= B" := (incl A B) (at level 70).
Notation "| A |" := (length A) (at level 65).

Hint Extern 4 =>
match goal with
|[ H: ?x el nil |- _ ] => destruct H
end.

Lemma incl_nil X (A : list X) :
  nil <<= A.
Proof. intros x []. Qed.

Hint Rewrite <- app_assoc : list.
Hint Rewrite rev_app_distr map_app prod_length : list.
Hint Resolve in_eq in_nil in_cons in_or_app.
Hint Resolve incl_refl incl_tl incl_cons incl_appl incl_appr incl_app incl_nil.

(** **** Tactics *)
Ltac inv H := inversion H; subst; try clear H.

Tactic Notation "destruct" "_":=
  match goal with
  | [ |- context[match ?X with _ => _ end] ] => destruct X
  | [ H : context[match ?X with _ => _ end] |- _ ] => destruct X
  end.

Hint Extern 4 =>
match goal with
|[ H: False |- _ ] => destruct H
|[ H: true=false |- _ ] => discriminate H
|[ H: false=true |- _ ] => discriminate H
end.

Lemma size_induction X (f : X -> nat) (p : X -> Type) :
  (forall x, (forall y, f y < f x -> p y) -> p x) ->
  forall x, p x.

Proof.
  intros IH x. apply IH.
  assert (G: forall n y, f y < n -> p y).
  { intros n. induction n.
    - intros y B. exfalso. omega.
    - intros y B. apply IH. intros z C. apply IHn. omega. }
  apply G.
Qed.

Lemma size_induction_dep L (X : L -> Type) (f : forall l, X l -> nat) (p : forall l, X l -> Type) :
  (forall l x, (forall l' y, f l' y < f l x -> p l' y) -> p l x) ->
  forall l x, p l x.
Proof.
  intros IH l x. apply IH. intros l'.
  assert (G: forall n l' y, f l' y < n -> p l' y).
  { intros n. induction n; intros l'' y.
    - intros B. exfalso. omega.
    - intros B. apply IH. intros ll z C. eapply IHn. omega. }
  apply G.
Qed.

(* Definition Injective {A B} (f : A->B) := *)
(*  forall x y, f x = f y -> x = y. *)

(* Section fix_X. *)
(*   Variable X:Type. *)
(*   Implicit Types (A B: list X) (a b c: X).  *)

(*   Lemma last_app_eq A B a b : *)
(*     A++[a] = B++[b] -> A = B /\ a = b. *)
(*   Proof. *)
(*     intros H%(f_equal (@rev X)). rewrite !rev_app_distr in H. split. *)
(*     - inv H. apply (f_equal (@rev X)) in H2. now rewrite !rev_involutive in H2. *)
(*     - now inv H. *)
(*   Qed. *)

(*   Lemma rev_eq A B: *)
(*     List.rev A = List.rev B <-> A = B. *)
(*   Proof. *)
(*     split. *)
(*     - intros H%(f_equal (@rev X)). now rewrite !rev_involutive in H. *)
(*     - now intros <-. *)
(*   Qed. *)

(*   Lemma rev_nil A: *)
(*     rev A = [] -> A = []. *)
(*   Proof. *)
(*     destruct A. auto. now intros H%symmetry%app_cons_not_nil. *)
(*   Qed. *)

(*   Lemma map_inj (Y:Type) A B (f: X -> Y) : *)
(*     Injective f -> map f A = map f B <-> A = B. *)
(*   Proof. *)
(*     intros inj. split. *)
(*     - revert B. induction A; intros B H; cbn in *; destruct B; auto; cbn in H; inv H. *)
(*       rewrite (inj a x H1). specialize (IHA B H2). now subst. *)
(*     - now intros <-. *)
(*   Qed. *)
(* End fix_X. *)

Lemma app_incl_l X (A B C : list X) :
  A ++ B <<= C -> A <<= C.
Proof.
  firstorder eauto.
Qed.

Lemma app_incl_R X (A B C : list X) :
  A ++ B <<= C -> B <<= C.
Proof.
  firstorder eauto.
Qed.

Lemma cons_incl X (a : X) (A B : list X) : a :: A <<= B -> A <<= B.
Proof.
  intros ? ? ?. eapply H. firstorder.
Qed.

Lemma incl_sing X (a : X) A : a el A -> [a] <<= A.
Proof.
  now intros ? ? [-> | [] ].
Qed.

Hint Resolve app_incl_l app_incl_R cons_incl incl_sing.

(* Hint Extern 4 (_ el map _ _) => eapply in_map_iff. *)
(* Hint Extern 4 (_ el filter _ _) => eapply filter_In. *)

(* Fixpoint count (l : list nat) (n : nat)  := *)
(*   match l with *)
(*   | [] => 0 *)
(*   | m :: l => if Nat.eqb n m then S (count l n) else count l n *)
(*   end. *)

(* Lemma countSplit (A B: list nat) (x: nat)  : count A x + count B x = count (A ++ B) x.  *)
(* Proof.  *)
(*   induction A.  *)
(*   - reflexivity.  *)
(*   - cbn. destruct (x =? a). *)
(*     + cbn. f_equal; exact IHA.  *)
(*     + exact IHA.  *)
(* Qed. *)

(* Lemma notInZero (x: nat) A : *)
(*   not (x el A) <-> count A x = 0. *)
(* Proof. *)
(*   split; induction A. *)
(*   -  reflexivity. *)
(*   - intros H. cbn in *. destruct (Nat.eqb_spec x a). *)
(*     + exfalso. apply H. left. congruence. *)
(*     + apply IHA. intros F. apply H. now right. *)
(*   - tauto. *)
(*   - cbn. destruct (Nat.eqb_spec x a). *)
(*     + subst a. omega. *)
(*     + intros H [E | E]. *)
(*       * now symmetry in E. *)
(*       * tauto. *)
(* Qed. *)

(** Positions and map-products for lists *)

Section Positions.
  Variables (X: Type) (d: forall x y : X, {x = y} + {x <> y}).
  Implicit Types (x y: X) (A B : list X).

  Fixpoint pos x A : option nat :=
    match A with
    | nil => None
    | y :: A' => if d x y then Some 0
                else match pos x A' with
                     | Some n => Some (S n)
                     | None => None
                     end
    end.

  Lemma el_pos x A :
    x el A -> { n | pos x A = Some n }.
  Proof.
    induction A as [|y A IH]; cbn; intros H.
    - destruct H as [].
    - destruct (d x y) as [<-|H1].
      + now exists 0.
      + destruct IH as [n IH].
        * destruct H as [->|H]; tauto.
        * rewrite IH. now exists (S n).
  Qed.

  Notation nthe n A := (nth_error A n).

  Lemma nthe_length A n :
    length A > n -> { x | nthe n A = Some x }.
  Proof.
    revert n.
    induction A as [|y A IH]; cbn; intros n H.
    - exfalso. omega.
    - destruct n as [|n]; cbn.
      + now exists y.
      + destruct (IH n) as [x H1]. omega. now exists x.
  Qed.

 Lemma pos_nthe x A n :
    pos x A = Some n -> nthe n A = Some x.
 Proof.
   revert n.
   induction A as [|y A IH]; cbn; intros n.
    - intros [=].
    - destruct (d x y) as [<-|H1].
      + now intros [= <-].
      + destruct (pos x A) as [k|]; intros [= <-]; cbn.
        now apply IH.
  Qed.

  Lemma nthe_app_l x n A B :
    nthe n A = Some x -> nthe n (A ++ B) = Some x.
  Proof.
    revert n.
    induction A as [|y A IH]; cbn; intros k H.
    - destruct k; discriminate H.
    - destruct k as [|k]; cbn in *. exact H.
      apply IH, H.
  Qed.

End Positions.

Notation nthe n A := (nth_error A n).

Lemma pos_nth X d (x : X) l n def : pos d x l = Some n -> nth n l def = x.
Proof.
  revert n; induction l; cbn; intros; try congruence.
  destruct (d x a); try destruct (pos d x l) eqn:E; inv H; eauto.
Qed.

Lemma pos_length X d (x : X) l n : pos d x l = Some n -> n < | l |.
Proof.
  revert n; induction l; cbn; intros; try congruence.
  destruct (d x a).
  - inv H. omega.
  - destruct (pos d x l) eqn:E; inv H; try omega. specialize (IHl _ eq_refl). omega.
Qed.

Fixpoint omap X Y (f : X -> option Y) l :=
  match l with
  | nil => nil
  | x :: l => match f x with Some y => y :: omap f l | None => omap f l end
  end.

Lemma in_omap_iff X Y (f : X -> option Y) l y : y el omap f l <-> exists x, x el l /\ f x = Some y.
Proof.
  induction l; cbn.
  - firstorder.
  - destruct (f a) eqn:E; firstorder (subst; firstorder congruence).
Qed.

Section neList.

  Variable X : Type.
  Variable P : list X -> Prop.

  Hypothesis B : (forall x : X, P [x]).
  Hypothesis S : (forall x A, P A -> P (x :: A)).

  Lemma list_ind_ne A : A <> [] -> P A.
  Proof.
    intros H. destruct A. congruence. clear H.
    revert x. induction A; eauto.
  Qed.

End neList.

(** **** Boolean propositions and decisions *)

Coercion bool2Prop (b : bool) := if b then True else False.

Lemma bool_Prop_true b :
  b = true -> b.
Proof.
  intros A. rewrite A. exact I.
Qed.

Lemma bool_Prop_false b :
  b = false -> ~ b.
Proof.
  intros A. rewrite A. cbn. auto.
Qed.

Hint Resolve bool_Prop_true bool_Prop_false.

Hint Extern 4 =>
match goal with
|[ H: False |- _ ] => destruct H
|[ H: ~ bool2Prop true |- _ ] => destruct H
|[ H: bool2Prop false |- _ ] => destruct H
|[ H: true=false |- _ ] => discriminate H
|[ H: false=true |- _ ] => discriminate H
|[ H: ?b=false, H': bool2Prop(?b) |- _ ] => rewrite H in H'; destruct H'
(* |[ H: ?x el nil |- _ ] => destruct H *)
end.

Definition dec (X: Prop) : Type := {X} + {~ X}.

Coercion dec2bool P (d: dec P) := if d then true else false.

Existing Class dec.

Definition Dec (X: Prop) (d: dec X) : dec X := d.
Arguments Dec X {d}.

Ltac dec := repeat match goal with
                  | [ |- context [ Dec ?x ] ] => let E := fresh "E" in destruct (Dec x) as [E | ]; [ try (rewrite E in *; clear E)| ]
                  | [H : context [ Dec ?x ]  |- _ ] => let E := fresh "E" in destruct (Dec x) as [E | ];
                                                                      [ try (rewrite E in *; clear E)| ]
                  end; cbn [dec2bool].

Lemma Dec_reflect (X: Prop) (d: dec X) :
  Dec X <-> X.
Proof.
  destruct d as [A|A]; cbn; tauto.
Qed.

Notation Decb X := (dec2bool (Dec X)).

Lemma Dec_reflect_eq (X Y: Prop) (d: dec X) (e: dec Y) :
 Decb X = Decb Y <->  (X <-> Y).
Proof.
  destruct d as [D|D], e as [E|E]; cbn; intuition congruence.
Qed.

Lemma Dec_auto (X: Prop) (d: dec X) :
  X -> Dec X.
Proof.
  destruct d as [A|A]; cbn; tauto.
Qed.

Lemma Dec_auto_not (X: Prop) (d: dec X) :
  ~ X -> ~ Dec X.
Proof.
  destruct d as [A|A]; cbn; tauto.
Qed.

Hint Resolve Dec_auto Dec_auto_not.
Hint Extern 4 =>  (* Improves type class inference *)
match goal with
  | [  |- dec ((fun _ => _) _) ] => cbn
end : typeclass_instances.

Tactic Notation "decide" constr(p) :=
  destruct (Dec p).
Tactic Notation "decide" constr(p) "as" simple_intropattern(i) :=
  destruct (Dec p) as i.
Tactic Notation "decide" "_" :=
  destruct (Dec _).
Tactic Notation "have" constr(E) := let X := fresh "E" in decide E as [X|X]; subst; try congruence; try omega; clear X.

Lemma Dec_true P {H : dec P} : dec2bool (Dec P) = true -> P.
Proof.
  decide P; cbv in *; firstorder.
Qed.

Lemma Dec_false P {H : dec P} : dec2bool (Dec P) = false -> ~P.
Proof.
  decide P; cbv in *; firstorder.
Qed.

Hint Extern 4 =>
match goal with
  [ H : dec2bool (Dec ?P) = true  |- _ ] => apply Dec_true in  H
| [ H : dec2bool (Dec ?P) = false |- _ ] => apply Dec_false in H
end.

(** Decided propositions behave classically *)

Lemma dec_DN X :
  dec X -> ~~ X -> X.
Proof.
  unfold dec; tauto.
Qed.

Lemma dec_DM_and X Y :
  dec X -> dec Y -> ~ (X /\ Y) -> ~ X \/ ~ Y.
Proof.
  unfold dec; tauto.
Qed.

Lemma dec_DM_impl X Y :
  dec X -> dec Y -> ~ (X -> Y) -> X /\ ~ Y.
Proof.
  unfold dec; tauto.
Qed.

(** Propagation rules for decisions *)

Fact dec_transfer P Q :
  P <-> Q -> dec P -> dec Q.
Proof.
  unfold dec. tauto.
Qed.

Instance bool_dec (b: bool) :
  dec b.
Proof.
  unfold dec. destruct b; cbn; auto.
Qed.

Instance True_dec :
  dec True.
Proof.
  unfold dec; tauto.
Qed.

Instance False_dec :
  dec False.
Proof.
  unfold dec; tauto.
Qed.

Instance impl_dec (X Y : Prop) :
  dec X -> dec Y -> dec (X -> Y).
Proof.
  unfold dec; tauto.
Qed.

Instance and_dec (X Y : Prop) :
  dec X -> dec Y -> dec (X /\ Y).
Proof.
  unfold dec; tauto.
Qed.

Instance or_dec (X Y : Prop) :
  dec X -> dec Y -> dec (X \/ Y).
Proof.
  unfold dec; tauto.
Qed.

(* Coq standard modules make "not" and "iff" opaque for type class inference,
   can be seen with Print HintDb typeclass_instances. *)

Instance not_dec (X : Prop) :
  dec X -> dec (~ X).
Proof.
  unfold not. auto.
Qed.

Instance iff_dec (X Y : Prop) :
  dec X -> dec Y -> dec (X <-> Y).
Proof.
  unfold iff. auto.
Qed.

(** **** Discrete types *)

Notation "'eq_dec' X" := (forall x y : X, dec (x=y)) (at level 70).

Structure eqType := EqType {
  eqType_X :> Type;
  eqType_dec : eq_dec eqType_X }.

Arguments EqType X {_} : rename.

Canonical Structure eqType_CS X (A: eq_dec X) := EqType X.

Existing Instance eqType_dec.

Instance unit_eq_dec :
  eq_dec unit.
Proof.
  unfold dec. decide equality.
Qed.

Instance bool_eq_dec :
  eq_dec bool.
Proof.
  unfold dec. decide equality.
Defined.

Instance nat_eq_dec :
  eq_dec nat.
Proof.
  unfold dec. decide equality.
Defined.

Instance prod_eq_dec X Y :
  eq_dec X -> eq_dec Y -> eq_dec (X * Y).
Proof.
  unfold dec. decide equality.
Defined.

Instance list_eq_dec X :
  eq_dec X -> eq_dec (list X).
Proof.
  unfold dec. decide equality.
Defined.

Instance sum_eq_dec X Y :
  eq_dec X -> eq_dec Y -> eq_dec (X + Y).
Proof.
  unfold dec. decide equality.
Defined.

Instance option_eq_dec X :
  eq_dec X -> eq_dec (option X).
Proof.
  unfold dec. decide equality.
Defined.

Instance Empty_set_eq_dec:
  eq_dec Empty_set.
Proof.
  unfold dec. decide equality.
Qed.

Instance True_eq_dec:
  eq_dec True.
Proof.
  intros x y. destruct x,y. now left.
Qed.

Instance False_eq_dec:
  eq_dec False.
Proof.
  intros [].
Qed.

Export ListNotations.
Notation "x 'el' A" := (In x A) (at level 70).
Notation "A <<= B" := (incl A B) (at level 70).
Notation "| A |" := (length A) (at level 65).
Definition equi X (A B : list X) : Prop := incl A B /\ incl B A.
Notation "A === B" := (equi A B) (at level 70).
Hint Unfold equi.

Hint Extern 4 =>
match goal with
|[ H: ?x el nil |- _ ] => destruct H
end.

(** **** Lists *)

(* Register additional simplification rules with autorewrite / simpl_list *)
(* Print Rewrite HintDb list. *)
Hint Rewrite <- app_assoc : list.
Hint Rewrite rev_app_distr map_app prod_length : list.

Lemma list_cycle  (X : Type) (A : list X) x :
  x::A <> A.
Proof.
  intros B.
  assert (C: |x::A| <> |A|) by (cbn; omega).
  apply C. now rewrite B.
Qed.

(** ***** Decisions for lists *)

Instance list_in_dec X (x : X) (A : list X) :
  eq_dec X -> dec (x el A).
Proof.
  intros D. apply in_dec. exact D.
Qed.

(* Certifying find *)

Lemma cfind X A (p: X -> Prop) (p_dec: forall x, dec (p x)) :
  {x | x el A /\ p x} + {forall x, x el A -> ~ p x}.
Proof.
  destruct (find (fun x => Dec (p x)) A) eqn:E.
  - apply find_some in E. firstorder.
  - right. intros. eapply find_none in E; eauto.
Qed.

Arguments cfind {X} A p {p_dec}.

Instance list_forall_dec X A (p : X -> Prop) :
  (forall x, dec (p x)) -> dec (forall x, x el A -> p x).
Proof.
  intros p_dec.
  destruct (find (fun x => Dec (~ p x)) A) eqn:Eq.
  - apply find_some in Eq as [H1 H0 %Dec_true]; right; auto.
  - left. intros x E. apply find_none with (x := x) in Eq. apply dec_DN; auto. auto.
Qed.

Instance list_exists_dec X A (p : X -> Prop) :
  (forall x, dec (p x)) -> dec (exists x, x el A /\ p x).
Proof.
  intros p_dec.
  destruct (find (fun x => Dec (p x)) A) eqn:Eq. (* New: eta expansion needed *)
  - apply find_some in Eq as [H0 H1 %Dec_true]. firstorder. (* New: Need firstorder here *)
  - right. intros [x [E F]]. apply find_none with (x := x) in Eq; auto. eauto. (* New: Why can't auto solve this? *)
Qed.

Lemma list_exists_DM X A  (p : X -> Prop) :
  (forall x, dec (p x)) ->
  ~ (forall x, x el A -> ~ p x) -> exists x, x el A /\ p x.
Proof.
  intros D E.
  destruct (find (fun x => Dec (p x)) A) eqn:Eq.
  + apply find_some in Eq as [? ?%Dec_true]. eauto.
  + exfalso. apply E. intros. apply find_none with (x := x) in Eq;  eauto.
Qed.

Lemma list_exists_not_incl (X: eqType) (A B : list X) :
  ~ A <<= B -> exists x, x el A /\ ~ x el B.
Proof.
  intros E.
  apply list_exists_DM; auto.
  intros F. apply E. intros x G.
  apply dec_DN; auto.
Qed.

Lemma list_cc X (p : X -> Prop) A :
  (forall x, dec (p x)) ->
  (exists x, x el A /\ p x) -> {x | x el A /\ p x}.
Proof.
  intros D E.
  destruct (cfind A p) as [[x [F G]]|F].
  - eauto.
  - exfalso. destruct E as [x [G H]]. apply (F x); auto.
Qed.

(** ***** Membership

We use the following lemmas from Coq's standard library List.
- [in_eq :  x el x::A]
- [in_nil :  ~ x el nil]
- [in_cons :  x el A -> x el y::A]
- [in_or_app :  x el A \/ x el B -> x el A++B]
- [in_app_iff :  x el A++B <-> x el A \/ x el B]
- [in_map_iff :  y el map f A <-> exists x, f x = y /\ x el A]
*)

Hint Resolve in_eq in_nil in_cons in_or_app.

Section Membership.
  Variable X : Type.
  Implicit Types (x y: X) (A B: list X).

  Lemma in_sing x y :
    x el [y] -> x = y.
  Proof.
    cbn. intros [[]|[]]. reflexivity.
  Qed.

  Lemma in_cons_neq x y A :
    x el y::A -> x <> y -> x el A.
  Proof.
    cbn. intros [[]|D] E; congruence.
  Qed.

  Lemma not_in_cons x y A :
    ~ x el y :: A -> x <> y /\ ~ x el A.
  Proof.
    intuition; subst; auto.
  Qed.

(** ***** Disjointness *)

  Definition disjoint A B :=
    ~ exists x, x el A /\ x el B.

  Lemma disjoint_forall A B :
    disjoint A B <-> forall x, x el A -> ~ x el B.
  Proof.
    split.
    - intros D x E F. apply D. exists x. auto.
    - intros D [x [E F]]. exact (D x E F).
  Qed.

  Lemma disjoint_symm A B :
    disjoint A B -> disjoint B A.
  Proof.
    firstorder.
  Qed.

  Lemma disjoint_incl A B B' :
    B' <<= B -> disjoint A B -> disjoint A B'.
  Proof.
    firstorder.
  Qed.

  Lemma disjoint_nil B :
    disjoint nil B.
  Proof.
    firstorder.
  Qed.

  Lemma disjoint_nil' A :
    disjoint A nil.
  Proof.
    firstorder.
  Qed.

  Lemma disjoint_cons x A B :
    disjoint (x::A) B <-> ~ x el B /\ disjoint A B.
  Proof.
    split.
    - intros D. split.
      + intros E. apply D. eauto.
      + intros [y [E F]]. apply D. eauto.
    - intros [D E] [y [[F|F] G]].
      + congruence.
      + apply E. eauto.
  Qed.

  Lemma disjoint_app A B C :
    disjoint (A ++ B) C <-> disjoint A C /\ disjoint B C.
  Proof.
    split.
    - intros D. split.
      + intros [x [E F]]. eauto 6.
      + intros [x [E F]]. eauto 6.
    - intros [D E] [x [F G]].
      apply in_app_iff in F as [F|F]; eauto.
  Qed.

End Membership.

Hint Resolve disjoint_nil disjoint_nil'.

(** ***** Inclusion

We use the following lemmas from Coq's standard library List.
- [incl_refl :  A <<= A]
- [incl_tl :  A <<= B -> A <<= x::B]
- [incl_cons : x el B -> A <<= B -> x::A <<= B]
- [incl_appl : A <<= B -> A <<= B++C]
- [incl_appr : A <<= C -> A <<= B++C]
- [incl_app : A <<= C -> B <<= C -> A++B <<= C]
*)

Hint Resolve incl_refl incl_tl incl_cons incl_appl incl_appr incl_app.

Hint Resolve incl_nil.

Lemma incl_map X Y A B (f : X -> Y) :
  A <<= B -> map f A <<= map f B.

Proof.
  intros D y E. apply in_map_iff in E as [x [E E']].
  subst y. apply in_map_iff. eauto.
Qed.

Section Inclusion.
  Variable X : Type.
  Implicit Types A B : list X.

  Lemma incl_nil_eq A :
    A <<= nil -> A=nil.

  Proof.
    intros D. destruct A as [|x A].
    - reflexivity.
    - exfalso. apply (D x). auto.
  Qed.

  Lemma incl_shift x A B :
    A <<= B -> x::A <<= x::B.

  Proof. auto. Qed.

  Lemma incl_lcons x A B :
    x::A <<= B <-> x el B /\ A <<= B.
  Proof.
    split.
    - intros D. split; hnf; auto.
    - intros [D E] z [F|F]; subst; auto.
  Qed.

  Lemma incl_rcons x A B :
    A <<= x::B -> ~ x el A -> A <<= B.

  Proof. intros C D y E. destruct (C y E) as [F|F]; congruence. Qed.

  Lemma incl_lrcons x A B :
    x::A <<= x::B -> ~ x el A -> A <<= B.

  Proof.
    intros C D y E.
    assert (F: y el x::B) by auto.
    destruct F as [F|F]; congruence.
  Qed.

  Lemma incl_app_left A B C :
    A ++ B <<= C -> A <<= C /\ B <<= C.
  Proof.
    firstorder.
  Qed.

End Inclusion.

Definition inclp (X : Type) (A : list X) (p : X -> Prop) : Prop :=
  forall x, x el A -> p x.

(** ***** Setoid rewriting with list inclusion and list equivalence *)

Instance incl_preorder X :
  PreOrder (@incl X).
Proof.
  constructor; hnf; unfold incl; auto.
Qed.

Instance equi_Equivalence X :
  Equivalence (@equi X).
Proof.
  constructor; hnf; firstorder.
Qed.

Instance incl_equi_proper X :
  Proper (@equi X ==> @equi X ==> iff) (@incl X).
Proof.
  hnf. intros A B D. hnf. firstorder.
Qed.

Instance cons_incl_proper X x :
  Proper (@incl X ==> @incl X) (@cons X x).
Proof.
  hnf. apply incl_shift.
Qed.

Instance cons_equi_proper X x :
  Proper (@equi X ==> @equi X) (@cons X x).
Proof.
  hnf. firstorder.
Qed.

Instance in_incl_proper X x :
  Proper (@incl X ==> Basics.impl) (@In X x).
Proof.
  intros A B D. hnf. auto.
Qed.

Instance in_equi_proper X x :
  Proper (@equi X ==> iff) (@In X x).
Proof.
  intros A B D. firstorder.
Qed.

Instance app_incl_proper X :
  Proper (@incl X ==> @incl X ==> @incl X) (@app X).
Proof.
  intros A B D A' B' E. auto.
Qed.

Instance app_equi_proper X :
  Proper (@equi X ==> @equi X ==> @equi X) (@app X).
Proof.
  hnf. intros A B D. hnf. intros A' B' E.
  destruct D, E; auto.
Qed.

Section Equi.
  Variable X : Type.
  Implicit Types A B : list X.

  Lemma equi_push x A :
    x el A -> A === x::A.
  Proof.
    auto.
  Qed.

  Lemma equi_dup x A :
    x::A === x::x::A.

  Proof.
    auto.
  Qed.

  Lemma equi_swap x y A:
    x::y::A === y::x::A.
  Proof.
    split; intros z; cbn; tauto.
  Qed.

  Lemma equi_shift x A B :
    x::A++B === A++x::B.
  Proof.
    split; intros y.
    - intros [D|D].
      + subst; auto.
      + apply in_app_iff in D as [D|D]; auto.
    - intros D. apply in_app_iff in D as [D|D].
      + auto.
      + destruct D; subst; auto.
  Qed.

  Lemma equi_rotate x A :
    x::A === A++[x].
  Proof.
    split; intros y; cbn.
    - intros [D|D]; subst; auto.
    - intros D. apply in_app_iff in D as [D|D].
      + auto.
      + apply in_sing in D. auto.
  Qed.

End Equi.

Lemma in_concat_iff A l (a:A) : a el concat l <-> exists l', a el l' /\ l' el l.
Proof.
  induction l; cbn.
  - intuition. now destruct H.
  - rewrite in_app_iff, IHl. firstorder subst. auto. (* TODO: find something faster than firstorder subst *)
Qed.

(** ***** Filter *)

Section Filter.
  Variable X : Type.
  Implicit Types (x y: X) (A B C: list X) (p q: X -> bool).

  Fixpoint filter p A : list X :=
    match A with
    | nil => nil
    | x::A' => if p x then x :: filter p A' else filter p A'
    end.

  Lemma in_filter_iff x p A :
    x el filter p A <-> x el A /\ p x.
  Proof.
    induction A as [|y A]; cbn.
    - tauto.
    - destruct (p y) eqn:E; cbn;
      rewrite IHA; intuition; subst; auto.
  Qed.

  Lemma filter_incl p A :
    filter p A <<= A.
  Proof.
    intros x D. apply in_filter_iff in D. apply D.
  Qed.

  Lemma filter_mono p A B :
    A <<= B -> filter p A <<= filter p B.
  Proof.
    intros D x E. apply in_filter_iff in E as [E E'].
    apply in_filter_iff. auto.
  Qed.

  Lemma filter_id p A :
    (forall x, x el A -> p x) -> filter p A = A.
  Proof.
    intros D.
    induction A as [|x A]; cbn.
    - reflexivity.
    - destruct (p x) eqn:E.
      + f_equal; auto.
      + exfalso. apply bool_Prop_false in E. auto.
  Qed.

  Lemma filter_app p A B :
    filter p (A ++ B) = filter p A ++ filter p B.
  Proof.
    induction A as [|y A]; cbn.
    - reflexivity.
    - rewrite IHA. destruct (p y); reflexivity.
  Qed.

  Lemma filter_fst p x A :
    p x -> filter p (x::A) = x::filter p A.
  Proof.
    cbn. destruct (p x); auto.
  Qed.

  Lemma filter_fst' p x A :
    ~ p x -> filter p (x::A) = filter p A.
  Proof.
    cbn. destruct (p x); auto.
  Qed.

  Lemma filter_pq_mono p q A :
    (forall x, x el A -> p x -> q x) -> filter p A <<= filter q A.
  Proof.
    intros D x E. apply in_filter_iff in E as [E E'].
    apply in_filter_iff. auto.
  Qed.

  Lemma filter_pq_eq p q A :
    (forall x, x el A -> p x = q x) -> filter p A = filter q A.
  Proof.
    intros C; induction A as [|x A]; cbn.
    - reflexivity.
    - destruct (p x) eqn:D, (q x) eqn:E.
      + f_equal. auto.
      + exfalso. enough (p x = q x) by congruence. auto.
      + exfalso. enough (p x = q x) by congruence. auto.
      + auto.
  Qed.

  Lemma filter_and p q A :
    filter p (filter q A) = filter (fun x => p x && q x) A.
  Proof.
    induction A as [|x A]; cbn. reflexivity.
    destruct (p x) eqn:E, (q x); cbn;
      try rewrite E; now rewrite IHA.
  Qed.

  Lemma filter_comm p q A :
    filter p (filter q A) = filter q (filter p A).
  Proof.
    rewrite !filter_and. apply filter_pq_eq.
    intros x _. now destruct (p x), (q x).
  Qed.

End Filter.

(** ***** Element removal *)

Section Removal.
  Variable X : eqType.
  Implicit Types (x y: X) (A B: list X).

  Definition rem A x : list X :=
    filter (fun z => Dec (z <> x)) A.

  Lemma in_rem_iff x A y :
    x el rem A y <-> x el A /\ x <> y.
  Proof.
    unfold rem. rewrite in_filter_iff, Dec_reflect. tauto.
  Qed.

  Lemma rem_not_in x y A :
    x = y \/ ~ x el A -> ~ x el rem A y.
  Proof.
    unfold rem. rewrite in_filter_iff, Dec_reflect. tauto.
  Qed.

  Lemma rem_incl A x :
    rem A x <<= A.
  Proof.
    apply filter_incl.
  Qed.

  Lemma rem_mono A B x :
    A <<= B -> rem A x <<= rem B x.
  Proof.
    apply filter_mono.
  Qed.

  Lemma rem_cons A B x :
    A <<= B -> rem (x::A) x <<= B.
  Proof.
    intros E y F. apply E. apply in_rem_iff in F.
    destruct F as [[|]]; congruence.
  Qed.

  Lemma rem_cons' A B x y :
    x el B -> rem A y <<= B -> rem (x::A) y <<= B.
  Proof.
    intros E F u G.
    apply in_rem_iff in G as [[[]|G] H]. exact E.
    apply F. apply in_rem_iff. auto.
  Qed.

  Lemma rem_in x y A :
    x el rem A y -> x el A.
  Proof.
    apply rem_incl.
  Qed.

  Lemma rem_neq x y A :
    x <> y -> x el A -> x el rem A y.
  Proof.
    intros E F. apply in_rem_iff. auto.
  Qed.

  Lemma rem_app x A B :
    x el A -> B <<= A ++ rem B x.
  Proof.
    intros E y F. decide (x=y) as [[]|]; auto using rem_neq.
  Qed.

  Lemma rem_app' x A B C :
    rem A x <<= C -> rem B x <<= C -> rem (A ++ B) x <<= C.
  Proof.
    unfold rem; rewrite filter_app; auto.
  Qed.

  Lemma rem_equi x A :
    x::A === x::rem A x.
  Proof.
    split; intros y;
    intros [[]|E]; decide (x=y) as [[]|D];
    eauto using rem_in, rem_neq.
  Qed.

  Lemma rem_comm A x y :
    rem (rem A x) y = rem (rem A y) x.
  Proof.
    apply filter_comm.
  Qed.

  Lemma rem_fst x A :
    rem (x::A) x = rem A x.
  Proof.
    unfold rem. rewrite filter_fst'; auto.
  Qed.

  Lemma rem_fst' x y A :
    x <> y -> rem (x::A) y = x::rem A y.
  Proof.
    intros E. unfold rem. rewrite filter_fst; auto.
  Qed.

  Lemma rem_id x A :
    ~ x el A -> rem A x = A.
  Proof.
    intros D. apply filter_id. intros y E.
    apply Dec_reflect. congruence.
  Qed.

  Lemma rem_reorder x A :
    x el A -> A === x :: rem A x.
  Proof.
    intros D. rewrite <- rem_equi. apply equi_push, D.
  Qed.

  Lemma rem_inclr A B x :
    A <<= B -> ~ x el A -> A <<= rem B x.
  Proof.
    intros D E y F. apply in_rem_iff.
    intuition; subst; auto.
  Qed.

End Removal.

Hint Resolve rem_not_in rem_incl rem_mono rem_cons rem_cons' rem_app rem_app' rem_in rem_neq rem_inclr.

Notation "( A × B × .. × C )" := (list_prod .. (list_prod A B) .. C) (at level 0, left associativity).

Notation "[ s | p ∈ A ',' P ]" :=
  (map (fun p => s) (filter (fun p => Dec P) A)) (p pattern).
Notation "[ s | p ∈ A ]" :=
  (map (fun p => s) A) (p pattern).

Ltac in_app n :=
  (match goal with
  | [ |- _ el _ ++ _ ] =>
    match n with
    | 0 => idtac
    | 1 => eapply in_app_iff; left
    | S ?n => eapply in_app_iff; right; in_app n
    end
  | [ |- _ el _ :: _ ] => match n with 0 => idtac | 1 => left | S ?n => right; in_app n end
  end) || (repeat (try right; eapply in_app_iff; right)).

Lemma to_dec (P : Prop) `{dec P} : P <-> Dec P.
Proof.
  firstorder. destruct (Dec P); cbn in *; firstorder.
Qed.

Ltac in_collect a :=
  eapply in_map_iff; exists a; split; [ eauto | match goal with [ |- _ el filter _ _ ] =>  eapply in_filter_iff; split; [ try (rewrite !in_prod_iff; repeat split) | rewrite <- to_dec; repeat split; eauto ] | _ => try (rewrite !in_prod_iff; repeat split) end ].
Ltac inv_collect :=
  repeat
    (match goal with
    | [ H : ?x el concat _ |- _ ] => eapply in_concat_iff in H as (? & ? & ?)
    | [ H : ?x el map _ _ |- _ ] => let x := fresh "x" in eapply in_map_iff in H as (x & ? & ?)
    | [ x : ?A * ?B |- _ ] => destruct x; subst
    | [ H : ?x el filter _ _ |- _ ] => let H' := fresh "H" in eapply in_filter_iff in H as (? & H' % to_dec)
    | [ H : ?x el list_prod _ _ |- _ ] => eapply in_prod_iff in H
    | [ H : _ el _ ++ _ |- _ ] => try eapply in_app_iff in H as []
    | [H : _ el _ :: _ |- _ ] => destruct H
     end; intuition; subst).


(** * Induction: Proof by Induction *)

(** The next line imports all of our definitions from the
    previous chapter. *)

Require Export Basics.

(** For it to work, you need to use [coqc] to compile [Basics.v]
    into [Basics.vo].  (This is like making a .class file from a .java
    file, or a .o file from a .c file.)

    Here are two ways to compile your code:

     - CoqIDE:

         Open [Basics.v].
         In the "Compile" menu, click on "Compile Buffer".

     - Command line:

         Run [coqc Basics.v]

    *)

(* ###################################################################### *)
(** * Naming Cases *)

(** The fact that there is no explicit command for moving from
    one branch of a case analysis to the next can make proof scripts
    rather hard to read.  In larger proofs, with nested case analyses,
    it can even become hard to stay oriented when you're sitting with
    Coq and stepping through the proof.  (Imagine trying to remember
    that the first five subgoals belong to the inner case analysis and
    the remaining seven cases are what remains of the outer one...)
    Disciplined use of indentation and comments can help, but a better
    way is to use the [Case] tactic. *)

(* [Case] is not built into Coq: we need to define it ourselves.
    There is no need to understand how it works -- you can just skip
    over the definition to the example that follows.  It uses some
    facilities of Coq that we have not discussed -- the string
    library (just for the concrete syntax of quoted strings) and the
    [Ltac] command, which allows us to declare custom tactics.  Kudos
    to Aaron Bohannon for this nice hack! *)

Require String. Open Scope string_scope.

Ltac move_to_top x :=
  match reverse goal with
  | H : _ |- _ => try move x after H
  end.

Tactic Notation "assert_eq" ident(x) constr(v) :=
  let H := fresh in
  assert (x = v) as H by reflexivity;
  clear H.

Tactic Notation "Case_aux" ident(x) constr(name) :=
  first [
    set (x := name); move_to_top x
  | assert_eq x name; move_to_top x
  | fail 1 "because we are working on a different case" ].

Tactic Notation "Case" constr(name) := Case_aux Case name.
Tactic Notation "SCase" constr(name) := Case_aux SCase name.
Tactic Notation "SSCase" constr(name) := Case_aux SSCase name.
Tactic Notation "SSSCase" constr(name) := Case_aux SSSCase name.
Tactic Notation "SSSSCase" constr(name) := Case_aux SSSSCase name.
Tactic Notation "SSSSSCase" constr(name) := Case_aux SSSSSCase name.
Tactic Notation "SSSSSSCase" constr(name) := Case_aux SSSSSSCase name.
Tactic Notation "SSSSSSSCase" constr(name) := Case_aux SSSSSSSCase name.
(** Here's an example of how [Case] is used.  Step through the
   following proof and observe how the context changes. *)

Theorem andb_true_elim1 : forall b c : bool,
  andb b c = true -> b = true.
Proof.
  intros b c H.
  destruct b.
  Case "b = true".  (* <----- here *)
    reflexivity.
  Case "b = false".  (* <---- and here *)
    rewrite <- H.
    reflexivity.
Qed.

(** [Case] does something very straightforward: It simply adds a
    string that we choose (tagged with the identifier "Case") to the
    context for the current goal.  When subgoals are generated, this
    string is carried over into their contexts.  When the last of
    these subgoals is finally proved and the next top-level goal
    becomes active, this string will no longer appear in the context
    and we will be able to see that the case where we introduced it is
    complete.  Also, as a sanity check, if we try to execute a new
    [Case] tactic while the string left by the previous one is still
    in the context, we get a nice clear error message.

    For nested case analyses (e.g., when we want to use a [destruct]
    to solve a goal that has itself been generated by a [destruct]),
    there is an [SCase] ("subcase") tactic. *)

(** **** Exercise: 2 stars (andb_true_elim2) *)
(** Prove [andb_true_elim2], marking cases (and subcases) when
    you use [destruct]. *)

Theorem andb_true_elim2 : forall b c : bool,
  andb b c = true -> c = true.
Proof.
  intros b c H.
  destruct c.
  Case "c = true".
  reflexivity.
  Case "c = false".
  rewrite <- H.
  destruct b.
  SCase "b = false".
  reflexivity.
  reflexivity.
  Qed.

(** There are no hard and fast rules for how proofs should be
    formatted in Coq -- in particular, where lines should be broken
    and how sections of the proof should be indented to indicate their
    nested structure.  However, if the places where multiple subgoals
    are generated are marked with explicit [Case] tactics placed at
    the beginning of lines, then the proof will be readable almost no
    matter what choices are made about other aspects of layout.

    This is a good place to mention one other piece of (possibly
    obvious) advice about line lengths.  Beginning Coq users sometimes
    tend to the extremes, either writing each tactic on its own line
    or entire proofs on one line.  Good style lies somewhere in the
    middle.  In particular, one reasonable convention is to limit
    yourself to 80-character lines.  Lines longer than this are hard
    to read and can be inconvenient to display and print.  Many
    editors have features that help enforce this. *)

(* ###################################################################### *)
(** * Proof by Induction *)

(** We proved in the last chapter that [0] is a neutral element
    for [+] on the left using a simple argument.  The fact that it is
    also a neutral element on the _right_... *)

Theorem plus_0_r_firsttry : forall n:nat,
  n + 0 = n.

(** ... cannot be proved in the same simple way.  Just applying
  [reflexivity] doesn't work: the [n] in [n + 0] is an arbitrary
  unknown number, so the [match] in the definition of [+] can't be
  simplified.  *)

Proof.
  intros n.
  simpl. (* Does nothing! *)
Abort.

(** And reasoning by cases using [destruct n] doesn't get us much
   further: the branch of the case analysis where we assume [n = 0]
   goes through, but in the branch where [n = S n'] for some [n'] we
   get stuck in exactly the same way.  We could use [destruct n'] to
   get one step further, but since [n] can be arbitrarily large, if we
   try to keep on like this we'll never be done. *)

Theorem plus_0_r_secondtry : forall n:nat,
  n + 0 = n.
Proof.
  intros n. destruct n as [| n'].
  Case "n = 0".
    reflexivity. (* so far so good... *)
  Case "n = S n'".
    simpl.       (* ...but here we are stuck again *)
Abort.

(** To prove such facts -- indeed, to prove most interesting
    facts about numbers, lists, and other inductively defined sets --
    we need a more powerful reasoning principle: _induction_.

    Recall (from high school) the principle of induction over natural
    numbers: If [P(n)] is some proposition involving a natural number
    [n] and we want to show that P holds for _all_ numbers [n], we can
    reason like this:
         - show that [P(O)] holds;
         - show that, for any [n'], if [P(n')] holds, then so does
           [P(S n')];
         - conclude that [P(n)] holds for all [n].

    In Coq, the steps are the same but the order is backwards: we
    begin with the goal of proving [P(n)] for all [n] and break it
    down (by applying the [induction] tactic) into two separate
    subgoals: first showing [P(O)] and then showing [P(n') -> P(S
    n')].  Here's how this works for the theorem we are trying to
    prove at the moment: *)

Theorem plus_0_r : forall n:nat, n + 0 = n.
Proof.
  intros n. induction n as [| n'].
  Case "n = 0".     reflexivity.
  Case "n = S n'".  simpl. rewrite -> IHn'. reflexivity.  Qed.

(** Like [destruct], the [induction] tactic takes an [as...]
    clause that specifies the names of the variables to be introduced
    in the subgoals.  In the first branch, [n] is replaced by [0] and
    the goal becomes [0 + 0 = 0], which follows by simplification.  In
    the second, [n] is replaced by [S n'] and the assumption [n' + 0 =
    n'] is added to the context (with the name [IHn'], i.e., the
    Induction Hypothesis for [n']).  The goal in this case becomes [(S
    n') + 0 = S n'], which simplifies to [S (n' + 0) = S n'], which in
    turn follows from the induction hypothesis. *)

Theorem minus_diag : forall n,
  minus n n = 0.
Proof.
  (* WORKED IN CLASS *)
  intros n. induction n as [| n'].
  Case "n = 0".
    simpl. reflexivity.
  Case "n = S n'".
    simpl. rewrite -> IHn'. reflexivity.  Qed.

(** **** Exercise: 2 stars (basic_induction) *)

(** Prove the following lemmas using induction. You might need
    previously proven results. *)

Theorem mult_0_r : forall n:nat,
  n * 0 = 0.
Proof.
  intros n. induction n as [|n'].
  Case "n' = 0".
  reflexivity.
  Case "n' = S n'".
  simpl. rewrite -> IHn'.
  reflexivity. Qed.

Theorem plus_n_Sm : forall n m : nat,
  S (n + m) = n + (S m).
Proof.
  intros n m.
  induction n as [|n'].
  Case "n = 0".
  simpl. reflexivity.
  Case "n = S n'".
  simpl. rewrite -> IHn'. reflexivity.
  Qed.

Theorem plus_comm : forall n m : nat,
  n + m = m + n.
Proof.
  intros n m.
  induction n as [|n'].
  Case "n = 0".
  simpl. rewrite -> plus_0_r. reflexivity.
  Case "n = S n'".
  simpl. rewrite -> IHn'. rewrite -> plus_n_Sm.
  reflexivity. Qed.

Theorem plus_assoc : forall n m p : nat,
  n + (m + p) = (n + m) + p.
Proof.
  intros n m p.
  induction n as [|n'].
  Case "n = 0".
  simpl. reflexivity.
  Case "n = S n'".
  simpl. rewrite -> IHn'. reflexivity.
  Qed.

(** **** Exercise: 2 stars (double_plus) *)

(** Consider the following function, which doubles its argument: *)

Fixpoint double (n:nat) :=
  match n with
  | O => O
  | S n' => S (S (double n'))
  end.

(** Use induction to prove this simple fact about [double]: *)

Lemma double_plus : forall n, double n = n + n .
Proof.
  intros n.
  induction n as [|n'].
  Case "n = 0".
  simpl. reflexivity.
  Case "n = S n'".
  simpl. rewrite -> IHn'. rewrite -> plus_n_Sm.
  reflexivity. Qed.

(** **** Exercise: 1 star (destruct_induction) *)
(** Briefly explain the difference between the tactics
    [destruct] and [induction].

(* destruct performs case analysis, where as induction also provides us with
   an induction hypothesis that we can use for rewriting.*)

*)
(** [] *)

(* ###################################################################### *)
(** * Proofs Within Proofs *)

(** In Coq, as in informal mathematics, large proofs are very
    often broken into a sequence of theorems, with later proofs
    referring to earlier theorems.  Occasionally, however, a proof
    will need some miscellaneous fact that is too trivial (and of too
    little general interest) to bother giving it its own top-level
    name.  In such cases, it is convenient to be able to simply state
    and prove the needed "sub-theorem" right at the point where it is
    used.  The [assert] tactic allows us to do this.  For example, our
    earlier proof of the [mult_0_plus] theorem referred to a previous
    theorem named [plus_O_n].  We can also use [assert] to state and
    prove [plus_O_n] in-line: *)

Theorem mult_0_plus' : forall n m : nat,
  (0 + n) * m = n * m.
Proof.
  intros n m.
  assert (H: 0 + n = n).
    Case "Proof of assertion". reflexivity.
  rewrite -> H.
  reflexivity.  Qed.

(** The [assert] tactic introduces two sub-goals.  The first is
    the assertion itself; by prefixing it with [H:] we name the
    assertion [H].  (Note that we could also name the assertion with
    [as] just as we did above with [destruct] and [induction], i.e.,
    [assert (0 + n = n) as H].  Also note that we mark the proof of
    this assertion with a [Case], both for readability and so that,
    when using Coq interactively, we can see when we're finished
    proving the assertion by observing when the ["Proof of assertion"]
    string disappears from the context.)  The second goal is the same
    as the one at the point where we invoke [assert], except that, in
    the context, we have the assumption [H] that [0 + n = n].  That
    is, [assert] generates one subgoal where we must prove the
    asserted fact and a second subgoal where we can use the asserted
    fact to make progress on whatever we were trying to prove in the
    first place. *)

(** Actually, [assert] will turn out to be handy in many sorts of
    situations.  For example, suppose we want to prove that [(n + m)
    + (p + q) = (m + n) + (p + q)]. The only difference between the
    two sides of the [=] is that the arguments [m] and [n] to the
    first inner [+] are swapped, so it seems we should be able to
    use the commutativity of addition ([plus_comm]) to rewrite one
    into the other.  However, the [rewrite] tactic is a little stupid
    about _where_ it applies the rewrite.  There are three uses of
    [+] here, and it turns out that doing [rewrite -> plus_comm]
    will affect only the _outer_ one. *)

Theorem plus_rearrange_firsttry : forall n m p q : nat,
  (n + m) + (p + q) = (m + n) + (p + q).
Proof.
  intros n m p q.
  (* We just need to swap (n + m) for (m + n)...
     it seems like plus_comm should do the trick! *)
  rewrite -> plus_comm.
  (* Doesn't work...Coq rewrote the wrong plus! *)
Abort.

(** To get [plus_comm] to apply at the point where we want it, we can
    introduce a local lemma stating that [n + m = m + n] (for
    the particular [m] and [n] that we are talking about here), prove
    this lemma using [plus_comm], and then use this lemma to do the
    desired rewrite. *)

Theorem plus_rearrange : forall n m p q : nat,
  (n + m) + (p + q) = (m + n) + (p + q).
Proof.
  intros n m p q.
  assert (H: n + m = m + n).
    Case "Proof of assertion".
    rewrite -> plus_comm. reflexivity.
  rewrite -> H. reflexivity.  Qed.

(** **** Exercise: 4 stars (mult_comm) *)
(** Use [assert] to help prove this theorem.  You shouldn't need to
    use induction. *)

Theorem plus_swap : forall n m p : nat,
  n + (m + p) = m + (n + p).
Proof.
  intros n m p.
  assert(H : m + (n + p) = (m + n) + p).
    Case "Assertion".
    rewrite -> plus_assoc. reflexivity.
  rewrite -> H. rewrite -> plus_assoc.
  assert(H2 : n + m = m + n). rewrite -> plus_comm. reflexivity.
  rewrite -> H2. reflexivity. Qed.

(** Now prove commutativity of multiplication.  (You will probably
    need to define and prove a separate subsidiary theorem to be used
    in the proof of this one.)  You may find that [plus_swap] comes in
    handy. *)

Theorem multZero : forall n : nat, n * 0 = 0.
Proof.
  intros n.
  induction n as [|n'].
  Case "n = 0". reflexivity.
  Case "n = S n'". simpl. rewrite -> IHn'. reflexivity.
  Qed.

Lemma mult_comm_helper : forall m n : nat, m * S n = m + m * n.
Proof.
  intros m n.
  induction m as [| m'].
  Case "m = 0". reflexivity.
  Case "m = S m'".
  simpl. rewrite -> IHm'.
  rewrite -> plus_swap. reflexivity.
  Qed.

Theorem mult_comm : forall m n : nat,
 m * n = n * m.
Proof.
  intros m n.
  induction m as [ | m'].
  Case "m = 0". simpl. rewrite -> multZero. reflexivity.
  Case "m = S m'". simpl.
  rewrite -> mult_comm_helper. rewrite -> IHm'.
  reflexivity.
  Qed.

(** **** Exercise: 2 stars, optional (evenb_n__oddb_Sn) *)
(** Prove the following simple fact: *)
(*TODO: discuss this in class*)
Theorem evenb_n_oddb_Sn : forall n : nat,
  evenb n = negb (evenb (S n)).
Proof.
  intros n.
  induction n as [|n'].
  Case "n = 0". reflexivity.
  Case "n = S n'".
  assert(H:evenb(S(S(n'))) = evenb n'). reflexivity.
  rewrite -> H.  rewrite -> IHn'.
  assert(H2: forall x, negb(negb(x)) = x).
  intros x. destruct x as []. reflexivity. reflexivity.
  rewrite -> H2. reflexivity.
  Qed.

(* ###################################################################### *)
(** * More Exercises *)

(** **** Exercise: 3 stars, optional (more_exercises) *)
(** Take a piece of paper.  For each of the following theorems, first
    _think_ about whether (a) it can be proved using only
    simplification and rewriting, (b) it also requires case
    analysis ([destruct]), or (c) it also requires induction.  Write
    down your prediction.  Then fill in the proof.  (There is no need
    to turn in your piece of paper; this is just to encourage you to
    reflect before hacking!) *)

Theorem ble_nat_refl : forall n:nat,
  true = ble_nat n n.
Proof.
  intros n.
  induction n as [|n']. reflexivity.
  simpl. rewrite <- IHn'. reflexivity.
  Qed.

Theorem zero_nbeq_S : forall n:nat,
  beq_nat 0 (S n) = false.
Proof.
  intros n. simpl. reflexivity. Qed.

Theorem andb_false_r : forall b : bool,
  andb b false = false.
Proof.
  intros b.
  destruct b as [|b']. reflexivity.
  reflexivity. Qed.

Theorem plus_ble_compat_l : forall n m p : nat,
  ble_nat n m = true -> ble_nat (p + n) (p + m) = true.
Proof.
  intros n m p H.
  induction p as [|p'].
  Case "p = 0". simpl. rewrite -> H. reflexivity.
  Case "p = S p'".
  simpl. rewrite -> IHp'. reflexivity.
  Qed.

Theorem S_nbeq_0 : forall n:nat,
  beq_nat (S n) 0 = false.
Proof.
  intros n.
  simpl. reflexivity. Qed.

Theorem mult_1_l : forall n:nat, 1 * n = n.
Proof.
  intros n. simpl. rewrite -> plus_0_r.
  reflexivity. Qed.

Theorem all3_spec : forall b c : bool,
    orb
      (andb b c)
      (orb (negb b)
               (negb c))
  = true.
Proof.
  intros b c.
  destruct b as [|b'].
    destruct c as [|c'].
    simpl. reflexivity.
    simpl. reflexivity.
    simpl. reflexivity.
  Qed.

Theorem mult_plus_distr_r : forall n m p : nat,
  (n + m) * p = (n * p) + (m * p).
Proof.
  intros n m p.
  induction n as [|n'].
  simpl. reflexivity.
  simpl. rewrite -> IHn'. rewrite -> plus_assoc.
  reflexivity.
  Qed.

Theorem mult_assoc : forall n m p : nat,
  n * (m * p) = (n * m) * p.
Proof.
  intros n m p.
  induction n as [|n'].
  simpl. reflexivity.
  simpl. rewrite -> IHn'.
  rewrite -> mult_plus_distr_r.
  reflexivity.
  Qed.

(** **** Exercise: 2 stars, optional (beq_nat_refl) *)
(** Prove the following theorem.  Putting [true] on the left-hand side
of the equality may seem odd, but this is how the theorem is stated in
the standard library, so we follow suit.  Since rewriting
works equally well in either direction, we will have no
problem using the theorem no matter which way we state it. *)

Theorem beq_nat_refl : forall n : nat,
  true = beq_nat n n.
Proof.
  intros n.
  induction n as [|n']. reflexivity.
  simpl. rewrite -> IHn'. reflexivity.
  Qed.

(** **** Exercise: 2 stars, optional (plus_swap') *)
(** The [replace] tactic allows you to specify a particular subterm to
   rewrite and what you want it rewritten to.  More precisely,
   [replace (t) with (u)] replaces (all copies of) expression [t] in
   the goal by expression [u], and generates [t = u] as an additional
   subgoal. This is often useful when a plain [rewrite] acts on the wrong
   part of the goal.

   Use the [replace] tactic to do a proof of [plus_swap'], just like
   [plus_swap] but without needing [assert (n + m = m + n)].
*)

Theorem plus_swap' : forall n m p : nat,
  n + (m + p) = m + (n + p).
Proof.
  intros n m p.
  replace (m + (n + p)) with ((m + n) + p).
  rewrite -> plus_assoc.
  replace (n+m) with (m+n). reflexivity.
  rewrite -> plus_comm. reflexivity.
  rewrite -> plus_assoc. reflexivity.
  Qed.

(** **** Exercise: 3 stars (binary_commute) *)
(** Recall the [increment] and [binary-to-unary] functions that you
    wrote for the [binary] exercise in the [Basics] chapter.  Prove
    that these functions commute -- that is, incrementing a binary
    number and then converting it to unary yields the same result as
    first converting it to unary and then incrementing.

    (Before you start working on this exercise, please copy the
    definitions from your solution to the [binary] exercise here so
    that this file can be graded on its own.  If you find yourself
    wanting to change your original definitions to make the property
    easier to prove, feel free to do so.) *)

Inductive bin : Type :=
  | BinO : bin
  | Odd : bin -> bin
  | Even : bin -> bin.
(** [] *)

Fixpoint inc (n : bin) : bin :=
  match n with
      |BinO => Odd BinO
      |Even n => Odd n
      |Odd n => Even(inc n)
  end.

Fixpoint binToUnary(n : bin) : nat :=
  match n with
      |BinO => 0
      |Even n => 2 * (binToUnary n)
      |Odd n => 1 + 2 * binToUnary n
  end.
Lemma succXPlusOne : forall x : nat, S x = x + 1.
Proof.
  intros x. rewrite -> plus_comm. simpl.
  reflexivity.
  Qed.

(*TODO: discuss this in class*)
Theorem BinToUnaryCorrect : forall n : bin, binToUnary(inc n) = binToUnary(n) + 1.
Proof.
  intros n.
  induction n as [|n' | n'].
  Case "n = 0".
  simpl. reflexivity.
  Case "n = Even n'".
  simpl. rewrite -> plus_n_Sm. rewrite -> plus_0_r. rewrite -> plus_0_r.
  rewrite -> IHn'. rewrite -> plus_swap. rewrite -> plus_assoc. rewrite -> plus_assoc.
  assert(H: forall x, x + 1 + 1 = x + 2).
  intros x. rewrite -> plus_comm. rewrite -> plus_swap. simpl. reflexivity.
  rewrite -> H. reflexivity.
  simpl. rewrite -> plus_n_Sm. rewrite -> plus_0_r.
  rewrite -> succXPlusOne. rewrite -> plus_assoc. reflexivity.
  Qed.

(** **** Exercise: 5 stars, advanced (binary_inverse) *)
(** This exercise is a continuation of the previous exercise about
    binary numbers.  You will need your definitions and theorems from
    the previous exercise to complete this one.

    (a) First, write a function to convert natural numbers to binary
        numbers.  Then prove that starting with any natural number,
        converting to binary, then converting back yields the same
        natural number you started with.

    (b) You might naturally think that we should also prove the
        opposite direction: that starting with a binary number,
        converting to a natural, and then back to binary yields the
        same number we started with.  However, it is not true!
        Explain what the problem is.

    (c) Define a function [normalize] from binary numbers to binary
        numbers such that for any binary number b, converting to a
        natural and then back to binary yields [(normalize b)].  Prove
        it.

    Again, feel free to change your earlier definitions if this helps
    here.
*)

Fixpoint natToBin(n : nat) : bin :=
match n with
    |O => BinO
    |S n' => inc(natToBin n')
end.

Theorem natToBinCorrect : forall n : nat, binToUnary(natToBin n) = n.
Proof.
  intros n.
  induction n as [|n'].
  Case "n = 0". reflexivity.
  Case "n = S n'". simpl. rewrite -> BinToUnaryCorrect. rewrite -> IHn'. rewrite -> plus_comm.
  reflexivity.
  Qed.

Fixpoint normalize (b : bin) : bin :=
match b with
    |BinO => BinO
    |Even(b') => match normalize b' with
                     |BinO => BinO
                     |n => Even(Even n)
                 end
    |Odd(b') => Odd(normalize b')
end.

Theorem L : forall n, natToBin(n + n) = normalize(Even (natToBin n)).
Proof.
  intros. induction n as [|n'].
  simpl. reflexivity.
  simpl.  destruct (normalize(inc(natToBin n'))).

Theorem binToNatCorrect : forall b : bin, natToBin(binToUnary b) = normalize b.
Proof.
  intros b.
  induction b as [|n|n].
  reflexivity.
  Case "b = Odd n". simpl. rewrite -> plus_0_r.
  rewrite -> L. simpl. rewrite -> IHn. reflexivity.
  Case "b = Odd n". simpl. rewrite -> plus_0_r.
  rewrite -> L. rewrite -> IHn. reflexivity.
  Qed.

(*
Theorem NPlusNEven : forall n, natToBin(n + n) = Even (natToBin n).
Proof.
  intros. induction n as [|n'].
  Admitted.

Theorem binToNatCorrect : forall b : bin, natToBin(binToUnary b) = b.
Proof.
  intros b.
  induction b as [|n|n].
  reflexivity.
  Case "b = Odd n". simpl. rewrite -> plus_0_r.
  rewrite -> NPlusNEven. simpl. rewrite -> IHn. reflexivity.
  Case "b = Odd n". simpl. rewrite -> plus_0_r.
  rewrite -> NPlusNEven. rewrite -> IHn. reflexivity.
  Qed.*)

(* ###################################################################### *)
(** * Advanced Material *)

(** ** Formal vs. Informal Proof *)

(** "Informal proofs are algorithms; formal proofs are code." *)

(** The question of what, exactly, constitutes a "proof" of a
    mathematical claim has challenged philosophers for millenia.  A
    rough and ready definition, though, could be this: a proof of a
    mathematical proposition [P] is a written (or spoken) text that
    instills in the reader or hearer the certainty that [P] is true.
    That is, a proof is an act of communication.

    Now, acts of communication may involve different sorts of readers.
    On one hand, the "reader" can be a program like Coq, in which case
    the "belief" that is instilled is a simple mechanical check that
    [P] can be derived from a certain set of formal logical rules, and
    the proof is a recipe that guides the program in performing this
    check.  Such recipes are _formal_ proofs.

    Alternatively, the reader can be a human being, in which case the
    proof will be written in English or some other natural language,
    thus necessarily _informal_.  Here, the criteria for success are
    less clearly specified.  A "good" proof is one that makes the
    reader believe [P].  But the same proof may be read by many
    different readers, some of whom may be convinced by a particular
    way of phrasing the argument, while others may not be.  One reader
    may be particularly pedantic, inexperienced, or just plain
    thick-headed; the only way to convince them will be to make the
    argument in painstaking detail.  But another reader, more familiar
    in the area, may find all this detail so overwhelming that they
    lose the overall thread.  All they want is to be told the main
    ideas, because it is easier to fill in the details for themselves.
    Ultimately, there is no universal standard, because there is no
    single way of writing an informal proof that is guaranteed to
    convince every conceivable reader.  In practice, however,
    mathematicians have developed a rich set of conventions and idioms
    for writing about complex mathematical objects that, within a
    certain community, make communication fairly reliable.  The
    conventions of this stylized form of communication give a fairly
    clear standard for judging proofs good or bad.

    Because we are using Coq in this course, we will be working
    heavily with formal proofs.  But this doesn't mean we can ignore
    the informal ones!  Formal proofs are useful in many ways, but
    they are _not_ very efficient ways of communicating ideas between
    human beings. *)

(** For example, here is a proof that addition is associative: *)

Theorem plus_assoc' : forall n m p : nat,
  n + (m + p) = (n + m) + p.
Proof. intros n m p. induction n as [| n']. reflexivity.
  simpl. rewrite -> IHn'. reflexivity.  Qed.

(** Coq is perfectly happy with this as a proof.  For a human,
    however, it is difficult to make much sense of it.  If you're used
    to Coq you can probably step through the tactics one after the
    other in your mind and imagine the state of the context and goal
    stack at each point, but if the proof were even a little bit more
    complicated this would be next to impossible.  Instead, a
    mathematician might write it something like this: *)
(** - _Theorem_: For any [n], [m] and [p],
      n + (m + p) = (n + m) + p.
    _Proof_: By induction on [n].

    - First, suppose [n = 0].  We must show
        0 + (m + p) = (0 + m) + p.
      This follows directly from the definition of [+].

    - Next, suppose [n = S n'], where
        n' + (m + p) = (n' + m) + p.
      We must show
        (S n') + (m + p) = ((S n') + m) + p.
      By the definition of [+], this follows from
        S (n' + (m + p)) = S ((n' + m) + p),
      which is immediate from the induction hypothesis. [] *)

(** The overall form of the proof is basically similar.  This is
    no accident: Coq has been designed so that its [induction] tactic
    generates the same sub-goals, in the same order, as the bullet
    points that a mathematician would write.  But there are
    significant differences of detail: the formal proof is much more
    explicit in some ways (e.g., the use of [reflexivity]) but much
    less explicit in others (in particular, the "proof state" at any
    given point in the Coq proof is completely implicit, whereas the
    informal proof reminds the reader several times where things
    stand). *)

(** Here is a formal proof that shows the structure more
    clearly: *)

Theorem plus_assoc'' : forall n m p : nat,
  n + (m + p) = (n + m) + p.
Proof.
  intros n m p. induction n as [| n'].
  Case "n = 0".
    reflexivity.
  Case "n = S n'".
    simpl. rewrite -> IHn'. reflexivity.   Qed.

(** **** Exercise: 2 stars, advanced (plus_comm_informal) *)
(** Translate your solution for [plus_comm] into an informal proof. *)

(** Theorem: Addition is commutative.

    Proof: Proof by induction on n:
    Case (n = 0): We have "0 + m = m + 0", which simplifies to "m = m"
    Case (n = n' + 1): We have "(n' + 1) + m = m + (n' + 1)"
    By the definition of plus, we have "S(n' + m) = m + S n'"
    By the induction hypothesis, we have "S(m + n') = m + S n'"
    By Theorem plus_n_Sm, we have "m + S n' = m + S n'"

[]
*)

(** **** Exercise: 2 stars, optional (beq_nat_refl_informal) *)
(** Write an informal proof of the following theorem, using the
    informal proof of [plus_assoc] as a model.  Don't just
    paraphrase the Coq tactics into English!

    Theorem: [true = beq_nat n n] for any [n].

    Proof: (* FILL IN HERE *)
[]
 *)

(* $Date: 2013-07-17 16:19:11 -0400 (Wed, 17 Jul 2013) $ *)


(*|
########################################################
How to import the library ``Coq.Arith.PeanoNat`` in Coq?
########################################################

:Link: https://stackoverflow.com/q/36621752
|*)

(*|
Question
********

I need to use the part of the standard library called
``Coq.Arith.PeanoNat``
(https://coq.inria.fr/library/Coq.Arith.PeanoNat.html).

I've tried either importing the entire Arith library or just this
module, but I can't use it either way.

Every other library I've tried works just fine. When I do ``Require
Import Bool.`` I compile and I can use it correctly. Upon ``Print
Bool.`` I can take a look at all the functions inside in the next
format:

.. code-block::

    Module
    Bool
    := Struct
    Definition...
    .
    .
    .
    End

When I do either ``Require Import Arith.PeanoNat.`` or ``Require
Import Arith.`` I get this as immediate output:

.. code-block::

    [Loading ML file z_syntax_plugin.cmxs ... done]
    [Loading ML file quote_plugin.cmxs ... done]
    [Loading ML file newring_plugin.cmxs ... done]
    <W> Grammar extension: in [tactic:simple_tactic], some rule has been masked
    <W> Grammar extension: in [tactic:simple_tactic], some rule has been masked
    <W> Grammar extension: in [tactic:simple_tactic], some rule has been masked
    <W> Grammar extension: in [tactic:simple_tactic], some rule has been masked
    <W> Grammar extension: in [tactic:simple_tactic], some rule has been masked

When I ask Coq ``Print Arith.PeanoNat`` it outputs: ``Module Arith :=
Struct End``, it seems to be empty. When I try to use anything from
the library, for example ``le_le`` under boolean comparisons, I get
the standard ``Error: leb_le not a defined object.`` I have updated
Coq and the libraries, and I have no idea of what might be going on
here. I'd appreciate your input in fixing this library problem.
|*)

(*|
Answer
******

If I am not mistaken, ``Require`` is the keyword to load a file.
``Import`` has to do with managing name spaces. Often they are used
together, as in ``Require Import PeanoNat.``, but they are really
doing two different things.

When coq files (``DirName/FileName.vo``) are loaded with ``Require``,
it is as if the contents of ``FileName.vo`` is wrapped in ``Module
DirName.FileName ... End.`` Everyting defined in the file is then
accessed with ``DirName.FileName.Name``.

The file can itself have modules ``M`` inside of it, and to get to
``M``'s contents, one has to type
``DirName.FileName.ModuleName.Name1`` etc.

``Import`` is used to get all the definitions up to the top level. By
doing ``Import DirName.FileName.ModuleName`` the module ``Name1`` is
now imported to the top level, and can be referenced without the long
path.

In your example above, the file ``Arith/PeanoNat.vo`` defines the
module ``Nat``. Actually, that is all it defines. So if you do
``Require Import Arith.PeanoNat`` you get ``PeanoNat.Nat`` at the top
level. And then ``Import PeanoNat.Nat`` will bring ``Nat`` to the top
level. Note that you can't do ``Require Import PeanoNat.Nat`` because
it is no ``.vo`` file.

Coq can sometimes find a ``.vo`` file without you having to specify
the whole path, so you can also do ``Require Import PeanoNat.`` and
coq will find the file. If you wonder where it found it, do ``Locate
PeanoNat``.
|*)

Require Import PeanoNat.
Locate PeanoNat. (* .unfold *)

(*|
Another ``Nat`` is also available from another place than
``PeanoNat``.
|*)

Reset Initial. (* .none *)
Require Import Nat.
Locate Nat. (* .unfold *)

(*|
So, you don't ``Import`` a library, you ``Require`` it. You use
``Import`` to not have to use the full path name. I hope this helps
you debug what is happening.
|*)


(** **** Exercise: 3 stars, standard (binary)

 Après la représentation unaire des nombres naturels, nous proposons
 une représentation Binaire plus efficace en traitant un nombre Binaire
 comme une séquence de constructeurs [zero] et [un] (représentant 0 et 1),
 terminée par un [zeroFinal] ou un [unFinal]. En comparaison, dans la
 représentation unaire, un nombre est une séquence de [S] terminée par un [O].

        décimal             binaire                         unaire
           0                   0                              O
           1                   1                            S O
           2                (0 1)                        S (S O)
           3                (1 1)                     S (S (S O))
           4              0 (0 1)                  S (S (S (S O)))
           5              1 (0 1)               S (S (S (S (S O))))
           6              0 (1 1)            S (S (S (S (S (S O)))))
           7              1 (1 1)         S (S (S (S (S (S (S O))))))
           8           0 (0 (0 1))     S (S (S (S (S (S (S (S O)))))))

  Notez que le bit d'ordre inférieur est à gauche et le bit d'ordre supérieur
  est à droite -- à l'opposé de la façon dont les nombres binaires
  sont habituellement écrits. Ce choix facilite leur manipulation.

  Enfin, il peut exister plusieurs représentations binaires d'un entier naturel :
  il suffit d'ajouter des zéros à droite.
**)

(* Préambule *)
(* Quelques lemmes et définitions utiles, certaines donnant lieu à des tactiques standards.
 * Ces tactiques standards traitent de l'absurdité et de l'égalité. Elle sont à connaître :
 * - exfalso,
 * - f_equal
 * - discriminate.
 * On s'aperçoit que toutes ces tactiques peuvent s'exprimer en utilisant la tactique primitive
 * case, et quelques lemmes pratiques (appliqués avec apply ou refine).
 *)

(* Arguments implicites.
 * Il est possible de déclarer des paramètres entre accolades au lieu de parenthèses.
 * Ces paramètres donnent des arguments implicites : Coq les devine par lui-même.
 * Si on veut utiliser tous les arguments d'une fonction f, écrire l'appel ainsi :
 * - (@f args).
 *)

(* Lors de définitions par cas, il arrive que certains cas soient absurdes.
   On utilise alors la fonction casImpossible. *)
Definition casImpossible{T : Type}(H : False) : T.
  case H. (* voir le principe récursif False_rect. *)
Defined.

Print False_ind.
Print False_rect.

Print casImpossible.

(* alternative utilisant exfalso et False_rect. *)
Definition casImpossible'{T : Type}(H : False) : T.
    exfalso.
    exact H.
Defined.

Print casImpossible'.

(* Cette notation permet de masquer la preuve de l'absurdité (du Faux). *)
Notation "'Impossible'" := (casImpossible _) : type_scope.

(* Très souvent, l'absurdité vient d'une égalité impossible
 * entre deux termes de structures différentes. *)

Ltac egaliteImpossible eg := (apply casImpossible; discriminate eg).

(* discriminate est une tactique élaborée. Elle utilise la préservation
 * de l'égalité par les fonctions pour déduire le Faux, à partir d'une égalité impossible.
 * La préservation de l'égalité est exploitée par la tactique f_equal.
 *)

Lemma preservationFonctionnelleEgalite :
  forall {A B : Type}{F : A -> B},
  forall x y, x = y -> F x = F y.
Proof.
  intros A B F x y eg.
  case eg.
  reflexivity.
Qed.

(* Plutôt que "case eg", on peut utiliser "rewrite eg".
 *  rewrite est une tactique plus élaborée fondée sur l'égalité et sa décomposition via case.
 *)
Lemma preservationFonctionnelleEgalite' :
  forall {A B : Type}{F : A -> B},
  forall x y, x = y -> F x = F y.
Proof.
  intros A B F x y eg.
  (* rewrite eg. (* équivalent à case (eq_sym eg)
                    (où eq_sym est le lemme de symmétrie / égalité *)
   *)
  rewrite <- eg. (* équivalent à case eg *)
  reflexivity.
Qed.

(* Tactique f_equal := apply preservationFonctionnelleEgalite *)

Ltac egaliteFctn := apply preservationFonctionnelleEgalite.

(* Vers la discrimination :
 * si x = y, (P x) et non (P y), alors on a une contradiction.
 * On utilise la préservation de l'égalité par un prédicat (une fonction vers Prop).
 *)

Lemma discrimination :
  forall {A : Type},
  forall x y,
    x = y -> (exists (P : A -> Prop), (P x) /\ (P y -> False)) -> False.
Proof.
  intros A x y eg.
  case eg.
  intro H.
  case H as [P [Px abs]].
  apply (abs Px).
Qed.

(* Tactique pour discriminer une égalité fausse :
 * - appliquer discrimination
 * - définir (P n) par : si n vaut x alors True sinon False.
 * Le test relativement à x se fait par filtrage, sans utiliser l'égalité.
 * eg : x = y
 * discriminant :=
 * (fun n => match n with (filtrage étendu)
           | x => True (x à remplacer par la valeur)
           | _ => False
           end)
 *)
Ltac discriminer eg discriminant :=
 apply (discrimination _ _ eg);
 simple refine (ex_intro _ _ _);
 [ exact discriminant
 | simpl; tauto].

Example disc_5_6 : 5 = 6 -> False.
intro eg.
discriminer eg (fun n => match n with
                         | 5 => True
                         | _ => False
                         end).
Qed.

Print disc_5_6. (* Noter que le filtrage est plus élémentaire que dans l'écriture au-dessus.
                 * Il décrit tous les sous-termes de 5.
                 *)

(* discriminate produit automatiquement le discriminant. *)

Example disc_5_6' : 5 = 6 -> False.
intro eg.
discriminate eg.
Qed.

Print disc_5_6'. (* discriminate a effectivement produit automatiquement le discriminant. *)

(* Fin du préambule *)

(* Définition permettant d'obtenir une correspondance bi-univoque *)
Inductive SorteBinaire : Type
  := T1 | N.

(* N ::= 0 | T1 (N = forme normale)
   T1 ::= 1 | 0.T1 | 1 T1 (T1 = se termine par un 1)
 *)
Inductive Binaire : SorteBinaire -> Type :=
  | zeroFinal : Binaire N
  | unFinal : Binaire T1
  | double : Binaire T1 -> Binaire T1
  | successeurDouble : Binaire T1 -> Binaire T1
  | formeNormale : Binaire T1 -> Binaire N.

(** nat comme Binaire forment des algèbres initiales.
  * - nat : signature Nat := (0 : nat), (S : nat -> nat)
  * - Binaire : signature Bin avec deux sortes N et T1
  * - cinq opérations := (OF : Binaire N), (1F : Binaire T1),
  *    (double : Binaire T1 -> Binaire T1), (successeurDouble: Binaire T1 -> Binaire T1),
  *    (fn : Binaire T1 -> Binaire N)
  *
  * Propriété importante des algèbres initiales :
  - il existe un unique morphisme d'une algèbre initiale vers une algèbre sur la même signature.
  *
  * Une algèbre est donnée par des ensembles (un par sorte) et des fonctions sur ces ensembles
  * (une par opération de la sorte, en respectant le type).
  *
  * L'algèbre initiale est formée des termes, soit les arbres formés à partir des opérations,
  * en respectant le typage.
  *
  * Signature Bin
  * On interprète N par nat et T1 par nat* (nat - 0). Comme nat* est isomorphe à nat,
  * on simplifie : on interprète T1 par nat.
  * (nat/N, nat/T1) est une algèbre sur Bin.
  * Signature Nat
  * Binaire N est une algèbre sur Nat, Binaire T1 est une algèbre sur Nat.
  *
  * La propriété d'initialité permet de relier nat et Binaire.
  *  - si (nat/N, nat/T1) est une algèbre sur Bin,
  *    alors il existe un unique morphisme BN := (BN N, BN T1)
  *    sur Bin de Binaire vers (nat/N, nat/T1).
  *  - si (Binaire s) est une algèbre sur Nat,
  *    alors il existe un unique morphisme (NB s) sur Nat de nat vers (Binaire s).
  *
  * Pour montrer qu'il s'agit d'isomorphismes, il suffit de montrer que
  * - chaque composante (BN s) est aussi un morphisme sur Nat de (Binaire s) vers nat/s,
  * - (NB N, NB T1) est aussi un morphisme sur Bin de (nat/N, nat/T1) vers Bin.
  * NB o BN est alors un morphisme sur Bin de Binaire vers Binaire :
  * par unicité, il est égal à l'identité.
  * De même (BN s) o (NB s) est un morphisme sur Nat de nat vers nat :
  * par unicité, il est égal à l'identité.
*)

Local Notation "'BO'" := zeroFinal : type_scope.
Local Notation "'I'" := (unFinal) : type_scope.
Local Notation "'O,' x" := (double x) (at level 30, right associativity) : type_scope.
Local Notation "'I,' x" := (successeurDouble x) (at level 30, right associativity) : type_scope.
Local Notation "'B' x" := (formeNormale x) (at level 30, right associativity) : type_scope.

Check (B O,I,O,I).
Check (O,I,O,I).
Check (BO).

(* On cherche à définir les opérations de la signature Bin dans (nat/N, nat/T1)
 * et celles de la signature Nat dans (Binaire s) (s : sorte).
 * Rappel
 * Comme il y a deux sortes, on associe deux ensembles, côté nat :
 * - sorte N : nat
 * - sorte T1 : nat pour représenter nat* (nat - 0) (0 pour représenter 1, 1 pour représenter 2, etc.)
 * Attention : bien penser au décalage dans nat/T1.
 *)

(* Cette fonction correspond à la notation nat/s, utilisée en commentaire. *)
Definition natBinaire(e : SorteBinaire) : Type := nat.

(* natBinaire comme algèbre sur B *)

Definition zeroFinalNat : natBinaire N := 0.

Definition unFinalNat : natBinaire T1 := 0. (* Rappel : décalage
                                             * car représentation de T1 par nat au lieu de nat* *)

(* Fonction qui à n associe (n + 1) * 2 - 1.
 *   (à cause du décalage)
 * Pour simplifier les calculs, il est préférable d'utiliser seulement
 * la fonction S (successeur).
 *)
Definition doubleNat : natBinaire T1 -> natBinaire T1. (* +1 -> * 2 -> -1 *)
  intro n.
  induction n as [ | n'].
  - exact 1.
  - exact (S (S IHn')).
Defined.

(* Fonction qui à n associe (n + 1) * 2.
 *   (à cause du décalage)
 * Pour simplifier les calculs, il est préférable d'utiliser seulement
 * la fonction S (successeur).
 *)
Definition successeurDoubleNat(n : natBinaire T1) : natBinaire T1. (* -> +1 -> *2  *)
  exact (S (doubleNat n)).
Defined.

Definition formeNormaleNat(n : natBinaire T1) : natBinaire N := S n.  (* Rappel : décalage *)

(* (Binaire s) comme algèbre sur Nat *)

(* Interprétation de zéro dans (Binaire N) et (Binaire T1).
 * Prise en compte du décalge.
 *)
Definition zeroBinaire(s : SorteBinaire) : Binaire s.
  case s.
  - exact unFinal.
  - exact zeroFinal.
Defined.

(* interprétation de successeur dans (Binaire N) et (Binaire T1).
 * prise en compte du décalge.
 *)
Fixpoint successeurBinaire {e : SorteBinaire}(b : Binaire e) : Binaire e.
  case b as [ | | sb | sb | bn ].
  - exact (formeNormale (unFinal)).
  - exact (double unFinal).
  - exact (successeurDouble sb).
  - exact (double (successeurBinaire T1 sb)).
  - exact (formeNormale (successeurBinaire T1 bn)).
Defined.
(* Ou avec les notations :
  case b as [ | | sb | sb | bn].
  - exact (B I).
  - exact (O,I).
  - exact (I, sb).
  - exact (O, (successeurBin _ sb)).
  - exact (B (successeurBin _ bn)).
 *)

(** Conversion de [natBinaire] vers [Binaire] *)
Fixpoint valeurNatEnBinaire(s : SorteBinaire)(n : natBinaire s) : Binaire s.
  case n as [ | n'].
  - exact (zeroBinaire s).
  - exact (successeurBinaire (valeurNatEnBinaire _ n')).
Defined.

(* La conversion est un morphisme d'algèbre sur Nat.
 * Par définition - simple vérification.
 *)
Lemma morphismeNat_valeurNatEnBinaire :
  forall s,
  valeurNatEnBinaire s 0 = zeroBinaire s
  /\
  forall n,
    valeurNatEnBinaire s (S n) = successeurBinaire (valeurNatEnBinaire s n).
Proof.
  split; reflexivity.
Qed.

(* La conversion valeurNatEnBinaire est aussi un morphisme d'algèbre sur Bin.
 * Cinq lemmes, un par opération de la signature.
 *)
Lemma morphismeZeroFinal_valeurNatEnBinaire :
  valeurNatEnBinaire _ zeroFinalNat  = zeroFinal.
Proof.
  reflexivity.
Qed.

Lemma morphismeUnFinal_valeurNatEnBinaire :
  valeurNatEnBinaire _ unFinalNat  = unFinal.
Proof.
  reflexivity.
Qed.

Lemma morphismeDouble_valeurNatEnBinaire :
  forall n : natBinaire T1,
    valeurNatEnBinaire _ (doubleNat n)  = double (valeurNatEnBinaire _ n).
Proof.
  induction n.
  - simpl. reflexivity.
  - simpl. rewrite IHn. simpl. reflexivity.
Qed.

Lemma morphismeSuccesseurDouble_valeurNatEnBinaire :
  forall n : natBinaire T1,
    valeurNatEnBinaire _ (successeurDoubleNat n)  = successeurDouble (valeurNatEnBinaire _ n).
Proof.
  intro n. simpl. rewrite morphismeDouble_valeurNatEnBinaire.
  reflexivity.
Qed.

Lemma morphismeFormeNormale_valeurNatEnBinaire :
  forall n : natBinaire T1,
    valeurNatEnBinaire _ (formeNormaleNat n)  = formeNormale (valeurNatEnBinaire _ n).
Proof.
  induction n.
  - simpl. reflexivity.
  - simpl. simpl in IHn. rewrite IHn.
    simpl. reflexivity.
Qed.

(** Conversion de [Binaire] vers [natBinaire]
 *)

Fixpoint valeurBinaireEnNat{s : SorteBinaire}(b : Binaire s) : natBinaire s.
  case b as [ | | b' | b' | b'].
  - exact zeroFinalNat.
  - exact unFinalNat.
  - exact (doubleNat (valeurBinaireEnNat _ b')).
  - exact (successeurDoubleNat (valeurBinaireEnNat _ b')).
  - exact (formeNormaleNat (valeurBinaireEnNat _ b')).
Defined.

Compute valeurBinaireEnNat (B O,I,O,I).
(* Le paramètre s entre accolades est implicite.
   Il est déterminé automatiquement à partir du type de b. *)

(* La conversion est un morphisme d'algèbre sur Bin.
 * Par définition - simple vérification
 *)
Lemma morphismeBin_valeurBinaireEnNat :
  valeurBinaireEnNat zeroFinal  = zeroFinalNat
  /\
  valeurBinaireEnNat unFinal  = unFinalNat
  /\
  (forall b : Binaire T1,
    valeurBinaireEnNat (double b) = doubleNat (valeurBinaireEnNat b))
  /\
  (forall b : Binaire T1,
    valeurBinaireEnNat (successeurDouble b) = successeurDoubleNat (valeurBinaireEnNat b))
  /\
  forall b : Binaire T1,
    valeurBinaireEnNat (formeNormale b) = formeNormaleNat (valeurBinaireEnNat b).
Proof.
  repeat split; reflexivity.
Qed.

(* Chaque composante de la conversion est aussi un morphisme d'algèbre sur Nat.
 * Deux lemmes, un par opération.
 *)
Lemma morphismeZero_valeurBinaireEnNat :
  forall s,
      valeurBinaireEnNat (zeroBinaire s)  = 0.
Proof.
  intro s. case s; reflexivity.
Qed.

Lemma morphismeSuccesseur_valeurBinaireEnNat :
  forall s, forall b : Binaire s,
      valeurBinaireEnNat (successeurBinaire b)  = S (valeurBinaireEnNat b).
Proof.
  induction b as [ | | b' | b' | b'].
  - reflexivity.
  - reflexivity.
  - simpl. reflexivity.
  - simpl. rewrite IHb'. reflexivity.
  - simpl. rewrite IHb'. reflexivity.
Qed.

(* Les isomorphismes *)

(** (valeurBinaireEnNat s) o (valeurNatEnBinaire s) : natBinaire s -> natBinaire s
 * est un morphisme d'algèbre sur N.
 * Par initialité, c'est l'identité.
 * Démonstration directe ci-dessous.
 *)
Proposition isomorphisme_natBinNat :
  forall s, forall n : natBinaire s, valeurBinaireEnNat (valeurNatEnBinaire s n) = n.
Proof.
  induction n.
  - simpl. rewrite morphismeZero_valeurBinaireEnNat. reflexivity.
  - simpl. rewrite morphismeSuccesseur_valeurBinaireEnNat. rewrite IHn. reflexivity.
Qed.

(** valeurNatEnBinaire o valeurBinaireEnNat : Binaire  -> Binaire
 * est un morphisme d'algèbre sur B.
 * Par initialité, c'est l'identité.
 * Démonstration directe ci-dessous.
 *)
Proposition isomorphisme_binNatBin :
  forall e, forall b : Binaire e,  (valeurNatEnBinaire e (valeurBinaireEnNat b)) = b.
Proof.
  induction b as [ | | b' | b' | b'].
  - simpl. reflexivity.
  - simpl. reflexivity.
  - simpl. rewrite morphismeDouble_valeurNatEnBinaire. rewrite IHb'. reflexivity.
  - simpl. rewrite morphismeDouble_valeurNatEnBinaire. rewrite IHb'. simpl. reflexivity.
  - simpl valeurBinaireEnNat. rewrite morphismeFormeNormale_valeurNatEnBinaire. rewrite IHb'. reflexivity.
Qed.

(* tests *)

Compute (valeurNatEnBinaire N 17).
Compute (valeurBinaireEnNat (B I,I,O,I)).
Compute (valeurBinaireEnNat (I,I,O,I)).
Compute (valeurNatEnBinaire N (valeurBinaireEnNat (B I,I,O,I))).

(* TODO - Quelques exemples à faire *)

(*
 * On montre que Binaire N est un ensemble quotient de SuiteBinaire (cf. le TP Binaire).
 * projection : SuiteBinaire -> Binaire N := forme normale
 * représentant : Binaire N -> SuiteBinaire := plongement
 *)

Inductive SuiteBinaire : Type :=
  | dernierZero : SuiteBinaire
  | dernierUn : SuiteBinaire
  | zero : forall n : SuiteBinaire, SuiteBinaire
  | un : forall n : SuiteBinaire, SuiteBinaire.

Definition representant : forall {s}, (Binaire s) -> SuiteBinaire.
  fix REC 2.
  intros s b.
  case b as [ | | sb | sb | bn].
  - exact dernierZero.
  - exact dernierUn.
  - exact (zero (REC _ sb)).
  - exact (un (REC _ sb)).
  - exact (REC _ bn).
Defined.

(* Pour faciliter l'inversion, on définit la version paramétrée de Bianire. *)
Inductive BinaireP(s : SorteBinaire) : Type :=
  | zeroFinalP : N = s -> BinaireP s
  | unFinalP : T1 = s -> BinaireP s
  | doubleP : T1 = s -> BinaireP T1 -> BinaireP s
  | successeurDoubleP : T1 = s -> BinaireP T1 -> BinaireP s
  | formeNormaleP : N = s -> BinaireP T1 -> BinaireP s.

(* On définit les morphismes entre les deux versions, indexée et paramétrée respectivement. *)
Definition binaireP :
  forall {s}, Binaire s -> BinaireP s.
  fix REC 2.
  intros s b.
  case b as [ | | sb | sb | bn].
  - exact (zeroFinalP N eq_refl).
  - exact (unFinalP T1 eq_refl).
  - exact (doubleP T1 eq_refl (REC _ sb)).
  - exact (successeurDoubleP T1 eq_refl (REC _ sb)).
  - exact (formeNormaleP N eq_refl (REC _ bn)).
Defined.

Print binaireP.

Definition binaireI :
  forall {s}, BinaireP s -> Binaire s.
  fix REC 2.
  intros s b.
  case b as [ eg | eg| eg sb | eg sb | eg bn]; case eg.
  - exact zeroFinal.
  - exact unFinal.
  - exact (double (REC _ sb)).
  - exact (successeurDouble (REC _ sb)).
  - exact (formeNormale (REC _ bn)).
Defined.

Print binaireI.

(* Ces morphismes sont des isomorphismes *)
Lemma isomorphisme_binaireIPI :
  forall s, forall b : Binaire s,
      binaireI (binaireP b) = b.
  (* TODO *)
  admit.
Admitted.

Lemma isomorphisme_binairePIP :
  forall s, forall b : BinaireP s,
      binaireP (binaireI b) = b.
  (* TODO *)
  admit.
Admitted.

(* La projection réalise le calcul de la fome normale, sans zéro inutile à droite.
 * Noter la conversion en BinaireP. Sans celle-ci, l'inversion échoue.
 *)
Definition projection : SuiteBinaire -> Binaire N.
  fix REC 1.
  intro b.
  case b as [ | | sb | sb].
  - exact BO.
  - exact (B I).
  - case (binaireP (REC sb)) as [ eg| eg | eg sx | eg sx | eg nx];
      try (egaliteImpossible eg).
    + exact BO.
    + pose (y := nx).
      case y as [ eg1| eg1 | eg1 sx1 | eg1 sx1 | eg1 nx1];
        try (egaliteImpossible eg1); exact (B O, (binaireI nx)).
  - case (binaireP (REC sb)) as [ eg| eg | eg sx | eg sx | eg nx];
      try (egaliteImpossible eg).
    + exact (B I).
    + pose (y := nx).
      case y as [ eg1| eg1 | eg1 sx1 | eg1 sx1 | eg1 nx1];
        try (egaliteImpossible eg1); exact (B I, (binaireI nx)).
Defined.

Print projection.

(* La fonction de normalisation est l'identité ou formeNormale, suivant la sorte. *)
Definition normalisation : forall {s}, Binaire s -> Binaire N.
  intro s.
  case s.
  - intro b.
    exact (formeNormale b).
  - intro b. exact b.
Defined.

Print normalisation.

(* Les deux propositions suivantes établissent la propriété fondamentale d'un ensemble quotient.
 * Le premier lemme utilise la version paramétrée pour permettre une inversion sans perte
 * d'informations.
 *)
Lemma quotient_BinaireP :
  forall e, forall b : BinaireP e,
    projection (representant (binaireI b)) = normalisation (binaireI b).
Proof.
  (* TODO *)
  admit.
Admitted.

Proposition quotient_Binaire :
  forall s, forall b : Binaire s,
    projection (representant b) = normalisation b.
  (* TODO *)
  admit.
Admitted.

Print quotient_Binaire.

(* On déduit la propriété souhaitée pour la sorte N. *)
Corollary quotient_BinaireN :
  forall b : Binaire N,
    projection (representant b) = b.
Proof.
  (* TODO *)
  admit.
Admitted.