(** * Prop: Propositions and Evidence *)

Require Export Logic.

(* ####################################################### *)
(** * Inductively Defined Propositions *)

(** In chapter [Basics] we defined a _function_ [evenb] that tests a
    number for evenness, yielding [true] if so.  We can use this
    function to define the _proposition_ that some number [n] is
    even: *)

Definition even (n:nat) : Prop :=
  evenb n = true.

(** That is, we can define "[n] is even" to mean "the function [evenb]
    returns [true] when applied to [n]."

    Note that here we have given a name
    to a proposition using a [Definition], just as we have
    given names to expressions of other sorts. This isn't a fundamentally
    new kind of proposition;  it is still just an equality. *)

(** Another alternative is to define the concept of evenness
    directly.  Instead of going via the [evenb] function ("a number is
    even if a certain computation yields [true]"), we can say what the
    concept of evenness means by giving two different ways of
    presenting _evidence_ that a number is even. *)

Inductive ev : nat -> Prop :=
  | ev_0 : ev O
  | ev_SS : forall n:nat, ev n -> ev (S (S n)).

(** The first line declares that [ev] is a proposition -- or,
    more formally, a family of propositions "indexed by" natural
    numbers.  (That is, for each number [n], the claim that "[n] is
    even" is a proposition.)  Such a family of propositions is
    often called a _property_ of numbers.

    The last two lines declare the two ways to give evidence that a
    number [m] is even.  First, [0] is even, and [ev_0] is evidence
    for this.  Second, if [m = S (S n)] for some [n] and we can give
    evidence [e] that [n] is even, then [m] is also even, and [ev_SS n
    e] is the evidence.
*)

(** **** Exercise: 1 star (double_even)  *)

Theorem double_even : forall n,
  ev (double n).
Proof.
  intros.
  induction n as [|n'].
  Case "n = 0".
    apply ev_0.
  Case "n = S n'".
    simpl. apply ev_SS. apply IHn'.
Qed.

(** [] *)

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

(** For [ev], we had already defined [even] as a function (returning a
   boolean), and then defined an inductive relation that agreed with
   it. However, we don't necessarily need to think about propositions
   first as boolean functions, we can start off with the inductive
   definition.
*)

(** As another example of an inductively defined proposition, let's
    define a simple property of natural numbers -- we'll call it
    "[beautiful]." *)

(** Informally, a number is [beautiful] if it is [0], [3], [5], or the
    sum of two [beautiful] numbers.

    More pedantically, we can define [beautiful] numbers by giving four
    rules:

       - Rule [b_0]: The number [0] is [beautiful].
       - Rule [b_3]: The number [3] is [beautiful].
       - Rule [b_5]: The number [5] is [beautiful].
       - Rule [b_sum]: If [n] and [m] are both [beautiful], then so is
         their sum. *)

(** We will see many definitions like this one during the rest
    of the course, and for purposes of informal discussions, it is
    helpful to have a lightweight notation that makes them easy to
    read and write.  _Inference rules_ are one such notation: *)
(**
                              -----------                               (b_0)
                              beautiful 0

                              ------------                              (b_3)
                              beautiful 3

                              ------------                              (b_5)
                              beautiful 5

                       beautiful n     beautiful m
                       ---------------------------                      (b_sum)
                              beautiful (n+m)
*)

(** *** *)
(** Each of the textual rules above is reformatted here as an
    inference rule; the intended reading is that, if the _premises_
    above the line all hold, then the _conclusion_ below the line
    follows.  For example, the rule [b_sum] says that, if [n] and [m]
    are both [beautiful] numbers, then it follows that [n+m] is
    [beautiful] too.  If a rule has no premises above the line, then
    its conclusion holds unconditionally.

    These rules _define_ the property [beautiful].  That is, if we
    want to convince someone that some particular number is [beautiful],
    our argument must be based on these rules.  For a simple example,
    suppose we claim that the number [5] is [beautiful].  To support
    this claim, we just need to point out that rule [b_5] says so.
    Or, if we want to claim that [8] is [beautiful], we can support our
    claim by first observing that [3] and [5] are both [beautiful] (by
    rules [b_3] and [b_5]) and then pointing out that their sum, [8],
    is therefore [beautiful] by rule [b_sum].  This argument can be
    expressed graphically with the following _proof tree_: *)
(**
         ----------- (b_3)   ----------- (b_5)
         beautiful 3         beautiful 5
         ------------------------------- (b_sum)
                   beautiful 8
*)
(** *** *)
(**
    Of course, there are other ways of using these rules to argue that
    [8] is [beautiful], for instance:
         ----------- (b_5)   ----------- (b_3)
         beautiful 5         beautiful 3
         ------------------------------- (b_sum)
                   beautiful 8
*)

(** **** Exercise: 1 star (varieties_of_beauty)  *)
(** How many different ways are there to show that [8] is [beautiful]? *)

(* Infinite, because you can keep adding 0 to all the numbers involved (prolonging use of b_3 and b_5). *)
(** [] *)

(* ####################################################### *)
(** ** Constructing Evidence *)

(** In Coq, we can express the definition of [beautiful] as
    follows: *)

Inductive beautiful : nat -> Prop :=
  b_0   : beautiful 0
| b_3   : beautiful 3
| b_5   : beautiful 5
| b_sum : forall n m, beautiful n -> beautiful m -> beautiful (n+m).

(** *** *)
(**
    The rules introduced this way have the same status as proven
    theorems; that is, they are true axiomatically.
    So we can use Coq's [apply] tactic with the rule names to prove
    that particular numbers are [beautiful].  *)

Theorem three_is_beautiful: beautiful 3.
Proof.
   (* This simply follows from the rule [b_3]. *)
   apply b_3.
Qed.

Theorem eight_is_beautiful: beautiful 8.
Proof.
   (* First we use the rule [b_sum], telling Coq how to
      instantiate [n] and [m]. *)
   apply b_sum with (n:=3) (m:=5).
   (* To solve the subgoals generated by [b_sum], we must provide
      evidence of [beautiful 3] and [beautiful 5]. Fortunately we
      have rules for both. *)
   apply b_3.
   apply b_5.
Qed.

(** *** *)
(** As you would expect, we can also prove theorems that have
hypotheses about [beautiful]. *)

Theorem beautiful_plus_eight: forall n, beautiful n -> beautiful (8+n).
Proof.
  intros n B.
  apply b_sum with (n:=8) (m:=n).
  apply eight_is_beautiful.
  apply B.
Qed.

(** **** Exercise: 2 stars (b_times2)  *)
Theorem b_times2: forall n, beautiful n -> beautiful (2*n).
Proof.
  intros n B.
  simpl. rewrite plus_0_r.
  apply b_sum.
  apply B. apply B.
Qed.

(** [] *)

(** **** Exercise: 3 stars (b_timesm)  *)
Theorem b_timesm: forall n m, beautiful n -> beautiful (m*n).
Proof.
  intros n m BN.
  induction m as [|m'].
  Case "m = 0". apply b_0.
  Case "m = S m'".
    simpl.
    apply b_sum.
    SCase "n". apply BN.
    SCase "m' * n". apply IHm'.
Qed.

(** [] *)

(* ####################################################### *)
(** * Using Evidence in Proofs *)
(** ** Induction over Evidence *)

(** Besides _constructing_ evidence that numbers are beautiful, we can
    also _reason about_ such evidence. *)

(** The fact that we introduced [beautiful] with an [Inductive]
    declaration tells Coq not only that the constructors [b_0], [b_3],
    [b_5] and [b_sum] are ways to build evidence, but also that these
    four constructors are the _only_ ways to build evidence that
    numbers are beautiful. *)

(** In other words, if someone gives us evidence [E] for the assertion
    [beautiful n], then we know that [E] must have one of four shapes:

      - [E] is [b_0] (and [n] is [O]),
      - [E] is [b_3] (and [n] is [3]),
      - [E] is [b_5] (and [n] is [5]), or
      - [E] is [b_sum n1 n2 E1 E2] (and [n] is [n1+n2], where [E1] is
        evidence that [n1] is beautiful and [E2] is evidence that [n2]
        is beautiful). *)

(** *** *)
(** This permits us to _analyze_ any hypothesis of the form [beautiful
    n] to see how it was constructed, using the tactics we already
    know.  In particular, we can use the [induction] tactic that we
    have already seen for reasoning about inductively defined _data_
    to reason about inductively defined _evidence_.

    To illustrate this, let's define another property of numbers: *)

Inductive gorgeous : nat -> Prop :=
  g_0 : gorgeous 0
| g_plus3 : forall n, gorgeous n -> gorgeous (3+n)
| g_plus5 : forall n, gorgeous n -> gorgeous (5+n).

(** **** Exercise: 1 star (gorgeous_tree)  *)
(** Write out the definition of [gorgeous] numbers using inference rule
    notation.

---------- (g_0)
gorgeous 0

gorgeous n
---------------- (g_plus3)
gorgeous (3 + n)

gorgous n
---------------- (g_plus5)
gorgeous (5 + n)

[]
*)

(** **** Exercise: 1 star (gorgeous_plus13)  *)
Theorem gorgeous_plus13: forall n,
  gorgeous n -> gorgeous (13+n).
Proof.
  intros.
  apply g_plus5 with (n := 8 + n).
  apply g_plus5 with (n := 3 + n).
  apply g_plus3.
  apply H.
Qed.

(** [] *)

(** *** *)
(** It seems intuitively obvious that, although [gorgeous] and
    [beautiful] are presented using slightly different rules, they are
    actually the same property in the sense that they are true of the
    same numbers.  Indeed, we can prove this. *)

Theorem gorgeous__beautiful_FAILED : forall n,
  gorgeous n -> beautiful n.
Proof.
   intros. induction n as [| n'].
   Case "n = 0". apply b_0.
   Case "n = S n'". (* We are stuck! *)
Abort.

(** The problem here is that doing induction on [n] doesn't yield a
    useful induction hypothesis. Knowing how the property we are
    interested in behaves on the predecessor of [n] doesn't help us
    prove that it holds for [n]. Instead, we would like to be able to
    have induction hypotheses that mention other numbers, such as [n -
    3] and [n - 5]. This is given precisely by the shape of the
    constructors for [gorgeous]. *)

(** *** *)

(** Let's see what happens if we try to prove this by induction on the evidence [H]
   instead of on [n]. *)

Theorem gorgeous__beautiful : forall n,
  gorgeous n -> beautiful n.
Proof.
   intros n H.
   induction H as [|n'|n'].
   Case "g_0".
       apply b_0.
   Case "g_plus3".
       apply b_sum. apply b_3.
       apply IHgorgeous.
   Case "g_plus5".
       apply b_sum. apply b_5. apply IHgorgeous.
Qed.

(* These exercises also require the use of induction on the evidence. *)

(** **** Exercise: 2 stars (gorgeous_sum)  *)
Theorem gorgeous_sum : forall n m,
  gorgeous n -> gorgeous m -> gorgeous (n + m).
Proof.
  intros n m GN GM.
  induction GN as [| n' | n' ].
  Case "n = 0".
    induction GM as [| m' | m' ].
    SCase "m = 0". apply g_0.
    SCase "m = 3 + m'". apply g_plus3. apply IHGM.
    SCase "m = 5 + m'". apply g_plus5. apply IHGM.
  Case "n = 3 + n'".
    apply g_plus3 with (n := n' + m). apply IHGN.
  Case "n = 5 + n'".
    apply g_plus5 with (n := n' + m). apply IHGN.
Qed.

(** [] *)

(** **** Exercise: 3 stars, advanced (beautiful__gorgeous)  *)
Theorem beautiful__gorgeous : forall n, beautiful n -> gorgeous n.
Proof.
  intros n BN.
  induction BN as [ | | | n' m'].
  Case "n = 0". apply g_0.
  Case "n = 3".
    apply g_plus3. apply g_0.
  Case "n = 5".
    apply g_plus5. apply g_0.
  Case "n = n' + m'".
    apply gorgeous_sum. apply IHBN1. apply IHBN2.
Qed.

(** [] *)

(** **** Exercise: 3 stars, optional (g_times2)  *)
(** Prove the [g_times2] theorem below without using [gorgeous__beautiful].
    You might find the following helper lemma useful. *)

Lemma helper_g_times2 : forall x y z, x + (z + y) = z + x + y.
Proof.
  intros.
  rewrite plus_assoc.
  rewrite plus_comm with (n := x) (m := z).
  reflexivity.
Qed.

Theorem g_times2: forall n, gorgeous n -> gorgeous (2*n).
Proof.
   intros n H. simpl.
   rewrite -> plus_0_r.
   induction H.
   Case "n is 0". apply g_0.
   Case "n is 3 + n".
     rewrite -> helper_g_times2. rewrite -> helper_g_times2.
     apply g_plus3 with (n := 3 + n + n).
     apply g_plus3 with (n := n + n).
     apply IHgorgeous.
   Case "n is 5 + n".
     rewrite -> helper_g_times2. rewrite -> helper_g_times2.
     apply g_plus5 with (n := 5 + n + n).
     apply g_plus5 with (n := n + n).
     apply IHgorgeous.
Qed.

(** [] *)

(** Here is a proof that the inductive definition of evenness implies
the computational one. *)

Theorem ev__even : forall n,
  ev n -> even n.
Proof.
  intros n E. induction E as [| n' E'].
  Case "E = ev_0".
    unfold even. reflexivity.
  Case "E = ev_SS n' E'".
    unfold even. apply IHE'.
Qed.

(** **** Exercise: 1 star (ev__even)  *)
(** Could this proof also be carried out by induction on [n] instead
    of [E]?  If not, why not? *)

(* No, induction on n always gives an induction hypothesis in terms of
   n - 1, which is useless for proving things about evenness because the
   evenness of n - 1 implies that n isn't even! *)

(** [] *)

(** Intuitively, the induction principle [ev n] evidence [ev n] is
    similar to induction on [n], but restricts our attention to only
    those numbers for which evidence [ev n] could be generated. *)

(** **** Exercise: 1 star (l_fails)  *)
(** The following proof attempt will not succeed.
     Theorem l : forall n,
       ev n.
     Proof.
       intros n. induction n.
         Case "O". simpl. apply ev_0.
         Case "S".
           ...
   Intuitively, we expect the proof to fail because not every
   number is even. However, what exactly causes the proof to fail?

   A useless induction hypothesis.
*)
(** [] *)

(** Here's another exercise requiring induction on evidence. *)
(** **** Exercise: 2 stars (ev_sum)  *)

Theorem ev_sum : forall n m,
   ev n -> ev m -> ev (n+m).
Proof.
  intros n m EN EM.
  induction EN.
  Case "n is 0". apply EM.
  Case "n is S (S n')". simpl. apply ev_SS. apply IHEN.
Qed.
(** [] *)

(* ####################################################### *)
(** ** Inversion on Evidence *)

(** Having evidence for a proposition is useful while proving, because we
   can _look_ at that evidence for more information. For example, consider
    proving that, if [n] is even, then [pred (pred n)] is
    too.  In this case, we don't need to do an inductive proof.  Instead
    the [inversion] tactic provides all of the information that we need.

 *)

Theorem ev_minus2: forall n,  ev n -> ev (pred (pred n)).
Proof.
  intros n E.
  inversion E as [| n' E'].
  Case "E = ev_0". simpl. apply ev_0.
  Case "E = ev_SS n' E'". simpl. apply E'.  Qed.

(** **** Exercise: 1 star, optional (ev_minus2_n)  *)
(** What happens if we try to use [destruct] on [n] instead of [inversion] on [E]? *)

(* We get stuck with useless hypotheses. *)
(** [] *)

(** *** *)
(** Here is another example, in which [inversion] helps narrow down to
the relevant cases. *)

Theorem SSev__even : forall n,
  ev (S (S n)) -> ev n.
Proof.
  intros n E.
  inversion E as [| n' E'].
  apply E'. Qed.

(** ** The Inversion Tactic Revisited *)

(** These uses of [inversion] may seem a bit mysterious at first.
    Until now, we've only used [inversion] on equality
    propositions, to utilize injectivity of constructors or to
    discriminate between different constructors.  But we see here
    that [inversion] can also be applied to analyzing evidence
    for inductively defined propositions.

    (You might also expect that [destruct] would be a more suitable
    tactic to use here. Indeed, it is possible to use [destruct], but
    it often throws away useful information, and the [eqn:] qualifier
    doesn't help much in this case.)

    Here's how [inversion] works in general.  Suppose the name
    [I] refers to an assumption [P] in the current context, where
    [P] has been defined by an [Inductive] declaration.  Then,
    for each of the constructors of [P], [inversion I] generates
    a subgoal in which [I] has been replaced by the exact,
    specific conditions under which this constructor could have
    been used to prove [P].  Some of these subgoals will be
    self-contradictory; [inversion] throws these away.  The ones
    that are left represent the cases that must be proved to
    establish the original goal.

    In this particular case, the [inversion] analyzed the construction
    [ev (S (S n))], determined that this could only have been
    constructed using [ev_SS], and generated a new subgoal with the
    arguments of that constructor as new hypotheses.  (It also
    produced an auxiliary equality, which happens to be useless here.)
    We'll begin exploring this more general behavior of inversion in
    what follows. *)

(** **** Exercise: 1 star (inversion_practice)  *)
Theorem SSSSev__even : forall n,
  ev (S (S (S (S n)))) -> ev n.
Proof.
  intros.
  inversion H.
  inversion H1.
  apply H3.
Qed.

(** The [inversion] tactic can also be used to derive goals by showing
    the absurdity of a hypothesis. *)

Theorem even5_nonsense :
  ev 5 -> 2 + 2 = 9.
Proof.
  intros. inversion H. inversion H1. inversion H3.
Qed.

(** [] *)

(** **** Exercise: 3 stars, advanced (ev_ev__ev)  *)
(** Finding the appropriate thing to do induction on is a
    bit tricky here: *)

Theorem ev_ev__ev : forall n m,
  ev (n+m) -> ev n -> ev m.
Proof.
  intros n m EPLUS EN.
  induction EN.
  Case "n is 0". apply EPLUS.
  Case "n i S (S n)".
    inversion EPLUS.
    apply IHEN.
    apply H0.
Qed.

(** [] *)

(** **** Exercise: 3 stars, optional (ev_plus_plus)  *)
(** Here's an exercise that just requires applying existing lemmas.  No
    induction or even case analysis is needed, but some of the rewriting
    may be tedious. *)

Theorem ev_plus_plus : forall n m p,
  ev (n+m) -> ev (n+p) -> ev (m+p).
Proof.
  intros.
  apply ev_ev__ev with (n := n + n) (m := (m + p)).
  Case "total is even".
    apply ev_sum with (n := (n + m)) (m := (n + p)) in H.
    rewrite plus_assoc in H.
    rewrite <- plus_assoc with (n := n) (m := m) (p := n) in H.
    rewrite plus_comm with (n := m) (m := n) in H.
    rewrite plus_assoc with (n := n) (m := n) (p := m) in H.
    rewrite <- plus_assoc in H.
    apply H.
    apply H0.
  Case "n + n is even".
    rewrite <- double_plus.
    apply double_even.
Qed.

(** [] *)

(* ####################################################### *)
(** * Discussion and Variations *)
(** ** Computational vs. Inductive Definitions *)

(** We have seen that the proposition "[n] is even" can be
    phrased in two different ways -- indirectly, via a boolean testing
    function [evenb], or directly, by inductively describing what
    constitutes evidence for evenness.  These two ways of defining
    evenness are about equally easy to state and work with.  Which we
    choose is basically a question of taste.

    However, for many other properties of interest, the direct
    inductive definition is preferable, since writing a testing
    function may be awkward or even impossible.

    One such property is [beautiful].  This is a perfectly sensible
    definition of a set of numbers, but we cannot translate its
    definition directly into a Coq Fixpoint (or into a recursive
    function in any other common programming language).  We might be
    able to find a clever way of testing this property using a
    [Fixpoint] (indeed, it is not too hard to find one in this case),
    but in general this could require arbitrarily deep thinking.  In
    fact, if the property we are interested in is uncomputable, then
    we cannot define it as a [Fixpoint] no matter how hard we try,
    because Coq requires that all [Fixpoint]s correspond to
    terminating computations.

    On the other hand, writing an inductive definition of what it
    means to give evidence for the property [beautiful] is
    straightforward. *)

(* ####################################################### *)
(** ** Parameterized Data Structures *)

(** So far, we have only looked at propositions about natural numbers. However,
   we can define inductive predicates about any type of data. For example,
   suppose we would like to characterize lists of _even_ length. We can
   do that with the following definition.  *)

Inductive ev_list {X:Type} : list X -> Prop :=
  | el_nil : ev_list []
  | el_cc  : forall x y l, ev_list l -> ev_list (x :: y :: l).

(** Of course, this proposition is equivalent to just saying that the
length of the list is even. *)

Lemma ev_list__ev_length: forall X (l : list X), ev_list l -> ev (length l).
Proof.
    intros X l H. induction H.
    Case "el_nil". simpl. apply ev_0.
    Case "el_cc".  simpl.  apply ev_SS. apply IHev_list.
Qed.

(** However, because evidence for [ev] contains less information than
evidence for [ev_list], the converse direction must be stated very
carefully. *)

Lemma ev_length__ev_list: forall X n, ev n -> forall (l : list X), n = length l -> ev_list l.
Proof.
  intros X n H.
  induction H.
  Case "ev_0". intros l H. destruct l.
    SCase "[]". apply el_nil.
    SCase "x::l". inversion H.
  Case "ev_SS". intros l H2. destruct l.
    SCase "[]". inversion H2. destruct l.
    SCase "[x]". inversion H2.
    SCase "x :: x0 :: l". apply el_cc. apply IHev. inversion H2. reflexivity.
Qed.

(** **** Exercise: 4 stars (palindromes)  *)
(** A palindrome is a sequence that reads the same backwards as
    forwards.

    - Define an inductive proposition [pal] on [list X] that
      captures what it means to be a palindrome. (Hint: You'll need
      three cases.  Your definition should be based on the structure
      of the list; just having a single constructor
        c : forall l, l = rev l -> pal l
      may seem obvious, but will not work very well.)

    - Prove [pal_app_rev] that
       forall l, pal (l ++ rev l).
    - Prove [pal_rev] that
       forall l, pal l -> l = rev l.
*)

Inductive pal {X : Type} : list X -> Prop :=
  | pal_nil : pal []
  | pal_one : forall x, pal [x]
  | pal_add : forall mid x, pal mid -> pal (x :: (snoc mid x)).

Lemma pal_app_rev : forall {X : Type} (l : list X), pal (l ++ rev l).
Proof.
  intros X l.
  induction l as [|h t].
  Case "l = []".
    apply pal_nil.
  Case "l = h :: t".
    simpl.
    rewrite <- snoc_with_append.
    apply pal_add.
    apply IHt.
Qed.

Lemma pal_rev : forall {X : Type} (l : list X), pal l -> l = rev l.
Proof.
  intros.
  induction H.
  Case "pal_nil". reflexivity.
  Case "pal_one". reflexivity.
  Case "pal_add".
    simpl.
    rewrite rev_snoc.
    simpl.
    rewrite <- IHpal.
    reflexivity.
Qed.

(** [] *)

(* Again, the converse direction is much more difficult, due to the
lack of evidence. *)

(** **** Exercise: 5 stars, optional (palindrome_converse)  *)
(** Using your definition of [pal] from the previous exercise, prove
    that
     forall l, l = rev l -> pal l.
*)

(* Skipped (hard) *)

(** [] *)

(* ####################################################### *)
(** ** Relations *)

(** A proposition parameterized by a number (such as [ev] or
    [beautiful]) can be thought of as a _property_ -- i.e., it defines
    a subset of [nat], namely those numbers for which the proposition
    is provable.  In the same way, a two-argument proposition can be
    thought of as a _relation_ -- i.e., it defines a set of pairs for
    which the proposition is provable. *)

Module LeModule.

(** One useful example is the "less than or equal to"
    relation on numbers. *)

(** The following definition should be fairly intuitive.  It
    says that there are two ways to give evidence that one number is
    less than or equal to another: either observe that they are the
    same number, or give evidence that the first is less than or equal
    to the predecessor of the second. *)

Inductive le : nat -> nat -> Prop :=
  | le_n : forall n, le n n
  | le_S : forall n m, (le n m) -> (le n (S m)).

Notation "m <= n" := (le m n).

(** Proofs of facts about [<=] using the constructors [le_n] and
    [le_S] follow the same patterns as proofs about properties, like
    [ev] in chapter [Prop].  We can [apply] the constructors to prove [<=]
    goals (e.g., to show that [3<=3] or [3<=6]), and we can use
    tactics like [inversion] to extract information from [<=]
    hypotheses in the context (e.g., to prove that [(2 <= 1) -> 2+2=5].) *)

(** *** *)
(** Here are some sanity checks on the definition.  (Notice that,
    although these are the same kind of simple "unit tests" as we gave
    for the testing functions we wrote in the first few lectures, we
    must construct their proofs explicitly -- [simpl] and
    [reflexivity] don't do the job, because the proofs aren't just a
    matter of simplifying computations.) *)

Theorem test_le1 :
  3 <= 3.
Proof.
  (* WORKED IN CLASS *)
  apply le_n.  Qed.

Theorem test_le2 :
  3 <= 6.
Proof.
  (* WORKED IN CLASS *)
  apply le_S. apply le_S. apply le_S. apply le_n.  Qed.

Theorem test_le3 :
  (2 <= 1) -> 2 + 2 = 5.
Proof.
  (* WORKED IN CLASS *)
  intros H. inversion H. inversion H2.  Qed.

(** *** *)
(** The "strictly less than" relation [n < m] can now be defined
    in terms of [le]. *)

End LeModule.

Definition lt (n m:nat) := le (S n) m.

Notation "m < n" := (lt m n).

(** Here are a few more simple relations on numbers: *)

Inductive square_of : nat -> nat -> Prop :=
  sq : forall n:nat, square_of n (n * n).

Inductive next_nat : nat -> nat -> Prop :=
  | nn : forall n:nat, next_nat n (S n).

Inductive next_even : nat -> nat -> Prop :=
  | ne_1 : forall n, ev (S n) -> next_even n (S n)
  | ne_2 : forall n, ev (S (S n)) -> next_even n (S (S n)).

(** **** Exercise: 2 stars (total_relation)  *)
(** Define an inductive binary relation [total_relation] that holds
    between every pair of natural numbers. *)

Inductive total_relation (n m : nat) : Prop :=
  | total_rel : total_relation n m.

Example total_is_total : forall (n m : nat), total_relation n m.
Proof. apply total_rel. Qed.

(** [] *)

(** **** Exercise: 2 stars (empty_relation)  *)
(** Define an inductive binary relation [empty_relation] (on numbers)
    that never holds. *)

Inductive empty_relation : nat -> nat -> Prop := .

Example empty_is_empty : forall (n m : nat), ~(empty_relation n m).
Proof.
  unfold not.
  intros.
  inversion H.
Qed.

(** [] *)

(** **** Exercise: 2 stars, optional (le_exercises)  *)
(** Here are a number of facts about the [<=] and [<] relations that
    we are going to need later in the course.  The proofs make good
    practice exercises. *)

Lemma le_trans : forall m n o, m <= n -> n <= o -> m <= o.
Proof.
  intros m n o MN NO.
  induction NO as [|o' NO'].
  Case "n = o". apply MN.
  Case "n <= S o'".
    apply le_S.
    apply IHNO'.
Qed.

Theorem O_le_n : forall n,
  0 <= n.
Proof.
  intros n.
  induction n as [|n'].
  Case "n = 0". apply le_n.
  Case "n = S n'". apply le_S. apply IHn'.
Qed.

Theorem n_le_m__Sn_le_Sm : forall n m,
  n <= m -> S n <= S m.
Proof.
  intros n m NM.
  induction NM as [|m'].
  Case "n = m".
    apply le_n.
  Case "n <= S m'".
    apply le_S. apply IHNM.
Qed.

Theorem Sn_le_Sm__n_le_m : forall n m,
  S n <= S m -> n <= m.
Proof.
  intros n m SNM.
  generalize dependent n.
  induction m as [|m'].
  Case "m = 0".
    intros.
    inversion SNM.
    SCase "n = 0". apply le_n.
    SCase "S n <= 0". inversion H0.
  Case "n = S n'".
    intros.
    inversion SNM.
    SCase "n = S m'". apply le_n.
    SCase "S n <= S m'".
      apply le_S.
      apply IHm'.
      apply H0.
Qed.

Theorem le_plus_l : forall a b,
  a <= a + b.
Proof.
  intros.
  induction b as [|b'].
  Case "b = 0". rewrite plus_0_r. apply le_n.
  Case "b = S b'".
    rewrite <- plus_n_Sm.
    apply le_S. apply IHb'.
Qed.

Theorem plus_lt : forall n1 n2 m,
  n1 + n2 < m ->
  n1 < m /\ n2 < m.
Proof.
  unfold lt.
  intros.
  induction H as [|m'].
  Case "S (n1 + n2) = m".
    constructor.
    SCase "left".
      apply n_le_m__Sn_le_Sm.
      apply le_plus_l.
    SCase "right".
      apply n_le_m__Sn_le_Sm.
      rewrite plus_comm.
      apply le_plus_l.
  Case "S (n1 + n2) <= m'".
    destruct IHle.
    constructor.
    SCase "left". apply le_S. apply H0.
    SCase "right". apply le_S. apply H1.
Qed.

Theorem lt_S : forall n m,
  n < m ->
  n < S m.
Proof.
  unfold lt.
  intros.
  apply le_S.
  apply H.
Qed.

Theorem ble_nat_true : forall n m,
  ble_nat n m = true -> n <= m.
Proof.
  intros.
  generalize dependent n.
  induction m as [|m'].
  Case "m = 0".
    intros.
    destruct n as [|n']. reflexivity. inversion H.
  Case "m = Sm'".
    intros.
    destruct n as [|n'].
    SCase "n = 0". apply O_le_n.
    SCase "n = S n'".
      simpl in H.
      apply IHm' in H.
      apply n_le_m__Sn_le_Sm.
      apply H.
Qed.

Theorem le_ble_nat : forall n m,
  n <= m ->
  ble_nat n m = true.
Proof.
  intros.
  generalize dependent n.
  induction m as [|m'].
  Case "m = 0".
    intros. inversion H. subst n. reflexivity.
  Case "m = S m'".
    intros.
    destruct n as [|n'].
    SCase "n = 0". reflexivity.
    SCase "n = S n'".
      apply Sn_le_Sm__n_le_m in H.
      apply IHm' in H.
      simpl. apply H.
Qed.

Theorem ble_nat_true_trans : forall n m o,
  ble_nat n m = true -> ble_nat m o = true -> ble_nat n o = true.
Proof.
  intros n m o NM MO.
  apply ble_nat_true in NM.
  apply ble_nat_true in MO.
  apply le_ble_nat.
  apply le_trans with (n := m) (m := n) (o := o).
  apply NM.
  apply MO.
Qed.

(** **** Exercise: 2 stars, optional (ble_nat_false)  *)
Theorem ble_nat_false : forall n m,
  ble_nat n m = false -> ~(n <= m).
Proof.
  unfold not.
  intros.
  apply le_ble_nat in H0.
  rewrite H0 in H.
  inversion H.
Qed.

(** [] *)

(** **** Exercise: 3 stars (R_provability2)  *)
Module R.
(** We can define three-place relations, four-place relations,
    etc., in just the same way as binary relations.  For example,
    consider the following three-place relation on numbers: *)

Inductive R : nat -> nat -> nat -> Prop :=
   | c1 : R 0 0 0
   | c2 : forall m n o, R m n o -> R (S m) n (S o)
   | c3 : forall m n o, R m n o -> R m (S n) (S o)
   | c4 : forall m n o, R (S m) (S n) (S (S o)) -> R m n o
   | c5 : forall m n o, R m n o -> R n m o.

(** - Which of the following propositions are provable?
      - [R 1 1 2] YES
      - [R 2 2 6] NO

    - If we dropped constructor [c5] from the definition of [R],
      would the set of provable propositions change?  Briefly (1
      sentence) explain your answer.

      No! c2 and c3 provide equivalent operations and c4 treats m and n the same.

    - If we dropped constructor [c4] from the definition of [R],
      would the set of provable propositions change?  Briefly (1
      sentence) explain your answer.

      No, as c4 reverses the application of a c2 and a c3, by not applying
      them in the first place you get the same set of propositions.

      Also, R is +.
[]
*)

(** **** Exercise: 3 stars, optional (R_fact)  *)
(** Relation [R] actually encodes a familiar function.  State and prove two
    theorems that formally connects the relation and the function.
    That is, if [R m n o] is true, what can we say about [m],
    [n], and [o], and vice versa?
*)

Theorem R_is_plus : forall n m o, R n m o -> n + m = o.
Proof.
  intros.
  induction H.
  Case "c1". reflexivity.
  Case "c2". simpl. apply f_equal. apply IHR.
  Case "c3". rewrite <- plus_n_Sm. apply f_equal. apply IHR.
  Case "c4". inversion IHR. rewrite <- plus_n_Sm in H1. inversion H1. reflexivity.
  Case "c5". rewrite plus_comm. apply IHR.
Qed.

Theorem plus_is_R : forall n m o, n + m = o -> R n m o.
Proof.
  intros n.
  induction n as [|n'].
  Case "n = 0".
    simpl.
    induction m as [|m'].
    SCase "m = 0". intros. subst o. apply c1.
    SCase "m = S m'".
      intros.
      destruct o as [|o']. inversion H.
      apply c3.
      apply IHm'.
      inversion H.
      reflexivity.
  Case "n = S n'".
    simpl.
    intros.
    destruct o as [|o']. inversion H.
    apply c2.
    apply IHn'.
    inversion H.
    reflexivity.
Qed.

End R.

(** **** Exercise: 4 stars, advanced (subsequence)  *)
(** A list is a _subsequence_ of another list if all of the elements
    in the first list occur in the same order in the second list,
    possibly with some extra elements in between. For example,
    [1,2,3]
    is a subsequence of each of the lists
    [1,2,3]
    [1,1,1,2,2,3]
    [1,2,7,3]
    [5,6,1,9,9,2,7,3,8]
    but it is _not_ a subsequence of any of the lists
    [1,2]
    [1,3]
    [5,6,2,1,7,3,8]

    - Define an inductive proposition [subseq] on [list nat] that
      captures what it means to be a subsequence. (Hint: You'll need
      three cases.)

    - Prove [subseq_refl] that subsequence is reflexive, that is,
      any list is a subsequence of itself.

    - Prove [subseq_app] that for any lists [l1], [l2], and [l3],
      if [l1] is a subsequence of [l2], then [l1] is also a subsequence
      of [l2 ++ l3].

    - (Optional, harder) Prove [subseq_trans] that subsequence is
      transitive -- that is, if [l1] is a subsequence of [l2] and [l2]
      is a subsequence of [l3], then [l1] is a subsequence of [l3].
      Hint: choose your induction carefully!
*)

Inductive subseq : list nat -> list nat -> Prop :=
  | subseq_nil : forall l, subseq [] l
  | subseq_match : forall v l1 l2, subseq l1 l2 -> subseq (v :: l1) (v :: l2)
  | subseq_skip : forall v l1 l2, subseq l1 l2 -> subseq l1 (v :: l2).

Theorem subseq_refl : forall l, subseq l l.
Proof.
  intros.
  induction l as [|h t].
  Case "l = []".
    apply subseq_nil.
  Case "l = h :: t".
    apply subseq_match.
    apply IHt.
Qed.

Theorem subseq_app : forall l1 l2 l3, subseq l1 l2 -> subseq l1 (l2 ++ l3).
Proof.
  intros.
  induction H.
  Case "subseq_nil". apply subseq_nil.
  Case "subseq_match".
    simpl.
    apply subseq_match.
    apply IHsubseq.
  Case "subseq_skip".
    simpl.
    apply subseq_skip.
    apply IHsubseq.
Qed.

(* SKIPPED subseq_trans *)

(** [] *)

(** **** Exercise: 2 stars, optional (R_provability)  *)
(** Suppose we give Coq the following definition:
    Inductive R : nat -> list nat -> Prop :=
      | c1 : R 0 []
      | c2 : forall n l, R n l -> R (S n) (n :: l)
      | c3 : forall n l, R (S n) l -> R n l.
    Which of the following propositions are provable?

    - [R 2 [1,0]]
    - [R 1 [1,2,1,0]]
    - [R 6 [3,2,1,0]]
*)

(* SKIPPED *)

(** [] *)

(* ##################################################### *)
(** * Programming with Propositions *)

(** As we have seen, a _proposition_ is a statement expressing a factual claim,
    like "two plus two equals four."  In Coq, propositions are written
    as expressions of type [Prop]. . *)

Check (2 + 2 = 4).
(* ===> 2 + 2 = 4 : Prop *)

Check (ble_nat 3 2 = false).
(* ===> ble_nat 3 2 = false : Prop *)

Check (beautiful 8).
(* ===> beautiful 8 : Prop *)

(** *** *)
(** Both provable and unprovable claims are perfectly good
    propositions.  Simply _being_ a proposition is one thing; being
    _provable_ is something else! *)

Check (2 + 2 = 5).
(* ===> 2 + 2 = 5 : Prop *)

Check (beautiful 4).
(* ===> beautiful 4 : Prop *)

(** Both [2 + 2 = 4] and [2 + 2 = 5] are legal expressions
    of type [Prop]. *)

(** *** *)
(** We've mainly seen one place that propositions can appear in Coq: in
    [Theorem] (and [Lemma] and [Example]) declarations. *)

Theorem plus_2_2_is_4 :
  2 + 2 = 4.
Proof. reflexivity.  Qed.

(** But they can be used in many other ways.  For example, we have also seen that
    we can give a name to a proposition using a [Definition], just as we have
    given names to expressions of other sorts. *)

Definition plus_fact : Prop  :=  2 + 2 = 4.
Check plus_fact.
(* ===> plus_fact : Prop *)

(** We can later use this name in any situation where a proposition is
    expected -- for example, as the claim in a [Theorem] declaration. *)

Theorem plus_fact_is_true :
  plus_fact.
Proof. reflexivity.  Qed.

(** *** *)
(** We've seen several ways of constructing propositions.

       - We can define a new proposition primitively using [Inductive].

       - Given two expressions [e1] and [e2] of the same type, we can
         form the proposition [e1 = e2], which states that their
         values are equal.

       - We can combine propositions using implication and
         quantification. *)
(** *** *)
(** We have also seen _parameterized propositions_, such as [even] and
    [beautiful]. *)

Check (even 4).
(* ===> even 4 : Prop *)
Check (even 3).
(* ===> even 3 : Prop *)
Check even.
(* ===> even : nat -> Prop *)

(** *** *)
(** The type of [even], i.e., [nat->Prop], can be pronounced in
    three equivalent ways: (1) "[even] is a _function_ from numbers to
    propositions," (2) "[even] is a _family_ of propositions, indexed
    by a number [n]," or (3) "[even] is a _property_ of numbers."  *)

(** Propositions -- including parameterized propositions -- are
    first-class citizens in Coq.  For example, we can define functions
    from numbers to propositions... *)

Definition between (n m o: nat) : Prop :=
  andb (ble_nat n o) (ble_nat o m) = true.

(** ... and then partially apply them: *)

Definition teen : nat->Prop := between 13 19.

(** We can even pass propositions -- including parameterized
    propositions -- as arguments to functions: *)

Definition true_for_zero (P:nat->Prop) : Prop :=
  P 0.

(** *** *)
(** Here are two more examples of passing parameterized propositions
    as arguments to a function.

    The first function, [true_for_all_numbers], takes a proposition
    [P] as argument and builds the proposition that [P] is true for
    all natural numbers. *)

Definition true_for_all_numbers (P:nat->Prop) : Prop :=
  forall n, P n.

(** The second, [preserved_by_S], takes [P] and builds the proposition
    that, if [P] is true for some natural number [n'], then it is also
    true by the successor of [n'] -- i.e. that [P] is _preserved by
    successor_: *)

Definition preserved_by_S (P:nat->Prop) : Prop :=
  forall n', P n' -> P (S n').

(** *** *)
(** Finally, we can put these ingredients together to define
a proposition stating that induction is valid for natural numbers: *)

Definition natural_number_induction_valid : Prop :=
  forall (P:nat->Prop),
    true_for_zero P ->
    preserved_by_S P ->
    true_for_all_numbers P.

(** **** Exercise: 3 stars (combine_odd_even)  *)
(** Complete the definition of the [combine_odd_even] function
    below. It takes as arguments two properties of numbers [Podd] and
    [Peven]. As its result, it should return a new property [P] such
    that [P n] is equivalent to [Podd n] when [n] is odd, and
    equivalent to [Peven n] otherwise. *)

Definition combine_odd_even (Podd Peven : nat -> Prop) (n : nat) : Prop :=
  match evenb n with
  | true => Peven n
  | false => Podd n
  end.

(** To test your definition, see whether you can prove the following
    facts: *)

Theorem combine_odd_even_intro :
  forall (Podd Peven : nat -> Prop) (n : nat),
    (oddb n = true -> Podd n) ->
    (oddb n = false -> Peven n) ->
    combine_odd_even Podd Peven n.
Proof.
  unfold oddb. unfold combine_odd_even.
  intros.
  destruct (evenb n).
  Case "n is even". apply H0. reflexivity.
  Case "n is odd". apply H. reflexivity.
Qed.

Theorem combine_odd_even_elim_odd :
  forall (Podd Peven : nat -> Prop) (n : nat),
    combine_odd_even Podd Peven n ->
    oddb n = true ->
    Podd n.
Proof.
  unfold oddb. unfold combine_odd_even.
  intros.
  apply f_equal with (f := negb) in H0.
  rewrite negb_involutive in H0.
  simpl in H0.
  rewrite H0 in H.
  apply H.
Qed.

Theorem combine_odd_even_elim_even :
  forall (Podd Peven : nat -> Prop) (n : nat),
    combine_odd_even Podd Peven n ->
    oddb n = false ->
    Peven n.
Proof.
  unfold oddb. unfold combine_odd_even.
  intros.
  apply f_equal with (f := negb) in H0.
  rewrite negb_involutive in H0.
  simpl in H0.
  rewrite H0 in H.
  apply H.
Qed.

(** [] *)

(* ##################################################### *)
(** One more quick digression, for adventurous souls: if we can define
    parameterized propositions using [Definition], then can we also
    define them using [Fixpoint]?  Of course we can!  However, this
    kind of "recursive parameterization" doesn't correspond to
    anything very familiar from everyday mathematics.  The following
    exercise gives a slightly contrived example. *)

(** **** Exercise: 4 stars, optional (true_upto_n__true_everywhere)  *)
(** Define a recursive function
    [true_upto_n__true_everywhere] that makes
    [true_upto_n_example] work. *)

(*
Fixpoint true_upto_n__true_everywhere
(* SKIPPED *)

Example true_upto_n_example :
    (true_upto_n__true_everywhere 3 (fun n => even n))
  = (even 3 -> even 2 -> even 1 -> forall m : nat, even m).
Proof. reflexivity.  Qed.
*)
(** [] *)

(** $Date: 2014-12-31 11:17:56 -0500 (Wed, 31 Dec 2014) $ *)


(** * Lists: Trabajando con Datos Estructurados *)

Require Export Induction.

Module NatList.

(* ###################################################### *)
(** * Pares de Numeros *)

(** En una definicion de tipo inductivo ([Inductive]), cada
    constructor puede tomar cualquier numero de argumentos -- ninguno
    (como con [true] y [O]), uno (como con [S]), o mas de uno, como en
    esta definicion: *)

Inductive natprod : Type :=
  pair : nat -> nat -> natprod.

(** Esta declaracion puede ser leida como: "Hay una unica manera de
    construir un par de numeros: aplicando el constructor [pair] a dos
    argumentos de tipo [nat]". *)

(** Podemos construir un elemento de [natprod] asi: *)

Check (pair 3 5).

(** Aqui hay dos funciones sencillas para extraer el primer y el
    segundo componente de un par.  (Las definiciones tambien ilustran
    como hacer pattern matching con constructores con dos
    argumentos). *)

Definition fst (p : natprod) : nat :=
  match p with
  | pair x y => x
  end.
Definition snd (p : natprod) : nat :=
  match p with
  | pair x y => y
  end.

Eval compute in (fst (pair 3 5)).
(* ===> 3 *)

(** Como los pares se usan seguido, es mejor poder trabajar con la
    notacion matematica standard [(x,y)] en vez de [pair x y].
    Podemos hacer esto con el comando [Notation] de Coq. *)

Notation "( x , y )" := (pair x y).

(** La nueva notacion puede ser utilizada tanto en expresiones como en
    pattern matches (de hecho, ya vimos esto en el capitulo anterior
    -- esta notacion es provista en la libreria estandard de Coq): *)

Eval compute in (fst (3,5)).

Definition fst' (p : natprod) : nat :=
  match p with
  | (x,y) => x
  end.
Definition snd' (p : natprod) : nat :=
  match p with
  | (x,y) => y
  end.

Definition swap_pair (p : natprod) : natprod :=
  match p with
  | (x,y) => (y,x)
  end.

(** Intentemos probar algunos teoremas basicos de pares.  De acuerdo a
    como definimos un teorema, vamos a poder probarlo simplemente con
    [reflexivity] (y su simplificacion interna): *)

Theorem surjective_pairing' : forall (n m : nat),
  (n,m) = (fst (n,m), snd (n,m)).
Proof.
  reflexivity.  Qed.

(** Note que [reflexivity] no es suficiente si escribimos el lema en
    una forma mas natural: *)

Theorem surjective_pairing_stuck : forall (p : natprod),
  p = (fst p, snd p).
Proof.
  simpl. (* No reduce nada! *)
Abort.

(** Tenemos que exponer la estructura de [p] de forma tal que [simpl]
    pueda realizar el patern matching en [fst] y [snd].  Podemos hacer esto con
    [destruct].

    Note que, a diferencia de para [nat]urales, [destruct] no genera
    un sub-objetivo extra.  Esto es porque los [natprod]s solo pueden
    ser construidos en una sola forma.  *)

Theorem surjective_pairing : forall (p : natprod),
  p = (fst p, snd p).
Proof.
  intros p.  destruct p as [n m].  simpl.  reflexivity.  Qed.

(** **** Ejercicio: 1 star (snd_fst_is_swap) *)
Theorem snd_fst_is_swap : forall (p : natprod),
  (snd p, fst p) = swap_pair p.
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Ejercicio: 1 star, optional (fst_swap_is_snd) *)
Theorem fst_swap_is_snd : forall (p : natprod),
  fst (swap_pair p) = snd p.
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(* ###################################################### *)
(** * Listas de Numeros *)

(** Generalizando la definicion de pares un poco, vamos a describir el
    tipo de _listas_ de numeros de la siguiente forma: "Una lista es o
    la lista vacia, o un par conteniendo un numero y otra
    lista". *)

Inductive natlist : Type :=
  | nil : natlist
  | cons : nat -> natlist -> natlist.

(** Por ejemplo, aqui hay una lista con tres elementos: *)

Definition mylist := cons 1 (cons 2 (cons 3 nil)).

(** Como con pares, es conveniente escribir listas con la notacion
    familiar.  Las siguientes declaraciones nos permite escribir [::]
    como operador infijo de [cons], y corchetes como notacion para
    construir listas. *)

Notation "x :: l" := (cons x l) (at level 60, right associativity).
Notation "[ ]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).

(** No es necesario entender enteramente estas declaraciones, pero en
    caso de que este interesado, aqui hay una descripcion breve de lo
    que esta pasando.

    La anotacion [right associativity] le dice a Coq como parentizar
    expresiones involucrando varios usos de [::] de forma que, por
    ejemplo, las siguientes declaraciones signifiquen exactamente lo
    mismo: *)

Definition mylist1 := 1 :: (2 :: (3 :: nil)).
Definition mylist2 := 1 :: 2 :: 3 :: nil.
Definition mylist3 := [1;2;3].

(** La anotacion [at level 60] dice a Coq como parentizar expresiones
    que involucren tanto a [::] como a otro operador infijo.  Por
    ejemplo, como definimos [+] como operador infijo para la funcion
    [plus] a nivel 50,

    Notation "x + y" := (plus x y) (at level 50, left associativity).

    el operador [+] va a tener prioridad por sobre [::] de forma que
    [1 + 2 :: [3]] va a ser interpretado, como esperariamos, como [(1
    + 2) :: [3]] en vez de [1 + (2 :: [3])].

   (Dicho sea de paso, es importante recalcar que la expresion como
   "[1 + 2 :: [3]]" puede ser un poco confusa de leer en un archivo
   .v.  Los corchetes internos, alrededor de 3, indican una lista,
   pero los externos, que son invisibles en la version HTML, indican a
   la herramienta de impresion "coqdoc" que lo que esta entre
   parentesis deben ser mostrados como codigo Coq en vez de texto
   plano).

   La segunda y tercer declaracion [Notation] de arriba introducen las
   notaciones estandard de listas con corchetes.  En el lado derecho
   de la tercera se puede apreciar la notacion para declarar
   notaciones n-arias, en este caso resultando en una sequencia de
   [cons] anidados. *)

(** A continuacion, una serie de funciones utiles para manipular
    listas.  Por ejemplo, la funcion [repeat] que toma un numero [n] y
    otro [count] y produce una lista con [count] repeticiones del
    elemento [n]. *)

Fixpoint repeat (n count : nat) : natlist :=
  match count with
  | O => nil
  | S count' => n :: (repeat n count')
  end.

(** La funcion [length] calcula el largo de una lista. *)

Fixpoint length (l:natlist) : nat :=
  match l with
  | nil => O
  | h :: t => S (length t)
  end.

(** La funcion [app] ("append" en ingles) concatena dos listas. *)

Fixpoint app (l1 l2 : natlist) : natlist :=
  match l1 with
  | nil    => l2
  | h :: t => h :: (app t l2)
  end.

(** Como [app] va a ser utilizado en muchas partes a continuacion,
    creamos una notacion infija. *)

Notation "x ++ y" := (app x y)
                     (right associativity, at level 60).

Example test_app1:             [1;2;3] ++ [4;5] = [1;2;3;4;5].
Proof. reflexivity.  Qed.
Example test_app2:             nil ++ [4;5] = [4;5].
Proof. reflexivity.  Qed.
Example test_app3:             [1;2;3] ++ nil = [1;2;3].
Proof. reflexivity.  Qed.

(** Aqui hay dos pequenios ejemplos de programacion con listas.  La
    funcion [hd] retorna el primer elemento (la cabeza, "head") de una
    lista, mientras que [tl] retorna la lista sin la cabeza (es decir,
    la cola, "tail").  Por supuesto, la lista vacia no tiene primer
    elemento, asi que debemos pasar un valor para ser retornado en ese
    caso.  *)

Definition hd (default:nat) (l:natlist) : nat :=
  match l with
  | nil => default
  | h :: t => h
  end.

Definition tl (l:natlist) : natlist :=
  match l with
  | nil => nil
  | h :: t => t
  end.

Example test_hd1:             hd 0 [1;2;3] = 1.
Proof. reflexivity.  Qed.
Example test_hd2:             hd 0 [] = 0.
Proof. reflexivity.  Qed.
Example test_tl:              tl [1;2;3] = [2;3].
Proof. reflexivity.  Qed.

(** **** Ejercicio: 2 stars (list_funs) *)
(** Complete las definiciones de [nonzeros], [oddmembers] y
    [countoddmembers] de mas abajo.  Echele un vistazo a los tests
    para entender que deberian hacer estas funciones. *)

Fixpoint nonzeros (l:natlist) : natlist :=
  (* FILL IN HERE *) admit.

Example test_nonzeros:            nonzeros [0;1;0;2;3;0;0] = [1;2;3].
 (* FILL IN HERE *) Admitted.

Fixpoint oddmembers (l:natlist) : natlist :=
  (* FILL IN HERE *) admit.

Example test_oddmembers:            oddmembers [0;1;0;2;3;0;0] = [1;3].
 (* FILL IN HERE *) Admitted.

Fixpoint countoddmembers (l:natlist) : nat :=
  (* FILL IN HERE *) admit.

Example test_countoddmembers1:    countoddmembers [1;0;3;1;4;5] = 4.
 (* FILL IN HERE *) Admitted.
Example test_countoddmembers2:    countoddmembers [0;2;4] = 0.
 (* FILL IN HERE *) Admitted.
Example test_countoddmembers3:    countoddmembers nil = 0.
 (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Ejercicio: 3 stars, advanced (alternate) *)
(** Complete la definicion de [alternate], que "zippea" dos listas en
    una, alternando entre los elementos tomados de la primera lista y
    los de la segunda.  Mire a los tests abajo para entenderlo con
    ejemplos especificos.

    Nota: una forma natural y elegante de escribir [alternate] puede
    no satisfacer el requerimiento de Coq de que toda definicion
    [Fixpoint] debe ser "obviamente terminante".  Si se encuentra con
    este problema, considere una solucion mas verbosa que considera a
    los elementos de ambas listas a la vez.  (Otra solucion podria ser
    utilizar un tipo diferente de pares, pero esta no es la unica
    forma).  *)

Fixpoint alternate (l1 l2 : natlist) : natlist :=
  (* FILL IN HERE *) admit.

Example test_alternate1:        alternate [1;2;3] [4;5;6] = [1;4;2;5;3;6].
 (* FILL IN HERE *) Admitted.
Example test_alternate2:        alternate [1] [4;5;6] = [1;4;5;6].
 (* FILL IN HERE *) Admitted.
Example test_alternate3:        alternate [1;2;3] [4] = [1;4;2;3].
 (* FILL IN HERE *) Admitted.
Example test_alternate4:        alternate [] [20;30] = [20;30].
 (* FILL IN HERE *) Admitted.
(** [] *)

(* ###################################################### *)
(** ** Bolsas ("Bags") via Listas *)

(** Una bolsa "[bag]" (o multiconjunto "[multiset]") es como un
    conjunto, pero cada elemento puede aparecer muchas veces en vez de
    solamente una.  Una forma razonable de implementar bags es
    representandolas como listas de numeros naturales. *)

Definition bag := natlist.

(** **** Ejercicio: 3 stars (bag_functions) *)
(** Complete las siguientes definiciones para las funciones [count],
    [sum], [add], y [member] para bags. *)

Fixpoint count (v:nat) (s:bag) : nat :=
  (* FILL IN HERE *) admit.

(** Todas estas pruebas deben solucionarse con [reflexivity]. *)

Example test_count1:              count 1 [1;2;3;1;4;1] = 3.
 (* FILL IN HERE *) Admitted.
Example test_count2:              count 6 [1;2;3;1;4;1] = 0.
 (* FILL IN HERE *) Admitted.

(** La operacion [sum] de multiset es similar a la [union] de sets:
    [sum a b] contiene los elementos de [a] y [b].  (Los matematicos
    usualmente definen [union] ligeramente diferente, y por ello no
    usamos el mismo nombre para esta operacion).  Para [sum] le damos
    un hint: no nombre los argumentos de la funcion.  Es mas,
    escribimos [Definition] en vez de [Fixpoint], de forma que si
    incluso persiste en dar nombres para los argumentos, no podra
    procesarlos recursivamente.  El punto de establecer el problema de
    este modo es para promover otras formas de crear esta funcion, tal
    vez utilizando funciones que ya han sido definidas.  *)

Definition sum : bag -> bag -> bag :=
  (* FILL IN HERE *) admit.

Example test_sum1:              count 1 (sum [1;2;3] [1;4;1]) = 3.
 (* FILL IN HERE *) Admitted.

Definition add (v:nat) (s:bag) : bag :=
  (* FILL IN HERE *) admit.

Example test_add1:                count 1 (add 1 [1;4;1]) = 3.
 (* FILL IN HERE *) Admitted.
Example test_add2:                count 5 (add 1 [1;4;1]) = 0.
 (* FILL IN HERE *) Admitted.

Definition member (v:nat) (s:bag) : bool :=
  (* FILL IN HERE *) admit.

Example test_member1:             member 1 [1;4;1] = true.
 (* FILL IN HERE *) Admitted.
Example test_member2:             member 2 [1;4;1] = false.
 (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Ejercicio: 3 stars, optional (bag_more_functions) *)
(** Aqui hay algunas otras funciones sobre bags para que practique. *)

Fixpoint remove_one (v:nat) (s:bag) : bag :=
  (* Cuando remove_one es aplicado a un bag sin el numero en cuestion,
     debe retornar el mismo bag sin cambiar. *)
  (* FILL IN HERE *) admit.

Example test_remove_one1:         count 5 (remove_one 5 [2;1;5;4;1]) = 0.
 (* FILL IN HERE *) Admitted.
Example test_remove_one2:         count 5 (remove_one 5 [2;1;4;1]) = 0.
 (* FILL IN HERE *) Admitted.
Example test_remove_one3:         count 4 (remove_one 5 [2;1;4;5;1;4]) = 2.
 (* FILL IN HERE *) Admitted.
Example test_remove_one4:         count 5 (remove_one 5 [2;1;5;4;5;1;4]) = 1.
 (* FILL IN HERE *) Admitted.

Fixpoint remove_all (v:nat) (s:bag) : bag :=
  (* FILL IN HERE *) admit.

Example test_remove_all1:          count 5 (remove_all 5 [2;1;5;4;1]) = 0.
 (* FILL IN HERE *) Admitted.
Example test_remove_all2:          count 5 (remove_all 5 [2;1;4;1]) = 0.
 (* FILL IN HERE *) Admitted.
Example test_remove_all3:          count 4 (remove_all 5 [2;1;4;5;1;4]) = 2.
 (* FILL IN HERE *) Admitted.
Example test_remove_all4:          count 5 (remove_all 5 [2;1;5;4;5;1;4;5;1;4]) = 0.
 (* FILL IN HERE *) Admitted.

Fixpoint subset (s1:bag) (s2:bag) : bool :=
  (* FILL IN HERE *) admit.

Example test_subset1:              subset [1;2] [2;1;4;1] = true.
 (* FILL IN HERE *) Admitted.
Example test_subset2:              subset [1;2;2] [2;1;4;1] = false.
 (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Ejercicio: 3 stars (bag_theorem) *)
(** Escriba un teorema interesante sobre bags involucrando las
    funciones [count] y [add], y demuestrelo.  Note que, como el
    problema no esta totalmente especificado, es posible que usted
    cree un teorema que es cierto, pero cuya prueba requira tecnicas
    que no ha aprendido todavia.  Sientase libre de preguntar por
    ayuda si se queda estancado! *)

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

(* ###################################################### *)
(** * Razonando acerca de listas *)

(** De igual manera que con los numeros, se pueden probar muchos hechos
    simples acerca de funciones sobre listas con simplificacion.  Por
    ejemplo, la simplificacion realizada por [reflexivity] es
    suficiente para este teorema... *)

Theorem nil_app : forall l:natlist,
  [] ++ l = l.
Proof. reflexivity. Qed.

(** ... porque el [[]] es sustituido en la posicion del match en la
    definicion de [app], permitiendo que el match pueda ser
    simplificado. *)

(** Tambien, como con los numeros, a veces es util realizar un
    analisis por caso en las posibles formas (vacia o no vacia) que
    puede tener una lista desconocida. *)

Theorem tl_length_pred : forall l:natlist,
  pred (length l) = length (tl l).
Proof.
  intros l. destruct l as [| n l'].
  Case "l = nil".
    reflexivity.
  Case "l = cons n l'".
    reflexivity.  Qed.

(** Aqui, el caso para [nil] fuciona porque decidimos definir [tl nil
    = nil].  Note que la anotacion [as] en la tactica [destruct]
    introduce dos nombres, [n] y [l'], correspondiendo al hecho que el
    operador [cons] para listas tomas dos argumentos (la cabeza y cola
    de la lista que se esta construyendo). *)

(** Usualmente, sin embargo, ciertos teoremas interesantes acerca de
    listas requiere induccion para sus pruebas. *)

(* ###################################################### *)
(** ** Micro-Sermon *)

(** Simplemente leer pruebas no le va a permitir llegar muy lejos!  Es
    muy importante entender los pasos involucrados en cada prueba,
    usando Coq y pensando en cada paso que razonamiento se esta
    llevando a cabo.  De otra forma, esta garantizado que los
    ejercicios no van a tener ningun sentido.  *)

(* ###################################################### *)
(** ** Induccion sobre Listas *)

(** Pruebas por induccion en tipos de datos como [natlist] son quizas
    un poco menos familiares que induccion sobre numeros naturales,
    pero la idea basica es la misma.  Cada declaracion [Inductive]
    define un conjunto de constructores que establecen como construir
    un elemento del tipo: un booleano puede ser [true] o [false]; un
    numero puede ser [O] o [S] aplicado a otro numero; una lista puede
    ser [nil] o [cons] aplicado a un numero y una lista.

    Es mas, la aplicacion de los constructores declarados en un tipo
    inductivo a si mismos es la _unica_ forma que los valores del tipo
    inductivo pueden tomar.  Este hecho da forma a un estilo de
    razonamiento acerca conjuntos definidos inductivamente: un numero
    es [O] o es [S] aplicado a otro numero _mas pequenio_; una lista
    es [nil] o [cons] aplicado a un numero y una lista _mas pequnia_;
    etc.  Entonces, si tenemos en mente una proposicion [P] que
    menciona una lista [l] y queremos argumentar que [P] vale para
    _todas_ las listas, podemos razonar de la siguiente manera:

      - Primero, mostrar que [P] es verdadero para [l] cuando [l] es [nil].

      - Luego mostrar que [P] es verdadero para [l] cuando [l] es
        [cons n l'] para algun numero [n] y alguna lista mas chica [l'],
        asumiendo que [P] es verdadero para [l'].

    Como listas largas solo pueden ser construidas de listas mas
    cortas, eventualmente llegando a [nil], estos dos principios
    bastan para establecer la prueba de [P] para toda lista [l].  Aqui
    hay un ejemplo concreto: *)

Theorem app_ass : forall l1 l2 l3 : natlist,
  (l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
Proof.
  intros l1 l2 l3. induction l1 as [| n l1'].
  Case "l1 = nil".
    reflexivity.
  Case "l1 = cons n l1'".
    simpl. rewrite -> IHl1'. reflexivity.  Qed.

(** De vuelta, esta prueba en Coq no es muy iluminadora como documento
    estatico -- es mas facil ver que esta pasando si se esta
    trabajando en una sesion interactiva de Coq, donde se puede ver el
    estado del objetivo actual y su contexto en cada paso, pero este
    estado no es visible en la prueba estatica.  Entonces, la prueba
    en lenguaje natural -- es decir, apta para humanos -- debe incluir
    informacion mas explicita; en particular, puede ayudar recordarle
    al lector cual es la hipotesis inductiva en el segundo caso.  *)

(** _Teorema_: Para todas listas [l1], [l2], y [l3],
   [(l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3)].

   _Prueba_: Por induccion en [l1].

   - Primero, suponga [l1 = []].  Debemos mostrar
       ([] ++ l2) ++ l3 = [] ++ (l2 ++ l3),
     que se desprende directamente de la definicion de [++].

   - Ahora suponga [l1 = n::l1'], con
       (l1' ++ l2) ++ l3 = l1' ++ (l2 ++ l3)
     (la hipotesis inductiva). Debemos mostrar
       ((n :: l1') ++ l2) ++ l3 = (n :: l1') ++ (l2 ++ l3).

     Por definicion de [++], esto se sigue de
       n :: ((l1' ++ l2) ++ l3) = n :: (l1' ++ (l2 ++ l3)),
     que es inmediato utilizando la hipotesis inductiva.  []

  Aqui hay una prueba similar: *)

Theorem app_length : forall l1 l2 : natlist,
  length (l1 ++ l2) = (length l1) + (length l2).
Proof.
  (* WORKED IN CLASS *)
  intros l1 l2. induction l1 as [| n l1'].
  Case "l1 = nil".
    reflexivity.
  Case "l1 = cons".
    simpl. rewrite -> IHl1'. reflexivity.  Qed.

(** Para un ejemplo un poco mas complejo sobre induccion en listas,
    suponga que definimos la funcion "cons a la derecha" como... *)

Fixpoint snoc (l:natlist) (v:nat) : natlist :=
  match l with
  | nil    => [v]
  | h :: t => h :: (snoc t v)
  end.

(** ... y la usamos para definir la funcion [rev] para revertir una lista: *)

Fixpoint rev (l:natlist) : natlist :=
  match l with
  | nil    => nil
  | h :: t => snoc (rev t) h
  end.

Example test_rev1:            rev [1;2;3] = [3;2;1].
Proof. reflexivity.  Qed.
Example test_rev2:            rev nil = nil.
Proof. reflexivity.  Qed.

(** Pasemos ahora a probar algunos teoremas sobre las nuevas funciones
    [snoc] y [rev].  Probemos una propiedad un poco mas compleja que
    las que hemos visto hasta ahora, que revertir una lista no cambia
    su largo.  Nuestro primer intento se va a quedar estancado en el
    caso [cons]... *)

Theorem rev_length_firsttry : forall l : natlist,
  length (rev l) = length l.
Proof.
  intros l. induction l as [| n l'].
  Case "l = []".
    reflexivity.
  Case "l = n :: l'".
    (* Este es el caso dificil.  Empezemos por simplificar la ecuacion. *)
    simpl.
    (* Ahora parece que estamos estancados: el objetivo es una
       igualdad involucrando [snoc], pero no tenemos ninguna ecuacion
       en el contexto local o en el ambiente global que diga algo
       sobre [snoc]!

       Podemos hacer un poco de progreso si reescribimos la hipotesis inductiva... *)
    rewrite <- IHl'.
    (* ... pero ya no podemos hacer nada. *)
Abort.

(** Entonces tomemos la ecuacion sobre [snoc] que nos permite hacer
    progreso y provemosla como un lema separado. *)

Theorem length_snoc : forall n : nat, forall l : natlist,
  length (snoc l n) = S (length l).
Proof.
  intros n l. induction l as [| n' l'].
  Case "l = nil".
    reflexivity.
  Case "l = cons n' l'".
    simpl. rewrite -> IHl'. reflexivity.  Qed.

(** Note que hicimos el lema lo mas _general_ posible: en particular,
    cuantificamos sobre _toda_ [natlist], no solo aquella que resulta
    de la aplicacion de [rev].  Esto debe resultar natural, puesto que
    la verdad del objetivo no depende de que la lista haya sido
    revertida.  Es mas, es mucho mas facil provar la propiedad mas
    general.  *)

(** Ahora podemos completar la prueba original. *)

Theorem rev_length : forall l : natlist,
  length (rev l) = length l.
Proof.
  intros l. induction l as [| n l'].
  Case "l = nil".
    reflexivity.
  Case "l = cons".
    simpl. rewrite -> length_snoc.
    rewrite -> IHl'. reflexivity.  Qed.

(** Para comparar, aqui estan las pruebas informales de estos dos teoremas:

    _Teorema_: Para todo numero [n] y lista [l],
       [length (snoc l n) = S (length l)].

    _Prueba_: Por induccion en [l].

    - Primero, suponga [l = []].  Debemos probar
        length (snoc [] n) = S (length []),
      que resulta inmediato de las definiciones de
      [length] y [snoc].

    - Luego, suponga [l = n'::l'], con
        length (snoc l' n) = S (length l').
      Tenemos que mostrar
        length (snoc (n' :: l') n) = S (length (n' :: l')).
      Por la definicion de [length] y [snoc], esto es equivalente a
        S (length (snoc l' n)) = S (S (length l')),
      que es inmediato por la hipotesis inductiva. [] *)

(** _Teorema_: Para toda lista [l], [length (rev l) = length l].

    _Prueba_: Por induccion en [l].

      - Primero, suponga [l = []].  Debemos mostrar
          length (rev []) = length [],
        que resulta inmediato de considerar las definiciones de [length]
        y [rev].

      - Luego, suponga [l = n::l'], con
          length (rev l') = length l'.
        Tenemos que mostrar que
          length (rev (n :: l')) = length (n :: l').
        Porla definicion de [rev], esto es equivalente a
          length (snoc (rev l') n) = S (length l')
        que, por el lema anterior, es lo mismo que
          S (length (rev l')) = S (length l').
        Concluimos gracias a la hipotesis inductiva. [] *)

(** Obviamente, el estilo de estas pruebas es mas bien verborragica y
    pedante.  Luego de un par, podemos encontrar mas facil de seguir
    pruebas que no muestran tanto detalle (puesto que podemos
    entenderlas en nuestras mentes o en un papel borrador si es
    necesario) y simplemente resaltar los pasos no obvios.  En este
    estilo mas comprimido, la prueba de arriba puede verse mas como:
    *)

(** _Teorema_:
     Para toda lista [l], [length (rev l) = length l].

    _Prueba_: Primero, observe que length (snoc l n) = S (length l)
       para cualquier [l].  Esto se desprende de una simple induccion
       en [l].  La propiedad principal se desprende tambien por otra
       simple induccion en [l], usando la observacion junto con la
       hipotesis inductiva en el caso donde [l = n'::l']. [] *)

(** Que estilo es preferible en una determinada situacion depende en
    la sofisticacion de la audencia y en que tan similar la prueba en
    cuestion sea a otras a las que la audencia se haya ya
    expuesto.  El estilo mas pedante es un buen default por ahora. *)

(* ###################################################### *)
(** ** [SearchAbout] *)

(** Hemos visto que las pruebas pueden hacer uso de otros teoremas que
    ya se han provado, usando [rewrite], y mas tarde vamos a ver otras
    formas de reusar teoremas.  Pero para referirse a un teorema, es
    necesario saber su nombre, y recordar los nombres de todos los
    posibles teoremas que podamos querer usar puede ser una tarea muy
    dificil!  Usualmente es imposible recordar los teoremas que se han
    demostrado; mucho menos sus nombres.

    El comando de Coq [SearchAbout] es bastante util para esto.
    Escribiendo [SearchAbout foo] hace que Coq muestre una lista de
    teoremas involucrando a [foo].  Por ejemplo, observe la lista que
    muestra el siguiente comando, mostrando los teoremas que demostramos
    sobre [rev]: *)

SearchAbout rev.

(** Mantenga [SearchAbout] en mente cuando haga los siguientes
    ejercicios, y durante el resto del curso; puede salvarle mucho
    tiempo!  *)

(* ###################################################### *)
(** ** Ejercicios Sobre Listas, Parte 1 *)

(** **** Ejercicio: 3 stars (list_exercises) *)
(** Mas practica sobre listas. *)

Theorem app_nil_end : forall l : natlist,
  l ++ [] = l.
Proof.
  (* FILL IN HERE *) Admitted.

Theorem rev_involutive : forall l : natlist,
  rev (rev l) = l.
Proof.
  (* FILL IN HERE *) Admitted.

(** Hay una solucion corta al siguiente ejercicio.  Si usted se
    encuentra en un embrollo, vuelva atras y busque una solucion mas
    facil. *)

Theorem app_ass4 : forall l1 l2 l3 l4 : natlist,
  l1 ++ (l2 ++ (l3 ++ l4)) = ((l1 ++ l2) ++ l3) ++ l4.
Proof.
  (* FILL IN HERE *) Admitted.

Theorem snoc_append : forall (l:natlist) (n:nat),
  snoc l n = l ++ [n].
Proof.
  (* FILL IN HERE *) Admitted.

Theorem distr_rev : forall l1 l2 : natlist,
  rev (l1 ++ l2) = (rev l2) ++ (rev l1).
Proof.
  (* FILL IN HERE *) Admitted.

(** Un ejercicio acerca de su implementacion de [nonzeros]: *)

Lemma nonzeros_app : forall l1 l2 : natlist,
  nonzeros (l1 ++ l2) = (nonzeros l1) ++ (nonzeros l2).
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(* ###################################################### *)
(** ** Ejercicios Sobre Listas, Part 2 *)

(** **** Ejercicio: 2 stars (list_design) *)
(** Ejercicio de disenio:
     - Escriba un teorema no trivial involucrando [cons]
       ([::]), [snoc], y [app] ([++]).
     - Demuestrelo. *)

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

(** **** Ejercicio: 3 stars, advanced (bag_proofs) *)
(** Aqui hay un par de pequenios teoremas para demostrar acerca de su
    definicion de bags. *)

Theorem count_member_nonzero : forall (s : bag),
  ble_nat 1 (count 1 (1 :: s)) = true.
Proof.
  (* FILL IN HERE *) Admitted.

(** El siguiente lema acerca [ble_nat] puede ayudarle en la siguiente prueba. *)

Theorem ble_n_Sn : forall n,
  ble_nat n (S n) = true.
Proof.
  intros n. induction n as [| n'].
  Case "0".
    simpl.  reflexivity.
  Case "S n'".
    simpl.  rewrite IHn'.  reflexivity.  Qed.

Theorem remove_decreases_count: forall (s : bag),
  ble_nat (count 0 (remove_one 0 s)) (count 0 s) = true.
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Ejercicio: 3 stars, optional (bag_count_sum) *)
(** Escriba un teorema interesante acerca de bags involucrando las
    funciones [count] y [sum], y demuestrelo.*)

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

(** **** Ejercicio: 4 stars, advanced (rev_injective) *)
(** Demuestre que la funcion [rev] es injectiva, es decir,

    forall (l1 l2 : natlist), rev l1 = rev l2 -> l1 = l2.

Hay una forma dificil y otra facil de resolver este ejercicio.
*)

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

(* ###################################################### *)
(** * Opciones (Options) *)

(** Aqui hay otra definicion de tipo que es usada comunmente en
    programacion: *)

Inductive natoption : Type :=
  | Some : nat -> natoption
  | None : natoption.

(** El uso de [natoption] es una forma de retornar "error" en
    funciones.  Por ejemplo, suponga que queremos escribir una funcion
    que retorne el elemento [n]-ario de una lista.  Si le damos tipo
    [nat -> natlist -> nat], entonces vamos a tener que retornar un
    numero arbitrario cuando la lista es muy corta! *)

Fixpoint index_bad (n:nat) (l:natlist) : nat :=
  match l with
  | nil => 42  (* arbitrario! *)
  | a :: l' => match beq_nat n O with
               | true => a
               | false => index_bad (pred n) l'
               end
  end.

(** Por otro lado, si le damos el tipo [nat -> natlist -> natoption],
    entonces podemos retornar [None] cuando la lista es muy corta y
    [Some a] cuando la lista tiene suficientes elementos y [a] aparece
    en la posicion [n]. *)

Fixpoint index (n:nat) (l:natlist) : natoption :=
  match l with
  | nil => None
  | a :: l' => match beq_nat n O with
               | true => Some a
               | false => index (pred n) l'
               end
  end.

Example test_index1 :    index 0 [4;5;6;7]  = Some 4.
Proof. reflexivity.  Qed.
Example test_index2 :    index 3 [4;5;6;7]  = Some 7.
Proof. reflexivity.  Qed.
Example test_index3 :    index 10 [4;5;6;7] = None.
Proof. reflexivity.  Qed.

(** Este ejemplo es una buena oportunidad para traer a colacion otra
    pequenia caracteristica del lenguaje de programacion de Coq: las
    expresiones condicionales... *)

Fixpoint index' (n:nat) (l:natlist) : natoption :=
  match l with
  | nil => None
  | a :: l' => if beq_nat n O then Some a else index' (pred n) l'
  end.

(** Los condicionales de Coq son como aquellos encontrados en
    cualquier otro lenguaje, con una pequenia generalizacion.  Como el
    tipo booleano no esta insertado en la logica subyacente, Coq
    permite expresiones condicionales de _cualquier_ tipo inductivo
    definido con exactamente dos constructores.  La guarda es
    considerada [true] si evalua al primer constructor en la
    definicion [Inductive] y [false] si evalua al segundo. *)

(** La funcion de abajo extrae el [nat] de un [natoption], retornando
    un default especificado en el caso [None]. *)

Definition option_elim (d : nat) (o : natoption) : nat :=
  match o with
  | Some n' => n'
  | None => d
  end.

(** **** Ejercicio: 2 stars (hd_opt) *)
(** Usando la misma idea, modifique la funcion [hd] definida
   anteriormente de forma que no tengamos que pasar un elemento por
   default en el caso [nil].  *)

Definition hd_opt (l : natlist) : natoption :=
  (* FILL IN HERE *) admit.

Example test_hd_opt1 : hd_opt [] = None.
 (* FILL IN HERE *) Admitted.

Example test_hd_opt2 : hd_opt [1] = Some 1.
 (* FILL IN HERE *) Admitted.

Example test_hd_opt3 : hd_opt [5;6] = Some 5.
 (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Ejercicio: 1 star, optional (option_elim_hd) *)
(** Este ejercicio relaciona [hd_opt] con el viejo [hd]. *)

Theorem option_elim_hd : forall (l:natlist) (default:nat),
  hd default l = option_elim default (hd_opt l).
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Ejercicio: 2 stars (beq_natlist) *)
(** Complete la definicion de [beq_natlist], que compara dos listas de
    numeros por igualdad.  Demuestre que [beq_natlist l l] retorna [true]
    para toda lista [l]. *)

Fixpoint beq_natlist (l1 l2 : natlist) : bool :=
  (* FILL IN HERE *) admit.

Example test_beq_natlist1 :   (beq_natlist nil nil = true).
 (* FILL IN HERE *) Admitted.
Example test_beq_natlist2 :   beq_natlist [1;2;3] [1;2;3] = true.
 (* FILL IN HERE *) Admitted.
Example test_beq_natlist3 :   beq_natlist [1;2;3] [1;2;4] = false.
 (* FILL IN HERE *) Admitted.

Theorem beq_natlist_refl : forall l:natlist,
  true = beq_natlist l l.
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(* ###################################################### *)
(** * Diccionarios *)

(** Como ejemplo final sobre como se pueden definir tipos de datos
    fundamentales, aqui hay una declaracion de un simple tipo de datos
    [dictionary], usando numeros para tanto las claves como los
    valores guardados en esas claves.  (Es decir, un diccionario
    representa un mapeo finito de numeros a numeros).  *)

Module Dictionary.

Inductive dictionary : Type :=
  | empty  : dictionary
  | record : nat -> nat -> dictionary -> dictionary.

(** Esta declaracion puede ser leida como: "Hay dos formas de
    construir un [dictionary]: mediante el constructor [empty], para
    representar el diccionario vacio, o mediante el constructor
    [record] aplicado a una clave, un valor, y un [dictionary] ya
    existente, para construir un diccionario con el mapeo clave-valor
    nuevo".  *)

Definition insert (key value : nat) (d : dictionary) : dictionary :=
  (record key value d).

(** Aqui hay una funcion [find] que busca un [dictionary] por una
    determinada clave.  Evalua a [None] si la clave no se encontro, y
    a [Some val] si la clave [key] esta mapeada a [val] en el
    diccionario.  Si la misma clave esta mapeada a multiples valores, [find]
    retorna la primera que encuentra. *)

Fixpoint find (key : nat) (d : dictionary) : natoption :=
  match d with
  | empty         => None
  | record k v d' => if (beq_nat key k)
                       then (Some v)
                       else (find key d')
  end.

(** **** Ejercicio: 1 star (dictionary_invariant1) *)
(** Complete la siguiente prueba. *)

Theorem dictionary_invariant1' : forall (d : dictionary) (k v: nat),
  (find k (insert k v d)) = Some v.
Proof.
 (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Ejercicio: 1 star (dictionary_invariant2) *)
(** Complete la siguiente prueba. *)

Theorem dictionary_invariant2' : forall (d : dictionary) (m n o: nat),
  beq_nat m n = false -> find m d = find m (insert n o d).
Proof.
 (* FILL IN HERE *) Admitted.
(** [] *)

End Dictionary.

End NatList.

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


Require Import Arith.
Require Import Omega.

Section section_for_chapter_7.

(* 7.1 p186 *)

Definition divides (n m:nat) := exists (p:nat), p*n=m.

Theorem divides_0 : forall n, divides n 0.
Proof.
intros.
unfold divides.
exists 0.
trivial.
Qed.

Print divides_0.

Check ex_intro (fun p : nat => p * 1 = 0) 0 eq_refl.

Theorem divides_plus : forall n m, divides n m -> divides n (n + m).
Proof.
intros.
elim H.
intros.
elim H0.
exists (S x).
simpl.
trivial.
Qed.

Theorem not_divides_plus : forall n m:nat, ~ divides n m -> ~ divides n (n+m).
Proof.
intros n m H; red; intro H'; elim H'; intro y.
case y; simpl.
intro H2; apply H.
cut (m=0).
intro H3; rewrite H3; apply divides_0.
omega.
intros n0 Hn0.
apply H.
exists n0.
omega.
Qed.

Theorem not_divides_lt : forall n m:nat, 0 < m ->  m < n -> ~ divides n m.
Proof.
intros n m H H0 H1.
elim H1; intros q Hq.
rewrite <- Hq in H.
rewrite <- Hq in H0.
generalize H H0. case q.
simpl.
intros; absurd (0 < 0); auto with arith.
clear H H0; intros y Hy Hy'.
simpl in Hy'.
absurd (n <= n + y * n); auto with arith.
Qed.

Theorem not_lt_2_divides : forall n m:nat, n <> 1 -> n < 2 -> 0 < m -> ~ divides n m.
Proof.
intros n m H H0; cut (n=0).
intro e;rewrite e.
case m.
intro; absurd (0 < 0); auto with arith.
intros n0 Hn0 H1.
elim H1; intros q Hq.
rewrite mult_0_r in Hq; discriminate Hq.
omega.
Qed.

Theorem le_plus_minus : forall n m:nat, le n m -> m = n+(m-n).
Proof.
intros; omega.
Qed.

Theorem lt_lt_or_eq : forall n m:nat, n < S m ->  n<m \/ n=m.
Proof.
intros; omega.
Qed.

End section_for_chapter_7.


Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq.

(* Coq Faust Demo for LAC 2015 *)

(* Basic syntax

- Functions:

  fun x => x + x

  - takes a parameter named x, return x + x

- Application is just juxtaposition:

  f g

 *)

Eval compute in ([fun x => x + x] 2).

(* Datatypes and pattern matching

Inductive nat : Type :=
| 0    : nat
| n.+1 : nat -> nat

*)

Fixpoint is_even n :=
  match n with
  | 0    => true
  | n.+1 => ~~ (is_even n)
  end.

Eval compute in (is_even 3, is_even 2).

Lemma is_even4 : is_even 4 = true.
Proof.
  compute.
  reflexivity.
Qed.

Lemma is_even_mult2 : forall n, is_even (n.*2) = true.
Proof.
  elim=> [|n ihn].
  + done.
  + rewrite /=.
    rewrite negbK.
    rewrite ihn.
    done.
Qed.

(* Types:

Types are the most important part of Coq.

The key type is the function type, A → B,
which represents programs with input A and
output B.

The main typing or reasoning rule is:

Γ ⊢ f : A → B         Γ ⊢ x : A
───────────────────────────────
         Γ ⊢ f x : B

This codifies the rule of "modus ponens".

Another important rule is:

    Γ, x : A ⊢ e : B
────────────────────────
 Γ ⊢ fun x => e : A → B

This captures the act of
"reasoning under a hypothesis"

*)

(***********************************************)
(* Proof idioms                                *)

Definition trivial_1 P : P -> P :=
  fun x => x.

Definition trivial_2 P Q : P -> Q -> P :=
  fun x y => x.

(* Replace by y *)

(* We can build programs interactively *)
Lemma trivial_3 P Q : P -> Q -> P.
Proof.
  move=> p q.
  exact: p.
Qed.

Lemma trivial_4 P Q : P -> Q -> P.
Proof. exact: trivial_2. Qed.

(* Proof by Shuffling: move/apply
Bookeping a significant cost in formal proof.
*)

(* Proof by inspection: case/elim *)
Lemma OrC P Q : P \/ Q -> Q \/ P.
Proof.
  case.
  + right. done.
  + left. done.
Qed.

(* Naming: add naturals Commutative *)
Print plus.
(*
plus (n m : nat) :=
  match n with
  | 0 => m
  | p.+1 => (plus p m).+1
  end

Or
 - 0 + m    => m
 - n.+1 + m => (n+m).+1

Note no rule for m + 0!

*)

Lemma addn0 n : n + 0 = n.
(* Welcome to equality

   equality is a type, such that the only
   constructor is when programs are the same.

   erefl x : x = x

   When we know A = B, we can use rewrite to
   replace A by B!
 *)
Proof.
  elim: n => [ | n ihn] /=.
  - compute. done.
  - Check addSn.
    rewrite addSn.
    rewrite ihn. done.
Qed.

(* Equality works for more complicated things *)
Require Import ssralg.
Open Scope ring_scope.
Import GRing.Theory.

Lemma roots (N : fieldType) (x : N) :
  (x - 1) * (x - 1) = x ^+ 2 - x *+ 2 + 1.
Proof.
  rewrite !(mulrDl, mulrDr).
  Search _ (_ * -1).
  rewrite !(mulrN1, mulN1r).
  rewrite opprK mulr2n !addrA.
  rewrite opprD addrA.
  done.
Qed.


Require Import Arith.

Inductive BinaryTree : Type :=
| Leaf : BinaryTree
| Node : BinaryTree->BinaryTree->BinaryTree.

Check Node (Node Leaf Leaf) Leaf.

Check Leaf.

Check Node Leaf Leaf.

Definition max (a:nat) (b:nat) : nat :=
    if a <=? b then b else a.

Fixpoint TreeDepth (tree : BinaryTree) : nat :=
    match tree with
    | Leaf => 0
    | Node leftc rightc => (max (TreeDepth rightc) (TreeDepth leftc)) + 1
    end.

Compute TreeDepth Leaf.
Compute TreeDepth (Node Leaf Leaf).
Compute TreeDepth (Node Leaf (Node Leaf (Node Leaf Leaf))).

Inductive bin : Type :=
    L : bin
    | N : bin -> bin -> bin.

Fixpoint flatten_aux (t1 t2:bin) : bin :=
    match t1 with
    | L => N L t2
    | N t'1 t'2 => flatten_aux t'1 (flatten_aux t'2 t2)
    end.

Fixpoint flatten (t:bin) : bin :=
    match t with
    | L => L
    | N t1 t2 => flatten_aux t1 (flatten t2)
    end.

Fixpoint size (t:bin) : nat :=
    match t with
    | L => 1
    | N t1 t2 => 1 + size t1 + size t2
    end.

Compute flatten (N (N L L) (N (N L L) L)).

Definition example7 (t : bin): bool :=
match t with N L L => false | _ => true end.

(*My Proof*)
Lemma example7_size : forall t, example7 t = false -> size t = 3.
Proof.
    induction t.
    simpl example7.
    discriminate.
    case t1.
    case t2.
    simpl.
    intros h; reflexivity.
    intros b0 b1.
    simpl.
    discriminate.
    intros b0 b1.
    simpl.
    discriminate.
Qed.

(*Book Proof*)
Lemma example7_size_book : forall t, example7 t = false -> size t = 3.
Proof.
    intros t.
    destruct t.
    simpl.
    discriminate.
    destruct t1.
    destruct t2.
    simpl.
    auto.
    simpl; discriminate.
    simpl; discriminate.
Qed.

(*My Proof*)
Lemma flatten_aux_size :
forall t1 t2, size (flatten_aux t1 t2) = size t1 + size t2 + 1.
Proof.
    induction t1.
    intros t2.
    simpl.
    ring.

    intros t2.
    simpl.
    rewrite IHt1_1.
    rewrite IHt1_2.
    ring.
Qed.

(*Book Proof*)
Lemma flatten_aux_size_book :
forall t1 t2, size (flatten_aux t1 t2) = size t1 + size t2 + 1.
Proof.
    induction t1.
    intros t2.
    simpl.
    ring. (*Same as my base case nice*)

    intros t2; simpl.
    rewrite IHt1_1.
    rewrite IHt1_2.
    ring. (*Wow same proof I used!*)
Qed.

(*Exersize, My proof*)
Lemma flatten_size : forall t, size (flatten t) = size t.
Proof.
    induction t.
    simpl.
    reflexivity.
    simpl.
    rewrite flatten_aux_size.
    rewrite IHt2.
    ring.
Qed.

Lemma not_subterm_self_l : forall x y, ~ x = N x y.
Proof.
    induction x.
    intros y.
    discriminate. (*Unsure why this works since*)

    intros y.
    intros abs.
    injection abs.
    intros H1 H2.

    assert (IHx1' : x1 <> N x1 x2).
    apply IHx1.
    case IHx1'.
    exact H2.
Qed.