(** * Logic: Logic in Coq *)

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

(** 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_ ([e1 = e2]), implications ([P -> Q]), and 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 3 = 2 : Prop.

Check forall n : nat, n = 2 : Prop.

(** Indeed, propositions not only have types: they are
    _first-class_ entities that can be manipulated in all the same
    ways as any of the other things 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 other kinds of expressions. *)

Definition plus_claim : Prop := 2 + 2 = 4.
Check 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 in
    Coq's standard library using the [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, and we need to turn
    off the inference of this implicit argument 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 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 (and_exercise)  *)
Example and_exercise :
  forall n m : nat, n + m = 0
    -> n = 0 /\ m = 0.
Proof.
  intros n m H.
  apply and_intro.
  - (* n = 0 *) induction n as [| n'].
    + reflexivity.
    + discriminate H.
  - (* m = 0 *) induction m as [| m'].
    + reflexivity.
    + rewrite plus_comm in H. discriminate H.
Qed.

Lemma and_example2 :
  forall n m : nat, n = 0 /\ m = 0 -> n + m = 0.
Proof.
  intros n m H.
  destruct H as [Hn Hm].
  rewrite -> Hn. rewrite -> Hm. simpl.
  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 specific 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 larger 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.
  apply and_exercise in H.
  destruct H as [Hn Hm].
  rewrite -> Hn. simpl. 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. *)

Lemma proj1 : forall P Q : Prop,
  P /\ Q -> P.
Proof.
  intros P Q HPQ.
  destruct HPQ as [HP _].
  (** destruct HPQ as [_ HQ]. to return Q instead of P **)
  apply HP.
Qed.

(** **** Exercise: 1 star, standard, optional (proj2)  *)
Lemma proj2 : forall P Q : Prop,
  P /\ Q -> Q.
Proof.
  intros P Q HPQ.
  destruct HPQ as [_ 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]].
  split.
  - (* inner *) split.
    + apply HP.
    + apply HQ.
  - (* outer *) 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 : Prop -> Prop -> Prop.
(* ===> 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 with other data types like [nat], 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, it suffices to show
    that one of its sides holds. 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. induction n as [|n'].
  - (* left *)
  left. reflexivity.
  - (* right *)
  right. simpl. reflexivity.
Qed.

Search ( _ * _ = _ ).
(** **** Exercise: 1 star (mult_eq_0)  *)
Lemma mult_eq_0 : forall n m,
    n * m = 0 -> n = 0 \/ m = 0.
Proof.
  intros n. induction n as [|n'].
  - (* n = O *)
  intros m H. rewrite mult_O_1 in H. left. apply H.
  - (* n = S n' *) intros m. induction m as [| m].
    + intros H. simpl in H. rewrite mult_n_O in H. right. apply H.
    + intros H. discriminate H.
Qed.

(** **** Exercise: 1 star (or_commut)  *)
Theorem or_commut : forall P Q : Prop,
  P \/ Q  -> Q \/ P.
Proof.
  intros P Q [HP|HQ].
  - right. apply HP.
  - 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, demonstrating that some given proposition is
    _not_ true. Such statements are expressed with the logical
    negation operator [~]. *)

(** To see how negation works, recall the _principle of explosion_
    from the [Tactics] chapter, which 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.

(** **** 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.
  intros P HPnot Q HP.
  contradiction.
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
    make Coq 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, recommended (contrapositive)  *)
Theorem contrapositive : forall P Q : Prop,
  (P -> Q) -> (~Q -> ~P).
Proof.
  intros P Q H HQnot.
  unfold not. unfold not in HQnot. intros P'. apply HQnot. apply H. apply P'.
Qed.

(** **** Exercise: 1 star (not_both_true_and_false)  *)
Theorem not_both_true_and_false : forall P : Prop,
  ~ (P /\ ~P).
Proof.
  intros P. unfold not. intros [HP HPnot].
  apply HPnot. apply HP.
Qed.

(** Since inequality involves a negation, it also 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 eqn:HE.
  - (* 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 later on. *)

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

(** The handy "if and only if" connective, which asserts that two
    propositions have the same truth value, is simply 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.

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

Theorem iff_trans : forall P Q R : Prop,
  (P <-> Q) -> (Q <-> R) -> (P <-> R).
Proof.
  intros P Q R [HPQ HQP] [HQR HRQ]. split.
  - (* -> *) intros P'. apply HPQ in P'. apply HQR in P'. apply P'.
  - (* <- *) intros R'. apply HRQ in R'. apply HQP in R'. apply R'.
Qed.

(** **** Exercise: 3 stars (or_distributes_over_and)  *)
Theorem or_distributes_over_and : forall P Q R : Prop,
  P \/ (Q /\ R) <-> (P \/ Q) /\ (P \/ R).
Proof.
  intros P Q R. split.
  - (* -> *) intros H. inversion H.
    + split.
      { left. apply H0. }
      { left. apply H0. }
    + split.
      { apply proj1 in H0. right. apply H0. }
      { apply proj2 in H0. right. apply H0. }
  - (* <- *) intros H. inversion H.
    + destruct H1.
      { left. apply H1. }
      { destruct H0.
        { left. apply H0. }
        { right. split.
          { apply H0. }
          { apply H1. } } }
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 have
    to import the 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 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.

Theorem 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.  For
    example, 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]. *)

Definition even x := exists n : nat, x = double n.

Lemma four_is_even : even 4.
Proof.
  (* witness_ of the existential is given as 2 in this context *)
  unfold even. 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, especially useful (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.
  intros X P Hall Hexist.
  destruct Hexist as [x HPnot].
  unfold not in HPnot. apply HPnot. apply Hall.
Qed.

(** **** Exercise: 2 stars (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.
  intros X P Q. split.
  - (* -> *) intros H. destruct H as [X' H]. destruct H as [HP | HQ].
    + left. exists X'. apply HP.
    + right. exists X'. apply HQ.
  - (* <- *) intros H. destruct H as [HP | HQ].
    + destruct HP as [X' HP]. exists X'. left. apply HP.
    + destruct HQ as [X' HQ]. exists X'. right. apply HQ.
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 first, how [In] starts out applied to a variable and
    only gets expanded when we do case analysis on this variable: *)

Theorem 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: 2 stars (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.
  - (* -> *) intros H. induction l as [| x' l' IHl'].
    + (* l = [] *)
      simpl in H. contradiction.
    + (* l = x' :: l' *)
      simpl in H. destruct H as [H1 | H2].
      { exists x'. split.
        - apply H1.
        - simpl. left. reflexivity. }
      { apply IHl' in H2. destruct H2 as [x2 H2]. exists x2. split.
        - apply proj1 in H2. apply H2.
        - simpl. right. apply proj2 in H2. apply H2. }
  - (* <- *) intros H. induction l as [| x' l' IHl'].
    + (* l = [] *)
      simpl in H. destruct H as [x' H]. apply proj2 in H. contradiction.
    + (* l = x' :: l' *)
      simpl. simpl in H. destruct H as [x'' H]. inversion H. destruct H1 as [H2 | H3].
      { left. rewrite H2. apply H0. }
      { right. apply IHl'. exists x''. split.
        - apply H0.
        - apply H3. }
Qed.

(** **** Exercise: 2 stars (in_app_iff)  *)
Lemma in_app_iff : forall A l l' (a:A),
  In a (l++l') <-> In a l \/ In a l'.
Proof.
  intros A l l' x. split.
  - intros H. induction l.
    + simpl. simpl in H. right. apply H.
    + simpl. simpl in H. destruct H.
      { left. left. apply H. }
      { apply IHl in H. apply or_assoc. right. apply H. }
  - intros H. induction l.
    + simpl. simpl in H. destruct H.
      { contradiction. }
      { apply H. }
    + simpl. simpl in H. apply or_assoc in H. destruct H as [H1 | [H2 | H3]].
      { left. apply H1. }
      { right. apply IHl. left. apply H2. }
      { right. apply IHl. right. apply H3. }
Qed.

(** **** Exercise: 3 stars (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} (P : T -> Prop) (l : list T) : Prop :=
  match l with
  | [] => True
  | h :: t => P h /\ All P t
  end.

Lemma All_In : forall T (P : T -> Prop) (l : list T),
    (forall x, In x l -> P x) <->
    All P l.
Proof.
  intros T P l. split.
  - (* -> *) intros H. induction l as [| n l' IHl'].
    + (* l = nil *) simpl. auto.
    + (* l = n :: l' *) simpl. split.
      (* left *) { apply H. simpl. left. reflexivity. }
      (* right *) { apply IHl'. intros x0 H0. apply H. simpl. right. apply H0. }
  - (* <- *) intros H. induction l.
    + (* l = nil *) simpl. intros x0 H0. contradiction.
    + (* l = cons n l' *) simpl. intros x0 H0. destruct H0 as [|H1 H2].
      (* left *) { simpl in H. apply proj1 in H. rewrite H0 in H. apply H. }
      (* right *) { simpl in H. apply proj2 in H. apply IHl with x0 in H. apply H. apply H1. }
Qed.

(** **** Exercise: 3 stars (combine_odd_even)  *)
(** Complete the definition of the [combine_odd_even] function below.
    It takes as arguments two properties of numbers, [Podd] and
    [Peven], 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 :=
  fun (n : nat) => 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.
  intros Podd Peven n Hodd Heven.
  unfold combine_odd_even. destruct (oddb n) eqn: H.
  - apply Hodd. reflexivity.
  - 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.
  intros Podd Peven n Hcomb Hodd.
  unfold combine_odd_even in Hcomb.
  rewrite Hodd in Hcomb. assumption.
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.
  intros Podd Peven n Hcomb Heven.
  unfold combine_odd_even in Hcomb.
  rewrite Heven in Hcomb. assumption.
Qed.

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

(** One feature that distinguishes Coq 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 [Check] to ask Coq to print the type
    of an expression.  We can also use it to ask what theorem a
    particular identifier refers to. *)

Check plus_comm.

(** Coq checks the _statement_ of the [plus_comm] theorem (or prints
    it for us, if we leave off the part beginning with the colon) in
    the same way that it checks 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_, which 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 proposition which 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, i.e., applying it 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's see another example of using a theorem like a
    function. The following theorem says: any list [l]
    containing some element must be nonempty. *)

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

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

(** We should be able to use this theorem to prove the special case
    where [x] is [42]. However, naively, the tactic [apply in_not_nil]
    will fail because it cannot infer the value of [x]. *)

Lemma in_not_nil_42 :
  forall l : list nat, In 42 l -> l <> [].
Proof.
  intros l H.
  Fail apply in_not_nil.
Abort.

(** There are several ways to work around this: *)

(** Use [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.

(** Use [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_n_O in Hm. rewrite <- Hm. reflexivity.
Qed.

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

(* ================================================================= *)
(** ** 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, we can use it at
    _any_ type -- 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 output on every input:

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

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

(** Informally, 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., the results we obtain
    after applying it. *)

(** However, functional extensionality is not part of Coq's built-in
    logic.  This means that some apparently "obvious" 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.

(** Defining something as an [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 this can render it _inconsistent_ -- that is, it may
    become 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 mathematicians is
    often 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 *)

Print Assumptions functional_extensionality.

(* recursion with acumulation *)
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 of [rev] 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. *)

Theorem tr_rev_correct : forall X, @tr_rev X = @rev X.
Proof.
(* FILL IN HERE *) Admitted.

(* ================================================================= *)
(** ** Propositions vs. 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 : even 42.
Proof. unfold even. 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. *)
Lemma evenb_double : forall k, evenb (double k) = true.
Proof.
  intros k. induction k as [|k' IHk'].
  - reflexivity.
  - simpl. apply IHk'.
Qed.

Theorem plus_1_l : forall n:nat, 1 + n = S n.
Proof.
  intros n. reflexivity.  Qed.

Print evenb_S.

(** **** Exercise: 3 stars, standard (evenb_double_conv)  *)
Lemma 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]. *)
  intros n. induction n as [| n'].
  - (* n = O *) simpl. exists 0. reflexivity.
  - (* n = S n' *) destruct (evenb n') eqn: Heq.
    + (* evenb n' = true *) rewrite evenb_S. rewrite Heq. simpl. destruct IHn' as [n'' IHn'].
      exists n''. rewrite IHn'. reflexivity.
    + (* evenb n' = false *) rewrite evenb_S. rewrite Heq. simpl. destruct IHn' as [n'' IHn'].
      exists (n''+1). rewrite IHn'. rewrite double_plus. rewrite double_plus.
      rewrite plus_n_Sm. rewrite <- plus_1_l. rewrite <- plus_n_Sm. rewrite <- (plus_1_l (n'' + n'')).
      rewrite plus_comm. rewrite plus_assoc.  rewrite (plus_comm 1).
      rewrite plus_assoc. reflexivity.
Qed.

(** Now the main theorem: *)
Theorem even_bool_prop : forall n,
  evenb n = true <-> even n.
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. *)

Print eqb_refl.

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 are
    often not equivalent from the point of view of convenience for
    some specific purpose. *)

(** 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) -> even n]. *)

(** 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 has to do with the _computational_ nature
    of Coq's core language, which is designed so that every function
    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.

    Beyond the fact that non-computable properties are impossible in
    general to phrase as boolean computations, even many _computable_
    properties are easier to express using [Prop] than [bool], since
    recursive function definitions in Coq are subject to significant
    restrictions.  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 matching algorithm, which would be more
    complicated, harder to understand, and harder to reason about than
    a simple (non-algorithmic) definition of this property.

    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 : even 1000.

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

Proof. unfold even. exists 500. reflexivity. Qed.

(** The proof of the corresponding boolean statement is even
    simpler (because we don't have to invent the witness: Coq's
    computation mechanism does it for us!).

    In this case the witness is the nat term with value 500 *)

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'' : even 1000.
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, a famous
    Coq proof of the even more 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 can be more difficult
    to work with directly. *)

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

(** Equality provides a complementary example, where it is sometimes
    easier to work in the propositional world. 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.

(** **** Exercise: 2 stars (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.
  intros b1 b2. split.
  - (* -> *) intros H. split.
    + (* b1 = true *) rewrite andb_commutative in H. apply andb_true_elim2 in H. apply H.
    + (* b2 = true *) apply andb_true_elim2 in H. apply H.
  - (* <- *) intros H. inversion H. rewrite H0. rewrite H1. reflexivity.
Qed.

Lemma orb_true_iff : forall b1 b2,
  b1 || b2 = true <-> b1 = true \/ b2 = true.
Proof.
  intros b1 b2. split.
  - (* -> *) intros H. destruct H. destruct b1.
    + simpl. left. reflexivity.
    + simpl. right. reflexivity.
  - (* -> *) intros H. destruct H as [H1 | H2].
    + rewrite H1. simpl. reflexivity.
    + rewrite H2. destruct b1.
      { reflexivity. }
      { reflexivity. }
Qed.

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

Theorem eqb_nat_false_iff : forall x y : nat,
  eqb x y = false <-> x <> y.
Proof.
  intros x y. unfold not. split.
  - (* -> *) intros H0 H1. apply eqb_eq in H1. rewrite H1 in H0. inversion H0.
  - (* <- *) intros H. induction x as [| x'].
    + induction y as [| y'].
      { exfalso. apply H. reflexivity. }
      { generalize dependent y'. auto. }
    + induction y as [| y'].
      { generalize dependent x'. auto. }
      { simpl. destruct (eqb x' y') eqn:Heq.
        - exfalso. apply H. apply f_equal. apply eqb_eq. apply Heq.
        - reflexivity. }
Qed.

(** **** Exercise: 3 stars (beq_list)  *)
(** Given a boolean operator [beq] for testing equality of elements of
    some type [A], we can define a function [beq_list beq] for testing
    equality of lists with elements in [A].  Complete the definition
    of the [beq_list] function below.  To make sure that your
    definition is correct, prove the lemma [beq_list_true_iff]. *)

Fixpoint eqb_list {A} (eqb : A -> A -> bool)
         (l1 l2 : list A) : bool :=
  match l1, l2 with
  | [] , [] => true
  | _  , [] => false
  | [] , _  => false
  | h1 :: t1 , h2 :: t2 => if(eqb h1 h2) then eqb_list eqb t1 t2 else false
  end.

Lemma beq_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.
  intros A eqb H l1 l2. split.
  - (* -> *) generalize dependent l2. induction l1 as [| h1 l1' IHl1'].
    + induction l2 as [| h2 l2' IHl2'].
      { reflexivity. }
      { simpl. intros H1. inversion H1. }
    + induction l2 as [| h2 l2' IHl2'].
      { simpl. intros H1. inversion H1. }
      { simpl. intros H1. destruct (eqb h1 h2) eqn:Heq.
        + apply H in Heq. rewrite <- Heq.
          assert (l1' = l2' -> h1 :: l1' = h1 :: l2') as H2.
          { intros H3. rewrite H3. reflexivity. } apply H2. apply IHl1'. apply H1.
        + inversion H1. }
  - (* <- *) generalize dependent l2. induction l1 as [| h1 l1' IHl1'].
    + induction l2 as [| h2 l2' IHl2'].
      { simpl. reflexivity. }
      { simpl. intros H1. inversion H1. }
    + induction l2 as [| h2 l2' IHl2'].
      { simpl. intros H1. inversion H1. }
      { simpl. intros H1. destruct (eqb h1 h2) eqn:Heq.
        + apply IHl1'. apply H in Heq. rewrite Heq in H1.
          assert (h1 :: l1' = h1 :: l2' -> l1' = l2') as H2.
          { intros H3. inversion H1. reflexivity. } apply H2. rewrite Heq. apply H1.
        + inversion H1. apply H in H2. rewrite H2 in Heq. symmetry. apply Heq. }
Qed.

(** **** Exercise: 2 stars, 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 of the above exercise. *)

Theorem forallb_true_iff : forall X test (l : list X),
   forallb test l = true <-> All (fun x => test x = true) l.
Proof.
  intros. split.
  - (* -> *) intros. induction l.
    + simpl. auto.
    + simpl. split.
      { unfold forallb in H. rewrite andb_true_iff in H. apply proj1 in H. assumption. }
      { apply IHl. inversion H. rewrite andb_true_iff in H1. destruct H1 as [H2 H3]. rewrite H2.
        rewrite H3. auto. }
  - (* <- *) intros. induction l.
    + reflexivity.
    + simpl. rewrite andb_true_iff. split.
      { simpl in H. apply proj1 in H. assumption. }
      { simpl in H. apply proj2 in H. apply IHl in H. assumption. }
Qed.


Require Import Nat.
Require Import Arith.

(*
  The "Tm" type and the previously defined typing relation.
*)

Inductive Tm : Set :=
  | num (n : nat)
  | plus (t t' : Tm)
  | isZero (t : Tm)
  | true
  | false
  | ifThenElse (t t' t'' : Tm)
.

Notation "t + t'" := (plus t t') : term_scope.
Notation
  "'If' t 'then' t' 'else' t''" :=
  (ifThenElse t t' t'')
  (at level 100)
  : term_scope
.

Delimit Scope term_scope with term.
(* Bind Scope term_scope with Tm. *)
Open Scope term_scope.

(* The set of Ty and the inductive typing relation. *)

Inductive Ty : Set := Nat | Bool.

Inductive TypeJudgement : Tm -> Ty -> Prop :=
  | TJ_num {n : nat} : (num n) :: Nat
  | TJ_plus {t t' : Tm} (j : t :: Nat) (j' : t' :: Nat) : (t + t') :: Nat
  | TJ_isZero {t : Tm} (j : t :: Nat) : (isZero t) :: Bool
  | TJ_true : true :: Bool
  | TJ_false : false :: Bool

  | TJ_ifThenElse
    {t t' t'' : Tm} {A : Ty}
    (j : t :: Bool) (j' : t' :: A) (j'' : t'' :: A)
    : (If t then t' else t'') :: A

where "tm :: ty" := (TypeJudgement tm ty) : term_scope.

(*
  2.3. Operációs szemantika
*)

(* val : 2.9. - 2.11. *)

Reserved Notation "t 'val'" (at level 1).

(*
  Value judgements.
*)

Inductive ValueJudgement : Tm -> Prop :=
  | VJ_num {n : nat} : (num n) val
  | VJ_true          : true val
  | VJ_false         : false val
where "t 'val'" := (ValueJudgement t).

(* |-> : 2.14. - 2.22. *)

Reserved Notation "t |-> t'" (at level 100).

(*
  One Step Transition judgements.
*)

Inductive OneStepTransitionJudgement : Tm -> Tm -> Prop :=
  | OSTJ_plus {n n1 n2 : nat} :
    ((n1 + n2)%nat = n) ->
    num n1 + num n2 |-> num n

  | OSTJ_isZero_true :
    isZero (num 0) |-> true

  | OSTJ_isZero_false {n : nat} :
    (n > 0) ->
    isZero (num n) |-> false

  | OSTJ_ifThenElse_true {t1 t2 : Tm} :
    If true then t1 else t2 |-> t1

  | OSTJ_ifThenElse_false {t1 t2 : Tm} :
    If false then t1 else t2 |-> t2

  | OSTJ_plus_left {t1 t2 t1' : Tm} :
    (t1 |-> t1') ->
    (t1 + t2 |-> t1' + t2)

  | OSTJ_plus_right {t1 t2 t2' : Tm} :
    t1 val ->
    (t2 |-> t2') ->
    (t1 + t2 |-> t1 + t2')

  | OSTJ_isZero {t t' : Tm} :
    (t |-> t') ->
    (isZero t |-> isZero t')

  | OSTJ_ifThenElse {t t' t1 t2 : Tm} :
    (t |-> t') ->
    (If t then t1 else t2 |-> If t' then t1 else t2)
where "t |-> t'" := (OneStepTransitionJudgement t t').

(* |->* : 2.12. - 2.13. *)

Reserved Notation "t |->* t'" (at level 100).

(*
  Any Step Transition judgements.
*)

Inductive AnyStepTransitionJudegement : Tm -> Tm -> Prop :=
  | ASTJ_refl  {e : Tm} :
    (e |->* e)

  | ASTJ_trans {e e' e'' : Tm} :
    (e  |->  e') ->
    (e' |->* e'') ->
    (e  |->* e'')
where "t |->* t'" := (AnyStepTransitionJudegement t t').

Lemma p :
  (num 1 + num 2) + (num 3 + num 4) |-> num 3 + (num 3 + num 4).
Proof.
  apply OSTJ_plus_left.
  apply OSTJ_plus.
  trivial.
Qed.

Lemma q :
  num 3 + (num 3 + num 4) |-> num 3 + num 7.
Proof.
  apply OSTJ_plus_right.
  + apply VJ_num.
  + apply OSTJ_plus. trivial.
Qed.

Lemma r :
  num 3 + num 7 |-> num 10.
Proof.
  apply OSTJ_plus.
  trivial.
Qed.

Lemma TransitionTest1 :
  (num 1 + num 2) + (num 3 + num 4) |->* num 10.
Proof.
  eapply ASTJ_trans.
  + eapply OSTJ_plus_left. eapply OSTJ_plus. reflexivity.
  + eapply ASTJ_trans.
    - simpl. eapply OSTJ_plus_right.
      * exact VJ_num.
      * eapply OSTJ_plus. reflexivity.
    - simpl. eapply ASTJ_trans.
      * eapply OSTJ_plus. reflexivity.
      * simpl. apply ASTJ_refl.
Qed.

Lemma TransitionTest2 :
  If isZero (num 1 + num 2) then num 1 else (num 3 + num 1) |->* num 4.
Proof.
  eapply ASTJ_trans.
  + eapply OSTJ_ifThenElse. eapply OSTJ_isZero. eapply OSTJ_plus. reflexivity.
  + eapply ASTJ_trans.
    - eapply OSTJ_ifThenElse. eapply OSTJ_isZero_false. auto.
    - eapply ASTJ_trans.
      * eapply OSTJ_ifThenElse_false.
      * eapply ASTJ_trans.
        ** eapply OSTJ_plus. reflexivity.
        ** simpl. eapply ASTJ_refl.
Qed.

(*
  2.19. Lemma.
  No transition exists from val.
*)

(*
  2.20. Lemma.
  Deterministicity.
*)

(*
  2.27. Tétel.
  Theorem of type preservation.
*)

(*
  2.29. Lemma
*)

(*
  2.28. Tétel.
  Theorem of progress.
*)


(*

  _   _    ___    __  __   _____  __        __   ___    ____    _  __    ____
 | | | |  / _ \  |  \/  | | ____| \ \      / /  / _ \  |  _ \  | |/ /   |___ \
 | |_| | | | | | | |\/| | |  _|    \ \ /\ / /  | | | | | |_) | | ' /      __) |
 |  _  | | |_| | | |  | | | |___    \ V  V /   | |_| | |  _ <  | . \     / __/
 |_| |_|  \___/  |_|  |_| |_____|    \_/\_/     \___/  |_| \_\ |_|\_\   |_____|

Howdy folks! In this homework assignment we'll explore more Coq programming
and proving and get some practice with interpreters.

There are 21 problems worth a total of 120 core points and
30 challenge points.

*)

Require Import Arith Lia List String.
Import ListNotations.
Open Scope string.

(*
               ____                  _     _                     _
              / ___|    ___    ___  | |_  (_)   ___    _ __     / |
              \___ \   / _ \  / __| | __| | |  / _ \  | '_ \    | |
               ___) | |  __/ | (__  | |_  | | | (_) | | | | |   | |
              |____/   \___|  \___|  \__| |_|  \___/  |_| |_|   |_|

                          SECTION 1 : More Coq Practice
*)

(*
 * In this part of the homework, we get some practice with lists and other
 * data structures.
 *
 * Step through this file with VSCode (or another IDE for Coq), and complete the
 * problems. Search for "PROBLEM" to be sure you find them all---not all
 * problems correspond to Coq code!
 *
 * Throughout, we include the approximate lines of code (LOC) or number of
 * tactics for each of our solutions to these problems.  These are rough
 * guidelines to help you figure out if you are getting off track.
 *
 * There is no penalty for writing a shorter or longer solution! However, if
 * you find yourself writing a much longer solution, you might save yourself
 * some time by taking a step back and seeing if you can find a simpler way.
 *
 *)

(* --- More arithmetic --- *)

(* This module creates a namespace where we can travel back in time to
 * Homework 1. Later in this homework we will be using the Coq standard library
 * version of nat, so we hide our own definitions in this namespace so we can
 * close it later.
 *)
Module Homework1_TimeTravel. (* get in the DeLorean *)
Set Implicit Arguments.

(*
 * Here are some definitions again from Homework 1.
 *)
Inductive nat : Type :=
| O : nat
| S : nat -> nat.

Fixpoint add (n1 : nat) (n2 : nat) : nat :=
  match n1 with
  | O => n2
  | S m1 => S (add m1 n2)
  end.

(*
 * PROBLEM 1 [0 points, ~4 tactics]
 *
 * You'll need this, which you proved in Homework 1. Feel free to copy/paste
 * your solution.
 *)
Lemma add_n_S :
  forall n1 n2,
    add n1 (S n2) = S (add n1 n2).
Proof.
induction n1 as [| p IHp]; intros; simpl.
- reflexivity.
- rewrite IHp. reflexivity.
Qed. (* Change to Qed when done *)

(* We can define multiplication recursively as repeated addition. *)

Fixpoint mult (n1 : nat) (n2 : nat) : nat :=
  match n1 with
  | O => O
  | S m1 => add n2 (mult m1 n2)
  end.

(*
 * PROBLEM 2 [8 points, ~35 tactics]
 *
 * Prove that multiplication is commutative, as stated by the lemma below.
 *
 * Hint: Proceed by induction.
 *
 * Hint: Don't use more than one induction inside a single proof.  Instead,
 * figure out lemmas that might be useful, and then prove those separately
 * (by induction, probably). See the style guide for more information.
 *
 * Hint: It might be useful to review all the lemmas about `add` in Homework 1.
 * Feel free to copy them over if you need to use them. You might need to
 * state and prove some analogous lemmas about `mult`.
 *
 * Hint: In order to prove your helpers about `mult`, you might need 1 or 2
 * additional facts about `add`.  Try to keep these simple, based on facts
 * you know from math, and prove them by induction.
 *)

 Lemma add_n_O :
  forall n,
  add n O = n.
Proof.
  induction n.
  - reflexivity.
  - simpl. rewrite IHn. reflexivity.
  Qed.

Lemma mult_n_O :
 forall n,
   mult n O = O.
Proof.
 induction n; auto.
Qed.

Lemma add_comm :
  forall n1 n2,
    add n1 n2 = add n2 n1.
Proof.
  induction n1.
  intros.
  - simpl. rewrite -> add_n_O. reflexivity.
  - simpl. intro n2. symmetry. rewrite add_n_S.
    rewrite IHn1. auto.
Qed.

Lemma add_assoc :
  forall n1 n2 n3,
    add n1 (add n2 n3) = add (add n1 n2) n3.
Proof.
  intros.
  induction n1.
  - simpl. reflexivity.
  - simpl. f_equal. assumption.
Qed.

Lemma mult_n_Sm :
  forall n m : nat,
    mult n (S m) =  add n (mult n m).
Proof.
  induction n; simpl; intros.
  - reflexivity.
  - rewrite IHn. f_equal.
    rewrite -> add_assoc.
    replace (add m n) with (add n m) by apply add_comm.
    rewrite <- add_assoc.
    reflexivity.
Qed.

Lemma mult_comm :
  forall n m : nat,
    mult n m = mult m n.
Proof.
  induction n; simpl; intros.
  - symmetry. apply mult_n_O.
  - rewrite IHn. rewrite mult_n_Sm. reflexivity.
Qed.

Lemma add_swap:
  forall n1 n2 n3,
    add n1 (add n2 n3) = add n2 (add n1 n3).
Proof.
  intros.
  induction n1 as [| p IHp].
  - simpl. reflexivity.
  - simpl. rewrite IHp. rewrite <- add_n_S. reflexivity.
Qed.

Lemma mult_distributive:
  forall n1 n2 n3,
    mult (add n1 n2) n3 = add (mult n1 n3) (mult n2 n3).
Proof.
  intros.
  induction n1 as [| m1 IHm1].
  - simpl. reflexivity.
  - simpl. rewrite <- add_assoc. rewrite IHm1. reflexivity.
Qed.
(*
 * PROBLEM 3 [10 points, ~25 tactics]
 *
 * State and prove that the `mult` function is associative.
 *
 * Hint: You'll probably need ~2 more lemmas about mult and/or add.
 *)
(* YOUR LEMMA STATEMENT AND PROOF HERE *)
Lemma mult_assoc :
  forall n1 n2 n3,
    mult n1 (mult n2 n3) = mult (mult n1 n2) n3.
Proof.
  intros.
  induction n1 as [| p IHp].
  - simpl. reflexivity.
  - simpl. rewrite IHp. rewrite <- mult_distributive. reflexivity.
Qed.

(* Here's the definition of `le` from lecture (and the standard library). *)
Inductive le (n : nat) : nat -> Prop :=
| le_n : le n n
| le_S : forall m, le n m -> le n (S m).

(*
 * PROBLEM 4 [10 points, ~9 tactics]
 *
 * State and prove the following:
 * If a <= b then a + c <= b + c
 *
 * Hint: Proceed by induction.
 *)
Lemma a_le_b_add_c_le_b_add_c :
  forall a b c: nat,
    le a b -> le (add a c) (add b c).
  Proof.
    intros.
    induction H.
    - simpl. apply le_n.
    - simpl. apply le_S. assumption.
Qed.

End Homework1_TimeTravel. (* return to the present *)

(*
 * Now that you've seen how nat, add, and mult are defined under the hood,
 * from now on we'll use the versions in Coq's standard library. These come
 * with nice notations like `1 + 3 * 4`, and with lots of free lemmas.
 *
 * There are also some useful tactics for working with arithmetic.
 *
 * Here are some automated proofs that the Coq standard library versions
 * of add and mult are commutative. (These lemmas are also in the standard
 * library, but we prove them from scratch just to demonstrate the tactics.)
 *)

Lemma add_comm_again :
  forall a b, a + b = b + a.
Proof.
  intros.
  lia.
Qed.

Lemma mult_comm_again :
  forall a b, a * b = b * a.
Proof.
  intros.
  ring.
Qed.

(*
 * PROBLEM 5 [4 points, ~5 tactics]
 *
 * Prove this simple fact about and.
 *
 * Hint: Review tactics that are useful for taking apart hypotheses.
 *
 * Hint: Review tactics that are useful for decomposing goals.
 *
 * Hint: Our solution uses little automation. If you feel like it, you can solve
 * it with ~1 tactic as well, using automation.
 *)
Lemma and_comm :
  forall (A B : Prop),
    A /\ B ->
    B /\ A.
Proof.
  intros.
  destruct H as [Ha Hb].
  split. (* works because we're proving a conjunction.*)
  - assumption.
  - assumption.
Qed.

(*
 * PROBLEM 6 [4 points, ~7 tactics]
 *
 * Prove this simple fact about or.
 *
 * Hint: Review tactics that are useful for taking apart hypotheses.
 *
 * Hint: Review tactics that are useful for decomposing goals.
 *
 * Hint: Our solution uses little automation. If you feel like it, you can solve
 * it with ~1 tactic as well, using automation.
 *)
Lemma or_comm :
  forall (A B : Prop),
    A \/ B ->
    B \/ A.
Proof.
  intros.
  destruct H as [Ha | Hb].
  - right. exact Ha. (* can't split here because not a conjunction - have to extract each side *)
  - left. exact Hb.
Qed.

(* Here is an inductive definition of evenness for natural numbers. *)
Inductive even : nat -> Prop :=
| even_O : even O
| even_SS : forall n, even n -> even (S (S n)).

(*
 * There are two constructors that can prove even. 0 is even. And a number of
 * the form S (S n) is even if n is even.
 *)

(*
 * PROBLEM 7 [9 points, ~3 sentences]
 *
 * Write a comment answering the following questions.
 *
 * (a) If n is a nat in Coq, how many constructors of the type nat (O or S) is
 *     it built with? How many of those are S constructors and how many are O?
 *
 * (b) If n is a nat in Coq, and pf is a proof of even n, how many constructors
 *     of the proposition even is pf built with? How many are even_O and how
 *     many are even_SS?
 *
 * (c) If n is a nat in Coq, and n is odd, explain intuitively why your answer
 *     to part (b) implies that there is no proof of "even n".
 *
 * Hint: Your answer to part (a) should be three quantities: how many
 * constructors total, how many are O, and how many are S? Each quantity should
 * be expressed in terms of n. For example, one (wrong) answer would be "There
 * are n / 3 + 7 constructors total, n / 3 are O, and 7 are S."
 *
 * Hint: Your answer to part (b) should be of a similar "shape" to part (a):
 * three quantities expressed in terms of n.
 *)
(*
 * (a) Each nat is built with n + 1 constructors, where n is the number of S () and + 1 O constuctor.
 * (b) Each even n is built with n/2 even_SS constructors and one even_O constructor.
 * (c) There's isn't one! The construcors described in #2 are predicated on the recursive nature of even numbers.
       As there isn't a constructor for False in Coq, so there is no proof of even n if n is odd.
 *)

(* Here is a function that returns twice its argument. *)
Fixpoint double (n : nat) : nat :=
  match n with
  | O => O
  | S m => S (S (double m))
  end.

(*
 * PROBLEM 8 [4 points, ~5 tactics]
 *
 * Prove that double always returns an even number.
 *
 * Hint: Proceed by induction.
 *)
Lemma double_even :
  forall n,
    even (double n).
Proof.
  intros.
  induction n.
  simpl.
  - exact even_O.
  - simpl. apply even_SS. assumption.
Qed.

(*
 * PROBLEM 9 [4 points, ~2 sentences]
 *
 * Consider the following lemma, which says that every even number is the output
 * of double on some input.
 *
 * Attempt to prove this lemma by induction on n. No need to turn in your
 * partial proof.
 *
 * Explain in a comment where you get stuck.
 *)

Lemma even_double :
  forall n,
    even n ->
    exists k,
      n = double k.
Proof.
  induction n.
  - intros. exists 0. reflexivity.
  - intros. destruct IHn as [k Hk].
    contradict H. exfalso. (* I could have stopped before the destruct, but going this deep proves that the approach truly is broken,
                              because the first goal is "False" and the only element above the line is "n: nat" *)
Abort.
(* We don't know enough about the computation of even n, or double k so the proof breaks down almost immediately. Immediately after
proving the base case, n = 0, the proof assistant poses us with even S( n ) which is, of course, false. But we don't have enough
information to demonstrate that. Instead, if we were to induct on the derivation of even n we can prove that, for all cases
where n is even, the implication holds. *)

(*
 * PROBLEM 10 [5 points, ~9 tactics]
 *
 * Now prove the lemma by induction on the derivation of even n.
 *)
Lemma even_double :
  forall n,
    even n ->
    exists k,
      n = double k.
Proof.
  intros.
  induction H.
  - exists O. reflexivity.
  - destruct IHeven as [d1 d2].
    + exists (S d1). simpl in *. lia.
Qed.

(*
 * PROBLEM 11 [5 points, ~6 tactics]
 *
 * Prove that the sum of two even numbers is even.
 *
 * Hint: What should you induct on?
 *)
Lemma plus_even :
  forall x y,
    even x ->
    even y ->
    even (x + y).
Proof.
  intros. induction H.
  - simpl. assumption.
  - simpl. constructor. assumption.
Qed.

(*
 * PROBLEM 12 [5 points, ~4 tactics]
 *
 * Prove that 3 is not even.
 *
 * Hint: No need for induction. "Call Coq's bluff".
 *)
Lemma three_not_even :
  even 3 -> False.
Proof.
  intro Ht.
  inversion Ht. (* "I'll prove false for all cases which could have proved this true"*)
  inversion H0. (* even 1 is totally impossible to prove, so we're done *)
Qed.

Module Data_Structures.
Set Implicit Arguments.

(* --- List practice --- *)

(* Here are some definitions copied from the Coq standard library. *)
Inductive list (A : Type) : Type :=
| nil
| cons (hd : A) (tl : list A).

Arguments nil {A}.
Infix "::" := cons.
Notation "[ ]" := nil.
Notation "[ x1 ; .. ; xN ]" := (cons x1 (.. (cons xN nil) ..)).

Fixpoint length {A} (ls : list A) : nat :=
  match ls with
  | nil => 0
  | _ :: ls' => 1 + length ls'
  end.

Fixpoint app {A} (ls1 ls2 : list A) : list A :=
  match ls1 with
  | nil => ls2
  | x :: ls1' => x :: app ls1' ls2
  end.
Infix "++" := app.

Fixpoint rev {A} (ls : list A) : list A :=
  match ls with
  | nil => nil
  | x :: ls' => rev ls' ++ [x]
  end.

(*
 * PROBLEM 13 [12 points, ~15 tactics]
 *
 * Prove the following theorem about the length of a reversed list.
 *
 * Hint: Proceed by induction on l.
 *
 * Hint: You'll need a helper lemma!
 *)

 Lemma len_1_S :
  forall A (l: list A),
    length l + 1 = S (length l).
 Proof.
  induction l.
  - simpl. reflexivity.
  - simpl. rewrite IHl. reflexivity.
 Qed.

 Lemma length_comp:
  forall A (l1 l2 : list A),
    length (l1 ++ l2) = length l1 + length l2.
Proof.
  induction l1; auto.
  - simpl. intros. rewrite IHl1. reflexivity.
Qed.

Lemma length_rev :
  forall A (l : list A),
    length (rev l) = length l.
Proof.
  induction l.
  - auto.
  - simpl. rewrite length_comp. simpl. rewrite IHl. apply len_1_S.
Qed.

(*
 * Here is a function that adds up all the elements of a list of nats.
 *)
Fixpoint sum_list (l : list nat) : nat :=
  match l with
  | [] => 0
  | x :: xs => x + sum_list xs
  end.

(*
 * And here are two functions that computes the same result with tail recursion.
 *)
Fixpoint sum_list_tr (l : list nat) (acc : nat) : nat :=
  match l with
  | [] => acc
  | x :: xs => sum_list_tr xs (x + acc)
  end.

Definition sum_list_tailrec (l : list nat) : nat :=
  sum_list_tr l 0.

(*
 * PROBLEM 14 [5 points, ~1 sentences]
 *
 * Consider the following theorem about the two ways of summing a list. You will
 * be asked to prove it in the next problem. For now, make a proof attempt that
 * proceeds directly by induction on `l`, without any nested inductions or
 * extra lemmas. At some point, you should get stuck.
 *
 * Write a sentence or two explaining why your proof attempt fails. Focus on the
 * induction hypothesis and whether it gives you enough information to complete
 * the proof.
 *
 * There is no need to turn in your proof attempt. Just turn in your
 * explanation.
 *
 * Hint: The base case should be provable.
 *
 * Hint: You might be able to make slightly more progress if you use the
 * `unfold` tactic to see inside the definition of `sum_list_tailrec`.
 *)
Theorem sum_list_tailrec_ok : forall l,
  sum_list_tailrec l = sum_list l.
Proof.
  induction l.
  - auto.
  - simpl. rewrite <- IHl. unfold sum_list_tailrec. simpl.
Abort.
  (* The remaining goal of the proof when we left off above is: "sum_list_tr l (hd + 0) = hd + sum_list_tr l 0" and the inductive
  hypothesis is "sum_list_tailrec l = sum_list l". The inductive hypothesis here is helpful if we
  have a reference to sum_list_tailrec or sum_list in our remaining goal, but that's not the case *)

(*
 * PROBLEM 15 [8 points, ~15 tactics]
 *
 * Now fix your broken proof by backing up and stating and proving a more
 * general helper lemma. Then use your helper lemma to finish the proof.
 *
 * Hint: Since `sum_list_tailrec` is defined in terms of the recursive function
 * `sum_list_tr`, it makes sense for your lemma to be about `sum_list_tr`.
 *
 * Hint: Your lemma should be proved by induction.
 *
 * Hint: Your fixed proof of `sum_list_tailrec_ok` should *not* use induction.
 *)

 Lemma sum_list_tr_sum_list_pl_acc : forall l acc,
 sum_list_tr l acc = sum_list l + acc.
Proof.
 induction l.
 - auto.
 - simpl. intros. rewrite -> IHl. lia.
Qed.

Theorem sum_list_tailrec_ok : forall l,
  sum_list_tailrec l = sum_list l.
Proof.
  intros. unfold sum_list_tailrec.
  replace (sum_list l) with (sum_list l + 0) by lia. apply sum_list_tr_sum_list_pl_acc.
Qed.

(* --- Binary Tree practice --- *)

Inductive tree (A : Type) : Type :=
| Leaf
| Node : tree A -> A -> tree A -> tree A.
Arguments Leaf {A}.

Fixpoint reverse {A} (t : tree A) : tree A :=
  match t with
  | Leaf => Leaf
  | Node l n r => Node (reverse r) n (reverse l)
  end.

(*
 * PROBLEM 16 [5 points, ~5 LOC]
 *
 * Define a function that adds up all the elements of a tree of nats.
 *)
Fixpoint sum_tree (t : tree nat) : nat :=
  match t with
  | Leaf => 0
  | Node l d r => d + sum_tree l + sum_tree r
  end.

(*
 * PROBLEM 17 [5 points, ~5 tactics]
 *
 * Prove that `reverse` has no effect on the sum of the elements in a tree.
 *)
Lemma sum_tree_reverse :
  forall t,
    sum_tree (reverse t) = sum_tree t.
Proof.
  induction t.
  - auto.
  - simpl. rewrite IHt1. rewrite IHt2. lia.
Qed.

(*
 * PROBLEM 18 [12 points, ~20 tactics]
 *
 * Prove the following similar lemma about `sum_list` and `rev`.
 *
 * Hint: Proceed by induction on l.
 *
 * Hint: You'll need a helper lemma about sum_list.
 *)

Lemma sum_list_app_eq_sum_list_split :
  forall l1 l2,
    sum_list (l1 ++ l2) = sum_list l1 + sum_list l2.
Proof.
  induction l1.
  - auto.
  - simpl. intros. rewrite IHl1. lia.
Qed.

(* sum_list_nat is mostly just for reorganizing a goal and I couldn't
   figure out another way to do that *)
Lemma sum_list_nat :
  forall (hd : nat) (l1: list nat),
     hd = sum_list [hd].
  Proof.
    intros. simpl. lia.
Qed.

Lemma sum_list_rev :
  forall l,
    sum_list (rev l) = sum_list l.
Proof.
  induction l.
  - auto.
  - simpl. rewrite <- IHl. simpl. rewrite (sum_list_nat hd) at 2.
    rewrite sum_list_app_eq_sum_list_split. lia. auto.
Qed.

End Data_Structures.

(*
 * PROBLEM 19 [5 points, ~3 sentences]
 *
 * Please take a moment to let us know how the homework went for you by
 * answering the following three questions:
 *    1. How long did the homework take you?
 *    2. Which parts were most interesting or helpful for you?
 *    3. Which parts were especially frustrating, confusing, or tedious?
 *       What should we do better next time?
 *
 * It's fine if your answers are short if you don't have much to say!
 *)

(*
* answers to the questions above:
 *    1. probably 15 or so hours, but I got stuck for a long time on the
  *       first few. Office hours with Zach was really helpful!
 *    2. Everything was interesting, but I enjoyed the data structures
  *       section the most. It's cool that there is a (truly challenging)
  *       challenges section.
 *    3. Everything made sense eventually. I would definitely benefit from
         seeing the instructors work through more proofs with more tactics.
         Office hours was a game-changer for me mostly because I was able to
        see how Zach thought through a few lemmas.
 *)

(* --- End of Core Points --- *)

(*
 * This is the end of the core points available for Homework 2. See below for
 * challenge points.
 *
 * To submit your homework, please follow the instructions at the end of the
 * README.md file in this directory.
 *
 * Please also see the README.md file to read about how we will grade this
 * homework assignment.
 *)

(*
             ____                  _     _                     ____
            / ___|    ___    ___  | |_  (_)   ___    _ __     |___ \
            \___ \   / _ \  / __| | __| | |  / _ \  | '_ \      __) |
             ___) | |  __/ | (__  | |_  | | | (_) | | | | |    / __/
            |____/   \___|  \___|  \__| |_|  \___/  |_| |_|   |_____|

                          SECTION 2 : Challenge Problem
*)

(*
 * CHALLENGE 20 [10 points, ~20 tactics]
 *
 * Give an alternative characterization of evenness as follows.
 *)

 Lemma even_double_nat :
 forall n, n + n = double n .
Proof.
  intros.
  induction n.
  - auto.
  - simpl. lia.
Qed.

Lemma even_n_add_n :
 forall n, even (n + n).
Proof.
  intros.
  induction n.
  - simpl. constructor.
  - rewrite even_double_nat. apply double_even.
Qed.

Lemma even_iff_exists_half :
  forall n,
    even n <-> exists k, n = 2 * k.
Proof.
  split.
  intros.
  induction H.
  - exists 0. auto.
  - destruct IHeven.
    + exists (S x). lia.
  - simpl in *. (* exists k, n = 2 * k. is NOT recursive so we shouldn't need induction. *)
    intros.
    destruct H.
    rewrite H.
    replace (x + (x + 0)) with (x + x) by lia.
    apply even_n_add_n.
Qed.

(* CHALLENGE 21 [20 points, ~8 tactics]
 *
 * In class we saw a proof that Peirce's law implies the law of excluded middle.
 * Now prove the reverse direction
 *
 * Hint: This way should be easier than the proof from lecture.
 *)
Lemma lem_implies_peirce :
  (forall P : Prop, P \/ ~P) -> forall P Q : Prop, ((P -> Q) -> P) -> P.
Proof.
   simpl. intros.
    destruct (H P) as [P1 | P2]. (* P1 = P, P2 = ~P - comment in here because I became *very* confused*)
    - apply P1.
    - apply H0. intros. contradict H1. apply P2.
  Qed.


Inductive bool : Type :=
| true : bool
| false : bool.

Definition negb (x : bool) : bool :=
match x with
| true => false
| false => true
end.

Check negb.

Lemma L1 : negb true = false.
Proof.
simpl.
reflexivity.
Qed.

Lemma negb_negb (x:bool) : negb (negb x) = x.
Proof.
destruct x.
reflexivity.
reflexivity.
Qed.

Definition andb (x y : bool) : bool :=
match x with
| true => y
| false => false
end.

Lemma andb_com x y : andb x y = andb y x.
Proof.
destruct x.
destruct y.
reflexivity.
reflexivity.
destruct y.
reflexivity.
reflexivity.
Defined.

Definition orb (x y : bool) : bool :=
match x with
| true => true
| false => y
end.

Check orb.

Lemma orb_com x y : orb x y = orb y x.
Proof.
destruct x.
destruct y.
reflexivity.
reflexivity.
destruct y.
reflexivity.
reflexivity.
Qed.

Lemma demorgan x y : negb (orb x y) = andb (negb x) (negb y).
Proof.
destruct x.
destruct y.
reflexivity.
reflexivity.
destruct y.
reflexivity.
reflexivity.
Qed.

Check fun x : bool => x.

Compute (fun x : bool => x) true.

Check andb true.

Compute andb true.

Definition andb' : bool -> bool -> bool :=
 fun x : bool =>
   fun y : bool =>
     match x with
      | true => y
      | false => false
end.

Print negb.

Print andb.
Print andb'.


Require Import List.

Set Implicit Arguments.

Print List.

Fixpoint sum (l : list nat) : nat :=
match l with
  | nil => 0
  | cons h tail =>
    let n:= sum tail in h + n
end.

Eval compute in sum(cons 5 (cons 5 (cons 5 nil))).

Print "++".
Print rev.
Print length.
Print map.

Lemma ex3_1 : forall (l1 : list nat) (l2 : list nat), sum (l1 ++ l2) = sum l1 + sum l2.
Proof.
  induction l1.
  - intro. simpl. reflexivity.
  - simpl. intro. rewrite IHl1. rewrite PeanoNat.Nat.add_assoc. reflexivity.
Qed.

Lemma ex3_2 : forall (A:Type) (l:list A), length (rev l) = length l.
Proof.
  induction l.
  - simpl. reflexivity.
  - simpl. rewrite app_length. simpl. rewrite IHl. rewrite PeanoNat.Nat.add_comm. reflexivity.
Qed.

Lemma ex3_3 : forall (A B:Type) (f:A->B) (l1 l2:list A), (map f l1)++(map f l2) = map f (l1++l2).
Proof.
  induction l1.
  - induction l2.
    + simpl. reflexivity.
    + simpl. reflexivity.
  - induction l2.
    + simpl. rewrite <- IHl1. simpl. reflexivity.
    + simpl. rewrite <- IHl1. simpl. reflexivity.
Qed.

Lemma ex3_4 : forall (A B:Type) (f:A->B) (l:list A), rev (map f l) = map f (rev l).
Proof.
  induction l.
  - simpl. reflexivity.
  - simpl. rewrite IHl. rewrite map_app. simpl. reflexivity.
Qed.

Print In.

Inductive In (A:Type) (y:A) : list A -> Prop :=
| InHead : forall xs:list A, In y (cons y xs)
| InTail : forall (x:A) (xs:list A), In y xs -> In y (cons x xs).

Print In.

Lemma ex4_1_aux : forall (A:Type) (x:A) (l1 l2: list A), (In x l1 \/ In x l2) -> In x (l1 ++ l2).
Proof.
  intros.
  destruct H as [H|H].
  - induction H.
    + rewrite <- app_comm_cons. apply InHead.
    + rewrite <- app_comm_cons. apply InTail. assumption.
  - induction l1.
    + simpl. assumption.
    + rewrite <- app_comm_cons. apply InTail. assumption.
Qed.

Lemma ex4_1 : forall (A:Type) (x:A) (l:list A), In x l -> In x (rev l).
Proof.
  intros.
  induction H.
  - simpl.
    apply ex4_1_aux.
    right.
    apply InHead.
  - simpl.
    apply ex4_1_aux.
    left.
    assumption.
Qed.

Lemma ex4_2 : forall (A B:Type) (y:B) (f:A->B) (l:list A), In y (map f l) -> exists x, In x l /\ y = f x.
Proof.
  intros.
  induction l.
  - simpl in H. inversion H.
  - simpl in H. inversion H.
    * exists a. split.
      + apply InHead.
      + reflexivity.
    * cut (exists x:A, In x l /\ y = f x).
      + intro. destruct H3. exists x0. destruct H3 as [H3 H4]; split.
        -- constructor; assumption.
        -- assumption.
      + exact (IHl H1).
Qed.

Lemma ex4_3 : forall (A:Type) (x:A) (l :list A), In x l -> exists l1, exists l2, l = l1 ++ (x::l2).
Proof.
  intros.
  induction H.
  + exists nil. exists xs. simpl. reflexivity.
  + destruct IHIn as [I1 H1]. destruct H1 as [I2 H1]. exists (x0::I1). exists I2. rewrite <-app_comm_cons. rewrite H1; reflexivity.
Qed.

Inductive Prefix (A:Type) : list A -> list A -> Prop :=
| PreNil : forall l:list A, Prefix nil l
| PreCons : forall (x:A) (l1 l2:list A), Prefix l1 l2 -> Prefix (x::l1) (x::l2).

Lemma ex5_1 : forall (A:Type) (l1 l2:list A), Prefix l1 l2 -> length l1 <= length l2.
Proof.
  intros.
  induction H.
  - simpl. intuition.
  - simpl. rewrite <- PeanoNat.Nat.succ_le_mono. assumption.
Qed.

Lemma ex5_2 : forall l1 l2, Prefix l1 l2 -> sum l1 <= sum l2.
Proof.
  intros.
  induction H.
  - simpl; intuition.
  - simpl. apply Plus.plus_le_compat_l. assumption.
Qed.

Lemma ex5_3 : forall (A:Type) (l1 l2:list A) (x:A), (In x l1) /\ (Prefix l1 l2) -> In x l2.
Proof.
  intros.
  destruct H as [H1 H2].
  induction H2.
  + induction l.
    - assumption.
    - apply InTail. assumption.
  + inversion H1.
    - constructor.
    - constructor. apply IHPrefix. assumption.
Qed.

Inductive SubList (A:Type) : list A -> list A -> Prop :=
| SLnil : forall l:list A, SubList nil l
| SLcons1 : forall (x:A) (l1 l2:list A), SubList l1 l2 -> SubList (x::l1) (x::l2)
| SLcons2 : forall (x:A) (l1 l2:list A), SubList l1 l2 -> SubList l1 (x::l2).

Lemma ex6_1 : forall (A:Type)(l1 l2 l3 l4:list A), SubList l1 l2 -> SubList l3 l4 -> SubList (l1++l3) (l2++l4).
Proof.
  intros.
  induction H.
  - simpl. induction l.
    + simpl; assumption.
    + rewrite <- app_comm_cons. constructor. assumption.
  - rewrite <- app_comm_cons; constructor; assumption.
  - rewrite <- app_comm_cons; constructor; assumption.
Qed.

Lemma ex6_2 : forall (A:Type) (l1 l2:list A), SubList l1 l2 -> SubList (rev l1) (rev l2).
Proof.
Admitted.

Lemma ex6_3 : forall (A:Type) (x:A) (l1 l2:list A), Prefix l1 l2 -> SubList l1 l2.
Proof.
  intros.
  induction H.
  - apply SLnil.
  - apply SLcons1. assumption.
Qed.

Lemma ex6_4 : forall (A:Type) (x:A) (l1 l2:list A), SubList l1 l2 -> In x l1 -> In x l2.
Proof.
  intros.
  induction H.
  - induction l.
    + assumption.
    + apply InTail. assumption.
  - inversion H0.
    + apply InHead.
    + apply InTail. apply IHSubList. assumption.
  - apply InTail. apply IHSubList; assumption.
Qed.


From mathcomp
     Require Import ssreflect ssrnat.

Section naturalNumber.

Lemma addOnEqn (n : nat) : 0 + n = n.
Proof. by []. Qed.

Lemma addn3Eq2n1 (n : nat) : n + 3 = 2 + n + 1.
Proof.
rewrite addn1.
rewrite add2n.
rewrite addnC.
by [].
Qed.

Fixpoint sum n := if n is m.+1 then (sum m) + n else 0.

Lemma sumGauss (n : nat) : (sum n) * 2 = (n + 1) * n.
Proof.
elim: n.
by [].
move=> n IHn.
rewrite mulnC.
rewrite (_ : sum (n.+1) = n.+1 + (sum n)); last first.
rewrite /=.
by rewrite addnC.

rewrite mulnDr.
rewrite mulnC in IHn.
rewrite IHn.
rewrite 2!addn1.
rewrite [_ * n] mulnC.
rewrite -mulnDl.
by [].
Qed.

End naturalNumber.


Require Import  Arith.
Set Implicit Arguments.

(** Similar to Vector_Def library, with different names
*)

Section Vector_definitions.

Variable A : Type.

Inductive vector : nat -> Type :=
| vnil : vector 0
| vcons (h:A)(n:nat)(v: vector n) : vector (S n).

About vector_rect.

(**
 vector_rect :
forall P : forall n : nat, vector n -> Type,
P 0 vnil ->
(forall (n : nat) (v : vector n), P n v -> P (S n) (vcons h v)) ->
forall (n : nat) (v : vector n), P n v
*)

(** Arguments for the recursor vector_rect *)

Let P_head (n:nat)(v: vector n) := n<>0 -> A.
Let P_tail (n:nat)(v: vector n) := n<>0 -> vector (pred n).

Definition  P_head_0 : P_head  vnil.
Proof.
  intro H. now destruct H.
Defined.

Definition  P_tail_0 : P_tail  vnil.
Proof.
  intro H; now destruct H.
Defined.

Definition P_head_S :
 forall (h : A) (n : nat) (v : vector n), P_head  v ->
                                       P_head  (vcons  h  v) .
Proof.
 intros h n v X _; exact h.
Defined.

Definition P_tail_S :
 forall (h : A) (n : nat) (v : vector n), P_tail  v ->
                                          P_tail  (vcons  h  v) .
Proof.
 intros h n v X _;  exact v.
Defined.

Definition v_head_aux := vector_rect P_head P_head_0 P_head_S.
Check v_head_aux.

Definition v_tail_aux := vector_rect  P_tail P_tail_0 P_tail_S.

Definition v_head (n:nat)(v: vector (S n)) : A :=
  v_head_aux  v (Nat.neq_succ_0 n).

Definition v_tail (n:nat)(v: vector (S n)) : vector n:=
 $( refine (v_tail_aux v _); auto)$.

Definition v_Id  : forall n, vector n -> vector n :=
fun n => match n with
| 0 => fun v => vnil
|S p => fun v => vcons (v_head v) (v_tail v)
end.

Compute fun v : vector 0 => v_Id v.

(*
Definition v_Id  : forall n, vector n -> vector n .
 destruct n; intro v.
 - exact vnil.
 - exact (vcons (v_head v) (v_tail v)).
Qed.
*)

Lemma v_Id_eq : forall n (v:vector n), v = v_Id v.
 intros n v; pattern n ,v;apply vector_rect.
- reflexivity.
- intros.  reflexivity.
Qed.

Lemma vector_0 : forall v: vector 0, v = vnil.
intro v.
  change  vnil with (v_Id v).
 apply v_Id_eq.
Qed.

Lemma vector_S : forall n (v:vector (S n)),
    v = vcons (v_head v) (v_tail v).
Proof.
  intros n v; change (vcons (v_head v) (v_tail v)) with (v_Id v).
  apply v_Id_eq.
Qed.

  End Vector_definitions.

Arguments vnil {A}.

Lemma test1 : v_head  (vcons  42  vnil) =  42.
Proof. reflexivity. Qed.

Lemma test : v_tail  (vcons  42  (vcons 22 vnil)) =  vcons 22 vnil .
Proof. reflexivity. Qed.


(**

This exposes all the operators on bitvector which is used to give
semantics.

 *)
Require Export Bvector.
Require Import BinNat.
Require Import Arith.
Require Import NArith.
Require Import ZArith.
Require Import Nat.
Require Import SetoidClass.

Declare Scope bitvector_scope.
Delimit Scope bitvector_scope with bitvector.

Arguments Bv2N [n].

(** computes 2^m *)
Definition twopower m : N := Nat.iter m N.double 1%N.
Definition twopower_nat (m:nat) : nat := Nat.iter m Nat.double 1.
Definition twopower_N   (m:N) : N := N.iter m N.double 1%N.
Definition arithm (func : N -> N -> N) sz : Bvector sz -> Bvector sz -> Bvector sz
  := fun x y => N2Bv_sized sz (func (@Bv2N _ x) (@Bv2N _ y)).

Definition BVN_size {sz} (vec : Bvector sz) := N.size (Bv2N vec).
Definition BVN_size_nat {sz} (vec : Bvector sz) := N.size_nat (Bv2N vec).

Definition BVplus      := arithm N.add.
Definition BVnegative sz (vec : Bvector sz) := N2Bv_sized sz (2^(N.of_nat sz) - Bv2N vec).
Definition BVminus sz  := arithm (fun x y => x + 2^(N.of_nat sz) - y)%N sz.

Definition BVmul       := arithm N.mul.
Definition BVquot      := arithm N.div.
Definition BVrem       := arithm N.modulo.

Definition BVones : forall sz, Bvector sz
  := Bvect_true.
Definition BVzeros : forall sz, Bvector sz
  := Bvect_false.

Check BVand. (* Comes directly from Bvector *)
Check BVor.  (* Comes directly from Bvector *)
Check BVxor. (* Comes directly from Bvector *)

Definition BVcomp   := Bneg. (* renaming for better naming convention *)

Definition BVshiftL1 sz : Bvector sz -> Bvector sz :=
  match sz with
  | 0 => @id (Bvector 0)
  | S sz0 => fun vec => BshiftL sz0 vec false
  end.

Definition BVshiftL sz (n : nat) : Bvector sz -> Bvector sz
  := Nat.iter n (BVshiftL1 sz).

Definition BVshiftR1 sz : Bvector sz -> Bvector sz :=
  match sz with
  | 0 => @id (Bvector 0)
  | S sz0 => fun vec => BshiftRl sz0 vec false
  end.

Definition BVshiftR sz (n : nat) : Bvector sz -> Bvector sz
  := Nat.iter n (BVshiftR1 sz).

Definition rotTowardsLSB sz (vec : Bvector sz) :=
  match vec with
  | [] => []
  | (x :: xs) => Vector.shiftin x xs
  end.

(** This is what shiftout should have been not sure why it was not defined this way *)
Definition popout {A} : forall n, Vector.t A (S n) -> (Vector.t A n * A) :=
  @Vector.rectS _ (fun n _ => (Vector.t A n * A)%type)
           (fun a => ([], a))
           (fun h _ _ H => let (xs, x) := H in (h :: xs, x)).

Arguments popout [A n].

Definition rotTowardsMSB sz (vec : Bvector sz) :=
  match vec with
  | [] => []
  | xs => let (xsp, x) := popout xs in (x :: xsp)
  end.

Definition BVrotR sz n
  := Nat.iter n (rotTowardsLSB sz).

Definition BVrotL sz n
  := Nat.iter n (rotTowardsMSB sz).

(** Generates a bit vector with n-lsb bits set *)
Definition lower_ones sz n : Bvector sz
  := N2Bv_sized sz (N.ones (N.of_nat n)).

(** Generate a bit vector with n-msb bits set *)
Definition upper_ones sz n : Bvector sz
  := BVcomp sz (lower_ones sz (sz - n)).

(** Generate a bit vector with 1 in all bits between m and n
    (inclusive) and zero otherwise. Used to select a particular range
    of values.

*)

Definition onesAt sz (pos : nat) (len : nat): Bvector sz
  := BVxor sz (lower_ones sz (len+pos)) (lower_ones sz pos).

(* begin hide *)
Arguments BVplus   [sz].
Arguments BVminus  [sz].
Arguments BVmul    [sz].
Arguments BVquot   [sz].
Arguments BVrem    [sz].
Arguments BVnegative [sz].
Arguments BVand    [_].
Arguments BVor     [_].
Arguments BVxor    [_].
Arguments BVcomp   [_].
Arguments BVshiftL [sz].
Arguments BVshiftL1 [sz].
Arguments BVshiftR [sz].
Arguments BVshiftR1 [sz].
Arguments BVrotR   [sz].
Arguments BVrotL   [sz].

Arguments lower_ones [sz].
Arguments upper_ones [sz].
Arguments onesAt     [sz].
Arguments BVzeros {sz}.
Arguments BVones  {sz}.
(* end hide *)

(** * Bitwise functions

This function essentially give the semantics of some of the bitwise
macros that we have in the library. Facts about them will help dealing
with semantics.

*)
Definition selectLower {sz} n (vec : Bvector sz) := BVand vec (lower_ones n).
Definition selectUpper {sz} n (vec : Bvector sz) := BVand vec (upper_ones n).
Definition clearUpper {sz}  n  := @selectLower sz (sz -n).
Definition clearLower {sz}  n  := @selectUpper sz (sz - n).

(** Some efficient arithmetic functions using bitwise operations. **)

Definition div2power_nat    {sz} n := @BVshiftR sz n.
Definition modulo2power_nat {sz} n := @selectLower sz n.
Definition of_N {sz}   : N -> Bvector sz := N2Bv_sized sz.
Definition to_N {sz}   : Bvector sz -> N := @Bv2N sz.
(*
Definition of_nat {sz} : nat -> Bvector sz :=  fun n => N2Bv_sized sz (N.of_nat n).
Definition to_nat {sz} : Bvector sz -> nat := fun vec => N.to_nat (to_N vec).
 *)

Definition bveq {sz}(v1 v2 : Bvector sz) := BVeq sz sz v1 v2.

(** * Notations for bitvector operations

We define instances for Algebraic syntax for bitvector. This gives
pretty arithmetic operations for bitvectors.

The bitwise operations are given unicode syntax and are used primarily
so that the proof obligations turn out to readable. They are not meant
for input.

TODO: Note that we have hidden the notation that uses unicode so that
coqdoc's pdf generation does not get stuck.

*)

(* begin hide *)
Require Export setoid_ring.Algebra_syntax.

Instance zero_Bvector sz : Zero (Bvector sz)     := N2Bv_sized sz 0.
Instance one_Bvector sz  : One (Bvector sz)      := N2Bv_sized sz 1.
Instance add_Bvector sz  : Addition (Bvector sz) := @BVplus sz.
Instance mul_Bvector sz  : Multiplication  := @BVmul sz.
Instance sub_Bvector sz  : Subtraction (Bvector sz) := @BVminus sz.
Instance opp_Bvector sz  : Opposite (Bvector sz)   := (@BVnegative sz).

Instance setoid_bvector sz : Setoid (Bvector sz) := {| SetoidClass.equiv := eq |}.

(* end hide *)
Definition of_Z {sz} (z :  Z) : Bvector sz:=
  match z with
  | Z0 => zero
  | Zpos zp => of_N (Npos zp)
  | Zneg zp => BVnegative (of_N (Npos zp))
  end.

Definition to_Z {sz}(bv : Bvector sz) := Z.of_N (to_N bv).

Fixpoint pow {sz}(eta : Bvector sz)(n : nat) : Bvector sz :=
  match n with
  | 0   => 1
  | S m => (eta  * (pow eta m))
  end.

Instance bitvector_power_nat (sz : nat) : Power := @pow sz.

Infix "=?" := (bveq) (at level 70): bitvector_scope.
(* begin hide *)

Declare Custom Entry bits.

Notation "[bits| e |]" := e (e custom bits).
Notation "( x )" := x  (in custom bits at level 0).
Notation "` x `" := x  (in custom bits at level 0, x constr, format "` x `").

Infix "*"           := BVmul            (in custom bits at level 40, left associativity).

Infix "/"           := BVquot           (in custom bits at level 40, left associativity).
Infix "%"           := BVrem            (in custom bits at level 40, left associativity).

Infix "+"           := BVplus           (in custom bits at level 50, left associativity).
Infix "-"           := BVminus          (in custom bits at level 50, left associativity).

Notation "~ E"      := (BVcomp E)  (in custom bits at level 30, right associativity).
Infix "&"         := BVand         (in custom bits at level 56, left associativity).
Infix "⊕"         := BVxor         (in custom bits at level 57, left associativity).
Infix "|"         := BVor          (in custom bits at level 59, left associativity).
Notation "A ≫ m" := (BVshiftR m A) (in custom bits at level 54, left associativity).
Notation "A ≪ m" := (BVshiftL m A) (in custom bits at level 54, left associativity).
Notation "A ⋘ m" := (BVrotL m A)   (in custom bits at level 54, left associativity).
Notation "A ⋙ m" := (BVrotR m A)   (in custom bits at level 54, left associativity).
Notation "⟦ A ⟧"  := (@Bv2N _ A)   (in custom bits).
(* end hide *)