(** * Logic: Logic in Coq *)

Set Warnings "-notation-overridden,-parsing".
From LF Require Export Tactics.

(** In previous chapters, we have seen many examples of factual
    claims (_propositions_) and ways of presenting evidence of their
    truth (_proofs_).  In particular, we have worked extensively with
    _equality propositions_ of the form [e1 = e2], with
    implications ([P -> Q]), and with quantified propositions ([forall
    x, P]).  In this chapter, we will see how Coq can be used to carry
    out other familiar forms of logical reasoning.

    Before diving into details, let's talk a bit about the status of
    mathematical statements in Coq.  Recall that Coq is a _typed_
    language, which means that every sensible expression in its world
    has an associated type.  Logical claims are no exception: any
    statement we might try to prove in Coq has a type, namely [Prop],
    the type of _propositions_.  We can see this with the [Check]
    command: *)

Check 3 = 3.
(* ===> Prop *)

Check forall n m : nat, n + m = m + n.
(* ===> Prop *)

(** Note that _all_ syntactically well-formed propositions have type
    [Prop] in Coq, regardless of whether they are true. *)

(** Simply _being_ a proposition is one thing; being _provable_ is
    something else! *)

Check 2 = 2.
(* ===> Prop *)

Check forall n : nat, n = 2.
(* ===> Prop *)

Check 3 = 4.
(* ===> Prop *)

(** Indeed, propositions don't just have types: they are
    _first-class objects_ that can be manipulated in the same ways as
    the other entities in Coq's world. *)

(** So far, we've seen one primary place that propositions can appear:
    in [Theorem] (and [Lemma] and [Example]) declarations. *)

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

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

Definition plus_claim : Prop := 2 + 2 = 4.
Check plus_claim.
(* ===> plus_claim : 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_claim_is_true :
  plus_claim.
Proof. reflexivity.  Qed.

(** We can also write _parameterized_ propositions -- that is,
    functions that take arguments of some type and return a
    proposition. *)

(** For instance, the following function takes a number
    and returns a proposition asserting that this number is equal to
    three: *)

Definition is_three (n : nat) : Prop :=
  n = 3.
Check is_three.
(* ===> nat -> Prop *)

(** In Coq, functions that return propositions are said to define
    _properties_ of their arguments.

    For instance, here's a (polymorphic) property defining the
    familiar notion of an _injective function_. *)

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

Lemma succ_inj : injective S.
Proof.
  intros n m H. injection H as H1. apply H1.
Qed.

(** The equality operator [=] is also a function that returns a
    [Prop].

    The expression [n = m] is syntactic sugar for [eq n m] (defined
    using Coq's [Notation] mechanism). Because [eq] can be used with
    elements of any type, it is also polymorphic: *)

Check @eq.
(* ===> forall A : Type, A -> A -> Prop *)

(** (Notice that we wrote [@eq] instead of [eq]: The type
    argument [A] to [eq] is declared as implicit, so we need to turn
    off implicit arguments to see the full type of [eq].) *)

(* ################################################################# *)
(** * Logical Connectives *)

(* ================================================================= *)
(** ** Conjunction *)

(** The _conjunction_, or _logical and_, of propositions [A] and [B]
    is written [A /\ B], representing the claim that both [A] and [B]
    are true. *)

Example and_example : 3 + 4 = 7 /\ 2 * 2 = 4.

(** To prove a conjunction, use the [split] tactic.  It will generate
    two subgoals, one for each part of the statement: *)

Proof.
  split.
  - (* 3 + 4 = 7 *) reflexivity.
  - (* 2 + 2 = 4 *) reflexivity.
Qed.

(** For any propositions [A] and [B], if we assume that [A] is true
    and we assume that [B] is true, we can conclude that [A /\ B] is
    also true. *)

Lemma and_intro : forall A B : Prop, A -> B -> A /\ B.
Proof.
  intros A B HA HB. split.
  - apply HA.
  - apply HB.
Qed.

(** Since applying a theorem with hypotheses to some goal has the
    effect of generating as many subgoals as there are hypotheses for
    that theorem, we can apply [and_intro] to achieve the same effect
    as [split]. *)

Example and_example' : 3 + 4 = 7 /\ 2 * 2 = 4.
Proof.
  apply and_intro.
  - (* 3 + 4 = 7 *) reflexivity.
  - (* 2 + 2 = 4 *) reflexivity.
Qed.

(** **** Exercise: 2 stars, standard (and_exercise)  *)
Example and_exercise :
  forall n m : nat, n + m = 0 -> n = 0 /\ m = 0.
Proof.
  (* SOLUTION: *)
    intros [|n] m.
  - (* n = 0 *)
    simpl. intros H. split.
    + reflexivity.
    + apply H.
  - (* n = S n' *)
    simpl. intros H. discriminate H.
Qed.
(** [] *)

(** So much for proving conjunctive statements.  To go in the other
    direction -- i.e., to _use_ a conjunctive hypothesis to help prove
    something else -- we employ the [destruct] tactic.

    If the proof context contains a hypothesis [H] of the form
    [A /\ B], writing [destruct H as [HA HB]] will remove [H] from the
    context and add two new hypotheses: [HA], stating that [A] is
    true, and [HB], stating that [B] is true.  *)

Lemma and_example2 :
  forall n m : nat, n = 0 /\ m = 0 -> n + m = 0.
Proof.
  (* WORKED IN CLASS *)
  intros n m H.
  destruct H as [Hn Hm].
  rewrite Hn. rewrite Hm.
  reflexivity.
Qed.

(** As usual, we can also destruct [H] right when we introduce it,
    instead of introducing and then destructing it: *)

Lemma and_example2' :
  forall n m : nat, n = 0 /\ m = 0 -> n + m = 0.
Proof.
  intros n m [Hn Hm].
  rewrite Hn. rewrite Hm.
  reflexivity.
Qed.

(** You may wonder why we bothered packing the two hypotheses [n = 0]
    and [m = 0] into a single conjunction, since we could have also
    stated the theorem with two separate premises: *)

Lemma and_example2'' :
  forall n m : nat, n = 0 -> m = 0 -> n + m = 0.
Proof.
  intros n m Hn Hm.
  rewrite Hn. rewrite Hm.
  reflexivity.
Qed.

(** For this theorem, both formulations are fine.  But it's important
    to understand how to work with conjunctive hypotheses because
    conjunctions often arise from intermediate steps in proofs,
    especially in bigger developments.  Here's a simple example: *)

Lemma and_example3 :
  forall n m : nat, n + m = 0 -> n * m = 0.
Proof.
  (* WORKED IN CLASS *)
  intros n m H.
  assert (H' : n = 0 /\ m = 0).
  { apply and_exercise. apply H. }
  destruct H' as [Hn Hm].
  rewrite Hn. reflexivity.
Qed.

(** Another common situation with conjunctions is that we know
    [A /\ B] but in some context we need just [A] (or just [B]).
    In such cases we can do a [destruct] (possibly as part of
    an [intros]) and use an underscore pattern [_] to indicate
    that the unneeded conjunct should just be thrown away.
    This is illustrated in the following proofs: *)

Lemma proj1 : forall P Q : Prop,
  P /\ Q -> P.
Proof.
  intros P Q HPQ.
  destruct HPQ as [HP _].
  apply HP.  Qed.

(** **** Exercise: 1 star, standard, optional (proj2)  *)
Lemma proj2 : forall P Q : Prop,
  P /\ Q -> Q.
Proof.
  (* SOLUTION: *)
  intros P Q [_ HQ].
  apply HQ.  Qed.
  (** [] *)

(** Finally, we sometimes need to rearrange the order of conjunctions
    and/or the grouping of multi-way conjunctions.  The following
    commutativity and associativity theorems are handy in such
    cases. *)

Theorem and_commut : forall P Q : Prop,
  P /\ Q -> Q /\ P.
Proof.
  intros P Q [HP HQ].
  split.
    - (* left *) apply HQ.
    - (* right *) apply HP.  Qed.

(** **** Exercise: 2 stars, standard (and_assoc)

    (In the following proof of associativity, notice how the _nested_
    [intros] pattern breaks the hypothesis [H : P /\ (Q /\ R)] down into
    [HP : P], [HQ : Q], and [HR : R].  Finish the proof from
    there.) *)

Theorem and_assoc : forall P Q R : Prop,
  P /\ (Q /\ R) -> (P /\ Q) /\ R.
Proof.
  intros P Q R [HP [HQ HR]].
  (* SOLUTION: *)
  split.
  - (* left *) split.
    + (* left *) apply HP.
    + (* right *) apply HQ.
  - (* right *) apply HR.  Qed.
(** [] *)

(** By the way, the infix notation [/\] is actually just syntactic
    sugar for [and A B].  That is, [and] is a Coq operator that takes
    two propositions as arguments and yields a proposition. *)

Check and.
(* ===> and : Prop -> Prop -> Prop *)

(* ================================================================= *)
(** ** Disjunction *)

(** Another important connective is the _disjunction_, or _logical or_,
    of two propositions: [A \/ B] is true when either [A] or [B]
    is.  (This infix notation stands for [or A B], where [or : Prop ->
    Prop -> Prop].) *)

(** To use a disjunctive hypothesis in a proof, we proceed by case
    analysis, which, as for [nat] or other data types, can be done
    explicitly with [destruct] or implicitly with an [intros] pattern: *)

Lemma eq_mult_0 :
  forall n m : nat, n = 0 \/ m = 0 -> n * m = 0.
Proof.
  (* This pattern implicitly does case analysis on
     [n = 0 \/ m = 0] *)
  intros n m [Hn | Hm].
  - (* Here, [n = 0] *)
    rewrite Hn. reflexivity.
  - (* Here, [m = 0] *)
    rewrite Hm. rewrite <- mult_n_O.
    reflexivity.
Qed.

(** Conversely, to show that a disjunction holds, we need to show that
    one of its sides does. This is done via two tactics, [left] and
    [right].  As their names imply, the first one requires
    proving the left side of the disjunction, while the second
    requires proving its right side.  Here is a trivial use... *)

Lemma or_intro_l : forall A B : Prop, A -> A \/ B.
Proof.
  intros A B HA.
  left.
  apply HA.
Qed.

(** ... and here is a slightly more interesting example requiring both
    [left] and [right]: *)

Lemma zero_or_succ :
  forall n : nat, n = 0 \/ n = S (pred n).
Proof.
  (* WORKED IN CLASS *)
  intros [|n].
  - left. reflexivity.
  - right. reflexivity.
Qed.

(** **** Exercise: 1 star, standard (mult_eq_0)  *)
Lemma mult_eq_0 :
  forall n m, n * m = 0 -> n = 0 \/ m = 0.
Proof.
  (* SOLUTION: *)
  intros [|n] m H.
  - left. reflexivity.
  - destruct m as [|m].
    + right. reflexivity.
    + simpl in H. discriminate H.
Qed.
(** [] *)

(** **** Exercise: 1 star, standard (or_commut)  *)
Theorem or_commut : forall P Q : Prop,
  P \/ Q  -> Q \/ P.
Proof.
  (* SOLUTION: *)
  intros P Q [HP | HQ].
  - (* left *) right. apply HP.
  - (* right *) left. apply HQ.  Qed.
(** [] *)

(* ================================================================= *)
(** ** Falsehood and Negation

    So far, we have mostly been concerned with proving that certain
    things are _true_ -- addition is commutative, appending lists is
    associative, etc.  Of course, we may also be interested in
    negative results, showing that some given proposition is _not_
    true. In Coq, such statements are expressed with the negation
    operator [~]. *)

(** To see how negation works, recall the _principle of explosion_
    from the [Tactics] chapter; it asserts that, if we assume a
    contradiction, then any other proposition can be derived.

    Following this intuition, we could define [~ P] ("not [P]") as
    [forall Q, P -> Q].

    Coq actually makes a slightly different (but equivalent) choice,
    defining [~ P] as [P -> False], where [False] is a specific
    contradictory proposition defined in the standard library. *)

Module MyNot.

Definition not (P:Prop) := P -> False.

Notation "~ x" := (not x) : type_scope.

Check not.
(* ===> Prop -> Prop *)

End MyNot.

(** Since [False] is a contradictory proposition, the principle of
    explosion also applies to it. If we get [False] into the proof
    context, we can use [destruct] on it to complete any goal: *)

Theorem ex_falso_quodlibet : forall (P:Prop),
  False -> P.
Proof.
  (* WORKED IN CLASS *)
  intros P contra.
  destruct contra.  Qed.

(** The Latin _ex falso quodlibet_ means, literally, "from falsehood
    follows whatever you like"; this is another common name for the
    principle of explosion. *)

(** **** Exercise: 2 stars, standard, optional (not_implies_our_not)

    Show that Coq's definition of negation implies the intuitive one
    mentioned above: *)

Fact not_implies_our_not : forall (P:Prop),
  ~ P -> (forall (Q:Prop), P -> Q).
Proof.
  (* SOLUTION: *)
  intros P H Q HP. apply ex_falso_quodlibet. apply H. apply HP.
Qed.
(** [] *)

(** Inequality is a frequent enough example of negated statement
    that there is a special notation for it, [x <> y]:

      Notation "x <> y" := (~(x = y)).
*)

(** We can use [not] to state that [0] and [1] are different elements
    of [nat]: *)

Theorem zero_not_one : 0 <> 1.
Proof.
  (** The proposition [0 <> 1] is exactly the same as
      [~(0 = 1)], that is [not (0 = 1)], which unfolds to
      [(0 = 1) -> False]. (We use [unfold not] explicitly here
      to illustrate that point, but generally it can be omitted.) *)
  unfold not.
  (** To prove an inequality, we may assume the opposite
      equality... *)
  intros contra.
  (** ... and deduce a contradiction from it. Here, the
      equality [O = S O] contradicts the disjointness of
      constructors [O] and [S], so [discriminate] takes care
      of it. *)
  discriminate contra.
Qed.

(** It takes a little practice to get used to working with negation in
    Coq.  Even though you can see perfectly well why a statement
    involving negation is true, it can be a little tricky at first to
    get things into the right configuration so that Coq can understand
    it!  Here are proofs of a few familiar facts to get you warmed
    up. *)

Theorem not_False :
  ~ False.
Proof.
  unfold not. intros H. destruct H. Qed.

Theorem contradiction_implies_anything : forall P Q : Prop,
  (P /\ ~P) -> Q.
Proof.
  (* WORKED IN CLASS *)
  intros P Q [HP HNA]. unfold not in HNA.
  apply HNA in HP. destruct HP.  Qed.

Theorem double_neg : forall P : Prop,
  P -> ~~P.
Proof.
  (* WORKED IN CLASS *)
  intros P H. unfold not. intros G. apply G. apply H.  Qed.

(** **** Exercise: 2 stars, advanced (double_neg_inf)

    Write an informal proof of [double_neg]:

   _Theorem_: [P] implies [~~P], for any proposition [P]. *)

(* SOLUTION: *)
(* _Proof_:
   Let a proposition [P] be given, and suppose we have
   evidence for [P].  We must show [~~P] -- i.e., [~P -> False], so
   suppose [~P] as well and try to derive [False].
   Then we have both [P] and [~P] (i.e., [P -> False]) from which
   we can indeed derive [False].  So [~~P] holds. *)

(* Do not modify the following line: *)
Definition manual_grade_for_double_neg_inf : option (nat*string) := None.
(** [] *)

(** **** Exercise: 2 stars, standard, recommended (contrapositive)  *)
Theorem contrapositive : forall (P Q : Prop),
  (P -> Q) -> (~Q -> ~P).
Proof.
  (* SOLUTION: *)
  intros P Q H HNotB HP.
  apply HNotB.  apply H. apply HP.  Qed.
(** [] *)

(** **** Exercise: 1 star, standard (not_both_true_and_false)  *)
Theorem not_both_true_and_false : forall P : Prop,
  ~ (P /\ ~P).
Proof.
  (* SOLUTION: *)
  intros P H. destruct H as [HP HNA]. apply HNA. apply HP.  Qed.
(** [] *)

(** **** Exercise: 1 star, advanced (informal_not_PNP)

    Write an informal proof (in English) of the proposition [forall P
    : Prop, ~(P /\ ~P)]. *)

(* SOLUTION: *)
(* _Proof_: Suppose, for some [P], that [(P /\ ~P)] holds.  Recall
  that [~P] is defined as [P -> False].  Given [P] and [P -> False],
  we can prove [False], so [(P /\ ~P) -> False], i.e., [~ (P /\ ~P)].
*)

(* Do not modify the following line: *)
Definition manual_grade_for_informal_not_PNP : option (nat*string) := None.
(** [] *)

(** Similarly, since inequality involves a negation, it requires a
    little practice to be able to work with it fluently.  Here is one
    useful trick.  If you are trying to prove a goal that is
    nonsensical (e.g., the goal state is [false = true]), apply
    [ex_falso_quodlibet] to change the goal to [False].  This makes it
    easier to use assumptions of the form [~P] that may be available
    in the context -- in particular, assumptions of the form
    [x<>y]. *)

Theorem not_true_is_false : forall b : bool,
  b <> true -> b = false.
Proof.
  intros b H.
  destruct b.
  - (* b = true *)
    unfold not in H.
    apply ex_falso_quodlibet.
    apply H. reflexivity.
  - (* b = false *)
    reflexivity.
Qed.

(** Since reasoning with [ex_falso_quodlibet] is quite common, Coq
    provides a built-in tactic, [exfalso], for applying it. *)

Theorem not_true_is_false' : forall b : bool,
  b <> true -> b = false.
Proof.
  intros [] H.          (* note implicit [destruct b] here *)
  - (* b = true *)
    unfold not in H.
    exfalso.                (* <=== *)
    apply H. reflexivity.
  - (* b = false *) reflexivity.
Qed.

(* ================================================================= *)
(** ** Truth *)

(** Besides [False], Coq's standard library also defines [True], a
    proposition that is trivially true. To prove it, we use the
    predefined constant [I : True]: *)

Lemma True_is_true : True.
Proof. apply I. Qed.

(** Unlike [False], which is used extensively, [True] is used quite
    rarely, since it is trivial (and therefore uninteresting) to prove
    as a goal, and it carries no useful information as a hypothesis.

    But it can be quite useful when defining complex [Prop]s using
    conditionals or as a parameter to higher-order [Prop]s.
    We will see examples of such uses of [True] later on. *)

(* ================================================================= *)
(** ** Logical Equivalence *)

(** The handy "if and only if" connective, which asserts that two
    propositions have the same truth value, is just the conjunction of
    two implications. *)

Module MyIff.

Definition iff (P Q : Prop) := (P -> Q) /\ (Q -> P).

Notation "P <-> Q" := (iff P Q)
                      (at level 95, no associativity)
                      : type_scope.

End MyIff.

Theorem iff_sym : forall P Q : Prop,
  (P <-> Q) -> (Q <-> P).
Proof.
  (* WORKED IN CLASS *)
  intros P Q [HAB HBA].
  split.
  - (* -> *) apply HBA.
  - (* <- *) apply HAB.  Qed.

Lemma not_true_iff_false : forall b,
  b <> true <-> b = false.
Proof.
  (* WORKED IN CLASS *)
  intros b. split.
  - (* -> *) apply not_true_is_false.
  - (* <- *)
    intros H. rewrite H. intros H'. discriminate H'.
Qed.

(** **** Exercise: 1 star, standard, optional (iff_properties)

    Using the above proof that [<->] is symmetric ([iff_sym]) as
    a guide, prove that it is also reflexive and transitive. *)

Theorem iff_refl : forall P : Prop,
  P <-> P.
Proof.
  (* SOLUTION: *)
  intros P. split.
    - (* -> *) intros H. apply H.
    - (* <- *) intros H. apply H.  Qed.

Theorem iff_trans : forall P Q R : Prop,
  (P <-> Q) -> (Q <-> R) -> (P <-> R).
Proof.
  (* SOLUTION: *)
  intros P Q R [HAB HBA] [HBC HCB].
  split.
    - (* -> *) intros HP. apply HBC. apply HAB. apply HP.
    - (* <- *) intros HR. apply HBA. apply HCB. apply HR.  Qed.
(** [] *)

(** **** Exercise: 3 stars, standard (or_distributes_over_and)  *)
Theorem or_distributes_over_and : forall P Q R : Prop,
  P \/ (Q /\ R) <-> (P \/ Q) /\ (P \/ R).
Proof.
  (* SOLUTION: *)
  intros P Q R. split.
  - (* -> *)
    intros [HP | [HQ HR]].
    + split.
      * left. apply HP.
      * left. apply HP.
    + split.
      * right. apply HQ.
      * right. apply HR.
  - (* <- *)
    intros [[HP1 | HQ] [HP2 | HR]].
    + left. apply HP1.
    + left. apply HP1.
    + left. apply HP2.
    + right. split.
      * apply HQ.
      * apply HR.
Qed.
(** [] *)

(* ================================================================= *)
(** ** Setoids and Logical Equivalence *)

(** Some of Coq's tactics treat [iff] statements specially, avoiding
    the need for some low-level proof-state manipulation.  In
    particular, [rewrite] and [reflexivity] can be used with [iff]
    statements, not just equalities.  To enable this behavior, we need
    to import a Coq library that supports it: *)

From Coq Require Import Setoids.Setoid.

(** A "setoid" is a set equipped with an equivalence relation,
    that is, a relation that is reflexive, symmetric, and transitive.
    When two elements of a set are equivalent according to the
    relation, [rewrite] can be used to replace one element with the
    other. We've seen that already with the equality relation [=] in
    Coq: when [x = y], we can use [rewrite] to replace [x] with [y],
    or vice-versa.

    Similarly, the logical equivalence relation [<->] is reflexive,
    symmetric, and transitive, so (after importing [Setoid]) we can
    use it to replace one part of a proposition with another: if [P <->
    Q], then we can use [rewrite] to replace [P] with [Q], or
    vice-versa. *)

(** Here is a simple example demonstrating how these tactics work with
    [iff].  First, let's prove a couple of basic iff equivalences... *)

Lemma mult_0 : forall n m, n * m = 0 <-> n = 0 \/ m = 0.
Proof.
  split.
  - apply mult_eq_0.
  - apply eq_mult_0.
Qed.

Lemma or_assoc :
  forall P Q R : Prop, P \/ (Q \/ R) <-> (P \/ Q) \/ R.
Proof.
  intros P Q R. split.
  - intros [H | [H | H]].
    + left. left. apply H.
    + left. right. apply H.
    + right. apply H.
  - intros [[H | H] | H].
    + left. apply H.
    + right. left. apply H.
    + right. right. apply H.
Qed.

(** We can now use these facts with [rewrite] and [reflexivity] to
    give smooth proofs of statements involving equivalences.  Here is
    a ternary version of the previous [mult_0] result: *)

Lemma mult_0_3 :
  forall n m p, n * m * p = 0 <-> n = 0 \/ m = 0 \/ p = 0.
Proof.
  intros n m p.
  rewrite mult_0. rewrite mult_0. rewrite or_assoc.
  reflexivity.
Qed.

(** The [apply] tactic can also be used with [<->]. When given an
    equivalence as its argument, [apply] tries to guess which direction of
    the equivalence will be useful.. *)

Lemma apply_iff_example :
  forall n m : nat, n * m = 0 -> n = 0 \/ m = 0.
Proof.
  intros n m H. apply mult_0. apply H.
Qed.

(* ================================================================= *)
(** ** Existential Quantification *)

(** Another important logical connective is _existential
    quantification_.  To say that there is some [x] of type [T] such
    that some property [P] holds of [x], we write [exists x : T,
    P]. As with [forall], the type annotation [: T] can be omitted if
    Coq is able to infer from the context what the type of [x] should
    be. *)

(** To prove a statement of the form [exists x, P], we must show that
    [P] holds for some specific choice of value for [x], known as the
    _witness_ of the existential.  This is done in two steps: First,
    we explicitly tell Coq which witness [t] we have in mind by
    invoking the tactic [exists t].  Then we prove that [P] holds after
    all occurrences of [x] are replaced by [t]. *)

Lemma four_is_even : exists n : nat, 4 = n + n.
Proof.
  exists 2. reflexivity.
Qed.

(** Conversely, if we have an existential hypothesis [exists x, P] in
    the context, we can destruct it to obtain a witness [x] and a
    hypothesis stating that [P] holds of [x]. *)

Theorem exists_example_2 : forall n,
  (exists m, n = 4 + m) ->
  (exists o, n = 2 + o).
Proof.
  (* WORKED IN CLASS *)
  intros n [m Hm]. (* note implicit [destruct] here *)
  exists (2 + m).
  apply Hm.  Qed.

(** **** Exercise: 1 star, standard, recommended (dist_not_exists)

    Prove that "[P] holds for all [x]" implies "there is no [x] for
    which [P] does not hold."  (Hint: [destruct H as [x E]] works on
    existential assumptions!)  *)

Theorem dist_not_exists : forall (X:Type) (P : X -> Prop),
  (forall x, P x) -> ~ (exists x, ~ P x).
Proof.
  (* SOLUTION: *)
  intros X P H [x Hx].
  apply Hx. apply H. Qed.
(** [] *)

(** **** Exercise: 2 stars, standard (dist_exists_or)

    Prove that existential quantification distributes over
    disjunction. *)

Theorem dist_exists_or : forall (X:Type) (P Q : X -> Prop),
  (exists x, P x \/ Q x) <-> (exists x, P x) \/ (exists x, Q x).
Proof.
   (* SOLUTION: *)
  intros X P Q. split.
  - (* -> *) intros [x [HP | HQ]].
    + (* P x *) left. exists x. apply HP.
    + (* Q x *) right. exists x. apply HQ.
  - (* <- *) intros [[x Hx] | [x Hx]].
    + (* exists x, P x *)
      exists x. left. apply Hx.
    + (* exists x, Q x *)
      exists x. right. apply Hx.
Qed.
(** [] *)

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

(** The logical connectives that we have seen provide a rich
    vocabulary for defining complex propositions from simpler ones.
    To illustrate, let's look at how to express the claim that an
    element [x] occurs in a list [l].  Notice that this property has a
    simple recursive structure:

       - If [l] is the empty list, then [x] cannot occur in it, so the
         property "[x] appears in [l]" is simply false.

       - Otherwise, [l] has the form [x' :: l'].  In this case, [x]
         occurs in [l] if either it is equal to [x'] or it occurs in
         [l']. *)

(** We can translate this directly into a straightforward recursive
    function taking an element and a list and returning a proposition: *)

Fixpoint In {A : Type} (x : A) (l : list A) : Prop :=
  match l with
  | [] => False
  | x' :: l' => x' = x \/ In x l'
  end.

(** When [In] is applied to a concrete list, it expands into a
    concrete sequence of nested disjunctions. *)

Example In_example_1 : In 4 [1; 2; 3; 4; 5].
Proof.
  (* WORKED IN CLASS *)
  simpl. right. right. right. left. reflexivity.
Qed.

Example In_example_2 :
  forall n, In n [2; 4] ->
  exists n', n = 2 * n'.
Proof.
  (* WORKED IN CLASS *)
  simpl.
  intros n [H | [H | []]].
  - exists 1. rewrite <- H. reflexivity.
  - exists 2. rewrite <- H. reflexivity.
Qed.
(** (Notice the use of the empty pattern to discharge the last case
    _en passant_.) *)

(** We can also prove more generic, higher-level lemmas about [In].

    Note, in the next, how [In] starts out applied to a variable and
    only gets expanded when we do case analysis on this variable: *)

Lemma In_map :
  forall (A B : Type) (f : A -> B) (l : list A) (x : A),
    In x l ->
    In (f x) (map f l).
Proof.
  intros A B f l x.
  induction l as [|x' l' IHl'].
  - (* l = nil, contradiction *)
    simpl. intros [].
  - (* l = x' :: l' *)
    simpl. intros [H | H].
    + rewrite H. left. reflexivity.
    + right. apply IHl'. apply H.
Qed.

(** This way of defining propositions recursively, though convenient
    in some cases, also has some drawbacks.  In particular, it is
    subject to Coq's usual restrictions regarding the definition of
    recursive functions, e.g., the requirement that they be "obviously
    terminating."  In the next chapter, we will see how to define
    propositions _inductively_, a different technique with its own set
    of strengths and limitations. *)

(** **** Exercise: 3 stars, standard (In_map_iff)  *)
Lemma In_map_iff :
  forall (A B : Type) (f : A -> B) (l : list A) (y : B),
    In y (map f l) <->
    exists x, f x = y /\ In x l.
Proof.
  intros A B f l y. split.
  (* SOLUTION: *)
  { induction l as [|x l' IHl'].
    - (* l = nil, contradiction *)
      simpl. intros [].
    - (* l = x :: l' *)
      simpl. intros [H | H].
      + exists x. split.
        * apply H.
        * left. reflexivity.
      + apply IHl' in H.
        destruct H as [x' [H1 H2]].
        exists x'. split.
        * apply H1.
        * right. apply H2. }
  { intros [x [H1 H2]]. rewrite <- H1.
    apply In_map. apply H2. }
Qed.
(** [] *)

(** **** Exercise: 2 stars, standard (In_app_iff)  *)
Lemma In_app_iff : forall A l l' (a:A),
  In a (l++l') <-> In a l \/ In a l'.
Proof.
  (* SOLUTION: *)
  intros A l. induction l as [|a' l' IH].
  - intros l' a. simpl. split.
    + intros H. right. apply H.
    + intros [[]|H]. apply H.
  - intros l'' a. simpl. rewrite IH. rewrite or_assoc.
    reflexivity. Qed.
(** [] *)

(** **** Exercise: 3 stars, standard, recommended (All)

    Recall that functions returning propositions can be seen as
    _properties_ of their arguments. For instance, if [P] has type
    [nat -> Prop], then [P n] states that property [P] holds of [n].

    Drawing inspiration from [In], write a recursive function [All]
    stating that some property [P] holds of all elements of a list
    [l]. To make sure your definition is correct, prove the [All_In]
    lemma below.  (Of course, your definition should _not_ just
    restate the left-hand side of [All_In].) *)

Fixpoint All {T : Type} (P : T -> Prop) (l : list T) : Prop
  (* SOLUTION: *) :=
  match l with
  | [] => True
  | x :: l' => P x /\ All P l'
  end.

Lemma All_In :
  forall T (P : T -> Prop) (l : list T),
    (forall x, In x l -> P x) <->
    All P l.
Proof.
  (* SOLUTION: *)
  intros T P l.
  induction l as [|x l IHl].
  - simpl. split.
    + intros _. split.
    + intros _ x [].
  - simpl. rewrite <- IHl. split.
    + intros H. split.
      * apply H. left. reflexivity.
      * intros x' Hx'. apply H. right.
        apply Hx'.
    + intros [Hx H] x' [Hxx'|Hl].
      * rewrite <- Hxx'. apply Hx.
      * apply H. apply Hl.
Qed.
(** [] *)

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

    Complete the definition of the [combine_odd_even] function below.
    It takes as arguments two properties of numbers, [Podd] and
    [Peven], and it should return a 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) : nat -> Prop
  (* SOLUTION: *) :=
  fun n => if oddb n then Podd n else Peven n.

(** To test your definition, 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.
  (* SOLUTION: *)
  intros Podd Peven n Hodd Heven.
  unfold combine_odd_even.
  destruct (oddb n).
  - (* oddn n = true *)
    apply Hodd. reflexivity.
  - (* oddn n = false *)
    apply Heven. 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.
  (* SOLUTION: *)
  unfold combine_odd_even.
  intros Podd Peven n H Hodd.
  destruct (oddb n).
  - (* oddb n = true *)
    apply H.
  - (* oddb n = false *)
    discriminate Hodd. 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.
  (* SOLUTION: *)
  unfold combine_odd_even.
  intros Podd Peven n H Heven.
  destruct (oddb n).
  - (* oddb n = true *)
    discriminate Heven.
  - (* oddb n = false *)
    apply H. Qed.
  (** [] *)

(* ################################################################# *)
(** * Applying Theorems to Arguments *)

(** One feature of Coq that distinguishes it from some other
    popular proof assistants (e.g., ACL2 and Isabelle) is that it
    treats _proofs_ as first-class objects.

    There is a great deal to be said about this, but it is not
    necessary to understand it all in detail in order to use Coq.  This
    section gives just a taste, while a deeper exploration can be
    found in the optional chapters [ProofObjects] and
    [IndPrinciples]. *)

(** We have seen that we can use the [Check] command to ask Coq to
    print the type of an expression.  We can also use [Check] to ask
    what theorem a particular identifier refers to. *)

Check plus_comm.
(* ===> forall n m : nat, n + m = m + n *)

(** Coq prints the _statement_ of the [plus_comm] theorem in the same
    way that it prints the _type_ of any term that we ask it to
    [Check].  Why? *)

(** The reason is that the identifier [plus_comm] actually refers to a
    _proof object_ -- a data structure that represents a logical
    derivation establishing of the truth of the statement [forall n m
    : nat, n + m = m + n].  The type of this object _is_ the statement
    of the theorem that it is a proof of. *)

(** Intuitively, this makes sense because the statement of a theorem
    tells us what we can use that theorem for, just as the type of a
    computational object tells us what we can do with that object --
    e.g., if we have a term of type [nat -> nat -> nat], we can give
    it two [nat]s as arguments and get a [nat] back.  Similarly, if we
    have an object of type [n = m -> n + n = m + m] and we provide it
    an "argument" of type [n = m], we can derive [n + n = m + m]. *)

(** Operationally, this analogy goes even further: by applying a
    theorem, as if it were a function, to hypotheses with matching
    types, we can specialize its result without having to resort to
    intermediate assertions.  For example, suppose we wanted to prove
    the following result: *)

Lemma plus_comm3 :
  forall x y z, x + (y + z) = (z + y) + x.

(** It appears at first sight that we ought to be able to prove this
    by rewriting with [plus_comm] twice to make the two sides match.
    The problem, however, is that the second [rewrite] will undo the
    effect of the first. *)

Proof.
  (* WORKED IN CLASS *)
  intros x y z.
  rewrite plus_comm.
  rewrite plus_comm.
  (* We are back where we started... *)
Abort.

(** We saw similar problems back in Chapter [Induction], and saw one
    way to work around them by using [assert] to derive a specialized version
    of [plus_comm] that can be used to rewrite exactly where we
    want. *)

Lemma plus_comm3_take2 :
  forall x y z, x + (y + z) = (z + y) + x.
Proof.
  intros x y z.
  rewrite plus_comm.
  assert (H : y + z = z + y).
  { rewrite plus_comm. reflexivity. }
  rewrite H.
  reflexivity.
Qed.

(** A more elegant alternative is to apply [plus_comm] directly to the
    arguments we want to instantiate it with, in much the same way as
    we apply a polymorphic function to a type argument. *)

Lemma plus_comm3_take3 :
  forall x y z, x + (y + z) = (z + y) + x.
Proof.
  intros x y z.
  rewrite plus_comm.
  rewrite (plus_comm y z).
  reflexivity.
Qed.

(** Let us show another example of using a theorem or lemma
    like a function. The following theorem says: any list [l]
    containing some element must be nonempty. *)

Lemma in_not_nil :
  forall A (x : A) (l : list A), In x l -> l <> [].
Proof.
  intros A x l H. unfold not. intro Hl. destruct l.
  - simpl in H. destruct H.
  - discriminate Hl.
Qed.

(** What makes this interesting is that one quantified variable
    ([x]) does not appear in the conclusion ([l <> []]). *)

(** We can use this lemma to prove the special case where [x]
    is [42]. Naively, the tactic [apply in_not_nil] will fail because
    it cannot infer the value of [x]. There are several ways to work
    around that... *)

Lemma in_not_nil_42 :
  forall l : list nat, In 42 l -> l <> [].
Proof.
  (* WORKED IN CLASS *)
  intros l H.
  Fail apply in_not_nil.
Abort.

(* [apply ... with ...] *)
Lemma in_not_nil_42_take2 :
  forall l : list nat, In 42 l -> l <> [].
Proof.
  intros l H.
  apply in_not_nil with (x := 42).
  apply H.
Qed.

(* [apply ... in ...] *)
Lemma in_not_nil_42_take3 :
  forall l : list nat, In 42 l -> l <> [].
Proof.
  intros l H.
  apply in_not_nil in H.
  apply H.
Qed.

(* Explicitly apply the lemma to the value for [x]. *)
Lemma in_not_nil_42_take4 :
  forall l : list nat, In 42 l -> l <> [].
Proof.
  intros l H.
  apply (in_not_nil nat 42).
  apply H.
Qed.

(* Explicitly apply the lemma to a hypothesis. *)
Lemma in_not_nil_42_take5 :
  forall l : list nat, In 42 l -> l <> [].
Proof.
  intros l H.
  apply (in_not_nil _ _ _ H).
Qed.

(** You can "use theorems as functions" in this way with almost all
    tactics that take a theorem name as an argument.  Note also that
    theorem application uses the same inference mechanisms as function
    application; thus, it is possible, for example, to supply
    wildcards as arguments to be inferred, or to declare some
    hypotheses to a theorem as implicit by default.  These features
    are illustrated in the proof below. (The details of how this proof
    works are not critical -- the goal here is just to illustrate what
    can be done.) *)

Example lemma_application_ex :
  forall {n : nat} {ns : list nat},
    In n (map (fun m => m * 0) ns) ->
    n = 0.
Proof.
  intros n ns H.
  destruct (proj1 _ _ (In_map_iff _ _ _ _ _) H)
           as [m [Hm _]].
  rewrite mult_0_r in Hm. rewrite <- Hm. reflexivity.
Qed.

(** We will see many more examples in later chapters. *)

(* ################################################################# *)
(** * Coq vs. Set Theory *)

(** Coq's logical core, the _Calculus of Inductive
    Constructions_, differs in some important ways from other formal
    systems that are used by mathematicians to write down precise and
    rigorous proofs.  For example, in the most popular foundation for
    paper-and-pencil mathematics, Zermelo-Fraenkel Set Theory (ZFC), a
    mathematical object can potentially be a member of many different
    sets; a term in Coq's logic, on the other hand, is a member of at
    most one type.  This difference often leads to slightly different
    ways of capturing informal mathematical concepts, but these are,
    by and large, about equally natural and easy to work with.  For
    example, instead of saying that a natural number [n] belongs to
    the set of even numbers, we would say in Coq that [even n] holds,
    where [even : nat -> Prop] is a property describing even numbers.

    However, there are some cases where translating standard
    mathematical reasoning into Coq can be cumbersome or sometimes
    even impossible, unless we enrich the core logic with additional
    axioms.

    We conclude this chapter with a brief discussion of some of the
    most significant differences between the two worlds. *)

(* ================================================================= *)
(** ** Functional Extensionality *)

(** The equality assertions that we have seen so far mostly have
    concerned elements of inductive types ([nat], [bool], etc.).  But
    since Coq's equality operator is polymorphic, these are not the
    only possibilities -- in particular, we can write propositions
    claiming that two _functions_ are equal to each other: *)

Example function_equality_ex1 :
  (fun x => 3 + x) = (fun x => (pred 4) + x).
Proof. reflexivity. Qed.

(** In common mathematical practice, two functions [f] and [g] are
    considered equal if they produce the same outputs:

    (forall x, f x = g x) -> f = g

    This is known as the principle of _functional extensionality_. *)

(** Informally speaking, an "extensional property" is one that
    pertains to an object's observable behavior.  Thus, functional
    extensionality simply means that a function's identity is
    completely determined by what we can observe from it -- i.e., in
    Coq terms, the results we obtain after applying it. *)

(** Functional extensionality is not part of Coq's built-in logic.
    This means that some "reasonable" propositions are not provable. *)

Example function_equality_ex2 :
  (fun x => plus x 1) = (fun x => plus 1 x).
Proof.
   (* Stuck *)
Abort.

(** However, we can add functional extensionality to Coq's core using
    the [Axiom] command. *)

Axiom functional_extensionality : forall {X Y: Type}
                                    {f g : X -> Y},
  (forall (x:X), f x = g x) -> f = g.

(** Using [Axiom] has the same effect as stating a theorem and
    skipping its proof using [Admitted], but it alerts the reader that
    this isn't just something we're going to come back and fill in
    later! *)

(** We can now invoke functional extensionality in proofs: *)

Example function_equality_ex2 :
  (fun x => plus x 1) = (fun x => plus 1 x).
Proof.
  apply functional_extensionality. intros x.
  apply plus_comm.
Qed.

(** Naturally, we must be careful when adding new axioms into Coq's
    logic, as they may render it _inconsistent_ -- that is, they may
    make it possible to prove every proposition, including [False],
    [2+2=5], etc.!

    Unfortunately, there is no simple way of telling whether an axiom
    is safe to add: hard work by highly-trained trained experts is
    generally required to establish the consistency of any particular
    combination of axioms.

    Fortunately, it is known that adding functional extensionality, in
    particular, _is_ consistent. *)

(** To check whether a particular proof relies on any additional
    axioms, use the [Print Assumptions] command.  *)

Print Assumptions function_equality_ex2.
(* ===>
     Axioms:
     functional_extensionality :
         forall (X Y : Type) (f g : X -> Y),
                (forall x : X, f x = g x) -> f = g *)

(** **** Exercise: 4 stars, standard (tr_rev_correct)

    One problem with the definition of the list-reversing function
    [rev] that we have is that it performs a call to [app] on each
    step; running [app] takes time asymptotically linear in the size
    of the list, which means that [rev] has quadratic running time.
    We can improve this with the following definition: *)

Fixpoint rev_append {X} (l1 l2 : list X) : list X :=
  match l1 with
  | [] => l2
  | x :: l1' => rev_append l1' (x :: l2)
  end.

Definition tr_rev {X} (l : list X) : list X :=
  rev_append l [].

(** This version is said to be _tail-recursive_, because the recursive
    call to the function is the last operation that needs to be
    performed (i.e., we don't have to execute [++] after the recursive
    call); a decent compiler will generate very efficient code in this
    case.  Prove that the two definitions are indeed equivalent. *)

Lemma rev_append_rev : forall X (l1 l2 : list X),
  rev_append l1 l2 = rev l1 ++ l2.
Proof.
  intros T l1. induction l1 as [|x l1' IHl1'].
  - intros acc. reflexivity.
  - intros acc. simpl. rewrite IHl1'. rewrite <- app_assoc.
    simpl. reflexivity.
Qed.

Lemma tr_rev_correct : forall X, @tr_rev X = @rev X.
(* SOLUTION: *)
Proof.
  intros X. apply functional_extensionality.
  intros l. unfold tr_rev.
  rewrite rev_append_rev. rewrite app_nil_r. reflexivity.
Qed.
(** [] *)

(* ================================================================= *)
(** ** Propositions and Booleans *)

(** We've seen two different ways of expressing logical claims in Coq:
    with _booleans_ (of type [bool]), and with _propositions_ (of type
    [Prop]).

    For instance, to claim that a number [n] is even, we can say
    either... *)

(** ... that [evenb n] evaluates to [true]... *)
Example even_42_bool : evenb 42 = true.
Proof. reflexivity. Qed.

(** ... or that there exists some [k] such that [n = double k]. *)
Example even_42_prop : exists k, 42 = double k.
Proof. exists 21. reflexivity. Qed.

(** Of course, it would be pretty strange if these two
    characterizations of evenness did not describe the same set of
    natural numbers!  Fortunately, we can prove that they do... *)

(** We first need two helper lemmas. *)
Theorem evenb_double : forall k, evenb (double k) = true.
Proof.
  intros k. induction k as [|k' IHk'].
  - reflexivity.
  - simpl. apply IHk'.
Qed.

(** **** Exercise: 3 stars, standard (evenb_double_conv)  *)
Theorem evenb_double_conv : forall n,
  exists k, n = if evenb n then double k
                else S (double k).
Proof.
  (* Hint: Use the [evenb_S] lemma from [Induction.v]. *)
  (* SOLUTION: *)
  intros n. induction n as [|n' [k Hk]].
  - simpl. exists 0. reflexivity.
  - rewrite evenb_S. destruct (evenb n').
    + simpl. exists k. rewrite Hk. reflexivity.
    + simpl. exists (S k). rewrite Hk. reflexivity.  Qed.
(** [] *)

Theorem even_bool_prop : forall n,
  evenb n = true <-> exists k, n = double k.
Proof.
  intros n. split.
  - intros H. destruct (evenb_double_conv n) as [k Hk].
    rewrite Hk. rewrite H. exists k. reflexivity.
  - intros [k Hk]. rewrite Hk. apply evenb_double.
Qed.

(** In view of this theorem, we say that the boolean computation
    [evenb n] is reflected in the truth of the proposition [exists k,
    n = double k]. *)

(** Similarly, to state that two numbers [n] and [m] are equal, we can
    say either
      - (1) that [n =? m] returns [true], or
      - (2) that [n = m].
    Again, these two notions are equivalent. *)

Theorem eqb_eq : forall n1 n2 : nat,
  n1 =? n2 = true <-> n1 = n2.
Proof.
  intros n1 n2. split.
  - apply eqb_true.
  - intros H. rewrite H. rewrite <- eqb_refl. reflexivity.
Qed.

(** However, even when the boolean and propositional formulations of a
    claim are equivalent from a purely logical perspective, they may
    not be equivalent _operationally_. *)

(** In the case of even numbers above, when proving the
    backwards direction of [even_bool_prop] (i.e., [evenb_double],
    going from the propositional to the boolean claim), we used a
    simple induction on [k].  On the other hand, the converse (the
    [evenb_double_conv] exercise) required a clever generalization,
    since we can't directly prove
    [(evenb n = true) -> (exists k, n = double k)]. *)

(** For these examples, the propositional claims are more useful than
    their boolean counterparts, but this is not always the case.  For
    instance, we cannot test whether a general proposition is true or
    not in a function definition; as a consequence, the following code
    fragment is rejected: *)

Fail Definition is_even_prime n :=
  if n = 2 then true
  else false.

(** Coq complains that [n = 2] has type [Prop], while it expects
    an element of [bool] (or some other inductive type with two
    elements).  The reason for this error message has to do with the
    _computational_ nature of Coq's core language, which is designed
    so that every function that it can express is computable and
    total.  One reason for this is to allow the extraction of
    executable programs from Coq developments.  As a consequence,
    [Prop] in Coq does _not_ have a universal case analysis operation
    telling whether any given proposition is true or false, since such
    an operation would allow us to write non-computable functions.

    Although general non-computable properties cannot be phrased as
    boolean computations, it is worth noting that even many
    _computable_ properties are easier to express using [Prop] than
    [bool], since recursive function definitions are subject to
    significant restrictions in Coq.  For instance, the next chapter
    shows how to define the property that a regular expression matches
    a given string using [Prop].  Doing the same with [bool] would
    amount to writing a regular expression matcher, which would be
    more complicated, harder to understand, and harder to reason
    about.

    Conversely, an important side benefit of stating facts using
    booleans is enabling some proof automation through computation
    with Coq terms, a technique known as _proof by
    reflection_.  Consider the following statement: *)

Example even_1000 : exists k, 1000 = double k.

(** The most direct proof of this fact is to give the value of [k]
    explicitly. *)

Proof. exists 500. reflexivity. Qed.

(** On the other hand, the proof of the corresponding boolean
    statement is even simpler: *)

Example even_1000' : evenb 1000 = true.
Proof. reflexivity. Qed.

(** What is interesting is that, since the two notions are equivalent,
    we can use the boolean formulation to prove the other one without
    mentioning the value 500 explicitly: *)

Example even_1000'' : exists k, 1000 = double k.
Proof. apply even_bool_prop. reflexivity. Qed.

(** Although we haven't gained much in terms of proof-script
    size in this case, larger proofs can often be made considerably
    simpler by the use of reflection.  As an extreme example, the Coq
    proof of the famous _4-color theorem_ uses reflection to reduce
    the analysis of hundreds of different cases to a boolean
    computation. *)

(** Another notable difference is that the negation of a "boolean
    fact" is straightforward to state and prove: simply flip the
    expected boolean result. *)

Example not_even_1001 : evenb 1001 = false.
Proof.
  (* WORKED IN CLASS *)
  reflexivity.
Qed.

(** In contrast, propositional negation may be more difficult
    to work with. *)

Example not_even_1001' : ~(exists k, 1001 = double k).
Proof.
  (* WORKED IN CLASS *)
  rewrite <- even_bool_prop.
  unfold not.
  simpl.
  intro H.
  discriminate H.
Qed.

(** Equality provides a complementary example: knowing that
    [n =? m = true] is generally of little direct help in the middle
    of a proof involving [n] and [m]; however, if we convert the
    statement to the equivalent form [n = m], we can rewrite with it.
 *)

Lemma plus_eqb_example : forall n m p : nat,
    n =? m = true -> n + p =? m + p = true.
Proof.
  (* WORKED IN CLASS *)
  intros n m p H.
    rewrite eqb_eq in H.
  rewrite H.
  rewrite eqb_eq.
  reflexivity.
Qed.

(** We won't cover reflection in much detail, but it serves as a good
    example showing the complementary strengths of booleans and
    general propositions. *)

(** **** Exercise: 2 stars, standard (logical_connectives)

    The following lemmas relate the propositional connectives studied
    in this chapter to the corresponding boolean operations. *)

Lemma andb_true_iff : forall b1 b2:bool,
  b1 && b2 = true <-> b1 = true /\ b2 = true.
Proof.
  (* SOLUTION: *)
  intros [].
  - simpl. intros b2. split.
    + intros H. split.
      * reflexivity.
      * apply H.
    + intros [_ H]. apply H.
  - simpl. intros b2. split.
    + intros H. discriminate H.
    + intros [H _]. discriminate H.  Qed.

Lemma orb_true_iff : forall b1 b2,
  b1 || b2 = true <-> b1 = true \/ b2 = true.
Proof.
  (* SOLUTION: *)
  intros [].
  - simpl. intros b2. split.
    + intros _. left. reflexivity.
    + intros _. reflexivity.
  - simpl. intros [].
    + split.
      * intros _. right. reflexivity.
      * intros _. reflexivity.
    + split.
      * intros H. discriminate H.
      * intros [H | H].
        { apply H. }
        { apply H. }  Qed.
(** [] *)

(** **** Exercise: 1 star, standard (eqb_neq)

    The following theorem is an alternate "negative" formulation of
    [eqb_eq] that is more convenient in certain
    situations (we'll see examples in later chapters). *)

Theorem eqb_neq : forall x y : nat,
  x =? y = false <-> x <> y.
Proof.
  (* SOLUTION: *)
  intros x y. rewrite <- not_true_iff_false. rewrite eqb_eq.
  reflexivity.  Qed.
(** [] *)

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

    Given a boolean operator [eqb] for testing equality of elements of
    some type [A], we can define a function [eqb_list] for testing
    equality of lists with elements in [A].  Complete the definition
    of the [eqb_list] function below.  To make sure that your
    definition is correct, prove the lemma [eqb_list_true_iff]. *)

Fixpoint eqb_list {A : Type} (eqb : A -> A -> bool)
                  (l1 l2 : list A) : bool
  (* SOLUTION: *) :=
  match l1, l2 with
  | [], [] => true
  | a1 :: l1, a2 :: l2 => eqb a1 a2 && eqb_list eqb l1 l2
  | _, _ => false
  end.

Lemma eqb_list_true_iff :
  forall A (eqb : A -> A -> bool),
    (forall a1 a2, eqb a1 a2 = true <-> a1 = a2) ->
    forall l1 l2, eqb_list eqb l1 l2 = true <-> l1 = l2.
Proof.
(* SOLUTION: *)
  intros A eqb Heqb l1.
  induction l1 as [|a1 l1 IH].
  - intros [|a2 l2].
    + split.
      * trivial.
      * trivial.
    + simpl. split.
      * intros contra. discriminate contra.
      * intros contra. discriminate contra.
  - intros [|a2 l2].
    + simpl. split.
      * intros contra. discriminate contra.
      * intros contra. discriminate contra.
    + simpl.
      rewrite andb_true_iff, Heqb, IH.
      split.
      * intros [H1 H2].
        rewrite H1, H2.
        reflexivity.
      * intros H. injection H as H1 H2.
        { split.
          - apply H1.
          - apply H2. }
Qed.
(** [] *)

(** **** Exercise: 2 stars, standard, recommended (All_forallb)

    Recall the function [forallb], from the exercise
    [forall_exists_challenge] in chapter [Tactics]: *)

Fixpoint forallb {X : Type} (test : X -> bool) (l : list X) : bool :=
  match l with
  | [] => true
  | x :: l' => andb (test x) (forallb test l')
  end.

(** Prove the theorem below, which relates [forallb] to the [All]
    property defined above. *)

Theorem forallb_true_iff : forall X test (l : list X),
   forallb test l = true <-> All (fun x => test x = true) l.
Proof.
  (* SOLUTION: *)
  intros X test l.
  induction l as [|x l' IHl'].
  - (* l = [] *)
    simpl. split.
    + intros _. split.
    + intros _. reflexivity.
  - (* l = x :: l' *)
    simpl.
    rewrite andb_true_iff. rewrite IHl'.
    reflexivity. Qed.

(** (Ungraded) Are there any important properties of the function
    [forallb] which are not captured by this specification? *)

(* SOLUTION: *)
(* This theorem exactly captures the input-output behaviour of
   [forallb]. However, it does not say anything about the running
   time.

    [] *)

(* ================================================================= *)
(** ** Classical vs. Constructive Logic *)

(** We have seen that it is not possible to test whether or not a
    proposition [P] holds while defining a Coq function.  You may be
    surprised to learn that a similar restriction applies to _proofs_!
    In other words, the following intuitive reasoning principle is not
    derivable in Coq: *)

Definition excluded_middle := forall P : Prop,
  P \/ ~ P.

(** To understand operationally why this is the case, recall
    that, to prove a statement of the form [P \/ Q], we use the [left]
    and [right] tactics, which effectively require knowing which side
    of the disjunction holds.  But the universally quantified [P] in
    [excluded_middle] is an _arbitrary_ proposition, which we know
    nothing about.  We don't have enough information to choose which
    of [left] or [right] to apply, just as Coq doesn't have enough
    information to mechanically decide whether [P] holds or not inside
    a function. *)

(** However, if we happen to know that [P] is reflected in some
    boolean term [b], then knowing whether it holds or not is trivial:
    we just have to check the value of [b]. *)

Theorem restricted_excluded_middle : forall P b,
  (P <-> b = true) -> P \/ ~ P.
Proof.
  intros P [] H.
  - left. rewrite H. reflexivity.
  - right. rewrite H. intros contra. discriminate contra.
Qed.

(** In particular, the excluded middle is valid for equations [n = m],
    between natural numbers [n] and [m]. *)

Theorem restricted_excluded_middle_eq : forall (n m : nat),
  n = m \/ n <> m.
Proof.
  intros n m.
  apply (restricted_excluded_middle (n = m) (n =? m)).
  symmetry.
  apply eqb_eq.
Qed.

(** It may seem strange that the general excluded middle is not
    available by default in Coq; after all, any given claim must be
    either true or false.  Nonetheless, there is an advantage in not
    assuming the excluded middle: statements in Coq can make stronger
    claims than the analogous statements in standard mathematics.
    Notably, if there is a Coq proof of [exists x, P x], it is
    possible to explicitly exhibit a value of [x] for which we can
    prove [P x] -- in other words, every proof of existence is
    necessarily _constructive_. *)

(** Logics like Coq's, which do not assume the excluded middle, are
    referred to as _constructive logics_.

    More conventional logical systems such as ZFC, in which the
    excluded middle does hold for arbitrary propositions, are referred
    to as _classical_. *)

(** The following example illustrates why assuming the excluded middle
    may lead to non-constructive proofs:

    _Claim_: There exist irrational numbers [a] and [b] such that [a ^
    b] is rational.

    _Proof_: It is not difficult to show that [sqrt 2] is irrational.
    If [sqrt 2 ^ sqrt 2] is rational, it suffices to take [a = b =
    sqrt 2] and we are done.  Otherwise, [sqrt 2 ^ sqrt 2] is
    irrational.  In this case, we can take [a = sqrt 2 ^ sqrt 2] and
    [b = sqrt 2], since [a ^ b = sqrt 2 ^ (sqrt 2 * sqrt 2) = sqrt 2 ^
    2 = 2].  []

    Do you see what happened here?  We used the excluded middle to
    consider separately the cases where [sqrt 2 ^ sqrt 2] is rational
    and where it is not, without knowing which one actually holds!
    Because of that, we wind up knowing that such [a] and [b] exist
    but we cannot determine what their actual values are (at least,
    using this line of argument).

    As useful as constructive logic is, it does have its limitations:
    There are many statements that can easily be proven in classical
    logic but that have much more complicated constructive proofs, and
    there are some that are known to have no constructive proof at
    all!  Fortunately, like functional extensionality, the excluded
    middle is known to be compatible with Coq's logic, allowing us to
    add it safely as an axiom.  However, we will not need to do so in
    this book: the results that we cover can be developed entirely
    within constructive logic at negligible extra cost.

    It takes some practice to understand which proof techniques must
    be avoided in constructive reasoning, but arguments by
    contradiction, in particular, are infamous for leading to
    non-constructive proofs.  Here's a typical example: suppose that
    we want to show that there exists [x] with some property [P],
    i.e., such that [P x].  We start by assuming that our conclusion
    is false; that is, [~ exists x, P x]. From this premise, it is not
    hard to derive [forall x, ~ P x].  If we manage to show that this
    intermediate fact results in a contradiction, we arrive at an
    existence proof without ever exhibiting a value of [x] for which
    [P x] holds!

    The technical flaw here, from a constructive standpoint, is that
    we claimed to prove [exists x, P x] using a proof of
    [~ ~ (exists x, P x)].  Allowing ourselves to remove double
    negations from arbitrary statements is equivalent to assuming the
    excluded middle, as shown in one of the exercises below.  Thus,
    this line of reasoning cannot be encoded in Coq without assuming
    additional axioms. *)

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

    Proving the consistency of Coq with the general excluded middle
    axiom requires complicated reasoning that cannot be carried out
    within Coq itself.  However, the following theorem implies that it
    is always safe to assume a decidability axiom (i.e., an instance
    of excluded middle) for any _particular_ Prop [P].  Why?  Because
    we cannot prove the negation of such an axiom.  If we could, we
    would have both [~ (P \/ ~P)] and [~ ~ (P \/ ~P)] (since [P]
    implies [~ ~ P], by lemma [double_neg], which we proved above),
    which would be a  contradiction.  But since we can't, it is safe
    to add [P \/ ~P] as an axiom. *)

Theorem excluded_middle_irrefutable: forall (P:Prop),
  ~ ~ (P \/ ~ P).
Proof.
  (* SOLUTION: *)
  intros P H.
  assert (H' : ~ P).
  { intros H'. apply H. left. apply H'. }
  assert (H'' : ~ ~ P).
  { intros H''. apply H. right. apply H'. }
  apply H''. apply H'. Qed.
(** [] *)

(** **** Exercise: 3 stars, advanced (not_exists_dist)

    It is a theorem of classical logic that the following two
    assertions are equivalent:

    ~ (exists x, ~ P x)
    forall x, P x

    The [dist_not_exists] theorem above proves one side of this
    equivalence. Interestingly, the other direction cannot be proved
    in constructive logic. Your job is to show that it is implied by
    the excluded middle. *)

Theorem not_exists_dist :
  excluded_middle ->
  forall (X:Type) (P : X -> Prop),
    ~ (exists x, ~ P x) -> (forall x, P x).
Proof.
  (* SOLUTION: *)
  intros Hem X P H x.
  destruct (Hem (P x)) as [HPx | HNPx].
  - (* P x *) apply HPx.
  - (* ~P x *)
    exfalso.
    apply H.
    exists x.
    apply HNPx.
Qed.
(** [] *)

(** **** Exercise: 5 stars, standard, optional (classical_axioms)

    For those who like a challenge, here is an exercise taken from the
    Coq'Art book by Bertot and Casteran (p. 123).  Each of the
    following four statements, together with [excluded_middle], can be
    considered as characterizing classical logic.  We can't prove any
    of them in Coq, but we can consistently add any one of them as an
    axiom if we wish to work in classical logic.

    Prove that all five propositions (these four plus
    [excluded_middle]) are equivalent.

    Hint: Rather than considering all pairs of statements pairwise,
          prove a single circular chain of implications that connects
          them all.
*)

Definition peirce := forall P Q: Prop,
  ((P->Q)->P)->P.

Definition double_negation_elimination := forall P:Prop,
  ~~P -> P.

Definition de_morgan_not_and_not := forall P Q:Prop,
  ~(~P /\ ~Q) -> P\/Q.

Definition implies_to_or := forall P Q:Prop,
  (P->Q) -> (~P\/Q).

(* SOLUTION: *)
Lemma ito__em :
  implies_to_or -> excluded_middle.
Proof.
  unfold implies_to_or, excluded_middle.
  intros Hito P.
  apply or_commut.
  apply Hito.
  intros HP. apply HP.
Qed.

Lemma em__ito :
  excluded_middle -> implies_to_or.
Proof.
  unfold implies_to_or, excluded_middle.
  intros Hem P Q H.
  destruct (Hem P) as [HP | HNP].
  - (* P *) right. apply H. apply HP.
  - (* ~P *) left. apply HNP.
Qed.

Lemma em__demorgan :
  excluded_middle -> de_morgan_not_and_not.
Proof.
  unfold excluded_middle, de_morgan_not_and_not.
  intros Hem P Q H.
  destruct (Hem P) as [HP | HNP].
  - (* P *)
    left. apply HP.
  - (* ~P *)
    destruct (Hem Q) as [HQ | HNQ].
    + (* Q *)
      right. apply HQ.
    + (* ~Q *)
      exfalso. apply H.
      split. apply HNP. apply HNQ.
Qed.

Lemma demorgan__em :
  de_morgan_not_and_not -> excluded_middle.
Proof.
  unfold de_morgan_not_and_not, excluded_middle.
  intros Hdm P.
  apply Hdm.
  unfold not. intros Hcontra.
  destruct Hcontra as [HNP HNNP].
  apply HNNP. apply HNP.
Qed.

Lemma em__dne :
  excluded_middle -> double_negation_elimination.
Proof.
  intros Hem P.
  destruct (Hem P) as [HP | HNP].
  - (* P *) intros H'. apply HP.
  - (* ~P *) intros H'. destruct (H' HNP).
Qed.

Lemma dne__demorgan :
  double_negation_elimination -> de_morgan_not_and_not.
Proof.
  intros Hc P Q H.
  apply Hc.
  intros H2.
  apply H.
  split.
  - (* left conjunct *) intros HP. apply H2. left. apply HP.
  - (* right conjunct *) intros HQ. apply H2. right. apply HQ.
Qed.

(** The above suffices (along with [demorgan__em]), but we can also
    prove it directly this way *)

Lemma dne__em :
  double_negation_elimination -> excluded_middle.
Proof.
  intros Hc P.
  apply Hc.
  apply excluded_middle_irrefutable.
Qed.

Lemma em__peirce :
  excluded_middle -> peirce.
Proof.
  intros Hem P Q H.
  destruct (Hem P) as [HP | HNP].
  - (* P *) apply HP.
  - (* ~P *)
    destruct (Hem (P -> Q)) as [HPQ | HNPQ].
    + (* P->Q *) apply H. apply HPQ.
    + (* ~(P->Q) *) assert (P -> Q) as HPQ.
      intros HP.
      exfalso.
      apply HNP. apply HP.
      apply H. apply HPQ.
Qed.

Lemma peirce__em :
  peirce -> excluded_middle.
Proof.
  intros Hp P.
  apply (Hp _ False).
  right.
  intros HP. apply H.
  left. apply HP.
Qed.
(** [] *)

(* Fri 30 Aug 2019 02:46:58 PM CEST *)


Require Import ZArith.
Require Import Arith.
Require Import List.

(* Define Heterogenous List by Inductive Types *)
Section hlist.
  (* Define HList *)
  Inductive hlist : (list Set) -> Type :=
  | HNil : hlist nil
  | HCons : forall (x:Set) (ls:list Set), x -> hlist ls -> hlist (x::ls).

(* Define Elements Access Function *)
(* build the function with the help of coq proof assistant
  Fixpoint hget0 (ls:list Set) (hls:hlist ls) (n:nat){struct n} : option (nth n ls Empty_set).
    inversion hls.
    exact None.
    induction n.
    simpl.
    exact (Some H).
    apply (hget0 ls0 X n).
  Defined.

  Print hget0.
*)

(* Build the function manually *)
  Fixpoint hget (ls:list Set) (hls:hlist ls) (n:nat){struct n} : option (nth n ls Empty_set) :=
    match hls in (hlist l) return option (nth n l Empty_set) with
    | HNil => None
    | HCons _ nil _ _ => None
    | HCons _ ys x xs =>
        match n with
        | 0 => Some x
        | S p => hget ys xs p
        end
    end.

End hlist.

Arguments HCons [x ls].
Arguments hget [ls].

(* Example of using HList *)
Definition someValues := HCons 3%Z (HCons 2%nat (HCons (3::5::4::nil) HNil)).
Check someValues.
Print someValues.

Eval simpl in (hget someValues 0).
Eval simpl in (hget someValues 1).
Eval simpl in (hget someValues 2).

(* Extract the code for Haskell or OCaml *)
Require Import Extraction.
(* Extraction Language Haskell. *)
(* Extraction Language OCaml. *)
Recursive Extraction hget.


Require Import Coq.Sets.Ensembles.
Require Import Coq.Sets.Finite_sets.

Lemma Sn_n : forall n : nat, ~ S n = n.
Proof.
  intros. induction n as [| n' IHn].
  - intro. discriminate H.
  - intro H. apply IHn.
    inversion H. assumption.
Qed.

Lemma le_trans : forall n m k : nat, n <= m -> m <= k -> n <= k.
Proof.
  intros * H1 H2. induction H2 as [| m'].
  - assumption.
  - constructor 2. assumption.
Qed.

Lemma S_le : forall n m :nat, n <= m -> S n <= S m .
Proof.
  intros * H. induction H.
  - constructor.
  - constructor 2. assumption.
Qed.

Lemma m_le_Sn : forall n m : nat, m <= S n <-> m <= n \/ m = S n.
Proof.
  intros. split; intro H.
  - destruct n as [| n']; inversion H as [| m' H1 H2].
    + right. reflexivity.
    + left. assumption.
    + right. reflexivity.
    + left. assumption.
  - elim H; intro H1.
    + constructor 2. assumption.
    + rewrite H1. constructor.
Qed.

Lemma lt_irrefl : forall n : nat, ~ n < n.
Proof.
  intros * H.
  absurd (S n <= n).
  - contradict H. exfalso.
    induction n as [|n' IHn].
    + inversion H.
    + apply IHn.
      assert (H1 : pred (S (S n')) <= pred (S n')).
        apply le_pred. assumption.
    simpl in H1. assumption.
  - assumption.
Qed.

Lemma lt_trans : forall n m k : nat, n < m -> m < k -> n < k.
Proof.
  intros * H1 H2.
  induction H2 as [| k]; [apply le_S in H1 | apply le_S]; assumption.
Qed.

Definition ininat (n : nat) : Ensemble nat := fun m => m <= n.

Lemma ininat_0 : forall n : nat, In nat (ininat 0) n <-> n = 0.
Proof.
  intro. split; intro H.
  - destruct n as [| n'].
    + reflexivity.
    + compute in H. exfalso. inversion H.
  - rewrite H. apply le_n.
Qed.

Lemma ininat_n_Sn : forall n : nat, ~ In nat (ininat n) (S n).
Proof.
  intro. apply lt_irrefl.
Qed.

Lemma ininat_0_add : Same_set nat (ininat 0) (Add nat (Empty_set nat) 0).
Proof.
  constructor.
  - constructor 2. apply ininat_0 in H. rewrite H. compute. constructor.
  - compute. intros * H. destruct H as [|n H1].
    + compute in H. exfalso. contradiction.
    + compute in H1. destruct H1. constructor.
Qed.

Lemma fin_ininat_0 : Finite nat (ininat 0).
Proof.
  pose (H := Extensionality_Ensembles nat).
  pose (H1 := H (ininat 0) (Add nat (Empty_set nat) 0)).
  pose (H2 := H1 ininat_0_add). rewrite H2.
  constructor.
  - constructor.
  - intro H3. contradiction.
Qed.

Lemma ininat_Sn_add :
  forall n : nat, Same_set nat (ininat (S n)) (Add nat (ininat n) (S n)).
Proof.
  intro. split.
  - compute. intros * H.
    assert (H1 : x <= n \/ x = S n). apply m_le_Sn. assumption.
    elim H1; intro H2.
      + constructor. compute. assumption.
      + constructor 2. compute. rewrite H2. constructor.
  - compute. intros * H. destruct H; compute in H.
    + apply le_S. assumption.
    + destruct H. apply le_n.
Qed.

Lemma fin_ininat : forall n : nat, Finite nat (ininat n).
Proof.
  intro. induction n as [| n' IHn].
  - apply fin_ininat_0.
  - pose (H := Extensionality_Ensembles nat).
    pose (H1 := H (ininat (S n')) (Add nat (ininat n') (S n'))).
    pose (H2 := H1 (ininat_Sn_add n')). rewrite H2.
    constructor 2. assumption.
    apply ininat_n_Sn.
Qed.


(*** SECTION 2: The Ensembles Library ***)

(* This file is -not- my work.  It is part of the standard Coq library.  We include it for the reader's reference. *)

Section Ensembles.
  Variable U : Type.

  Definition Ensemble := U -> Prop.

  Definition In (A:Ensemble) (x:U) : Prop := A x.

  Definition Included (B C:Ensemble) : Prop := forall x:U, In B x -> In C x.

  Inductive Empty_set : Ensemble :=.

  Inductive Full_set : Ensemble :=
    Full_intro : forall x:U, In Full_set x.

(* NB: The following definition builds-in equality of elements in U as Leibniz equality.
   This may have to be changed if we replace U by a Setoid on U with its own equality eqs, with In_singleton: (y: U)(eqs x y) -> (In (Singleton x) y). *)

  Inductive Singleton (x:U) : Ensemble :=
    In_singleton : In (Singleton x) x.

  Inductive Union (B C:Ensemble) : Ensemble :=
    | Union_introl : forall x:U, In B x -> In (Union B C) x
    | Union_intror : forall x:U, In C x -> In (Union B C) x.

  Definition Add (B:Ensemble) (x:U) : Ensemble := Union B (Singleton x).

  Inductive Intersection (B C:Ensemble) : Ensemble :=
    Intersection_intro :
    forall x:U, In B x -> In C x -> In (Intersection B C) x.

  Inductive Couple (x y:U) : Ensemble :=
    | Couple_l : In (Couple x y) x
    | Couple_r : In (Couple x y) y.

  Inductive Triple (x y z:U) : Ensemble :=
    | Triple_l : In (Triple x y z) x
    | Triple_m : In (Triple x y z) y
    | Triple_r : In (Triple x y z) z.

  Definition Complement (A:Ensemble) : Ensemble := fun x:U => ~ In A x.

  Definition Setminus (B C:Ensemble) : Ensemble :=
    fun x:U => In B x /\ ~ In C x.

  Definition Subtract (B:Ensemble) (x:U) : Ensemble := Setminus B (Singleton x).

  Inductive Disjoint (B C:Ensemble) : Prop :=
    Disjoint_intro : (forall x:U, ~ In (Intersection B C) x) -> Disjoint B C.

  Inductive Inhabited (B:Ensemble) : Prop :=
    Inhabited_intro : forall x:U, In B x -> Inhabited B.

  Definition Strict_Included (B C:Ensemble) : Prop := Included B C /\ B <> C.

  Definition Same_set (B C:Ensemble) : Prop := Included B C /\ Included C B.

(* Extensionality Axiom *)

  Axiom Extensionality_Ensembles : forall A B:Ensemble, Same_set A B -> A = B.

End Ensembles.

Hint Unfold In Included Same_set Strict_Included Add Setminus Subtract: sets
  v62.

Hint Resolve Union_introl Union_intror Intersection_intro In_singleton
  Couple_l Couple_r Triple_l Triple_m Triple_r Disjoint_intro
  Extensionality_Ensembles: sets v62.


(** DISCLAIMER: the current presentation of monoids uses typeclasses,
    but in fact it's not obvious that typeclasses are needed/useful here.
    Indeed, there is no overloading involved.
    Thus, the interface might change in the near future. *)

(**************************************************************************
* TLC: A library for Coq                                                  *
* Mathematical structures                                                 *
**************************************************************************)

Set Implicit Arguments.
Require Import LibTactics LibLogic LibOperation.
Generalizable Variables A B.

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

(* --------------------------------------------------------------------- *)
(* ################################################################# *)
(** * Structures *)

(** Monoid structure: binary operator and neutral element *)

Record monoid_op (A:Type) : Type := monoid_make {
   monoid_oper : A -> A -> A;
   monoid_neutral : A }.

(** Monoid properties
    Note that field names are suffixed by [_prop] because the corresponding
    properties are also available through typeclass instances. *)
(* -- LATER: factorize [let (o,n) := m] for the record definition *)

Class Monoid A (m:monoid_op A) : Prop := Monoid_make {
   monoid_assoc_prop : let (o,n) := m in assoc o;
   monoid_neutral_l_prop : let (o,n) := m in neutral_l o n;
   monoid_neutral_r_prop : let (o,n) := m in neutral_r o n }.

(** Commutative monoid *)

Class Comm_monoid A (m:monoid_op A) : Prop := Comm_monoid_make {
   comm_monoid_monoid : Monoid m;
   comm_monoid_comm : let (o,n) := m in comm o }.

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

(** Example:

  Instance monoid_plus_zero:
    Monoid (monoid_make plus 0).
  Proof using.
    constructor; repeat intro; omega.
  Qed.

*)

(* --------------------------------------------------------------------- *)
(* ################################################################# *)
(** * Properties *)

Section MonoidProp.
Context {A:Type}.

Class Monoid_assoc {m:monoid_op A} := {
  monoid_assoc : assoc (monoid_oper m) }.

Class Monoid_neutral_l {m:monoid_op A} := {
  monoid_neutral_l : neutral_l (monoid_oper m) (monoid_neutral m) }.

Class Monoid_neutral_r {m:monoid_op A} := {
  monoid_neutral_r : neutral_r (monoid_oper m) (monoid_neutral m) }.

Class Monoid_comm {m:monoid_op A} := {
  monoid_comm : comm (monoid_oper m) }.

End MonoidProp.

(* --------------------------------------------------------------------- *)
(* ################################################################# *)
(** * Derived Properties *)

Section MonoidInst.
Variables (A:Type).
Implicit Types m : monoid_op A.

Global Instance Monoid_assoc_of_Monoid : forall m (M:Monoid m),
  Monoid_assoc (m:=m).
Proof using.
  introv M. constructor. destruct M as [U ? ?]. destruct m. simpl. apply U.
Qed.

Global Instance Monoid_neutral_l_of_Monoid : forall m (M:Monoid m),
  Monoid_neutral_l (m:=m).
Proof using.
  introv M. constructor. destruct M as [? U ?]. destruct m. simpl. apply U.
Qed.

Global Instance Monoid_neutral_r_of_Monoid : forall m (M:Monoid m),
  Monoid_neutral_r (m:=m).
Proof using.
  introv M. constructor. destruct M as [? ? U]. destruct m. simpl. apply U.
Qed.

Global Instance Monoid_of_Comm_monoid : forall m (M:Comm_monoid m),
  Monoid m.
Proof using.
  introv M. destruct M as [U ?]. destruct m. simpl. apply U.
Qed.

Global Instance Monoid_comm_of_Comm_Monoid : forall m (M:Comm_monoid m),
  Monoid_comm (m:=m).
Proof using.
  introv M. constructor. destruct M as [? U]. destruct m. simpl. apply U.
Qed.

End MonoidInst.


(**  Iteration of a function (similar to [Nat.iter])
     Abstract Properties

Experimental use of LibHyps

*)

Open Scope nat_scope.
From Coq Require Import RelationClasses Relations Arith Max Lia.
From hydras Require Import Exp2.

From LibHyps Require Import LibHyps.
From hydras  Require Import MoreLibHyps.
Ltac rename_hyp n th ::= rename_short n th.

(* begin snippet iterateDef *)

Fixpoint iterate {A:Type}(f : A -> A) (n: nat)(x:A) :=
  match n with
  | 0 => x
  | S p => f (iterate  f p x)
  end. (* .no-out *)

(* end snippet iterateDef *)

Lemma iterate_comm {A: Type} f n (x:A)
  : iterate f n (f x) = f (iterate f n x).
Proof.
  induction n;  simpl.
  - trivial.
  - simpl;  now f_equal.
Qed.

(** Compatibility with Ackermann Library's definition *)

Lemma iterate_compat {f : nat -> nat}(n:nat)(x:nat):
  iterate f n x = nat_rec
                    (fun _ => nat -> nat)
                    (fun x : nat => x)
                    (fun (_ : nat) (rec : nat -> nat) (x : nat) => f (rec x))
                    n x.
Proof.
  induction n; cbn.
   - reflexivity.
   - now rewrite IHn.
Qed.

Lemma iterate_compat2 {A} (f : A -> A) n :
  forall x, iterate f n x = Nat.iter n f x.
Proof.
  induction n.
  - reflexivity.
  - simpl; intros; now rewrite IHn.
Qed.

(** TODO : move to more generic libraries *)

Lemma iterate_compat3 f x n :
  iterate f n x = nat_rec (fun _ : nat => nat) x (fun _ y : nat => f y) n.
Proof.
  induction n;cbn; auto.
Qed.

(** ** Abstract properties of arithmetic functions *)

(* begin snippet funLeDef *)

Definition strict_mono f := forall n p,  n < p -> f n < f p.

Definition dominates_from n g f := forall p, n <= p -> f p < g p.

Definition fun_le f g := forall n:nat, f n <=  g n.
Infix "<<=" := fun_le (at level 60).

Definition dominates g f := exists n : nat, dominates_from n g f .
Infix ">>" := dominates (at level 60).

Definition dominates_strong g f  := {n : nat | dominates_from n g f}.
Infix ">>s" := dominates_strong (at level 60).

(* end snippet funLeDef *)

Lemma S_pred_rw (f : nat -> nat) : S <<= f ->
                                   forall x, S (Nat.pred (f x)) = f x.
Proof.
  intros H x; case_eq (f x).
  - intro H0. specialize (H x); rewrite H0 in H.
    inversion H.
  - reflexivity.
Qed.

Lemma fun_le_trans f g h : f <<= g ->  g <<= h -> f <<= h.
Proof.
  intros; red; intro n; transitivity (g n); auto.
Qed.

Lemma mono_le f (Hf : strict_mono f) :  forall n, n <= f n.
Proof.
  induction n.
  - auto with arith.
  - apply Nat.le_lt_trans with (f n); auto with arith.
Qed.

Lemma mono_injective f (Hf : strict_mono f) :
  forall n p , f n = f p -> n = p.
Proof.
  intros n p H; destruct (PeanoNat.Nat.lt_total n p).
  - specialize (Hf _ _ H0); rewrite H in Hf;
      destruct (Nat.lt_irrefl _ Hf).
  -  destruct H0; trivial.
     + specialize (Hf _ _ H0); rewrite H in Hf;
         destruct (Nat.lt_irrefl _ Hf).
Qed.

Lemma mono_weak f (H: strict_mono f) :
  forall n p, n <= p -> f n <= f p.
Proof.
  induction 1.
  - left.
  - apply PeanoNat.Nat.lt_le_incl; apply H; auto with arith.
Qed.

Lemma dominates_from_trans :
  forall f g h i j, dominates_from i g f  ->
                    dominates_from j h g  ->
                    dominates_from  (Nat.max i j) h f .
Proof.
  intros f g h i j H H0 k Hk;  transitivity (g k).
  + apply H;  eauto with arith.
  + apply H0; eauto with arith.
Qed.

Lemma dominates_trans f g h :
  dominates g f ->  dominates h g -> dominates h f.
Proof.
  intros [i Hi] [j Hj]; exists (Nat.max i j);
    eapply dominates_from_trans with g; eauto.
Qed.

Lemma dominates_trans_strong : forall f g h,
    dominates_strong g f ->
    dominates_strong h g ->
    dominates_strong h f.
Proof.
  intros f g h [i Hi] [j Hj];exists (Nat.max i j);
    eapply dominates_from_trans with g; eauto.
Qed.

(** ** Abstract properties of iterate *)

Lemma iterate_S_eqn {A:Type}(f : A -> A) (n: nat)(x:A):
  iterate f (S n) x = f (iterate f n x).
Proof. reflexivity. Qed.

Lemma iterate_S_eqn2 {A:Type}(f : A -> A) (n: nat)(x:A):
  iterate f (S n) x =  (iterate f n (f x)).
Proof.
  induction n.
  - reflexivity.
  - rewrite (iterate_S_eqn f (S n)), IHn;  reflexivity.
Qed.

Lemma iterate_rw {A} {f : A -> A} n  :
  forall x, iterate f  (S n)  x = iterate f n (f x).
Proof.
  simpl; induction n.
  - reflexivity.
  -  intros; simpl; now f_equal.
Qed.

Lemma iterate_ext {A:Type}(f g: A -> A) (H: forall x, f x = g x):
  forall n x, iterate f n x = iterate g n x.
Proof.
  induction n; simpl; auto.
  intro; rewrite IHn; now rewrite H.
Qed.

Lemma iterate_le f (Hf : strict_mono f) :
  forall i j, i <= j -> forall z, iterate f i z <= iterate f j z.
Proof.
  induction 1.
  - trivial.
  - intros; rewrite iterate_S_eqn.
    transitivity (iterate f m z); auto.
    apply mono_le; auto.
Qed.

Lemma iterate_lt  f (Hf : strict_mono f)(Hf': fun_le S f):
  forall i j, i < j -> forall z, iterate f i z < iterate f j z.
Proof.
   induction 1.
  - intros; rewrite iterate_S_eqn; auto.
  - intros; rewrite iterate_S_eqn;
      transitivity (iterate f m z); auto.
Qed.

Lemma iterate_lt_from f k:
   strict_mono f ->
    ( forall n,  k <= n -> n < f n) ->
    forall i j : nat,  i < j ->
                       forall z : nat, k <= z ->
                                       iterate f i z < iterate f j z.
Proof.
  induction 3 /r.
 - intros Hmono Hind * Hkz; rewrite iterate_S_eqn; auto.
   apply Hind;  revert i; induction i /r.
   +  cbn; auto.
   +  transitivity (iterate f i z); auto.
      rewrite iterate_S_eqn; apply Nat.lt_le_incl.
      apply Hind; auto.
 - intros Hmono Hind * Hlt Hind2 * Kkz; transitivity (iterate f m z); auto.
   rewrite iterate_S_eqn; apply Hind.
   transitivity z; auto.
   clear i Hlt Hind2; induction m.
   + cbn; auto.
      + cbn; transitivity (iterate f m z); auto.
     apply Nat.lt_le_incl, Hind;  lia.
Qed.

(* begin snippet iterateLeNSN *)

Lemma iterate_le_n_Sn (f: nat -> nat):
  (forall x,  x <= f x) ->
  forall n x,  iterate f n x <= iterate f (S n)  x. (* .no-out *)

(* end snippet iterateLeNSN *)

Proof.
  induction n /n.
  - cbn; auto with arith.
  - cbn; intros; apply  h_all_le_x_.
Qed.

Lemma iterate_le_np_le (f: nat -> nat):
  (forall x,  (x <= f x)%nat) ->
  forall n p x,  (n <= p -> iterate f n x <= iterate f p x)%nat.
Proof.
  induction 2.
  - reflexivity.
  - transitivity (iterate f m x).
    + assumption.
    + apply iterate_le_n_Sn, H.
Qed.

Lemma iterate_mono2 (f: nat -> nat):
  (forall x y,  x <= y -> f x <= f y)%nat ->
  forall n x y,  (x <= y -> iterate f n x <= iterate f n y)%nat.
Proof.
induction n.
 - simpl; trivial.
 - simpl; intros; now apply H, IHn.
Qed.

Lemma iterate_mono f (Hf : strict_mono f) (Hf' :  S  <<= f):
  forall n, strict_mono (iterate f n).
Proof.
  induction n.
  - red; intro i; cbn;auto.
  - cbn; intros i j H; apply Hf;auto.
Qed.

Lemma iterate_ge : forall f , S  <<= f ->
                              forall  j n, j <= iterate f n j.
Proof.
  induction n.
  - cbn; auto with arith.
   -  apply Nat.lt_le_incl;rewrite iterate_S_eqn;
        apply Nat.le_lt_trans with (iterate f n j); auto.
Qed.

Lemma iterate_Sge f j : S <<= f -> S <<= iterate f (S j).
Proof.
  intros h x; rewrite iterate_rw.
   transitivity (f x).
    -  apply h.
    - now apply iterate_ge.
Qed.

Lemma iterate_ge' : forall f,  id <<= f ->
                               forall n j, 0 < n -> j <= iterate f n j.
Proof.
  induction n /r.
  - inversion 2.
  - intros *  H0; destruct n /r.
    + simpl; intros /n. apply h_fun_le_id_.
    +  intros /n;  transitivity (iterate f (S n) j).
       * auto with arith.
       * simpl; apply h_fun_le_id_.
Qed.

Lemma iterate_ge'' f : id <<= f -> strict_mono f -> forall i k,
      k <= Nat.pred (iterate (fun z => S (f z)) (S i) k).
Proof.
  induction i /n.
  - intros; cbn; apply h_fun_le_id_.
  - intros *;   rewrite iterate_rw;
    apply Nat.le_trans with
        (Nat.pred (iterate (fun z : nat => S (f z)) (S i) k)).
    + auto.
    + cbn;  assert (H1: strict_mono (fun z => S (f z))).
      { intros x y Hlt.  apply h_strict_mono_ in Hlt;  auto with arith. }
      generalize  (iterate_mono _ H1).
      assert (H2: S <<= (fun z : nat => S (f z))).
      { intro x; auto with arith.
        specialize (h_fun_le_id_ x); auto with arith.
      }
      intros H3;  specialize (H3 H2 i).
      apply Nat.lt_le_incl.
      assert (H4: k < S (f k)).
      { apply Nat.le_lt_trans with (f k).
        - apply h_fun_le_id_.
        - auto with arith.
      }
   specialize (H3 _ _ H4); auto.
Qed.

Lemma strict_mono_iterate_S f :
  strict_mono f -> id <<= f ->
  forall i,  strict_mono
               (fun k =>  Nat.pred (iterate (fun z => S (f z)) (S i) k)).
Proof.
  intros Hmono Hle; induction i.
  - cbn; apply Hmono.
  -  intros k l Hlt.
    assert (H: k <= Nat.pred (iterate (fun z : nat => S (f z)) (S (S i)) k))
    by (apply iterate_ge''; auto).
    assert (H0: k < iterate (fun z : nat => S (f z)) (S (S i)) k).
    {
      replace k with (iterate (fun z => S (f z)) 0 k) at 1.
      -  apply iterate_lt.
        +
            intros x y Hxy; specialize (Hmono _ _ Hxy); auto with arith.
            + intros x; auto with arith; specialize (Hle x); auto with arith.
        + auto with arith.
      - reflexivity.
    }
    rewrite <-  Nat.pred_lt_mono.
    + apply iterate_mono.
      * intros x y Hxy; specialize (Hmono _ _ Hxy); auto with arith.
      * intros x; specialize (Hle x); auto with arith.
      * auto.
    +  intro H1; rewrite H1 in H0; inversion H0.
Qed.

Lemma iterate_mono_1 (f g: nat -> nat) (k:nat) (Hf: strict_mono f)
      (Hf' : S <<= f)
      (H : forall n, k <= n -> f n <= g n) :
  forall i n, k <= n -> iterate f i n <= iterate g i n.
Proof.
  induction i.
  - simpl; auto with arith.
  - intros; repeat rewrite iterate_S_eqn.
    transitivity (f (iterate g i n)); auto.
    + apply mono_weak; auto.
    + apply H; transitivity (iterate f i n); [ | auto].
      * induction H0.
        -- clear IHi;  induction i.
       ++ simpl; auto with arith.
       ++  rewrite iterate_S_eqn.
           transitivity (iterate f i k).
           **  auto.
           **  apply Nat.lt_le_incl; auto.
        --  transitivity (S m).
            ++ auto with arith.
            ++ apply iterate_ge; auto.
Qed.

Lemma iterate_dom_prop :
  forall f g i (Hgt : S <<= f)
         (Hm : strict_mono f) (Hm': strict_mono g),
    dominates_from i g f ->
    forall k, 0 < k -> dominates_from i (iterate g k) (iterate f k).
Proof.
  induction k.
  - intro H0; inversion H0.
  - destruct k.
    + simpl;  intros _ l Hl;  apply H; auto.
    + intros _ l Hl; {autorename}.  repeat rewrite iterate_S_eqn.
      transitivity (g (f (iterate f k l))).
      * apply H; transitivity (f l).
        { transitivity l;  auto.
          apply PeanoNat.Nat.lt_le_incl.
          eapply Hgt; auto.
        }
        apply mono_weak;  auto.
        eapply iterate_ge;  auto.
      *  apply Hm';  assert (0 < S k)%nat by auto with arith.
         apply IHk in H0; specialize (H0 l).
         repeat rewrite iterate_S_eqn in H0; auto.
Qed.

Lemma dominates_from_le  i j g f : i <= j ->
                                   dominates_from i g f ->
                                   dominates_from j g f .
Proof.
  induction 1; auto.
  intros H0 x H1; apply IHle; auto.
  auto with arith.
Qed.

Lemma smono_Sle f : f 0 <> 0 -> strict_mono f -> S <<= f.
Proof.
  intros H H0 x; induction x.
  - destruct (f 0).
    + now destruct H.
    + auto with arith.
  - apply Nat.le_lt_trans with (f x).
    + auto.
    + apply H0; auto with arith.
Qed.

(**  ** Second-order iterate  *)

Lemma iterate_ext2 {A:Type} (f g : (A -> A) -> A -> A)
      (h i : A->A) : (forall x, h x = i x) ->
                     (forall h' i',  (forall x, h' x = i' x) ->
                                     forall x, f h' x = g i' x) ->
                     forall n x, iterate f n h x = iterate g n i x.
Proof.
  induction n.
  - intros; simpl; auto.
  - intros; simpl; apply H0. auto.
Qed.

Lemma iterate2_mono (f : (nat->nat)->(nat->nat)):
   (forall g, strict_mono g -> S <<= g -> strict_mono (f g))->
   (forall g, strict_mono g -> S <<= g -> S <<=  (f g))->
   forall k g x  y,  strict_mono g -> S <<= g ->
                     (x < y)%nat ->
                     (iterate f k g x < iterate f k g y)%nat.
  Proof.
   induction k.
   - cbn; intros; apply (H1 _ _ H3); auto.
   -  intros; rewrite iterate_S_eqn2.
      apply (IHk (f g) x y ); auto.
Qed.

Lemma iterate2_mono_weak (f : (nat->nat)->(nat->nat)):
   (forall g, strict_mono g -> S <<= g -> strict_mono (f g))->
   (forall g, strict_mono g -> S <<= g -> S <<=  (f g))->
   forall k g x  y,  strict_mono g -> S <<= g ->
                     (x <= y)%nat ->
                     (iterate f k g x <= iterate f k g y)%nat.
Proof.
  intros g Hg k g0 x y  Hmono ? Hxy.
  destruct (Nat.lt_eq_cases x y) as [H4 H5]; apply H4 in Hxy.
  destruct Hxy.
  -  apply Nat.lt_le_incl,  iterate2_mono; auto.
  - now subst.
Qed.

Lemma iterate2_mono3 (phi  : (nat->nat)->(nat->nat)) :
  (forall g, strict_mono g -> S <<= g ->
             strict_mono (phi g) /\ S <<= phi g)->
  (forall (f g : nat -> nat), strict_mono f -> S <<= f  ->
                              strict_mono g -> S <<= g ->
                              ((forall x, f x <= g x) ->
                               forall x, phi f x <= phi g x)) ->
  forall g h,  strict_mono g -> S <<= g -> strict_mono h -> S <<= h ->
             (forall x,  g x <= h x) ->
  forall k x y,  x <= y ->
                 iterate phi k  g x <= iterate phi k h y.
Proof.
  intros; revert k x y H6.
  assert (H6: forall k, strict_mono (iterate phi k h) /\
                        S <<= iterate phi k h).
  {
    intro k; induction k.
    - simpl; split; auto.
    - destruct IHk; split.
      + simpl; destruct (H (iterate phi k h)); auto.
      + simpl; destruct (H (iterate phi k h)); auto.
  }
  assert (H7: forall k, strict_mono (iterate phi k g) /\
                        S <<= iterate phi k g). {
    { intro k; induction k.
      - simpl; split; auto.
      - destruct IHk; split.
        + simpl; destruct (H (iterate phi k g));  auto.
        +  simpl;  destruct (H (iterate phi k g)); auto.
    }
  }
  induction k.
  - simpl; intros; transitivity (g y).
    +  apply mono_weak; auto.
    +  auto.
  -   intros; repeat rewrite iterate_S_eqn;
        transitivity (phi (iterate phi k h) x).
      +  apply H0.
         * destruct (H7 k); auto.
         * destruct (H7 k); auto.
         * destruct (H6 k); auto.
         * destruct (H6 k); auto.
         * intro; apply IHk; auto with arith.
      + clear IHk; destruct (H6 k).
        destruct  (H (iterate phi k h) H9 H10);  apply mono_weak; auto.
Qed.

Lemma iterate2_mono2 (phi psi : (nat->nat)->(nat->nat)):
  (forall g, strict_mono g -> S <<= g -> strict_mono (phi g))->
  (forall g, strict_mono g -> S <<= g -> S <<=  (phi g))->
  (forall g, strict_mono g -> S <<= g -> strict_mono (psi g))->
  (forall g, strict_mono g -> S <<= g -> S <<=  (psi g))->
  (forall g x, strict_mono g -> fun_le S g  -> phi g x <= psi g x) ->
  (forall f g, strict_mono f -> strict_mono g -> S <<= f -> S <<= g ->
               (forall x, f x <= g x) -> (forall x, psi f x <= psi g x)) ->
  forall k g x  y,  strict_mono g -> S <<= g ->
                    (x <= y)%nat ->
                    (iterate phi k g x <= iterate psi k g y)%nat.
Proof.
  induction k.
  -  simpl;  intros g x y H5 H6 H7;
       destruct (Nat.lt_eq_cases x y) as [H8 H9].
      destruct (H8 H7).
     + apply Nat.lt_le_incl.
       apply H5; auto.
     + subst; auto.
  -  intros;  repeat rewrite iterate_S_eqn2.
     transitivity (iterate psi k (psi g) x).
     +   specialize (IHk (phi g) x x (H g H5 H6) (H0 g H5 H6)).
        transitivity (iterate psi k (phi g) x).
        * apply IHk; auto.
        * apply iterate2_mono3; auto.
     +   apply iterate2_mono3; auto.
Qed.

(** ** Exponential and hyper exponential of base 2 *)

Lemma exp2_ge_S : S  <<= exp2.
Proof.
  red; induction n.
  - cbn; auto with arith.
  - cbn; abstract lia.
Qed.

Lemma exp2_mono : strict_mono exp2.
Proof.
  red; induction 1; cbn.
  - generalize (exp2_positive n); generalize (exp2 n);intros; abstract lia.
  - generalize IHle, (exp2_positive m); generalize (exp2 n), (exp2 m);
      intros;abstract lia.
Qed.

Lemma exp2_mono_weak : forall n p, n<= p -> exp2 n <= exp2 p.
Proof.
  intros n p H; elim H.
  - left.
  - intros ? ? ?; apply Nat.lt_le_incl, exp2_mono; auto with arith.
Qed.

Lemma exp2_as_iterate n : exp2 n = iterate (fun i => 2 * i)%nat n 1.
Proof.
  induction  n.
  - reflexivity.
  - rewrite iterate_S_eqn; simpl exp2; rewrite <- IHn; abstract lia.
Qed.

Definition hyper_exp2 k := iterate exp2 k 1.

Lemma hyper_exp2_S : forall n, hyper_exp2 (S n) = exp2 (hyper_exp2 n).
Proof.  induction n; cbn; auto. Qed.

Lemma iterate_ge_from : forall f i, dominates_from i f id ->
                               forall  j, i <= j ->
                                          forall n,
                                            j <= iterate f n j.
Proof.
  induction n.
  cbn.
  auto with arith.
  apply PeanoNat.Nat.lt_le_incl.
  rewrite iterate_S_eqn.
  apply Nat.le_lt_trans with (iterate f n j).
  auto.
  apply H.
  apply Nat.le_trans with j.
  auto.
  auto.
Qed.

Lemma dominates_iterate :
  forall  i f,
    dominates_from i f id ->
    strict_mono f ->
    forall n,
      {j:nat | i <= j /\ dominates_from j (iterate f (S n)) id}.

  induction n.
  exists i.
  split;auto.
  destruct IHn as [j [H1 H2]].
  exists j.
  split;auto.
  red in H2; red.
  intros k Hk.
  unfold id.
  rewrite iterate_S_eqn.
  apply Nat.le_lt_trans with (f (iterate f n k)).
  apply Nat.le_trans with  (iterate f n k).
  apply iterate_ge_from with i.
  auto.
  eauto with arith.
  apply PeanoNat.Nat.lt_le_incl.
  apply H.
  apply Nat.le_trans with k. eauto with arith.
  apply iterate_ge_from with i.
  auto.
  eauto with arith.
  apply H0.
  rewrite iterate_S_eqn.
  apply H.

  apply Nat.le_trans with  k.
  eauto with arith.
  apply iterate_ge_from with i.
  auto.
  eauto with arith.
Defined.

Corollary iterate_gt_diag' :
  forall  i f,
    dominates_from i f id ->
    strict_mono f ->
    forall n, 0 < n ->
              {j:nat | i<= j /\ dominates_from j (iterate f n) id}.
Proof.
  destruct n.
  -  intro H1;  cut False; [contradiction | lia].
  - intros _; apply dominates_iterate; auto.
Defined.

Corollary iterate_ge_diag' :
  forall  i f,
    dominates_from i f id->
    strict_mono f ->
    forall n,
      {j:nat | i<= j /\ forall k, j<= k -> k <= iterate f n k}.
Proof.
  intros; destruct n.
  -  exists i; split;auto.
  -  exists i; split; auto.
     intros.
     generalize  (H k H1); intros.
     unfold id in H2, H; rewrite iterate_S_eqn.
     apply Nat.lt_le_incl.
     apply Nat.lt_le_trans with (f k);  auto.
     apply mono_weak; auto.
     eapply iterate_ge_from with i; auto.
Qed.


Require Export Lists.List.
Import ListNotations.

Section Definitions.
Variable T : Type.

  Class Inhabited
  : Prop :=
    exist_proof : exists e : list T, e <> nil.
  Class Finite
  : Prop :=
    fin_proof : exists e : list T, forall t : T, In t e.

End Definitions.

Section BasicDependence.
Variable T : Type.

  Theorem not_Inhabited_is_Finite : ~ Inhabited T -> Finite T.
  Proof.
    intros* H.
    exists nil. intro. exfalso. apply H.
    exists (t :: nil). intro H'. discriminate H'.
  Qed.

End BasicDependence.

(* Inhabitence of some standard types                                       *)

Instance bool_is_Inhabited : Inhabited bool.
Proof. now exists (true :: nil). Defined.

Instance nat_is_Inhabited : Inhabited nat.
Proof. now exists (0 :: nil). Defined.

(* Some types are not inhabited *)

Example False_isnot_Inhabited : ~ Inhabited False.
Proof.
  intro. elim H. intros e H1. apply H1.
  destruct e as [| t e']; [ trivial | now exfalso ].
Qed.

(* Finiteness of some standard types                                        *)

Instance False_is_Finite : Finite False.
Proof.
  apply not_Inhabited_is_Finite.
  exact False_isnot_Inhabited.
Defined.

Instance unit_is_Finite : Finite unit.
Proof.
  exists (tt :: nil). intro.
  destruct t. now left.
Defined.

Instance bool_is_Finite : Finite bool.
Proof.
  exists (true :: false :: nil). intro.
  destruct t; [ now left | right; now left ].
Defined.

(* Infiniteness of type nat                                                 *)

Require Import Arith.Compare_dec.
Require Import Arith.Le.
Require Import Arith.Lt.
Section NatIsNotFinite.
(*  Here we prove that nat is not a finite type/
    The idea of the proof is to construct a function
    outside : list nat -> nat meeting the requirement
      forall e : list nat, ~ In (outside e) e.                              *)

  Fixpoint outside (ns : list nat) : nat :=
    match ns with
      nil     => 0
    | n :: ns' => let n' := outside ns' in
                  if le_lt_dec n' n then S n else n'
    end.

  Section Lemmas.
    Variable e : list nat.

    Lemma lt_member_outside : forall n : nat, In n e -> n < outside e.
    Proof.
      intros * H.
      induction e as [| m e' IHe]; simpl.
      - now exfalso.
      - case H; intro HC.
        + rewrite HC. case (le_lt_dec (outside e') n); intro HD;
          [apply le_n | assumption].
        + pose (IHe' := IHe HC).
          case (le_lt_dec (outside e') m); intro HD; assumption || idtac.
            assert (HAux : n < m). {
              now apply le_trans with (outside e'). }
            now apply le_S.
    Qed.

    Lemma outside_list : ~ In (outside e) e.
    Proof.
      intro H.
      pose (H1 := lt_member_outside (outside e) H).
      now pose (H2 := lt_irrefl (outside e)).
    Qed.
  End Lemmas.

  Theorem nat_isnot_Finite : ~ Finite nat.
  Proof.
    unfold Finite. intro. elim H. intros e H1.
    pose (H2 := H1 (outside e)).
    now pose (H2' := outside_list e).
  Qed.

End NatIsNotFinite.