Inductive day : Type :=
  | monday
  | tuesday
  | wednesday
  | thursday
  | friday
  | saturday
  | sunday.

Definition next_weekday (d:day) : day :=
  match d with
  | monday    => tuesday
  | tuesday   => wednesday
  | wednesday => thursday
  | thursday  => friday
  | friday    => monday
  | saturday  => monday
  | sunday    => monday
  end.

(* Compute (next_weekday friday). *)
(* ==> monday : day *)
(* Compute (next_weekday (next_weekday saturday)). *)
(* ==> tuesday : day *)
Example test_next_weekday:
  (next_weekday (next_weekday saturday)) = tuesday.
Proof. simpl. reflexivity. Qed.

Example test_next_two_weekday:
  (next_weekday (next_weekday monday)) =  wednesday .
Proof. simpl. reflexivity. Qed.

From Coq Require Export String.

(* Booleans *)

Inductive bool : Type :=
  | true
  | false.

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

Definition andb (b1 : bool) (b2 : bool) : bool :=
  match b1 with
  | true => b2
  | false => false
  end.

Definition orb (b1 : bool) (b2 : bool) : bool :=
  match b1 with
  | true => true
  | false => b2
  end.

Example test_orb1: (orb true true) = true.
Proof. simpl. reflexivity. Qed.
Example test_orb2: (orb true false) = true.
Proof. simpl. reflexivity. Qed.
Example test_orb3: (orb false true) = true.
Proof. simpl. reflexivity. Qed.
Example test_orb4: (orb false false) = false.
Proof. simpl. reflexivity. Qed.

(* Infix syntax for boolean operators *)
Notation "x && y" := (andb x y).
Notation "x || y" := (orb x y).

Example test_orb5:  false || false || true = true.
Proof. simpl. reflexivity. Qed.

(* If then else syntax *)
Definition negb' (b:bool) : bool :=
  if b then false
  else true.
Definition andb' (b1:bool) (b2:bool) : bool :=
  if b1 then b2
  else false.
Definition orb' (b1:bool) (b2:bool) : bool :=
  if b1 then true
  else b2.

Example test_andb1: (andb true true) = true.
Proof. simpl. reflexivity. Qed.
Example test_andb2: (andb true false) = false.
Proof. simpl. reflexivity. Qed.
Example test_andb3: (andb false true) = false.
Proof. simpl. reflexivity. Qed.
Example test_andb4: (andb false false) = false.
Proof. simpl. reflexivity. Qed.

(*  exercise 1 star *)
Definition nandb (b1 : bool) (b2 : bool) : bool :=
  (negb (andb b1 b2)).
Example test_nandb1:               (nandb true false) = true.
Proof. simpl. reflexivity. Qed.
Example test_nandb2:               (nandb false false) = true.
Proof. simpl. reflexivity. Qed.
Example test_nandb3:               (nandb false true) = true.
Proof. simpl. reflexivity. Qed.
Example test_nandb4:               (nandb true true) = false.
Proof. simpl. reflexivity. Qed.

Definition andb3 (b1:bool) (b2:bool) (b3:bool) : bool :=
  (andb b1 (andb b2 b3)).
Example test_andb31:                 (andb3 true true true) = true.
Proof. simpl. reflexivity. Qed.
Example test_andb32:                 (andb3 false true true) = false.
Proof. simpl. reflexivity. Qed.
Example test_andb33:                 (andb3 true false true) = false.
Proof. simpl. reflexivity. Qed.
Example test_andb34:                 (andb3 true true false) = false.
Proof. simpl. reflexivity. Qed.

(*
An Inductive definition does two things:
It defines a set of new constructors. E.g., red, primary, true, false, monday, etc. are constructors.
It groups them into a new named type, like bool, rgb, or color.

Constructor expressions are formed by applying a constructor
to zero or more other constructors or constructor expressions
matching the structure of the definition

red, green, and blue belong to the set rgb;
black and white belong to the set color;
if p is a constructor expression belonging to the set rgb,
then primary p (pronounced "the constructor primary applied to the argument p")
is a constructor expression belonging to the set color; and
constructor expressions formed in these ways are the only ones belonging
to the sets rgb and color.
*)
Inductive rgb : Type :=
  | red
  | green
  | blue.
Inductive color : Type :=
  | black
  | white
  | primary (p : rgb).

Definition monochrome (c : color) : bool :=
  match c with
  | black => true
  | white => true
  | primary p => false
  end.
Definition isred (c : color) : bool :=
  match c with
  | black => false
  | white => false
  | primary red => true
  | primary _ => false
  end.
(*
Modules

Coq provides a module system to aid in organizing large developments.
We won't need most of its features, but one is useful:
If we enclose a collection of declarations between Module X and End X markers,
then, in the remainder of the file after the End, these definitions are referred
to by names like X.foo instead of just foo.
We will use this feature to limit the scope of definitions,
so that we are free to reuse names.
*)

Module Playground.
Definition b : rgb := blue.
End Playground.

Definition b : bool := true.

Module TuplePlayground.

Inductive bit : Type :=
  | B0
  | B1.
Inductive nybble : Type :=
  | bits (b0 b1 b2 b3 : bit).

(*
the bits constructor is a wrapper for its contents
unwrapping can be done by pattern-matching
Use underscore ( _ ) as a wildcard  pattern to avoid inventing variable
names that aren't used.
*)
Definition all_zero (nb : nybble) : bool :=
  match nb with
  | (bits B0 B0 B0 B0) => true
  | (bits _ _ _ _) => false
  end.
Example all_zero_test1: (all_zero (bits B1 B0 B1 B0)) = false.
Proof. simpl. reflexivity. Qed.
Example all_zero_test2: (all_zero (bits B0 B0 B0 B0)) = true.
Proof. simpl. reflexivity. Qed.
End TuplePlayground.

(* Use module so it doesn't conflict with later usages of Nats from stdlib *)
Module NatPlayground.

(*
there is a representation of numbers that is even simpler than binary, namely unary (base 1)
 *)

 Inductive nat : Type :=
  | O            (* capital letter O stands for zero *)
  | S (n : nat). (* S stands for successor *)

(* This is just a representation of numbers: a way of writing them down
The names S and O are arbitrary, and at this point they have no special meaning
-- they are just two different marks that we can use to write down numbers (together with a rule that says any nat will be written as some string of S marks followed by an O).
If we like, we can write essentially the same definition this way: *)
Inductive nat' : Type :=
  | stop
  | tick (foo : nat').

(*
The interpretation of these marks comes from how we use them to compute.
*)

Definition pred (n : nat) : nat :=
  match n with
  | O => O
  | S n' => n'
  end.

(* Since natural numbers are such a pervasive form of data,
Coq has built-in parser and printer for them,
printing numbers in decimal by default.  *)

Definition minustwo (n : nat) : nat :=
  match n with
  | O => O
  | S O => O
  | S (S n') => n'
  end.
Example minus_two_test: (minustwo (S (S (S (S O))))) = (S (S O)).
Proof. simpl. reflexivity. Qed.

(*
However, there is a fundamental difference between S and the other two:
functions like pred and minustwo are defined by giving computation rules
-- e.g., the definition of pred says that pred 2 can be simplified to 1 --
while the definition of S has no such behavior attached.
Although it is like a function in the sense that it can be applied to
an argument, it does not do anything at all! It is just a way
of writing down numbers. *)

(*
For most interesting computations involving numbers,
simple pattern matching is not enough: we also need recursion.
Such functions are introduced using the keyword Fixpoint instead of Definition.
*)

Fixpoint even (n:nat) : bool :=
  match n with
  | O        => true
  | S O      => false
  | S (S n') => even n'
  end.
(* We can define odd in a similar way, but basing it off even is simpler *)
Definition odd (n:nat) : bool :=
  negb (even n).

Example test_odd1:    odd (S O) = true.
Proof. simpl. reflexivity. Qed.
Example test_odd2:    odd (S (S (S (S O)))) = false.
Proof. simpl. reflexivity. Qed.

(* Multi-argument functions by recursion *)
Fixpoint plus (n : nat) (m : nat) : nat :=
  match n with
  | O => m
  | S n' => S (plus n' m) (* Predicate (S n' => n') *)
  end.

Example test_add1: (plus (S (S (S O))) (S (S O))) = (S (S (S (S (S O))))).
Proof. simpl. reflexivity. Qed.

(* ===> 5 : nat *)
(*      plus 3 2
   i.e. plus (S (S (S O))) (S (S O))
    ==> S (plus (S (S O)) (S (S O)))
          by the second clause of the match
    ==> S (S (plus (S O) (S (S O))))
          by the second clause of the match
    ==> S (S (S (plus O (S (S O)))))
          by the second clause of the match
    ==> S (S (S (S (S O))))
          by the first clause of the match
   i.e. 5  *)

(*
As a notational convenience, if two or more arguments have the same type,
they can be written together. In the following definition, (n m : nat)
means just the same as if we had written (n : nat) (m : nat).
*)
Fixpoint mult (n m : nat) : nat :=
    match n with
      | O => O
      | S n' => (plus m (mult n' m))
    end.

Example test_mult1: (mult (S (S O)) (S (S O))) = (S (S (S (S O)))).
Proof. simpl. reflexivity. Qed.

(* Match two expressions at once with a comma between them *)
Fixpoint minus (n m : nat) : nat :=
  match n, m with
    | O, _ => O
    | S _, O => O
    | S n', S m' => minus n' m'
  end.

Example test_minus1: (minus O O) = O.
Proof. simpl. reflexivity. Qed.
Example test_minus2: (minus (S O) O) = O.
Proof. simpl. reflexivity. Qed.
Example test_minus3: (minus (S O) (S O)) = O.
Proof. simpl. reflexivity. Qed.

Fixpoint exp (base power : nat) : nat :=
  match base, power with
    | _, O => S O
    | O, _ => O
    | base, S power' => mult base (exp base power')
  end.

Example test_exp0: exp O (S O) = O.
Proof. simpl. reflexivity. Qed.
Example test_exp1: exp O O = S O.
Proof. simpl. reflexivity. Qed.
Example test_exp2: exp (S O) O = S O.
Proof. simpl. reflexivity. Qed.
Example test_exp3: exp (S (S O)) (S (S (S O))) = (S (S (S (S (S (S (S (S O)))))))).
Proof. simpl. reflexivity. Qed.

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

Example test_factorial0: factorial O = S O.
Proof. simpl. reflexivity. Qed.
Example test_factorial1: factorial( S O) = S O.
Proof. simpl. reflexivity. Qed.
Example test_factorial2: factorial (S (S O)) = S (S O).
Proof. simpl. reflexivity. Qed.
Example test_factorial3: factorial (S (S (S O))) = S (S (S (S (S (S O))))).
Proof. simpl. reflexivity. Qed.

End NatPlayground.

Notation "x + y" := (plus x y)
                       (at level 50, left associativity)
                       : nat_scope.
Notation "x - y" := (minus x y)
                       (at level 50, left associativity)
                       : nat_scope.
Notation "x * y" := (mult x y)
                       (at level 40, left associativity)
                       : nat_scope.

(* Note the use of nested matches (we could also have used a simultaneous match, as we did in minus.) *)
Fixpoint eqb (n m : nat) : bool :=
  match n with
  | O => match m with
         | O => true
         | S m' => false
         end
  | S n' => match m with
            | O => false
            | S m' => eqb n' m'
            end
end.

Fixpoint leb (n m : nat) : bool :=
  match n with
    | O => true
    | S n' =>
      match m with
        | O => false
        | S m' => leb n' m'
      end
end.

Example test_leb1: leb 0 2 = true.
Proof. simpl. reflexivity. Qed.
Example test_leb2: leb 2 2 = true.
Proof. simpl. reflexivity. Qed.
Example test_leb3: leb 5 4 = false.
Proof. simpl. reflexivity. Qed.

Fixpoint geb (n m : nat) : bool :=
  match n with
    | O => if (eqb m O) then true else false
    | S n' =>
      match m with
        | O => true
        | S m' => geb n' m'
      end
end.

Example test_geb1: geb 2 0 = true. Proof. simpl. reflexivity. Qed.
Example test_geb2: geb 2 2 = true. Proof. simpl. reflexivity. Qed.
Example test_geb3: geb 4 5 = false. Proof. simpl. reflexivity. Qed.

Notation "x =? y" := (eqb x y) (at level 70) : nat_scope.
Notation "x <=? y" := (leb x y) (at level 70) : nat_scope.
Notation "x >=? y" := (geb x y) (at level 70) : nat_scope.
Example test_leb3': (4 <=? 2) = false. Proof. simpl. reflexivity. Qed.
Example test_geb3': (4 >=? 5) = false. Proof. simpl. reflexivity. Qed.

Definition ltb (n m : nat) : bool :=
  andb (leb n m) (negb (eqb n m)).

Example test_ltb1: ltb 0 2 = true.
Proof. simpl. reflexivity. Qed.
Example test_ltb2: ltb 2 2 = false.
Proof. simpl. reflexivity. Qed.
Example test_ltb3: ltb 5 4 = false.
Proof. simpl. reflexivity. Qed.

(* Proof by Simplification *)
(* simpl is used when we want to understand the new goal it creates and not blindly expand definitions *)
(*  Informally, to prove theorems of this form, we generally start by saying
"Suppose n is some number..." Formally, this is achieved in the proof by intros n,
 which moves n from the quantifier in the goal to a context of current assumptions. *)
Theorem plus_O_n' : forall n : nat, 0 + n = n.
Proof.
  intros n. reflexivity. Qed.

  (* The keywords intros, simpl, and reflexivity are examples of tactics.
  A tactic is a command that is used between Proof and Qed to guide the process
  of checking some claim we are making.  *)
(* _l suffix is pronounced "on the left" *)
Theorem plus_1_l : forall n:nat, 1 + n = S n.
Proof.
  intros n. reflexivity. Qed.
Theorem mult_0_l : forall n:nat, 0 * n = 0.
Proof.
  intros n. reflexivity. Qed.

Theorem plus_id_example : forall n m:nat,
  n = m ->
  n + n = m + m.

Proof.
  (* move both quantifiers into the context: *)
  intros n m.
  (* move the hypothesis into the context: *)
  intros shamalamadingdong.
  (* rewrite the goal using the hypothesis: *)

  (* the arrow left to right and right to left has nothing to do with implication
  it just tells coq which direction to apply the rewrite. defaulting to -> *)
  (* rewrite -> shamalamadingdong. *)
  rewrite <- shamalamadingdong.
  reflexivity. Qed.

(*
Admitted makes Coq skip trying to prove this theorem
and just accept it as a given.
Useful for developing longer proofs,
since we can state subsidiary lemmas that can be useful
for making some larger argument,
use Admitted to accept them on faith for the moment,
and continue working on the main argument until we are sure it makes sense;
then we can go back and fill in the proofs we skipped.  *)
Theorem plus_id_exercise : forall n m o : nat,
  n = m -> m = o -> n + m = m + o.
Proof.
  intros n m o.
  intros H.
  intros H2.
  rewrite H.
  rewrite H2.
  reflexivity. Qed.

  (* Check is also used to examine statements of previously declared lemmas and theorems *)

(* We can use the rewrite tactic with a previously proved theorem instead
 of a hypothesis from the context. If the statement of the previously proved theorem
 involves quantified variables, as in the example below, Coq tries to instantiate them
 by matching with the current goal. *)

Theorem mult_n_0_m_0 : forall p q : nat,
  (p * 0) + (q * 0) = 0.
Proof.
  intros p q.
  rewrite <- mult_n_O.
  rewrite <- mult_n_O.
  reflexivity. Qed.

Theorem mult_n_1 : forall p : nat,
  p * 1 = p.
Proof.
  intros p.
  rewrite <- mult_n_Sm.
  rewrite <- mult_n_O.
  rewrite -> plus_O_n'.
  reflexivity. Qed.

(* Proof by Case Analysis *)

Theorem plus_1_neq_0 : forall n : nat,
  (n + 1) =? 0 = false.
Proof.
  intros n. destruct n as [| n'] eqn:E.
  - reflexivity.
  - reflexivity. Qed.
(*
The destruct generates two subgoals, which we must then prove,
separately, in order to get Coq to accept the theorem.

The annotation "as [| n']" is called an intro pattern.
It tells Coq what variable names to introduce in each subgoal.
In general, what goes between the square brackets is a list
of lists of names, separated by |.
eqn:E annotation tells destruct to give the name
E to this equation. Leaving off the eqn:E annotation
causes Coq to elide these assumptions in the subgoals.
This slightly streamlines proofs where the assumptions are not
explicitly used, but it is better practice to keep them for
the sake of documentation, as they can help keep you oriented
when working with the subgoals.
- are bullets, marking the parts of the proof that correcspond to the generated subgoals
the part of the proof script that comes after a bullet is the entire proof for the
corresponding subgoal. bullets are optional, but use them for readability, and also
disambiguate which subgoal coq is trying to complete before trying to verify the next one
preventing proofs for different subgoals from getting mixed up.
these issues are especially important when fragile proofs lead to long debugging.

*)
(* involutive - negation is its own inverse *)
Theorem negb_involutive : forall b : bool,
  negb (negb b) = b.
Proof.
  intros b. destruct b eqn:E.
  - reflexivity.
  - reflexivity. Qed.

Theorem andb_commutative : forall b c, andb b c = andb c b.
Proof.
  intros b c. destruct b eqn:Eb.
  - destruct c eqn:Ec.
    + reflexivity.
    + reflexivity.
  - destruct c eqn:Ec.
    + reflexivity.
    + reflexivity.
Qed.

Theorem andb_commutative' : forall b c, andb b c = andb c b.
Proof.
  intros b c. destruct b eqn:Eb.
  { destruct c eqn:Ec.
    { reflexivity. }
    { reflexivity. } }
  { destruct c eqn:Ec.
    { reflexivity. }
    { reflexivity. } }
Qed.

Theorem andb3_exchange :
  forall b c d, andb (andb b c) d = andb (andb b d) c.
Proof.
  intros b c d. destruct b eqn:Eb.
  - destruct c eqn:Ec.
    -- destruct d eqn:Ed.
      + reflexivity.
      + reflexivity.
    -- destruct d eqn:Ed.
      + reflexivity.
      + reflexivity.
  - destruct c eqn:Ec.
    -- destruct d eqn:Ed.
      + reflexivity.
      + reflexivity.
    -- destruct d eqn:Ed.
      + reflexivity.
      + reflexivity.
Qed.

Theorem andb_true_elim2 : forall b c : bool,
  andb b c = true -> c = true.
Proof.
  intros b c. destruct b eqn:Eb.
  - destruct c eqn:Ec.
    + intros H.
      reflexivity.
    + intros H.
      rewrite <- H.
      reflexivity.
  - destruct c eqn:Ec.
    + intros H.
      reflexivity.
    + intros H.
      rewrite <- H.
      reflexivity.
Qed.

(* many proofs perform case analysis on a
 variable right after introducing it:
 [| n] is intro + destruct *)
Theorem plus_1_neq_0' : forall n : nat,
  (n + 1) =? 0 = false.
Proof.
  intros [|n].
  - reflexivity.
  - reflexivity. Qed.

(* If there are no constructor arguments that need names,
  we can just write [] to get the case analysis. *)
Theorem andb_commutative'' :
  forall b c, andb b c = andb c b.
Proof.
  intros [] [].
  - reflexivity.
  - reflexivity.
  - reflexivity.
  - reflexivity.
Qed.

Theorem zero_nbeq_plus_1 : forall n : nat,
  0 =? (n + 1) = false.
Proof.
  intros n. destruct n eqn:E.
  - reflexivity.
  - reflexivity.
Qed.

(* Fixpoints and Structural Recursion *)
(* Recursive calls only on strictly smaller values of n
implies tha all calls to plus' will eventually terminate.
Coq demands that some argument of every Fixpoint defn is "decreasing"
This requirement guarantees every function defined in Coq
will terminate on all inputs. *)
Fixpoint plus' (n : nat) (m : nat) : nat :=
  match n with
  | O => m
  | S n' => S (plus' n' m)
  end.

  (* To get a concrete sense of this, find a way to write a sensible Fixpoint definition (of a simple function on numbers, say) that does terminate on all inputs, but that Coq will reject because of this restriction. (If you choose to turn in this optional exercise as part of a homework assignment, make sure you comment out your solution so that it doesn't cause Coq to reject the whole file!) *)
(* todo: *)

Theorem identity_fn_applied_twice :
  forall (f : bool -> bool),
  (forall (x : bool), f x = x) ->
  forall (b : bool), f (f b) = b.
Proof.
  intros f.
  intros x.
  intros b.
  rewrite -> x.
  rewrite -> x.
  reflexivity.
Qed.

Theorem negation_fn_applied_twice :
  forall (f : bool -> bool),
  (forall (x : bool), f x = negb x) ->
  forall (b : bool), f (f b) = b.
Proof.
  intros f.
  intros x.
  intros b.
  rewrite -> x.
  rewrite -> x.
  rewrite -> negb_involutive.
  reflexivity.
Qed.

(* 3 stars *)
Theorem andb_eq_orb :
  forall (b c : bool),
  (andb b c = orb b c) ->
  b = c.
Proof.
intros b c.
destruct b eqn:Eb.
- destruct c eqn:Ec.
  + reflexivity.
  + simpl.
  intros H. (* wtf *)
  rewrite H.
  reflexivity.
- destruct c eqn: Ec.
  + simpl.
  intros H.
  rewrite H.
  reflexivity.
  + simpl.
  intros H.
  reflexivity.
Qed.

(* 3 stars *)
Inductive bin : Type :=
  | Z
  | B0 (n : bin)
  | B1 (n : bin).

Fixpoint incr (m:bin) : bin :=
  match m with
    | Z => B1 Z
    | B1 m' => B0 (incr m')
    | B0 m' => B1 m'
  end.

Fixpoint bin_to_nat (m:bin) : nat :=
  match m with
    | Z => 0
    | B1 m' => 1 + (2 * bin_to_nat m')
    | B0 m' => (2 * bin_to_nat m')
  end.

Example test_bin_incr1 : (incr (B1 Z)) = B0 (B1 Z).
Proof. reflexivity. Qed.
Example test_bin_incr2 : (incr (B0 (B1 Z))) = B1 (B1 Z).
Proof. reflexivity. Qed.
Example test_bin_incr3 : (incr (B1 (B1 Z))) = B0 (B0 (B1 Z)).
Proof. reflexivity. Qed.

Example test_bin_incr4 : bin_to_nat (B0 (B1 Z)) = 2.
Proof. reflexivity. Qed.

Example test_bin_incr5 :
        bin_to_nat (incr (B1 Z)) = 1 + bin_to_nat (B1 Z).
Proof. reflexivity. Qed.

Example test_bin_incr6 :
        bin_to_nat (incr (incr (B1 Z))) = 2 + bin_to_nat (B1 Z).
Proof. reflexivity. Qed.


From Coq Require Import Arith.
From Coq Require Import ZArith.
From Coq Require Import Lia.

Open Scope nat_scope.

Fixpoint sum_n n :=
  match n with
  | 0 => 0
  | S p => p + sum_n p
end.

Lemma sum_n_p: forall n,
  2 * sum_n n + n = n * n.
Proof.
  intros. induction n.
  - reflexivity.
  - simpl. lia.
Qed.

Fixpoint sum_odd_n (n: nat): nat :=
  match n with
  | 0 => 0
  | S p => 1 + 2 * p + sum_odd_n p
end.

Lemma odd_sum: forall n: nat,
  sum_odd_n n = n * n.
Proof.
  intros. induction n.
  - reflexivity.
  - simpl. rewrite IHn. apply f_equal. rewrite Nat.add_0_r.
    rewrite Nat.mul_succ_r. rewrite <- Nat.add_comm with (n) (n * n).
    rewrite Nat.add_assoc. reflexivity.
Qed.

Fixpoint sum_n3 (n: nat): nat :=
  match n with
  | 0 => 0
  | S p => p * p * p + sum_n3 p
end.

Lemma sum_cube_p : forall n: nat,
  sum_n3 n = (sum_n n) * (sum_n n).
Proof.
  intros. induction n.
  - reflexivity.
  - simpl. rewrite IHn. Admitted.

Fixpoint fibonacci (n: nat): Z :=
  match n with
  | O => 1
  | S O => 1
  | S (S n as p) => fibonacci p + fibonacci n
end.

Theorem cassini_identity: forall (n: nat),
  fibonacci (n + 1) * fibonacci (n - 1) - (fibonacci n) ^ 2 = Zpower_nat (-1) n.
Proof.
  intros. induction n.
  - simpl.


Require Import Arith.
Require Import Omega.
Require Import Recdef.
Require Import List.
Require Import Program.Tactics.
Require Import Program.Equality.
Require Import Sorting.Permutation.
Require Import Sorting.Sorted.
Import ListNotations.

Definition pairlen (x : list nat * list nat) : nat :=
  length (fst x) + length (snd x).

Function merge (x : list nat * list nat) {measure pairlen x} : list nat :=
  match x with
  | ([], []) => []
  | ([], (b :: t)) => b :: t
  | ((a :: s), []) => a :: s
  | ((a :: s), (b :: t)) => match (a ?= b) with
                            | Lt => a :: (merge (s, (b :: t)))
                            | Eq => a :: (merge (s, (b :: t)))
                            | Gt => b :: (merge ((a :: s), t))
                            end
  end.
Proof.
  all: intros; cbn; omega.
Defined.

Function halve (l : list nat) : list nat * list nat :=
  match l with
  | [] => ([], [])
  | a :: [] => ([a], [])
  | a :: (b :: t) => let x := halve t in (a :: (fst x), b :: (snd x))
  end.

Lemma halve_sum (l : list nat) :
  length l = length (fst (halve l)) + length (snd (halve l)).
Proof.
  functional induction (halve l); simpl; omega.
Qed.

Function mergesort (l : list nat) {measure length l} : list nat :=
  match l with
  | [] => []
  | a :: [] => [a]
  | a :: (b :: t) => let x := halve l in merge (mergesort (fst x), mergesort (snd x))
  end.
Proof.
  all: intros; cbn; rewrite (halve_sum t); omega.
Defined.

Lemma halve_cons (a b : nat) (l : list nat) :
  halve (a :: b :: l) = (a :: (fst (halve l)), b :: (snd (halve l))).
Proof.
  unfold halve. cbn. auto.
Qed.

Lemma Permutation_merge (x : list nat * list nat) :
  Permutation ((fst x) ++ (snd x)) (merge x).
Proof.
  functional induction (merge x); cbn in *; auto.
  - rewrite app_nil_end. auto.
  - apply (perm_skip b) in IHl.
    assert (Permutation ((a :: s) ++ (b :: t)) (b :: (a :: s) ++ t)).
    + apply Permutation_sym. apply Permutation_middle.
    + rewrite app_comm_cons in *.
      apply Permutation_trans with (l':=(b :: (a :: s) ++ t)); auto.
Qed.

Lemma Permutation_halve (l : list nat) :
  Permutation l (fst (halve l) ++ snd (halve l)).
Proof.
  functional induction (halve l); simpl; auto.
  apply perm_skip. apply perm_trans with (l' := (b :: snd (halve t) ++ fst (halve t))).
  - apply perm_skip. apply perm_trans with (l' := (fst (halve t) ++ snd (halve t))); auto.
    apply Permutation_app_comm.
  - rewrite app_comm_cons. apply Permutation_app_comm.
Qed.

Lemma Permutation_mergesort (l : list nat) :
  Permutation l (mergesort l).
Proof.
  functional induction (mergesort l); auto.
  remember (halve (a :: b :: t)) as x.
  remember ((mergesort (fst x), mergesort (snd x))) as sortedx.
  assert (Permutation (fst (sortedx) ++ snd (sortedx)) (merge sortedx)) by apply Permutation_merge.
  apply perm_trans with (l' := (fst sortedx ++ snd sortedx)); auto.
  rewrite Heqsortedx; simpl.
  assert (Permutation (fst x ++ snd x) (mergesort (fst x) ++ mergesort (snd x))) by (apply Permutation_app; auto).
  apply perm_trans with (l' := (fst x ++ snd x)); auto.
  rewrite Heqx.
  apply Permutation_halve.
Qed.

Lemma Sorted_merge (x : list nat * list nat) :
  Sorted le (fst x) -> Sorted le (snd x) -> Sorted le (merge x).
Proof.
  intros. functional induction merge x.
  all: auto; cbn in *.
  1-2: apply Sorted_inv in H. 3: apply Sorted_inv in H0.
  all: destruct_conjs; apply Sorted_cons; auto.
  all: specialize (IHl H H0).
  1: apply nat_compare_lt in e0. 2: apply nat_compare_eq in e0. 3: apply nat_compare_gt in e0.
  - rewrite merge_equation in IHl. destruct s.
    + cbn. apply HdRel_cons; omega.
    + rewrite merge_equation. remember (n ?= b) as cmp.
      destruct cmp; symmetry in Heqcmp; apply HdRel_cons;
      apply HdRel_inv in H1; omega.
  - rewrite merge_equation in IHl. destruct s.
    + cbn. apply HdRel_cons. omega.
    + rewrite merge_equation. remember (n ?= b) as cmp.
      destruct cmp; symmetry in Heqcmp; apply HdRel_cons;
      apply HdRel_inv in H1; omega.
  - rewrite merge_equation in IHl. destruct t.
    + cbn. apply HdRel_cons. omega.
    + rewrite merge_equation. remember (a ?= n) as cmp.
      destruct cmp; apply HdRel_cons;
      apply HdRel_inv in H1; omega.
Qed.

Lemma Sorted_mergesort (l : list nat) :
  Sorted le (mergesort l).
Proof.
  functional induction mergesort l; auto.
  apply Sorted_merge; auto.
Qed.

Theorem mergesort_correct (l : list nat) :
  Permutation l (mergesort l) /\ Sorted le (mergesort l).
Proof.
  split.
  - apply Permutation_mergesort.
  - apply Sorted_mergesort.
Qed.


Require Import XR_Rsqr.
Require Import XR_Rabs.
Require Import XR_Rsqr_le_abs_1.
Require Import XR_Rle_antisym.

Local Open Scope R_scope.

Lemma Rsqr_eq_asb_1 : forall x y:R, Rabs x = Rabs y -> Rsqr x = Rsqr y.
Proof.
  intros x y h.
  apply Rle_antisym.
  {
    apply Rsqr_le_abs_1.
    right.
    exact h.
  }
  {
    apply Rsqr_le_abs_1.
    right.
    symmetry.
    exact h.
  }
Qed.


(** * Logic: Logic in Coq *)

Require Export Tactics.
Require Export Induction.
Require Export List.
Require Export MoreCoq.

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

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

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

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

(** Note that all well-formed propositions have type [Prop] in Coq,
    regardless of whether they are true or not. Simply _being_ a
    proposition is one thing; being _provable_ is something else! *)

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

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

(** Indeed, propositions don't just have types: they are _first-class
    objects_ that can be manipulated in the same ways as the other
    entities in Coq's world.  So far, we've seen one primary place
    that propositions can appear: in [Theorem] (and [Lemma] and
    [Example]) declarations. *)

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

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

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

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

Theorem plus_fact_is_true :
  plus_fact.
Proof. reflexivity.  Qed.

(** We 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. inversion H. reflexivity.
Qed.

(** The equality operator [=] that we have been using so far is also
    just a function that returns a [Prop]. The expression [n = m] is
    just syntactic sugar for [eq n m], defined using Coq's [Notation]
    mechanism. Because [=] can be used with elements of any type, it
    is also polymorphic: *)

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

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

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

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

(** The _conjunction_ or _logical and_ of propositions [A] and [B] is
    written [A /\ B], denoting 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.  Its effect is to
    generate two subgoals, one for each part of the statement: *)

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

(** More generally, the following principle works for any two
    propositions [A] and [B]: *)

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

(** A logical statement with multiple arrows is just a theorem that
    has several hypotheses.  Here, [and_intro] says that, for any
    propositions [A] and [B], if we assume that [A] is true and we
    assume that [B] is true, then [A /\ B] is also true.

    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 HA. split.
  - induction n as [| n']. reflexivity. inversion HA.
  - induction m as [| m']. reflexivity. rewrite plus_comm in HA. inversion HA.
    Qed.
(** [] *)

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

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

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.
  reflexivity.
Qed.

(** As usual, we can also destruct [H] 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.

(** In this case, there is not much difference between the two
    theorems.  But it is often necessary to explicitly decompose
    conjunctions that arise from intermediate steps in proofs,
    especially in bigger developments.  Here's a simplified
    example: *)

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

(** Another common situation with conjunctions is that we know [A /\
    B] but in some context we need just [A] (or just [B]).  The
    following lemmas are useful in such cases: *)

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

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

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

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

(** **** Exercise: 2 stars (and_assoc)  *)
(** (In the following proof of associativity, notice how the _nested_
    intro 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.
  - split. apply HP. apply HQ.
  - apply HR. Qed.

(** [] *)

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

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

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

(** Another important connective is the _disjunction_, or _logical or_
    of two propositions: [A \/ B] is true when either [A] or [B]
    is.  (Alternatively, we can write [or A B], where [or : Prop ->
    Prop -> Prop].)

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

Lemma or_example :
  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.

(** We can see in this example that, when we perform case analysis on
    a disjunction [A \/ B], we must satisfy two proof obligations,
    each showing that the conclusion holds under a different
    assumption -- [A] in the first subgoal and [B] in the second.
    Note that the case analysis pattern ([Hn | Hm]) allows us to name
    the hypothesis that is generated in each subgoal.

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

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

(** ... and a slightly more interesting example requiring the use of
    both [left] and [right]: *)

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

(** **** Exercise: 1 star (mult_eq_0)  *)
Lemma mult_eq_0 :
  forall n m, n * m = 0 -> n = 0 \/ m = 0.
Proof.
  intros n m H. induction n as [| n].
  Case "n = 0". destruct m.
    SCase "m = 0". left. reflexivity.
    SCase "m = S m". left.  reflexivity.
  Case "n = S m". destruct m.
    SCase "m = 0". right. reflexivity.
    SCase "m = S m". inversion 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, showing that certain propositions are _not_
    true. In Coq, such negative statements are expressed with the
    negation operator [~].

    To see how negation works, recall the discussion of the _principle
    of explosion_ from the [Tactics] chapter; it asserts that, if we
    assume a contradiction, then any other proposition can be derived.
    Following this intuition, we could define [~ P] ("not [P]") as
    [forall Q, P -> Q].  Coq actually makes a slightly different
    choice, defining [~ P] as [P -> False], where [False] is a
    _particular_ 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 [destruct] it to complete any goal: *)

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

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

(** **** Exercise: 2 stars, 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 H1 p1 H2. destruct H1. apply H2. Qed.
(** [] *)

(** This is how we use [not] to state that [0] and [1] are different
    elements of [nat]: *)

Theorem zero_not_one : ~(0 = 1).
Proof.
  intros contra. inversion contra.
Qed.

(** Such inequality statements are frequent enough to warrant a
    special notation, [x <> y]: *)

Check (0 <> 1).
(* ===> Prop *)

Theorem zero_not_one' : 0 <> 1.
Proof.
  intros H. inversion H.
Qed.

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

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

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

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

(** **** Exercise: 2 stars, advanced, recommended (double_neg_inf)  *)
(** Write an informal proof of [double_neg]:

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

   _Proof_:
(* Skip *)
   []
*)

(** **** Exercise: 2 stars, recommended (contrapositive)  *)
Theorem contrapositive : forall P Q : Prop,
  (P -> Q) -> (~Q -> ~P).
Proof.
  intros P Q Hp Hq. unfold not. intros eq. destruct Hq.  apply Hp.  apply eq.
  Qed.
(** [] *)

(** **** Exercise: 1 star (not_both_true_and_false)  *)
Theorem not_both_true_and_false : forall P : Prop,
  ~ (P /\ ~P).
Proof.
  intros P Hp. inversion Hp. destruct H0. apply H. Qed.
(** [] *)

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

(* Skip *)
(** [] *)

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

Theorem not_true_is_false : forall b : bool,
  b <> true -> b = false.
Proof.
  intros [] H.
  - (* 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.
  - (* b = false *)
    unfold not in H.
    exfalso.                (* <=== *)
    apply H. reflexivity.
  - (* b = true *) 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 some examples such uses of [True] later on. *)

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

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

Module MyIff.

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

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

End MyIff.

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

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

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

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

Theorem iff_trans : forall P Q R : Prop,
  (P <-> Q) -> (Q <-> R) -> (P <-> R).
Proof.
  intros P Q R [Hp Hq Hr]. split.
  - (* -> *) intros Hpp. apply Hr. apply Hp. apply Hpp.
  - (* <- *) intros Hpp. apply Hq. apply Hr. apply Hpp. 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. split. inversion H. left. apply H0.
             inversion H0. right. apply H1. inversion H. left. apply H0.
             inversion H0. right. apply H2.
  - (* <- *) intros H. destruct H. destruct H. left. apply H. destruct H0.
             left. apply H0. right. split. apply H. apply H0. Qed.

(** [] *)

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

Require Import Coq.Setoids.Setoid.

(** 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 or_example.
Qed.

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

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

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

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

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].  Here is an example: *)

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

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

Theorem exists_example_2 : forall n,
  (exists m, n = 4 + m) ->
  (exists o, n = 2 + o).
Proof.
  intros n [m Hm].
  exists (2 + m).
  apply Hm.  Qed.

(** **** Exercise: 1 star (dist_not_exists)  *)
(** Prove that "[P] holds for all [x]" implies "there is no [x] for
    which [P] does not hold." *)

Theorem dist_not_exists : forall (X:Type) (P : X -> Prop),
  (forall x, P x) -> ~ (exists x, ~ P x).
Proof.
  intros X P H. unfold not. intros eq. inversion eq. apply H0. apply H.
  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 Hp. inversion Hp. inversion H. left. exists (x).
  apply H0. right. exists (x). apply H0.
  - intros Hp. inversion Hp. inversion H. exists (x). left. apply H0.
    inversion H. exists (x). right. apply H0.
    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 on 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 Coq
    function, [In].  (It can also be found in the Coq standard
    library.) *)
Notation "x :: l" := (cons x l) (at level 60, right associativity).
Notation "[ ]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).

Fixpoint In {A : Type} (x : A) (l : list A) : Prop :=
  match l with
  | nil => 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 conjunctions. *)

Example In_example_1 : In 4 [3; 4; 5].
Proof.
  simpl. right. left. reflexivity.
Qed.

Example In_example_2 :
  forall n, In n [2; 4] ->
  exists n', n = 2 * n'.
Proof.
  simpl.
  intros n [H | [H | []]].
  - exists 1. rewrite <- H. reflexivity.
  - exists 2. rewrite <- H. reflexivity.
Qed.

(** (Notice the use of the empty pattern to discharge the last case
    _en passant_.) *)

(** We can also prove more generic, higher-level lemmas about [In].
    Note, in the next, how [In] starts out applied to a variable and
    only gets expanded when we do case analysis on this variable: *)

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

(** This way of defining propositions, 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.
  induction l; firstorder (subst;auto). 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' a. induction l as [| x].
  Case "l = []". simpl. split. intros H. right. apply H.
    intros [[] | H]. apply H.
    Case "l = cons". simpl. split.
    (* -> *) intros [[] | H]. left. left. reflexivity.
    apply or_assoc. right. apply IHl. apply H.
    (* <- *) intros [[] | H]. intros H1. left. apply H1. intros H. right.
      apply IHl. left. apply H. right. apply IHl. right. apply H.
      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
    | nil => True
    | h :: t => P h /\ All P t
    end.

Theorem 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. induction l as [| l'].
  Case "l = []". simpl. split.
  (* -> *) intros H. reflexivity.
  (* <- *) intros t T1 f.  destruct f.
  Case "l = cons". simpl. split.
  (* -> *) intros H. inversion IHl. split. apply H. left. reflexivity. apply H0.
    intros x H2. apply H. right. apply H2.
  (* <- *) intros H x H1. inversion H1. rewrite <- H0. inversion H. apply H2.
    apply IHl. inversion H. apply H3. apply H0. 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 x => if (oddb x) then Podd x else Peven x.

(** 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 Po Pe n Ho He. unfold combine_odd_even. induction n as [| n'].
  Case "n = 0". simpl. apply He. reflexivity.
  Case "n = S n'". destruct oddb.
    SCase "true". apply Ho. reflexivity.
    SCase "false". apply He. 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 Po Pe n H Ho. unfold combine_odd_even in H. rewrite Ho in H. apply H.
  Qed.

Theorem combine_odd_even_elim_even :
  forall (Podd Peven : nat -> Prop) (n : nat),
    combine_odd_even Podd Peven n ->
    oddb n = false ->
    Peven n.
Proof.
  intros Po Pe n H Ho. unfold combine_odd_even in H. rewrite Ho in H. apply H.
  Qed.
(** [] *)

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

(** One feature of Coq that distinguishes it from many other proof
    assistants 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 in detail in order to use Coq.  This
    section gives just a taste, while a deeper exploration can be
    found in the optional chapters [ProofObjects] and
    [IndPrinciples]. *)

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

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

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

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

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

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

Lemma plus_comm3 :
  forall n m p, n + (m + p) = (p + m) + n.

(** 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.
  intros n m p.
  rewrite plus_comm.
  rewrite plus_comm.
  (* We are back where we started... *)

(** One simple way of fixing this problem, using only tools that we
    already know, is to use [assert] to derive a specialized version
    of [plus_comm] that can be used to rewrite exactly where we
    want. *)

  rewrite plus_comm.
  assert (H : m + p = p + m).
  { 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_take2 :
  forall n m p, n + (m + p) = (p + m) + n.
Proof.
  intros n m p.
  rewrite plus_comm.
  rewrite (plus_comm m).
  reflexivity.
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. *)

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

(** We will see many more examples of the idioms from this section in
    later chapters. *)

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

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

    However, there are some cases where translating standard
    mathematical reasoning into Coq can be either cumbersome or
    sometimes even impossible, unless we enrich the core logic with
    additional axioms.  We conclude this chapter with a brief
    discussion of some of the most significant differences between the
    two worlds. *)

(** ** Functional Extensionality

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

Example function_equality_ex : plus 3 = plus (pred 4).
Proof. reflexivity. Qed.

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

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

    This is known as the principle of _functional extensionality_.

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

    Functional extensionality is not part of Coq's basic axioms: the
    only way to show that two functions are equal is by
    simplification (as we did in the proof of [function_equality_ex]).
    But we can add it to Coq's core logic using the [Axiom]
    command. *)

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

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

    We can now invoke functional extensionality in proofs: *)

Lemma plus_comm_ext : plus = fun n m => m + n.
Proof.
  apply functional_extensionality. intros n.
  apply functional_extensionality. intros m.
  apply plus_comm.
Qed.

(** Naturally, we must be careful when adding new axioms into Coq's
    logic, as they may render it inconsistent -- that is, it may
    become possible to prove every proposition, including [False]!
    Unfortunately, there is no simple way of telling whether an axiom
    is safe: hard work is generally required to establish the
    consistency of any particular combination of axioms.  Fortunately,
    it is known that adding functional extensionality, in particular,
    _is_ consistent.

    Note that it is possible to check whether a particular proof
    relies on any additional axioms, using the [Print Assumptions]
    command. For instance, if we run it on [plus_comm_ext], we see
    that it uses [functional_extensionality]: *)

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

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

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

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

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

Lemma tr_rev_correct : forall X, @tr_rev X = @rev X.
  intros X. apply functional_extensionality. intros x. unfold tr_rev.
  induction x as [| l].
  Case "l = []". simpl. reflexivity.
  Case "l = cons". simpl. rewrite <- IHx.
  (* skipping *) Admitted.
(** [] *)

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

(** We've seen that Coq has two different ways of encoding logical
    facts: 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 (1) that [evenb n] returns
    [true] or (2) that there exists some [k] such that [n = double k].
    Indeed, these two notions of evenness are equivalent, as can
    easily be shown with a couple of auxiliary lemmas (one of which is
    left as an exercise).

    We often say that the boolean [evenb n] _reflects_ the proposition
    [exists k, n = double k].  *)

Theorem evenb_double : forall k, evenb (double k) = true.
Proof.
  intros k. induction k as [|k' IHk'].
  - reflexivity.
  - simpl. apply IHk'.
Qed.

(** **** Exercise: 3 stars (evenb_double_conv)  *)
Theorem evenb_double_conv : forall n,
  exists k, n = if evenb n then double k
                else S (double k).
Proof.
  (* Hint: Use the [evenb_S] lemma from [Induction.v]. *)
  intros n. induction n as [| n']. simpl. exists 0. reflexivity.
  inversion IHn' as [k H]. assert (2 * k = double k).
  - simpl. rewrite -> double_plus. rewrite -> plus_0_r. reflexivity.
  - destruct (evenb (S n')). exists k. Admitted.

(** Skipping after working on this for 3 days [] *)
(** How does one show that there exists a k such that n = 2k or k = n / 2? **)
(** How do I show a value that's < n using the exist tactic? **)
(** How do I show that the inductive case S n' = 1? **)

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

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

Theorem beq_nat_true_iff : forall n1 n2 : nat,
  beq_nat n1 n2 = true <-> n1 = n2.
Proof.
  intros n1 n2. split.
  - apply beq_nat_true.
  - intros H. rewrite H. rewrite <- beq_nat_refl. reflexivity.
Qed.

(** However, while the boolean and propositional formulations of a
    claim are equivalent from a purely logical perspective, we have
    also seen that they need not be equivalent _operationally_.
    Equality provides an extreme example: knowing that [beq_nat n m =
    true] is generally of little 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.

    The case of even numbers is also interesting.  Recall that, when
    proving the backwards direction of
    [even_bool_prop] ([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
    [(exists k, n = double k) -> evenb n = true].

    For these examples, the propositional claims were 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
    elements of [bool] (or some other inductive type with two
    elements).  The reason for this error message has to do with the
    _computational_ nature of Coq's core language, which is designed
    so that every function that it can express is computable and
    total.  One reason for this is to allow the extraction of
    executable programs from Coq developments.  As a consequence,
    [Prop] in Coq does _not_ have a universal case analysis operation
    telling whether any given proposition is true or false, since such
    an operation would allow us to write non-computable functions.

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

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

Example even_1000 : exists k, 1000 = double k.

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

 Proof. exists 500. reflexivity. Qed.

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

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

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

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

(** Although we haven't gained much in terms of proof size in this
    case, larger proofs can often be made considerably simpler by the
    use of reflection.  As an extreme example, the Coq proof of the
    famous _4-color theorem_ uses reflection to reduce the analysis of
    hundreds of different cases to a boolean computation.  We won't
    cover reflection in great detail, but it serves as a good example
    showing the complementary strengths of booleans and general
    propositions. *)

(** **** Exercise: 2 stars (logical_connectives)  *)
(** The following lemmas relate the propositional connectives studied
    in this chapter to the corresponding boolean operations. *)

(** i replaced "&&" with andb, not the first typo i've come across **)
Lemma andb_true_iff : forall b1 b2:bool,
  andb b1 b2 = true <-> b1 = true /\ b2 = true.
Proof.
  intros b1 b2. split. intros H. split.
  - apply andb_true_elim1 in H. apply H.
  - apply andb_true_elim2 in H. apply H.
  - intros H. inversion H. rewrite H0. rewrite H1. simpl. reflexivity. Qed.

Lemma orb_true_iff : forall b1 b2,
  orb b1 b2 = true <-> b1 = true \/ b2 = true.
Proof.
  intros b1 b2. split.
  - intros H. inversion H. destruct b1. simpl. left. reflexivity.
    right. destruct b2. simpl. reflexivity. simpl. reflexivity.
  - intros H. inversion H. rewrite H0. simpl. reflexivity.
    rewrite H0. destruct b1. simpl. reflexivity. simpl. 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). *)

Lemma beq_nat_false : forall n m : nat,
  beq_nat n m = false -> n <> m.
Proof.
  intros n m H. unfold not. intro contra. rewrite contra in H.
  rewrite <- beq_nat_refl in H.
  inversion H. Qed.

Lemma beq_nat_false2 : forall n m : nat,
  n <> m -> beq_nat n m = false.
Proof.
  intros n. induction n.
  Case "n = 0". intros m H. destruct m as [| m']. simpl. exfalso. apply H.
    reflexivity.
    SCase "m = S m'". simpl. reflexivity.
  Case "n = S n". intros m H. destruct m as [| m']. simpl. reflexivity.
    SCase "m = S m'". simpl. apply IHn. unfold not. intros H1. unfold not in H.
    apply H. rewrite H1. reflexivity. Qed.

Theorem beq_nat_false_iff : forall x y : nat,
  beq_nat x y = false <-> x <> y.
Proof.
  intros x y. split.
  - (* -> *) intros H. unfold not. intros H1. rewrite H1 in H.
             assert (beq_nat y y = true). apply beq_nat_true_iff. reflexivity.
             rewrite H0 in H. inversion H.
  - (* <- *) apply beq_nat_false2. Qed.
(* that was pretty hard for a one star *)
(** [] *)

(** **** 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 beq_list {A} (beq : A -> A -> bool)
                  (l1 l2 : list A) : bool :=
  match l1 with
    | [] => match l2 with
              | [] => true
              | _ => false
            end
    | h1 :: t1 => match l2 with
                    | [] => false
                    | h2 :: t2 => match beq h1 h2 with
                                    | false => false
                                    | true => beq_list beq t1 t2
                                  end
                  end
end.

Lemma beq_list_true_iff :
  forall A (beq : A -> A -> bool),
    (forall a1 a2, beq a1 a2 = true <-> a1 = a2) ->
    forall l1 l2, beq_list beq l1 l2 = true <-> l1 = l2.
Proof.
  intros A beq H l1 l2. split.
  - (* -> *) induction l1 as [| l1' l1]. intros eq.
             Case "l1 = []". induction l2 as [| l2' l2].
               SCase "l2 = []". reflexivity.
               SCase "l2 = cons". unfold beq_list in eq. inversion eq.
             Case "l1 = cons". induction l2 as [| l2' l2].
               SCase "l2 = []". intros eq. unfold beq_list in eq. inversion eq.
               SCase "l2 = cons". intros eq. Admitted.

(* This section is a difficulty spike *)

(** [] *)

(** **** 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 X test l. split. Admitted.
  (* skipping hte rest of the Logic section. thinking of moving to previous
  editions because this section was clearly made for a class to go through *)

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

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

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

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

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

(** To understand operationally why this is the case, recall that, to
    prove a statement of the form [P \/ Q], we use the [left] and
    [right] tactics, which effectively require knowing which side of
    the disjunction holds.  However, the universally quantified [P] in
    [excluded_middle] is an _arbitrary_ proposition, which we know
    nothing about.  We don't have enough information to choose which
    of [left] or [right] to apply, just as Coq doesn't have enough
    information to mechanically decide whether [P] holds or not inside
    a function.  On the other hand, if we happen to know that [P] is
    reflected in some boolean term [b], then knowing whether it holds
    or not is trivial: we just have to check the value of [b].  This
    leads to the following theorem: *)

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

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

    You may find it strange that the general excluded middle is not
    available by default in Coq; after all, any given claim must be
    either true or false.  Nonetheless, there is an advantage in not
    assuming the excluded middle: statements in Coq can make stronger
    claims than the analogous statements in standard mathematics.
    Notably, if there is a Coq proof of [exists x, P x], it is
    possible to explicitly exhibit a value of [x] for which we can
    prove [P x] -- in other words, every proof of existence is
    necessarily _constructive_.  Because of this, logics like Coq's,
    which do not assume the excluded middle, are referred to as
    _constructive logics_.  More conventional logical systems such as
    ZFC, in which the excluded middle does hold for arbitrary
    propositions, are referred to as _classical_.

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

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

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

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

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

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

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

(** **** Exercise: 3 stars (excluded_middle_irrefutable)  *)
(** The consistency of Coq with the general excluded middle axiom
    requires complicated reasoning that cannot be carried out within
    Coq itself.  However, the following theorem implies that it is
    always safe to assume a decidability axiom (i.e., an instance of
    excluded middle) for any _particular_ Prop [P].  Why? Because we
    cannot prove the negation of such an axiom; if we could, we would
    have both [~ (P \/ ~P)] and [~ ~ (P \/ ~P)], a contradiction. *)

Theorem excluded_middle_irrefutable:  forall (P:Prop),
  ~ ~ (P \/ ~ P).
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

(** **** Exercise: 3 stars, optional (not_exists_dist)  *)
(** It is a theorem of classical logic that the following two
    assertions are equivalent:

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

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

Theorem not_exists_dist :
  excluded_middle ->
  forall (X:Type) (P : X -> Prop),
    ~ (exists x, ~ P x) -> (forall x, P x).
Proof.
  (* FILL IN HERE *) Admitted.
(** [] *)

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

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

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

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

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

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

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

(** $Date: 2015-08-11 12:03:04 -0400 (Tue, 11 Aug 2015) $ *)


Require Import Nat List.
Import ListNotations.

(*
 * This is an exercise on proof by reflection.
 * As always, it's about the journey, not the destination.
 * So there will be a discussion question at the bottom of the
 * file---you will be graded on answering that in the class
 * forum, and not your finished proofs. So don't worry
 * too much if you get stuck! But I'll be around to help.
 *
 * IMPORTANT NOTE: Throughout this exercise, you may find it
 * useful to look at the demo file from class on Tuesday:
 * https://dependenttyp.es/classes/fa2022/readings/reflectionnotes.v
 * It is totally fine to do this. The goal isn't to memorize
 * how to write these proofs, but rather to adapt this style
 * of automation to a different proof so you get a feel for
 * what it's like. Hope that helps!
 *)

(* --- Part 1: naive tactic proofs --- *)

(*
 * In this file, we're going to show that some very large
 * lists have the same length. It's a bit silly, but we'll
 * do so using this inductive relation, which is inhabited
 * whenever two lists are the same length:
 *)
Inductive Same_Length {T : Type} : list T -> list T -> Prop :=
| length_nil : Same_Length [] []
| length_cons :
    forall (hd1 hd2 : T) (tl1 tl2 : list T),
      Same_Length tl1 tl2 ->
      Same_Length (hd1 :: tl1) (hd2 :: tl2).

(*
 * Informally, note that for lists l1 and l2, we can construct a
 * term of type Same_Length l1 l2 if and only if l1 and l2 have
 * the same length: If l1 or l2 is nil, we construct this by the
 * length_nil constructor. If l1 and l2 are both not nil, and
 * the tails of l1 and l2 are the same length, then we can
 * construct this with the length_cons constructor. In all
 * other cases, we cannot construct an element of this type.
 *
 * The functions below let us construct very large lists easily.
 * The first gives us a list of length n that is all zeros:
 *)
Fixpoint nat_to_zero_list (n : nat) : list nat :=
  match n with
  | O => []
  | S m => O :: nat_to_zero_list m
  end.

(*
 * The second gives us a list of length n that is all ones:
 *)
Fixpoint nat_to_one_list (n : nat) : list nat :=
  match n with
  | O => []
  | S m => 1 :: nat_to_one_list m
  end.

(*
 * EXERCISE 1: I've written a proof below that shows that
 * two particular lists of length 50 are equal to each other.
 * Print the proof and look at it. Why is it so big?
 * Discuss with your group.
 *)
Lemma same_length_lists_50:
  Same_Length (nat_to_zero_list 50) (nat_to_one_list 50).
Proof.
  repeat constructor.
Qed.

Print same_length_lists_50.

(*
 * EXERCISE 2: I've written another inefficient proof, this
 * time about lists of length 1000. The "time" tactical here
 * reports how long this proof takes. How long does it take?
 * Why do you think it takes so long? (I strongly recommend
 * against printing this proof. Doing so crashed CoqIDE for me.)
 *)
Lemma same_length_lists_1000:
  Same_Length (nat_to_zero_list 1000) (nat_to_one_list 1000).
Proof.
  time (repeat constructor).
Qed.

(* --- Part 2: defining a decision procedure --- *)

(*
 * Next, we will write the same proofs by reflection, using
 * a decision procedure that checks whether two lists are
 * the same length.
 *
 * EXERCISE 3: Fill in the decision procedure below.
 * If you are successful, the proofs of both
 * same_length_lists_50_reflective and
 * same_length_lists_1000_reflective below
 * should go through, and they should do so efficiently.
 *)
Fixpoint check_same_length {T : Type} (l1 l2 : list T) : option (Same_Length l1 l2) :=
  match l1, l2 with
  | [], [] => None (* replace with your code *)
  | hd1 :: tl1, hd2 :: tl2 => None (* replace with your code *)
  | _, _ => None
  end.

(*
 * I define these for you---note that, as in the demo we
 * saw on Tuesday, these let us apply our decision procedure
 * easily even though our decision procedure is partial
 * (it returns an optional proof that the lists are the same length,
 * and that proof is None when the lists are not the same length).
 *)
Definition optionOutType (P : Prop) (o : option P) :=
  match o with
  | Some _ => P
  | _ => True
  end.

Definition optionOut (P : Prop) (o : option P) : optionOutType P o :=
  match o with
  | Some pf => pf
  | _ => I
  end.

(*
 * This proof should go through efficiently if your decision
 * procedure is correct:
 *)
Lemma same_length_lists_50_reflective:
  Same_Length (nat_to_zero_list 50) (nat_to_one_list 50).
Proof.
  exact (optionOut (Same_Length (nat_to_zero_list 50) (nat_to_one_list 50)) (check_same_length (nat_to_zero_list 50) (nat_to_one_list 50))).
Qed.

(*
 * This should be small if your decision procedure is correct:
 *)
Print same_length_lists_50_reflective.

(*
 * EXERCISE 4: How long does this proof by reflection take?
 * How much faster is it than the naive tactic proof of the
 * same theorem?
 *)
Lemma same_length_lists_1000_reflective:
  Same_Length (nat_to_zero_list 1000) (nat_to_one_list 1000).
Proof.
  time (exact (optionOut (Same_Length (nat_to_zero_list 1000) (nat_to_one_list 1000)) (check_same_length (nat_to_zero_list 1000) (nat_to_one_list 1000)))).
Qed.

(*
 * Note how small this is!
 *)
Print same_length_lists_1000_reflective.

(*
 * EXERCISE 5: What happens if you try to use the same tactic
 * to prove something unprovable? Why? What theorem could you
 * prove with this tactic instead?
 *)
Lemma same_length_bad : Same_Length (nat_to_zero_list 50) (nat_to_one_list 1000).
Proof.
  (*exact (optionOut (Same_Length (nat_to_zero_list 50) (nat_to_one_list 1000)) (check_same_length (nat_to_zero_list 50) (nat_to_one_list 1000))).*)
Abort.

(* --- Part 3: reflective tactics --- *)

(*
 * Now you can wrap all of this inside of a cute tactic, which
 * we'll call prove_same_length. You again may wish to refer to
 * the demo from Tuesday for this.
 *
 * EXERCISE 6: Implement the tactic prove_same_length, which
 * should prove the below proofs efficiently if the tactic
 * is correct.
 *)
Ltac prove_same_length := idtac. (* replace with your code *)

(*
 * These should all go through efficiently now:
 *)
Lemma same_length_lists_50_ltac:
  Same_Length (nat_to_zero_list 50) (nat_to_one_list 50).
Proof.
  prove_same_length.
Qed.

Lemma same_length_lists_1000_ltac:
  Same_Length (nat_to_zero_list 1000) (nat_to_one_list 1000).
Proof.
  prove_same_length.
Qed.

Lemma same_length_lists_5000_ltac:
  Same_Length (nat_to_zero_list 5000) (nat_to_one_list 5000).
Proof.
  prove_same_length.
Qed.

(* --- Part 4: proving your automation correct --- *)

(*
 * EXERCISE 7: Prove check_same_length_OK below, which shows
 * that your check_same_length decision procedure returns
 * some proof that two lists are the same length if and only if
 * they're actually the same length.
 *
 * It will probably help a lot to look at the proof from the
 * demo in class. Tactics I found helpful (YMMV):
 *   `intros` (introduction)
 *   `split` (split P <-> Q into P -> Q and Q -> P)
 *   `induction` (note that you can induct not just over lists,
 *                but also over all proofs of Same_Length l1 l2
 *                for any two lists l1 and l2, and this may be
 *                easier for this proof)
 *   `econstructor` (apply some constructor, but choose it for me)
 *   `reflexivity`
 *   `eauto` (like auto, but also infer some arguments I don't
 *            feel like figuring out myself)
 *   `destruct` (like induction, but don't bother defining an
 *               inductive hypothesis)
 *   `unfold` (delta-reduce or unfold constants)
 *   `rewrite` (rewrite by equalities, like subst in Agda)
 *   `apply` (apply a hypothesis or lemma to some arguments)
 *
 * It's OK if this proof is hard. If it is, I recommend stepping
 * through the corresponding proof about isEven in the demo from
 * class on Tuesday (linked at the top of this file), and making
 * sure you understand each step.
 *)

Definition check_same_length_is_some {T : Type} (l1 l2 : list T) :=
  exists (H : Same_Length l1 l2), check_same_length l1 l2 = Some H.

Theorem check_same_length_OK:
  forall {T : Type} (l1 l2 : list T),
    Same_Length l1 l2 <-> check_same_length_is_some l1 l2.
Proof.
  (* your proof here *)
Abort. (* <- change to Qed when done *)

(*
 * BONUS EXERCISE: If you have extra time, prove that this is
 * not only correct, but also respects the actual length function.
 * You may prove this however you like.
 *)
Theorem check_same_length_OK_alt:
  forall {T : Type} (l1 l2 : list T),
    length l1 = length l2 <-> check_same_length_is_some l1 l2.
Proof.
  (* your proof here *)
Abort. (* <- change to Qed when done *)

(* --- Discussion --- *)

(*
 * Answer in the class forum, exactly one answer per group,
 * listing the names of everyone in your group as always:
 * Think back to the three different ways you've written
 * proofs so far in this class: by constructing proof terms
 * directly (Agda, Artifact 1), by using tactics (Coq, Artifact 2),
 * and by reflection (Coq, Artifact 3). When do you think you'd
 * use each one over the others, if ever? And why (even if never)?
 *)


(** Generated by coq-of-ocaml *)
Require Import OCaml.OCaml.

Local Set Primitive Projections.
Local Open Scope string_scope.
Local Open Scope Z_scope.
Local Open Scope type_scope.
Import ListNotations.

Unset Positivity Checking.
Unset Guard Checking.

Inductive nat : Set :=
| O : nat
| S : nat -> nat.

Inductive natural : Set :=
| Zero : natural
| Succ : natural -> natural.

Fixpoint plus (plus_arg0 : natural) (plus_arg1 : natural) {struct plus_arg0}
  : natural :=
  match plus_arg0 with
  | Zero => plus_arg1
  | Succ n => Succ (plus n plus_arg1)
  end.

Fixpoint mult (mult_arg0 : natural) (mult_arg1 : natural) {struct mult_arg0}
  : natural :=
  match mult_arg0 with
  | Zero => Zero
  | Succ n => plus (mult n mult_arg1) mult_arg1
  end.

Definition synth (y : natural) (lf2 : natural) (x : natural) : natural :=
  plus y (mult x lf2).