(*
  Exercises for <Software Foundations> CH6.
  Author : Brethland, Late 2019.
*)

From Coq Require Import Peano.
From Coq Require Import Nat.
From Coq Require Import Arith.
From Coq Require Import List.

Module Logic.

Lemma and_intro : forall (A B : Prop),
  A -> B -> and A B.
Proof.
  intros.
  split.
  apply H.
  apply H0.
Qed.

Example and_exercise : forall (n m : nat),
  n + m = 0 -> and (n = 0) (m = 0).
Proof.
  intros.
  apply and_intro.
  - induction n.
    + auto.
    + inversion H.
  - induction m.
    + auto.
    + rewrite plus_comm in H.
      inversion H.
Qed.

Lemma proj1 : forall (P Q : Prop),
  P /\ Q -> P.
Proof.
  intros.
  destruct H.
  auto.
Qed.

Lemma proj2 : forall (P Q : Prop),
  P /\ Q -> Q.
Proof.
  intros.
  destruct H.
  auto.
Qed.

Lemma and_commut : forall (P Q : Prop),
  P /\ Q -> Q /\ P.
Proof.
  intros.
  destruct H.
  split.
  auto.
  auto.
Qed.

Lemma and_assoc : forall (P Q R : Prop),
  P /\ (Q /\ R) -> (P /\ Q) /\ R.
Proof.
  intros P Q R [HP [HQ HR]].
  split.
  split.
  trivial. trivial. trivial.
Qed.

(* ex falso quodlibet *)

Fact not_implies_our_not : forall (P : Prop),
  ~ P -> (forall (Q :Prop), P -> Q).
Proof.
  intros.
  destruct H.
  auto.
Qed.

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

Lemma contradiction_implies_everything : forall (P Q : Prop),
  (P /\ ~ P) -> Q.
Proof.
  intros P Q [HA HNA].
  unfold not in HNA.
  apply HNA in HA.
  inversion HA.
Qed.

Lemma double_neg : forall P : Prop,
  P -> ~~P.
Proof.
  intros.
  unfold not.
  intros.
  apply H0.
  auto.
Qed.

Lemma contrapositive : forall (P Q : Prop),
  (P -> Q) -> (~ Q -> ~ P).
Proof.
  intros P Q H.
  unfold not.
  intros.
  apply H in H1.
  apply H0 in H1.
  inversion H1.
Qed.

Fact not_both_true_false : forall P : Prop,
  ~ (P /\ ~ P).
Proof.
  intros.
  unfold not.
  intros [HA HNA].
  apply HNA in HA.
  inversion HA.
Qed.

Theorem not_true_is_false : forall b : bool,
  b <> true -> b = false.
Proof.
  intros [] H.
  - unfold not in H.
    exfalso.
    apply H.
    auto.
  - auto.
Qed.

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

Lemma iff_sym : forall (P Q : Prop),
  (P <-> Q) -> (Q <-> P).
Proof.
  intros P Q [HAB HBA].
  split.
  auto.
  auto.
Qed.

Lemma not_true_iff_false : forall b : bool,
  b <> true <-> b = false.
Proof.
  intros b.
  split.
  apply not_true_is_false.
  intros.
  rewrite H.
  intros H'.
  inversion H'.
Qed.

Lemma or_distriutes_over_and : forall (P Q R : Prop),
  P \/ (Q /\ R) <-> (P \/ Q) /\ (P \/ R).
Proof.
  intros P Q R.
  split.
  - intros [HR | [HQ HR]].
    + auto.
    + auto.
  - intros [[HP | HQ] [HP' | HR]].
    + auto.
    + auto.
    + auto.
    + auto.
Qed.

Require Import Coq.Setoids.Setoid.

Lemma mult_0 : forall (n m : nat),
  n * m = 0 <-> n = 0 \/ m = 0.
Admitted.

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

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

Lemma exists_example : forall n,
  (exists m, n = 4 + m) ->
  (exists o, n = 2 + o).
Proof.
  intros n [m Hm].
  exists (2 + m).
  auto.
Qed.

Lemma dist_not_exists : forall (X : Type) (P : X -> Prop),
  (forall x, P x) -> ~ (exists x, ~ P x).
Proof.
  intros.
  unfold not.
  intros.
  destruct H0 as [x E].
  apply E in H.
  inversion H.
Qed.

Lemma dist_exists_or : forall (X : Prop) (P Q : X -> Prop),
  (exists x, P x \/ Q x) <-> (exists x, P x) \/ (exists x, Q x).
Proof.
  intros.
  split.
  - intros [x0 [HP | HQ]].
    left. exists x0. auto.
    right. exists x0. auto.
  - intros [[x1 E1] | [x2 E2]].
    exists x1. left. auto.
    exists x2. right. auto.
Qed.

(* Fixpoint In {X : Type} (x : X) (l : list X) :=
  match l with
  | nil => False
  | cons e es => x = e \/ In x es
  end. *)

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

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

Lemma In_map : forall (X Y : Type) (f : X -> Y) (l : list X) (x : X),
  In x l -> In (f x) (map f l).
Proof.
  intros.
  induction l.
  - auto.
  - simpl.
    destruct H.
    + rewrite <- H. left. auto.
    + apply IHl in H.
      right. apply H.
Qed.

Lemma In_map_iff : forall (X Y : Type) (f : X -> Y) (l : list X) (y : Y),
  In y (map f l) <-> exists x, f x = y /\ In x l.
Proof.
  intros. split.
  induction l.
  - simpl. intros. inversion H.
  - simpl. intros [HA | HB].
    + exists a. split. auto. left. auto.
    + apply IHl in HB. destruct HB. exists x.
      split. destruct H. auto. right. destruct H. auto.
  - intros [x [HA HB]].
    rewrite <- HA. apply In_map. auto.
Qed.

Lemma In_app_iff : forall X l1 l2 (x : X),
  In x (l1 ++ l2) <-> In x l1 \/ In x l2.
Proof.
  intros.
  split.
  induction l1.
  - intros. simpl in H. right. auto.
  - simpl. intros [HA | HB].
    + left. left. auto.
    + apply IHl1 in HB. (* or_assoc *)
      destruct HB.
      ++ left. right. auto.
      ++ right. auto.
  - apply in_or_app.
Qed.

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

Lemma or_forall : forall {X : Type} (P1 P2 : Prop),
  (forall x : X, P1) \/ (forall x : X, P2) -> (forall x : X, P1 \/ P2).
Proof.
  intros.
  destruct H.
  auto. auto.
Qed.

Lemma All_In : forall T (P : T -> Prop) (l : list T),
  (forall x : T, In x l -> P x) <-> All P l.
Proof.
  intros. split.
  - induction l.
    + intros.
      unfold All. auto.
    + intros. simpl. split.
      ++ apply H. simpl. left. auto.
      ++ apply IHl. simpl in H.
         intros. apply H.
         right. auto.
  - induction l.
    + intros. inversion H0.
    + intros.
      destruct H.
      destruct H0 as [HA | HB].
      ++ rewrite <- HA. auto.
      ++ apply IHl. auto. auto.
Qed.

Definition combine_odd_even (Podd Peven : nat -> Prop) :=
  fun n => if odd n then Podd n else Peven n.

Require Import Unicode.Utf8.

Theorem combine_odd_even_intro :   ∀ (Podd Peven : nat → Prop) (n : nat),
  (odd n = true → Podd n) →
  (odd n = false → Peven n) →
  combine_odd_even Podd Peven n.
Proof.
  intros.
  unfold combine_odd_even.
  destruct (odd n).
  - apply H. auto.
  - apply H0. auto.
Qed.

Theorem combine_odd_even_elim_odd :
   ∀ (Podd Peven : nat → Prop) (n : nat),
  combine_odd_even Podd Peven n →
  odd n = true →
  Podd n.
Proof.
  intros.
  unfold combine_odd_even in H.
  rewrite H0 in H. auto.
Qed.

Theorem combine_odd_even_elim_even :
  ∀ (Podd Peven : nat → Prop) (n : nat),
  combine_odd_even Podd Peven n →
  odd n = false →
  Peven n.
Proof.
(*   Too trivial.*)
Admitted.

Axiom functional_extensionality : ∀ {X Y: Type} {f g : X → Y},
  (∀ (x:X), f x = g x) → f = g.

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

Print Assumptions All_In.

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 [].

Lemma rev_append_app: forall {X : Type} (l l1 l2: list X),
    rev_append l (l1 ++ l2) = rev_append l l1 ++ l2.
  intros.
  generalize dependent l2.
  generalize dependent l1.
  induction l as [| h t IH].
  - auto.
  - intros. simpl.
    apply (IH (h::l1)).
Qed.

Lemma tr_rev_app : forall {X : Type} (l l1 : list X),
  rev_append l l1 = tr_rev l ++ l1.
Proof.
  intros. generalize dependent l1. unfold tr_rev.
  induction l.
  - auto.
  - simpl. intros.
    rewrite <- (@rev_append_app X l [a] l1).
    auto.
Qed.

Theorem app_silly : forall X (l1 l2 l : list X),
  l1 = l2 -> l1 ++ l = l2 ++ l.
Proof.
  intros.
  rewrite H.
  auto.
Qed.

Check functional_extensionality.

Check f_equal.

Lemma tr_rev_correct : ∀ X, @tr_rev X = @rev X.
Proof.
  intros.
  apply functional_extensionality.
  intros.
  induction x.
  - auto.
  - simpl. unfold tr_rev. simpl.
    rewrite tr_rev_app.
    apply (@app_silly X (tr_rev x) (rev x) [a]).
    auto.
Qed.

Fixpoint evenb (n : nat) :=
  match n with
  | 0 => true
  | S 0 => false
  | S (S n') => evenb n'
  end.

Lemma neg_id : forall b : bool, negb (negb b) = b.
Proof.
  intros [].
  - auto.
  - auto.
Qed.

Lemma evenb_S: forall n : nat, evenb (S n) = negb (evenb n).
Proof.
  intros n.
  induction n.
  - auto.
  - rewrite -> IHn.
    rewrite -> neg_id.
    auto.
Qed.

Theorem evenb_double_conv : ∀ n, ∃ k, n = if evenb n then double k else S (double k).
Proof.
  intros.
  induction n.
  - exists 0. auto.
  - destruct IHn.
    destruct (evenb n) eqn:HA.
    + rewrite evenb_S. rewrite HA. simpl.
      exists x. auto.
    + rewrite evenb_S. rewrite HA. simpl.
      exists (S x). rewrite H. unfold double. simpl.
      rewrite plus_n_Sm. auto.
Qed.

Lemma andb_true_iff : ∀ b1 b2:bool, andb b1 b2 = true ↔ b1 = true ∧ b2 = true.
Proof.
  intros. split.
  - intros.
    destruct b1.
    + simpl in H. auto.
    + inversion H.
  - intros [HA HB].
    rewrite HA. rewrite HB. auto.
Qed.

Lemma orb_true_iff : ∀ b1 b2,  (b1 || b2)%bool = true ↔ b1 = true ∨ b2 = true.
Proof.
  intros.
  split.
  - intros.
    destruct b1.
    + left. auto.
    + simpl in H. right. auto.
  - intros [HA | HB].
    + rewrite HA. auto.
    + rewrite HB.
      destruct b1. auto. auto.
Qed.

Theorem beq_nat_true_iff : ∀ 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.

Theorem beq_nat_false_iff : ∀ x y : nat,   beq_nat x y = false ↔ x ≠ y.
Proof.
  intros. split.
  - intros.
    unfold not. intros. apply beq_nat_true_iff in H0.
    rewrite H0 in H. inversion H.
  - generalize (beq_nat_true_iff x y).
    destruct beq_nat.
    + intros. destruct H0. apply H. auto.
    + intros. auto.
Qed.

Fixpoint beq_list {A : Type} (beq : A -> A -> bool) (l1 l2 : list A) :=
  match l1,l2 with
  | [] , y :: et => false
  | x :: es , [] => false
  | []      , [] => true
  | x :: es , y :: et => andb (beq x y) (beq_list beq es et)
  end.

Lemma beq_list_true_iff :   ∀ A (beq : A → A → bool),
  (∀ a1 a2, beq a1 a2 = true ↔ a1 = a2) →
  ∀ l1 l2, beq_list beq l1 l2 = true ↔ l1 = l2.
Proof.
  intros.
  split.
  - intros.
    generalize dependent l2.
    induction l1.
    + intros. destruct l2. auto. inversion H0.
    + intros. destruct l2.
      ++ inversion H0.
      ++ simpl in H0. apply andb_true_iff in H0. destruct H0.
         apply H in H0. rewrite H0. apply IHl1 in H1. rewrite H1. auto.
  - intros. generalize dependent l2. induction l1.
    + intros. destruct l2. auto. inversion H0.
    + intros. destruct l2.
      ++ inversion H0.
      ++ simpl. apply andb_true_iff. split.
         inversion H0. apply H. auto.
         apply IHl1. inversion H0. auto.
Qed.

Theorem forallb_true_iff : ∀ X test (l : list X),    forallb test l = true ↔ All (fun x => test x = true) l.
Proof.
  intros. split.
  - intros. induction l.
    + simpl. auto.
    + simpl. split.
      simpl in H. apply andb_true_iff in H. destruct H. auto.
      apply IHl. simpl in H. apply andb_true_iff in H. destruct H. auto.
  - intros. induction l.
    + simpl. auto.
    + simpl. apply andb_true_iff. split.
      simpl in H. destruct H. auto.
      simpl in H. destruct H. apply IHl in H0. auto.
Qed.

Theorem excluded_middle_irrefutable: ∀ (P:Prop),   ¬ ¬ (P ∨ ¬ P).
Proof.
  intros.
  unfold not. intros. apply H.
  right. intros. apply H. left. auto.
Qed.

Theorem not_exists_dist :
  (forall (P : Prop), P \/ (~ P)) ->
  forall (X:Type) (P : X -> Prop),
    ~ (exists x, ~ P x) -> (forall x, P x).
Proof.
  intros.
  destruct (H (P x)).
  + auto.
  + exfalso. apply H0. exists x. auto.
Qed.

Definition peirce := ∀ P Q: Prop,   ((P→Q)→P)→P.
Definition double_negation_elimination := ∀ P:Prop,   ~~P → P.
Definition de_morgan_not_and_not := ∀ P Q:Prop,   ~(~P ∧ ¬Q) → P∨Q.
Definition implies_to_or := ∀ P Q:Prop,   (P→Q) → (¬P∨Q).
Definition excluded_middle := forall (P : Prop), P \/ (~ P).

Lemma double_negation_excluded_middle_iff :
  excluded_middle <-> double_negation_elimination.
Proof.
  split.
  - intros. unfold excluded_middle in H.
    unfold double_negation_elimination.
    unfold not. intros.
    specialize (H P). destruct H. auto.
    unfold not in H. elim H0. intros.
    apply H0. auto.
  - intros. unfold excluded_middle.
    unfold double_negation_elimination in H.
    intros. apply H. unfold not in *. auto.
Qed.


(** * 6.822 Formal Reasoning About Programs, Spring 2020 - Pset 1 *)

Require Import Frap Pset1Sig.

(* The first part of this assignment involves the [bool] datatype,
 * which has the following definition.
 * <<
     Inductive bool :=
     | true
     | false.
   >>
 * We will define logical negation and conjunction of Boolean values,
 * and prove some properties of these definitions.
 *)

(* Define [Neg] so that it implements Boolean negation, which flips
 * the truth value of a Boolean value.
 *)
Definition Neg (b : bool) : bool :=
  match b with
    | true => false
    | false => true
  end.

(* For instance, the negation of [true] should be [false].
 * This proof should follow from reducing both sides of the equation
 * and observing that they are identical.
 *)
Theorem Neg_true : Neg true = false.
Proof.
simplify.
equality.
Qed.
(* Negation should be involutive, meaning that if we negate
 * any Boolean value twice, we should get the original value back.

 * To prove a fact like this that holds for all Booleans, it suffices
 * to prove the fact for both [true] and [false] by using the
 * [cases] tactic.
 *)
Theorem Neg_involutive : forall b : bool, Neg (Neg b) = b.
Proof.
  simplify.
  cases b; try (simplify; equality).
Qed.

(* Define [And] so that it implements Boolean conjunction. That is,
 * the result value should be [true] exactly when both inputs
 * are [true].
 *)
Definition And (x y : bool) : bool :=
  match x,y with
    | true, true => true
    | _, _ => false
  end.

(* Here are a couple of examples of how [And] should act on
 * concrete inputs.
 *)
Theorem And_true_true : And true true = true.
Proof.
simplify.
equality.
Qed.

Theorem And_false_true : And false true = false.
Proof.
simplify.
equality.
Qed.

(* Prove that [And] is commutative, meaning that switching the order
 * of its arguments doesn't affect the result.
 *)
Theorem And_comm : forall x y : bool, And x y = And y x.
Proof.
simplify.
cases x; (cases y; simplify; equality).
Qed.
(* Prove that the conjunction of a Boolean value with [true]
 * doesn't change that value.
 *)
Theorem And_true_r : forall x : bool, And x true = x.
Proof.
simplify.
cases x; simplify; equality.
Qed.

(* In the second part of this assignment, we will work with a simple language
 * of imperative arithmetic programs that sequentially apply operations
 * to a natural-number-valued state.

 * The [Prog] datatype defines abstract syntax trees for this language.
 *)

Print Prog.

(* Define [run] such that [run p n] gives the final state
 * that running the program [p] should result in, when the
 * initial state is [n].
 *)
Fixpoint run (p : Prog) (initState : nat) : nat :=
  match p with
    | Done => initState
    | AddThen e1 p1 => run p1 (initState + e1)
    | MulThen e2 p2 => run p2 (initState * e2)
    | DivThen e3 p3 => run p3 (initState / e3)
    | VidThen e4 p4 => run p4 (e4 / initState)
    | SetToThen e5 p5 => run p5 e5
end.

Print Prog.
Example test := (MulThen 5 (AddThen 2 Done)).
Compute run test 2.
Theorem run_Example1 : run Done 0 = 0.
Proof.
simplify.
equality.
Qed.

Theorem run_Example2 : run (MulThen 5 (AddThen 2 Done)) 1 = 7.
Proof.
  simplify.
  equality.
Qed.

Theorem run_Example3 : run (SetToThen 3 (MulThen 2 Done)) 10 = 6.
Proof.
simplify.
equality.
Qed.
(* Define [numInstructions] to compute the number of instructions
 * in a program, not counting [Done] as an instruction.
 *)
Fixpoint numInstructions (p : Prog) : nat :=
  match p with
    | Done => 0
    | AddThen e1 p1   => 1 + numInstructions (p1)
    | MulThen e1 p1   => 1 + numInstructions (p1)
    | DivThen e1 p1   => 1 + numInstructions (p1)
    | VidThen e1 p1   => 1 + numInstructions (p1)
    | SetToThen e1 p1 => 1 + numInstructions (p1)
  end.

Theorem numInstructions_Example :
  numInstructions (MulThen 5 (AddThen 2 Done)) = 2.
Proof.
  simplify.
  equality.
Qed.

(* Define [concatProg] such that [concatProg p1 p2] is the program
 * that first runs [p1] and then runs [p2].
 *)
Fixpoint concatProg (p1 p2 : Prog) : Prog :=
  match p1 with
    | Done => p2
    | AddThen e1 res => AddThen e1 (concatProg res p2)
    | MulThen e1 res => MulThen e1 ( concatProg res p2)
    | DivThen e1 res => DivThen e1 ( concatProg res p2)
    | VidThen e1 res => VidThen e1 ( concatProg res p2)
    | SetToThen e1 res => SetToThen e1 (concatProg res p2)
  end.

Example a := concatProg (AddThen 1 Done) (MulThen 2 Done).
Compute a.

Theorem concatProg_Example :
     concatProg (AddThen 1 Done) (MulThen 2 Done)
     = AddThen 1 (MulThen 2 Done).
Proof.
simplify.
equality.
Qed.

Theorem concat_base
  : forall (p1: Prog), concatProg p1 Done = p1.
Proof.
  simplify.
  induct p1; simplify; equality.
Qed.
(* Prove that the number of instructions in the concatenation of
 * two programs is the sum of the number of instructions in each
 * program.
 *)
Theorem concatProg_numInstructions
  : forall (p1 p2 : Prog), numInstructions (concatProg p1 p2)
                      = numInstructions p1 + numInstructions p2.
Proof.
simplify.
induction p1; simplify; equality; simplify; rewrite IHp1; equality.
Qed.
(* Prove that running the concatenation of [p1] with [p2] is
   equivalent to running [p1] and then running [p2] on the
   result. *)
Theorem concatProg_run
  : forall (p1 p2 : Prog) (initState : nat),
    run (concatProg p1 p2) initState =
    run p2 (run p1 initState).
Proof.
induct p1; simplify; equality.
Qed.
(* Read this definition and understand how division by zero is handled. *)
Fixpoint runPortable (p : Prog) (state : nat) : bool * nat :=
  match p with
  | Done => (true, state)
  | AddThen n p => runPortable p (n+state)
  | MulThen n p => runPortable p (n*state)
  | DivThen n p =>
      if n ==n 0 then (false, state) else
      runPortable p (state/n)
  | VidThen n p =>
      if state ==n 0 then (false, 0) else
      runPortable p (n/state)
  | SetToThen n p =>
      runPortable p n
  end.
Arguments Nat.div : simpl never. (* you don't need to understand this line *)

Lemma run_portable_helper: forall (p: Prog) (n: nat),
  runPortable (DivThen 0 p) n = (false, n).
Proof.
  simplify.
  cases (n);simplify; equality.
Qed.

Lemma run_portable_helper_vid: forall (p: Prog) (n: nat),
  runPortable (VidThen n p) 0 = (false, 0).
Proof.
  simplify.
  cases (n);simplify; equality.
Qed.

Lemma run_portable_helper2: forall (p: Prog) (n n2: nat),
  n <> 0 -> runPortable (DivThen n p) n2 = runPortable p (n2 / n).
Proof.
  simplify.
  cases (n ==n 0).
  contradiction.
  equality.
Qed.

Lemma run_portable_helper_vid2: forall (p: Prog) (n n2: nat),
  n2 <> 0 -> runPortable (VidThen n p) n2 = runPortable p (n / n2).
Proof.
  simplify.
  cases (n2 ==n 0).
  contradiction.
  equality.
Qed.

Lemma run_portable_helper3: forall(p: Prog) (n s0 s1: nat),
   (runPortable (DivThen n p) s0 = (true, s1)) -> n <> 0.
Proof.
intros.
cases n.
assert(H2: runPortable (DivThen 0 p) s0 = (false, s0)).
apply run_portable_helper.
rewrite H in H2.
equality.
linear_arithmetic.
Qed.

Lemma run_portable_helper_vid3: forall(p: Prog) (n s0 s1: nat),
   (runPortable (VidThen n p) s0 = (true, s1)) -> s0 <> 0.
Proof.
intros.
cases s0.
assert(H2: runPortable (VidThen n p) 0 = (false, 0)).
apply run_portable_helper_vid.
rewrite H in H2.
equality.
linear_arithmetic.
Qed.

(* Prove that running the concatenation [p] using [runPortable]
   coincides with using [run], as long as [runPortable] returns
   [true] to confirm that no divison by zero occurred. *)

Lemma runPortable_run : forall p s0 s1,
  runPortable p s0 = (true, s1) -> run p s0 = s1.
Proof.
intros.
induct p; try(simplify; equality); try(apply IHp in H; simplify; try(rewrite plus_comm); try(rewrite mult_comm); try apply H).

apply IHp.
rewrite <- H.
rewrite run_portable_helper2.
equality.
assert(H2: runPortable (DivThen n p) s0 = (true, s1) -> n <> 0).
apply run_portable_helper3.
apply H2.
apply H.

apply IHp.
rewrite <- H.
rewrite run_portable_helper_vid2.
equality.
assert(H2: runPortable (VidThen n p) s0 = (true, s1) -> s0 <> 0).
apply run_portable_helper_vid3.
apply H2.
apply H.

Qed.

Fixpoint validate_helper p (b : bool) :=
    match p with
    | Done => true
    | AddThen n p => if n ==n 0 then validate_helper p b else validate_helper p true
    | MulThen n p => if n ==n 0 then validate_helper p false else validate_helper p b
    | DivThen n p => if n ==n 0 then false else (if b then validate_helper p (Nat.eqb n 1) else validate_helper p false)
    | VidThen n p => if b then validate_helper p false else false
    | SetToThen n p => if n ==n 0 then validate_helper p false else validate_helper p true
    end.

Definition validate (p : Prog) : bool :=
  validate_helper p false.

(*Lemma validate_sound_helper: forall (p: Prog), validate p = true -> forall s, fst(runPortable p s) = true -> fst(runPortable p 0) = true.
induct p; simplify; try(apply IHp); try(equality).
unfold validate in IHp.
apply IHp.
Qed.*)

(*Lemma b : forall (p: Prog)(n: nat), fst (runPortable p (n)) = true <-> (runPortable p n = (true, run p n)).
Proof.
  induct p; simplify; try (apply IHp in H); try(rewrite <- plus_comm); try(rewrite <- mult_comm);
  try(equality); try(rewrite plus_comm in H); try(rewrite mult_comm in H); try(assumption);
  try(cases(n ==n 0); cases (n0 ==n 0));simplify; try(equality); try(rewrite IHp); try(equality).
Qed.

Lemma concat_portable : forall p1 p2 n, runPortable (concatProg p1 p2) n
        = (let (x, answer) := runPortable p1 n in if x then runPortable p2 answer else (x,answer)).
Proof.
  induct p1; try equality; simplify; try apply IHp1; try equality; try( cases (n ==n 0); cases (n0 ==n 0);
  try equality;
  simplify;
  apply IHp1).
Qed.
*)
  (* If a clear plan hasn't emerged in 10 minutes (or if you get stuck later),
 * take a look at the hints for this pset on the course web site.
 * It is not expected that this pset is doable for everyone without the hints,
 * but some planning is required for successful proof.
 * In particular, repeatedly trying out different combinations of tactics
 * and ideas from hints until something sticks can go on for arbitrarily long
 * with little insight and no success; just guessing a solution is unlikely.
 * Thus, we encourage you to take your time thinking, look at the hints when
 * necessary, and only jump into coding when you have some idea why it should
 * succeed. Some may call Coq a video game, but it is not a grinding contest. *)

(* The final goal of this pset is to implement [validate : Prog -> bool]
   such that if this function returns [true], the program would not trigger
   division by zero regardless of what state it starts out in.  [validate] is
   allowed to return [false] for some perfectly good programs that never cause
   division by zero, but it must recognize as good the examples given below.  In
   jargon, [validate] is required to be sound but not complete, but "complete
   enough" for the use cases defined by the examples given here: *)

Definition goodProgram1 := AddThen 1 (VidThen 10 Done).
Example runPortable_good : forall n,
  runPortable goodProgram1 n = (true, 10/(1+n)).
Proof. simplify. equality. Qed.

Definition badProgram1 := AddThen 0 (VidThen 10 Done).
Example runPortable_bad : let n := 0 in
  runPortable badProgram1 n = (false, 0).
Proof. simplify. equality. Qed.

Definition badProgram2 := AddThen 1 (DivThen 0 Done).
Example runPortable_bad2 : forall n,
  runPortable badProgram2 n = (false, 1+n).
Proof. simplify. equality. Qed.

Definition goodProgram2 := AddThen 0 (MulThen 10 (AddThen 0 (DivThen 1 Done))).
Definition goodProgram3 := AddThen 1 (MulThen 10 (AddThen 0 (VidThen 1 Done))).
Definition goodProgram4 := Done.
Definition goodProgram5 := SetToThen 0 (DivThen 1 Done).
Definition goodProgram6 := SetToThen 1 (VidThen 1 Done).
Definition goodProgram7 := AddThen 1 (DivThen 1 (DivThen 1 (VidThen 1 Done))).

(* If you already see a way to build [validate] that meets the
 * requirements above, _and have a plan for how to prove it correct_,
 * feel free to just code away. Our solution uses one intermediate definition
 * and one intermediate lemma in the soundness proof -- both of which are more
 * sophisticated than the top-level versions given here. *)
Example validate1 : validate goodProgram1 = true. Proof. equality. Qed.
Example validate2 : validate goodProgram2 = true. Proof. equality. Qed.
Example validate3 : validate goodProgram3 = true. Proof. equality. Qed.
Example validate4 : validate goodProgram4 = true. Proof. equality. Qed.
Example validate5 : validate goodProgram5 = true. Proof. equality. Qed.
Example validate6 : validate goodProgram6 = true. Proof. equality. Qed.
Example validate7 : validate goodProgram7 = true. Proof. equality. Qed.
Example validate8 : validate badProgram1 = false. Proof. equality. Qed.
Example validate9 : validate badProgram2 = false. Proof. equality. Qed.

Lemma validate_sound_helper: forall p b, validate_helper p b = true ->
     forall s : nat, (b = true -> s <> 0) ->
     runPortable p s = (true, run p s).
induct p; try(simplify); try(equality).
rewrite plus_comm.
simplify.
cases (n ==n 0).
apply IHp with (b:=b).
assumption.
rewrite e.
simplify.
apply H0 in H1.
linear_arithmetic.
apply IHp with (b:=true).
assumption.
simplify.
linear_arithmetic.

rewrite mult_comm.
simplify.
cases (n ==n 0).
apply IHp with (b:=false).
assumption.
propositional.
equality.
apply IHp with (b:=b).
assumption.

simplify.
apply H0 in H1.
simplify.
apply Nat.neq_mul_0.
propositional.

cases (n ==n 0).
equality.
cases b.
apply IHp with (b:= Nat.eqb n 1).
assumption.
cases(Nat.eqb n 1).
intros.
apply Nat.eqb_eq in Heq.
rewrite Heq.
apply H0 in H1.
rewrite Nat.div_1_r.
assumption.
propositional.
equality.
apply IHp with (b := false) (s := s/n).
assumption.
propositional.
equality.

cases (s ==n 0).
cases b.
propositional.
equality.
cases b.
apply IHp with (b := false) (s := n/s).
assumption.
propositional.
equality.
equality.

cases (n ==n 0).
apply IHp with (b := false) (s := n).
assumption.
propositional.
equality.

apply IHp with (b := true) (s := n).
assumption.
propositional.
Qed.

Hint Rewrite Nat.add_0_r.
Lemma validate_sound : forall p, validate p = true ->
  forall s, runPortable p s = (true, run p s).
Proof.
induct p; try(simplify); try(equality); try(rewrite plus_comm).
unfold validate in IHp.

apply validate_sound_helper with (s:=s) in H.
simplify.
assert(ejre: n + s = s + n).
linear_arithmetic.
rewrite <- ejre.
rewrite <- ejre in H.
trivial.
propositional.
equality.

apply validate_sound_helper with (s:=s) in H.
simplify.
assumption.
propositional.
equality.

cases (n ==n 0).
apply validate_sound_helper with (s:=s) in H.
simplify.
rewrite e in H.
simplify.
rewrite e.
apply H.
propositional.
equality.

apply validate_sound_helper with (s:=s) in H.
simplify.
cases (n ==n 0).
equality.
apply H.

propositional.
equality.

apply validate_sound_helper with (s:= s) in H.
simplify.
apply H.

propositional.
equality.

apply validate_sound_helper with (s:= s) in H.
simplify.
apply H.

propositional.
equality.
Qed.
(* Here is the complete list of commands used in one possible solution:
  - Search, for example Search (_ + 0).
  - induct, for example induct x
  - simplify
  - propositional
  - equality
  - linear_arithmetic
  - cases, for example cases (X ==n Y)
  - apply, for example apply H
  - apply in, for example apply H1 in H2 or apply somelemma in H1
  - apply with, for example apply H1 with (x:=2)
  - apply in with, for example apply H1 with (x:=2) in H2
  - rewrite, for example rewrite H
  - rewrite in, for example rewrite H1 in H2 or rewrite somelemma in H1
  - ;, for example simplify; propositional *)


(**
    Written by Mitchell Buckley. Started on 25/11/2013 while a
    PhD student at Macquarie University.

    This file contains some basic lemmas concerning natural
    numbers that I could not find in the standard library,
    and were required for a formalisation of the theory
    of Parity Complexes.
  **)

Require Import Utf8_core.
Require Import Arith.

(* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *)
(* Independent Lemmas concerning nat                    *)
(* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *)

  (* Strong mathematical induction *)
  Lemma strong_induction :
    forall P : nat -> Prop,
    P O ->
    (forall n : nat, (forall m, m <= n -> P m) -> P (S n)) ->
    forall n : nat, P n.
  Proof with intuition.
    intros P.
    set (Q := fun n => (forall m, m <= n -> P m)).

    intros.
    assert (Q 0) as A.
      unfold Q... inversion H1...
    assert (forall l, Q l -> Q (S l)) as B.
      unfold Q...
      inversion H2...
    assert (Q n) as C.
      apply (nat_ind Q)...
    apply (C n).
    left.
  Qed.

  (* Less-than-or-equal-to is a total relation *)
  Lemma le_total : forall n m, (n <= m) \/ (m <= n).
  Proof with intuition.
    apply NPeano.Nat.le_ge_cases.
  Qed.

  (* A four case comparison of two natural numbers *)
  Lemma lt_eq_eq_lt_dec: forall k m, {k < m} + {k = m} + {k = S m} + {S m < k}.
  Proof with intuition.
    intros.
    pose (lt_eq_lt_dec k m)...
    unfold lt in b.
    apply le_lt_eq_dec in b...
  Qed.

  (* Three basic contradictions on less-than *)
  Lemma lt_Sn : forall n, (S n < n) -> False.
  Proof with intuition.
    intros n.
    induction n...
    apply (lt_n_0) in H...
  Qed.

  Lemma lt_SSn : ∀ n : nat, S (S n) < n -> False.
  Proof with intuition.
    intros n.
    induction n...
      inversion H...
  Qed.

  Lemma lt_SSSn : ∀ n : nat, S (S (S n)) < n -> False.
  Proof with intuition.
    intros n.
    induction n...
      inversion H...
  Qed.

  (* Two basic contradictions on less-than-or-equal-to *)
  Lemma le_SSn : ∀ n : nat, S (S n) <= n -> False.
  Proof with intuition.
    intros n.
    induction n...
      inversion H...
  Qed.

  Lemma le_SSSn : ∀ n : nat, S (S (S n)) <= n -> False.
  Proof with intuition.
      intros n.
    induction n...
      inversion H...
  Qed.

  (* A basic contradictions on equality *)
  Lemma SSn_n : forall n, S (S n) = n -> False.
  Proof with intuition.
    intros...
    induction n...
    inversion H...
  Qed.

  (* A basic property of the successor function *)
  Lemma Sn_minus_1 : forall n, (S n - 1 = n).
  Proof with intuition.
    simpl...
  Qed.

  Hint Resolve SSn_n le_SSn.


Require Import List Omega.
Set Implicit Arguments.

(* Part 1: introduction to proof search *)

Print plus.

Example four_plus_three : 4 + 3 = 7.
  reflexivity.
Qed.

Print four_plus_three.

Inductive plusR :
      nat -> nat -> nat -> Prop :=
| PlusO : forall m, plusR O m m
| PlusS : forall n m r, plusR n m r
  -> plusR (S n) m (S r).

Hint Constructors plusR.

Ltac crush' := intros; simpl;
  match goal with
  | [ H : ex _ |- _] => destruct H
  | [ H : _ |- _ ] => try (rewrite <- H || rewrite H)
  | _ => idtac
  end; try omega; eauto.

Ltac crush := repeat crush'.

Theorem plus_plusR : forall n m,
  plusR n m (n + m).
  induction n; crush.
Qed.

Theorem plusR_plus : forall n m r,
  plusR n m r
  -> r = n + m.
  induction 1; crush.
Qed.

Hint Immediate plus_O_n.

Lemma plusS : forall n m r,
  n + m = r
  -> S n + m = S r.
  crush.
Qed.

Hint Resolve plusS.

Lemma plusO : forall n m,
  n = m
  -> n + 0 = m.
  crush.
Qed.

Print plusO.

Hint Resolve plusO.

(* Path 2: Synthesizing Expressions *)

Inductive exp : Set :=
| Const : nat -> exp
| Var : exp
| Plus : exp -> exp -> exp.

Inductive eval (var : nat) : exp -> nat -> Prop :=
| EvalConst : forall n, eval var (Const n) n
| EvalVar : eval var Var var
| EvalPlus : forall e1 e2 n1 n2, eval var e1 n1
  -> eval var e2 n2
  -> eval var (Plus e1 e2) (n1 + n2).

Hint Constructors eval.

Theorem EvalPlus' :
   forall var e1 e2 n1 n2 n, eval var e1 n1
  -> eval var e2 n2
  -> n1 + n2 = n
  -> eval var (Plus e1 e2) n.
  crush.
Qed.

Hint Resolve EvalPlus'.

Hint Extern 1 (_ = _) => abstract omega.

Theorem EvalConst' : forall var n m, n = m
  -> eval var (Const n) m.
  crush.
Qed.

Hint Resolve EvalConst'.

Theorem zero_times : forall n m r,
  r = m
  -> r = 0 * n + m.
  crush.
Qed.

Hint Resolve zero_times.

Theorem EvalVar' : forall var n,
  var = n
  -> eval var Var n.
  crush.
Qed.

Hint Resolve EvalVar'.

Theorem plus_0 : forall n r,
  r = n
  -> r = n + 0.
  auto.
Qed.

Theorem times_1 : forall n, n = 1 * n.
  auto.
Qed.

Hint Resolve plus_0 times_1.

Require Import Arith Ring.

Theorem combine : forall x k1 k2 n1 n2,
  (k1 * x + n1) + (k2 * x + n2) = (k1 + k2) * x + (n1 + n2).
  intros; ring.
Qed.

Hint Resolve combine.

Theorem linear : forall e, exists k, exists n,
  forall var, eval var e (k * var + n).
  induction e; crush.
Qed.


(* Exercise 5.15 *)

Definition my_True : Prop := forall P : Prop, P -> P.
Definition my_False : Prop := forall P : Prop, P.

Definition my_I : my_True := fun P p => p.
Definition my_False_ind := forall P : Prop, my_False -> P.

Definition my_not (P : Prop) : Prop := P -> my_False.

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

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

Definition my_ex (A : Set) (P : A -> Prop) :=
  forall R : Prop, (forall x : A, P x -> R) -> R.

Theorem theorem1 : forall P Q : Prop, my_and P Q -> P.
Proof.
  unfold my_and.
  intros P Q H.
  apply H; intros; assumption.
Qed.

Theorem theorem2 : forall P Q : Prop, my_and P Q -> Q.
Proof.
  unfold my_and.
  intros P Q H.
  apply H; intros; assumption.
Qed.

Theorem theorem3 : forall P Q R : Prop, (P -> Q -> R) -> my_and P Q -> R.
Proof.
  unfold my_and.
  intros P Q R H H0.
  apply H0; assumption.
Qed.

Theorem theorem4 : forall P Q : Prop, P -> my_or P Q.
Proof.
  unfold my_or.
  intros P Q p R Hleft Hright.
  apply Hleft; assumption.
Qed.

Theorem theorem5 : forall P Q : Prop, Q -> my_or P Q.
Proof.
  unfold my_or.
  intros P Q p R Hleft Hright.
  apply Hright; assumption.
Qed.

Theorem theorem6 : forall P Q R : Prop, (P -> R) -> (Q -> R) -> my_or P Q -> R.
Proof.
  unfold my_or.
  intros P Q R H H0 H1.
  apply H1; assumption.
Qed.

Theorem theorem7 : forall P : Prop, my_or P my_False -> P.
Proof.
  unfold my_or, my_False.
  intros P H.
  apply H.
  apply id.
  intro H0.
  apply H0.
Qed.

Theorem theorem8 : forall P Q : Prop, my_or P Q -> my_or Q P.
Proof.
  unfold my_or.
  intros P Q H R H0 H1.
  apply H; assumption.
Qed.

Theorem theorem9 : forall (A : Set) (P : A -> Prop) (a : A), P a -> my_ex A P.
Proof.
  unfold my_ex.
  intros A P a H R H0.
  apply H0 with a; assumption.
Qed.

Theorem theorem10 : forall (A : Set) (P : A -> Prop), my_not (my_ex A P) -> forall a : A, my_not (P a).
Proof.
  unfold my_not, my_ex, my_False.
  intros A P H a H0 Q.
  apply H.
  intros R H1.
  apply H1 with a; assumption.
Qed.


(** * Logic: Logic in Coq *)

Require Export MoreCoq.

(** Coq's built-in logic is very small: the only primitives are
    [Inductive] definitions, universal quantification ([forall]), and
    implication ([->]), while all the other familiar logical
    connectives -- conjunction, disjunction, negation, existential
    quantification, even equality -- can be encoded using just these.

    This chapter explains the encodings and shows how the tactics
    we've seen can be used to carry out standard forms of logical
    reasoning involving these connectives.

*)

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

(** 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 Coq, the type of things that can (potentially)
    be proven is [Prop]. *)

(** Here is an example of a provable proposition: *)

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

(** Here is an example of an unprovable proposition: *)

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

(** Recall that [Check] asks Coq to tell us the type of the indicated
  expression. *)

(* ########################################################### *)
(** * Proofs and Evidence *)

(** In Coq, propositions have the same status as other types, such as
    [nat].  Just as the natural numbers [0], [1], [2], etc. inhabit
    the type [nat], a Coq proposition [P] is inhabited by its
    _proofs_.  We will refer to such inhabitants as _proof term_ or
    _proof object_ or _evidence_ for the truth of [P].

    In Coq, when we state and then prove a lemma such as:

Lemma silly : 0 * 3 = 0.
Proof. reflexivity. Qed.

    the tactics we use within the [Proof]...[Qed] keywords tell Coq
    how to construct a proof term that inhabits the proposition.  In
    this case, the proposition [0 * 3 = 0] is justified by a
    combination of the _definition_ of [mult], which says that [0 * 3]
    _simplifies_ to just [0], and the _reflexive_ principle of
    equality, which says that [0 = 0].

*)

(** *** *)

Lemma silly : 0 * 3 = 0.
Proof. reflexivity. Qed.

(** We can see which proof term Coq constructs for a given Lemma by
using the [Print] directive: *)

Print silly.
(* ===> silly = eq_refl : 0 * 3 = 0 *)

(** Here, the [eq_refl] proof term witnesses the equality. (More on
equality later!)*)

(** ** Implications _are_ functions *)

(** Just as we can implement natural number multiplication as a
function:

[
mult : nat -> nat -> nat
]

The _proof term_ for an implication [P -> Q] is a _function_ that
takes evidence for [P] as input and produces evidence for [Q] as its
output.
*)

Lemma silly_implication : (1 + 1) = 2  ->  0 * 3 = 0.
Proof. intros H. reflexivity. Qed.

(** We can see that the proof term for the above lemma is indeed a
function: *)

Print silly_implication.
(* ===> silly_implication = fun _ : 1 + 1 = 2 => eq_refl
     : 1 + 1 = 2 -> 0 * 3 = 0 *)

(** ** Defining propositions *)

(** Just as we can create user-defined inductive types (like the
    lists, binary representations of natural numbers, etc., that we
    seen before), we can also create _user-defined_ propositions.

    Question: How do you define the meaning of a proposition?
*)

(** *** *)

(** The meaning of a proposition is given by _rules_ and _definitions_
    that say how to construct _evidence_ for the truth of the
    proposition from other evidence.

    - Typically, rules are defined _inductively_, just like any other
      datatype.

    - Sometimes a proposition is declared to be true without
      substantiating evidence.  Such propositions are called _axioms_.

    In this, and subsequence chapters, we'll see more about how these
    proof terms work in more detail.
*)

(* ########################################################### *)
(** * Conjunction (Logical "and") *)

(** The logical conjunction of propositions [P] and [Q] can be
    represented using an [Inductive] definition with one
    constructor. *)

Inductive and (P Q : Prop) : Prop :=
  conj : P -> Q -> (and P Q).

(** The intuition behind this definition is simple: to
    construct evidence for [and P Q], we must provide evidence
    for [P] and evidence for [Q].  More precisely:

    - [conj p q] can be taken as evidence for [and P Q] if [p]
      is evidence for [P] and [q] is evidence for [Q]; and

    - this is the _only_ way to give evidence for [and P Q] --
      that is, if someone gives us evidence for [and P Q], we
      know it must have the form [conj p q], where [p] is
      evidence for [P] and [q] is evidence for [Q].

   Since we'll be using conjunction a lot, let's introduce a more
   familiar-looking infix notation for it. *)

Notation "P /\ Q" := (and P Q) : type_scope.

(** (The [type_scope] annotation tells Coq that this notation
    will be appearing in propositions, not values.) *)

(** Consider the "type" of the constructor [conj]: *)

Check conj.
(* ===>  forall P Q : Prop, P -> Q -> P /\ Q *)

(** Notice that it takes 4 inputs -- namely the propositions [P]
    and [Q] and evidence for [P] and [Q] -- and returns as output the
    evidence of [P /\ Q]. *)

(** ** "Introducing" conjunctions *)
(** Besides the elegance of building everything up from a tiny
    foundation, what's nice about defining conjunction this way is
    that we can prove statements involving conjunction using the
    tactics that we already know.  For example, if the goal statement
    is a conjuction, we can prove it by applying the single
    constructor [conj], which (as can be seen from the type of [conj])
    solves the current goal and leaves the two parts of the
    conjunction as subgoals to be proved separately. *)

Theorem and_example :
  (0 = 0) /\ (4 = mult 2 2).
Proof.
  apply conj.
  Case "left". reflexivity.
  Case "right". reflexivity.  Qed.

(** Just for convenience, we can use the tactic [split] as a shorthand for
    [apply conj]. *)

Theorem and_example' :
  (0 = 0) /\ (4 = mult 2 2).
Proof.
  split.
    Case "left". reflexivity.
    Case "right". reflexivity.  Qed.

(** ** "Eliminating" conjunctions *)
(** Conversely, the [destruct] tactic can be used to take a
    conjunction hypothesis in the context, calculate what evidence
    must have been used to build it, and add variables representing
    this evidence to the proof context. *)

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

(** **** Exercise: 1 star, optional (proj2)  *)
Theorem proj2 : forall P Q : Prop,
  P /\ Q -> Q.
Proof.
  intros P Q H.
  destruct H as [_ HQ].
  apply HQ.
Qed.
(** [] *)

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

(** **** Exercise: 2 stars (and_assoc)  *)
(** In the following proof, notice how the _nested pattern_ in the
    [destruct] 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 H.
  destruct H as [HP [HQ HR]].
  split.
  Case "P /\ Q".
    split. apply HP. apply HQ.
  Case "R".
    apply HR.
Qed.
(** [] *)

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

(** The handy "if and only if" connective is just the conjunction of
    two implications. *)

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

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

Theorem iff_implies : forall P Q : Prop,
  (P <-> Q) -> P -> Q.
Proof.
  intros P Q H.
  destruct H as [HAB HBA]. apply HAB.  Qed.

Theorem iff_sym : forall P Q : Prop,
  (P <-> Q) -> (Q <-> P).
Proof.
  (* WORKED IN CLASS *)
  intros P Q H.
  destruct H as [HAB HBA].
  split.
    Case "->". apply HBA.
    Case "<-". apply HAB.  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 H. apply H.
    intros H. apply H.
Qed.

Theorem iff_trans : forall P Q R : Prop,
  (P <-> Q) -> (Q <-> R) -> (P <-> R).
Proof.
  intros P Q R HPQ HQR.
  split.
    Case "P->R".
      destruct HPQ as [HPQ' _].
      destruct HQR as [HQR' _].
      intros HP.
      apply HQR'.
      apply HPQ'.
      apply HP.
    Case "R->P".
      destruct HPQ as [_ HQP'].
      destruct HQR as [_ HRQ'].
      intros HR.
      apply HQP'.
      apply HRQ'.
      apply HR.
Qed.

(** Hint: If you have an iff hypothesis in the context, you can use
    [inversion] to break it into two separate implications.  (Think
    about why this works.) *)
(** [] *)

(** Some of Coq's tactics treat [iff] statements specially, thus
    avoiding the need for some low-level manipulation when reasoning
    with them.  In particular, [rewrite] can be used with [iff]
    statements, not just equalities. *)

(* ############################################################ *)
(** * Disjunction (Logical "or") *)

(** ** Implementing disjunction *)

(** Disjunction ("logical or") can also be defined as an
    inductive proposition. *)

Inductive or (P Q : Prop) : Prop :=
  | or_introl : P -> or P Q
  | or_intror : Q -> or P Q.

Notation "P \/ Q" := (or P Q) : type_scope.

(** Consider the "type" of the constructor [or_introl]: *)

Check or_introl.
(* ===>  forall P Q : Prop, P -> P \/ Q *)

(** It takes 3 inputs, namely the propositions [P], [Q] and
    evidence of [P], and returns, as output, the evidence of [P \/ Q].
    Next, look at the type of [or_intror]: *)

Check or_intror.
(* ===>  forall P Q : Prop, Q -> P \/ Q *)

(** It is like [or_introl] but it requires evidence of [Q]
    instead of evidence of [P]. *)

(** Intuitively, there are two ways of giving evidence for [P \/ Q]:

    - give evidence for [P] (and say that it is [P] you are giving
      evidence for -- this is the function of the [or_introl]
      constructor), or

    - give evidence for [Q], tagged with the [or_intror]
      constructor. *)

(** *** *)
(** Since [P \/ Q] has two constructors, doing [destruct] on a
    hypothesis of type [P \/ Q] yields two subgoals. *)

Theorem or_commut : forall P Q : Prop,
  P \/ Q  -> Q \/ P.
Proof.
  intros P Q H.
  destruct H as [HP | HQ].
    Case "left". apply or_intror. apply HP.
    Case "right". apply or_introl. apply HQ.  Qed.

(** From here on, we'll use the shorthand tactics [left] and [right]
    in place of [apply or_introl] and [apply or_intror]. *)

Theorem or_commut' : forall P Q : Prop,
  P \/ Q  -> Q \/ P.
Proof.
  intros P Q H.
  destruct H as [HP | HQ].
    Case "left". right. apply HP.
    Case "right". left. apply HQ.  Qed.

Theorem or_distributes_over_and_1 : forall P Q R : Prop,
  P \/ (Q /\ R) -> (P \/ Q) /\ (P \/ R).
Proof.
  intros P Q R. intros H. destruct H as [HP | [HQ HR]].
    Case "left". split.
      SCase "left". left. apply HP.
      SCase "right". left. apply HP.
    Case "right". split.
      SCase "left". right. apply HQ.
      SCase "right". right. apply HR.  Qed.

(** **** Exercise: 2 stars (or_distributes_over_and_2)  *)
Theorem or_distributes_over_and_2 : forall P Q R : Prop,
  (P \/ Q) /\ (P \/ R) -> P \/ (Q /\ R).
Proof.
  intros P Q R H.
  inversion H.
  inversion H0.
    left. apply H2.
    inversion H1.
      left. apply H3.
      right. split.  apply H2.  apply H3.
Qed.
(** [] *)

(** **** Exercise: 1 star, optional (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.
  apply or_distributes_over_and_1.
  apply or_distributes_over_and_2.
Qed.
(** [] *)

(* ################################################### *)
(** ** Relating [/\] and [\/] with [andb] and [orb] *)

(** We've already seen several places where analogous structures
    can be found in Coq's computational ([Type]) and logical ([Prop])
    worlds.  Here is one more: the boolean operators [andb] and [orb]
    are clearly analogs of the logical connectives [/\] and [\/].
    This analogy can be made more precise by the following theorems,
    which show how to translate knowledge about [andb] and [orb]'s
    behaviors on certain inputs into propositional facts about those
    inputs. *)

Theorem andb_prop : forall b c,
  andb b c = true -> b = true /\ c = true.
Proof.
  (* WORKED IN CLASS *)
  intros b c H.
  destruct b.
    Case "b = true". destruct c.
      SCase "c = true". apply conj. reflexivity. reflexivity.
      SCase "c = false". inversion H.
    Case "b = false". inversion H.  Qed.

Theorem andb_true_intro : forall b c,
  b = true /\ c = true -> andb b c = true.
Proof.
  (* WORKED IN CLASS *)
  intros b c H.
  destruct H.
  rewrite H. rewrite H0. reflexivity. Qed.

(** **** Exercise: 2 stars, optional (andb_false)  *)
Theorem andb_false : forall b c,
  andb b c = false -> b = false \/ c = false.
Proof.
  intros b c H.
  destruct b.
  Case "b = true".
    simpl in H.
    right. apply H.
  Case "b = false".
    left. reflexivity.
Qed.

(** **** Exercise: 2 stars, optional (orb_false)  *)
Theorem orb_prop : forall b c,
  orb b c = true -> b = true \/ c = true.
Proof.
  intros b c H.
  destruct b.
  Case "b = true".
    left. reflexivity.
  Case "b = false".
    simpl in H.
    right.
    apply H.
Qed.

(** **** Exercise: 2 stars, optional (orb_false_elim)  *)
Theorem orb_false_elim : forall b c,
  orb b c = false -> b = false /\ c = false.
Proof.
  intros b c H.
  destruct b.
  Case "b = true".
    simpl in H.
    inversion H.
  Case "b = false".
    simpl in H.
    split.
      reflexivity.
      apply H.
Qed.
(** [] *)

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

(** Logical falsehood can be represented in Coq as an inductively
    defined proposition with no constructors. *)

Inductive False : Prop := .

(** Intuition: [False] is a proposition for which there is no way
    to give evidence. *)

(** Since [False] has no constructors, inverting an assumption
    of type [False] always yields zero subgoals, allowing us to
    immediately prove any goal. *)

Theorem False_implies_nonsense :
  False -> 2 + 2 = 5.
Proof.
  intros contra.
  inversion contra.  Qed.

(** How does this work? The [inversion] tactic breaks [contra] into
    each of its possible cases, and yields a subgoal for each case.
    As [contra] is evidence for [False], it has _no_ possible cases,
    hence, there are no possible subgoals and the proof is done. *)

(** *** *)
(** Conversely, the only way to prove [False] is if there is already
    something nonsensical or contradictory in the context: *)

Theorem nonsense_implies_False :
  2 + 2 = 5 -> False.
Proof.
  intros contra.
  inversion contra.  Qed.

(** Actually, since the proof of [False_implies_nonsense]
    doesn't actually have anything to do with the specific nonsensical
    thing being proved; it can easily be generalized to work for an
    arbitrary [P]: *)

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

(** The Latin _ex falso quodlibet_ means, literally, "from
    falsehood follows whatever you please."  This theorem is also
    known as the _principle of explosion_. *)

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

(** Since we have defined falsehood in Coq, one might wonder whether
    it is possible to define truth in the same way.  We can. *)

(** **** Exercise: 2 stars, advanced (True)  *)
(** Define [True] as another inductively defined proposition.  (The
    intution is that [True] should be a proposition for which it is
    trivial to give evidence.) *)

Inductive True : Prop :=
| truth : True.
(** [] *)

(** However, unlike [False], which we'll use extensively, [True] is
    used fairly rarely. By itself, it is trivial (and therefore
    uninteresting) to prove as a goal, and it carries no useful
    information as a hypothesis. But it can be useful when defining
    complex [Prop]s using conditionals, or as a parameter to
    higher-order [Prop]s. *)

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

(** The logical complement of a proposition [P] is written [not
    P] or, for shorthand, [~P]: *)

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

(** The intuition is that, if [P] is not true, then anything at
    all (even [False]) follows from assuming [P]. *)

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

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

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

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

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

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

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

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

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

(** **** Exercise: 2 stars (contrapositive)  *)
Theorem contrapositive : forall P Q : Prop,
  (P -> Q) -> (~Q -> ~P).
Proof.
  unfold not.
  intros P Q HPQ HQ HP.
  apply HQ.
  apply HPQ.
  apply HP.
Qed.
(** [] *)

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

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

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

(** *** Constructive logic *)
(** Note that some theorems that are true in classical logic are _not_
    provable in Coq's (constructive) logic.  E.g., let's look at how
    this proof gets stuck... *)

Theorem classic_double_neg : forall P : Prop,
  ~~P -> P.
Proof.
  (* WORKED IN CLASS *)
  intros P H. unfold not in H.
  (* But now what? There is no way to "invent" evidence for [~P]
     from evidence for [P]. *)
  Abort.

(** **** Exercise: 5 stars, advanced, optional (classical_axioms)  *)
(** For those who like a challenge, here is an exercise
    taken from the Coq'Art book (p. 123).  The following five
    statements are often considered as characterizations of
    classical logic (as opposed to constructive logic, which is
    what is "built in" to Coq).  We can't prove them in Coq, but
    we can consistently add any one of them as an unproven axiom
    if we wish to work in classical logic.  Prove that these five
    propositions are equivalent. *)

Definition peirce := forall P Q: Prop,
  ((P->Q)->P)->P.
Definition classic := forall P:Prop,
  ~~P -> P.
Definition excluded_middle := 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).

(* FIXME *)
(** [] *)

(** **** Exercise: 3 stars (excluded_middle_irrefutable)  *)
(** This theorem implies that it is always safe to add 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.
  unfold not.
  intros P H.
  apply H.
  right.
  intros HP.
  apply H.
  left.
  apply HP.
Qed.

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

(** Saying [x <> y] is just the same as saying [~(x = y)]. *)

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

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

Theorem not_false_then_true : forall b : bool,
  b <> false -> b = true.
Proof.
  intros b H. destruct b.
  Case "b = true". reflexivity.
  Case "b = false".
    unfold not in H.
    apply ex_falso_quodlibet.
    apply H. reflexivity.   Qed.

(** *** *)

(** *** *)

(** *** *)

(** *** *)

(** *** *)

(** **** Exercise: 2 stars (false_beq_nat)  *)
Theorem false_beq_nat : forall n m : nat,
     n <> m ->
     beq_nat n m = false.
Proof.
  induction n as [| n'].
  Case "n = 0".
    intros m H.
    destruct m as [| m'].
    SCase "m = 0".
      apply ex_falso_quodlibet.
      unfold not in H.
      apply H.
      reflexivity.
    SCase "m = S m'".
      reflexivity.
  Case "n = S n'".
    intros m H.
    destruct m as [| m'].
    SCase "m = 0".
      reflexivity.
    SCase "m = S m'".
      simpl.
      apply IHn'.
      unfold not.
      intros eq.
      apply H.
      rewrite -> eq.
      reflexivity.
Qed.
(** [] *)

(** **** Exercise: 2 stars, optional (beq_nat_false)  *)
Theorem beq_nat_false : forall n m,
  beq_nat n m = false -> n <> m.
Proof.
  induction n as [| n'].
  Case "n = 0".
    intros m H.
    destruct m as [| m'].
    SCase "m = 0".
      inversion H.
    SCase "m = S m'".
      unfold not.
      intros contra.
      inversion contra.
  Case "n = S n'".
    intros m H.
    destruct m as [| m'].
    SCase "m = 0".
      unfold not.
      intros contra.
      inversion contra.
    SCase "m = S m'".
      unfold not.
      intros eq.
      inversion eq.
      rewrite -> H1 in H.
      rewrite <- beq_nat_refl in H.
      inversion H.
Qed.
(** [] *)

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


Module Ex03_1.

(* Problem 11 *)
Require Import Arith.

Fixpoint sum_odd(n:nat) : nat :=
  match n with
  | O => O
  | S m => 1 + m + m + sum_odd m
  end.

Goal forall n, sum_odd n = n * n.
Proof.
  intro.
  induction n.
    simpl.
    reflexivity.

    simpl.
    rewrite -> IHn.
    SearchAbout (_ * S _).
    rewrite mult_succ_r.
    rewrite (plus_comm (n * n) n).
    rewrite (plus_assoc).
    reflexivity.
Qed.

End Ex03_1.


(* Proof by Induction *)

From LF Require Export Volume1_Ch1_Basics.

Theorem plus_n_O_firsttry : forall n:nat,
    n = n + 0.
Proof.
  intros n.
  simpl.
Abort.

Theorem plus_n_O_secondtry : forall n:nat,
    n = n + 0.
Proof.
  intros n. destruct n as [|n'] eqn:E.
  - reflexivity.
  - simpl.
Abort.

Theorem plus_n_O : forall n:nat,
    n = n + 0.
Proof.
  intros n. induction n as [|n' IHn'].
  - reflexivity.
  - simpl. rewrite <- IHn'. reflexivity. Qed.

Theorem minus_n_n : forall n,
    minus n n = 0.
Proof.
  intros n. induction n as [| n' IHn'].
  - simpl. reflexivity.
  - simpl. rewrite -> IHn'. reflexivity. Qed.

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

Theorem plus_n_Sm : forall n m : nat,
    S (n + m) = n + (S m).
Proof.
  intros n m.
  induction n.
  induction m.
  simpl. reflexivity.
  simpl. reflexivity.
  simpl. rewrite <- IHn.
  reflexivity.
Qed.

Theorem plus_comm : forall n m : nat,
    n + m = m + n.
Proof.
  intros.
  induction n.
  induction m.
  reflexivity.
  simpl.
  rewrite <- IHm.
  simpl.
  reflexivity.
  simpl.
  rewrite <- plus_n_Sm.
  rewrite <- IHn.
  reflexivity.
Qed.

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

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

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

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

Lemma neg_id : forall n,
    n = negb (negb n).
Proof.
  intros.
  induction n.
  reflexivity.
  reflexivity.
Qed.

Theorem evenb_S : forall n:nat,
    evenb (S n) = negb (evenb n).
Proof.
  intros.
  induction n.
  reflexivity.
  rewrite -> IHn.
  rewrite <- neg_id.
  reflexivity.
Qed.

(* Proofs Within Proofs *)

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

Theorem plus_rearrange_firsttry : forall n m p q : nat,
    (n + m) + (p + q) = (m + n) + (p + q).
Proof.
  intros.
  assert (H: n + m = m + n).
  { rewrite -> plus_comm.
    reflexivity. }
  rewrite <- H.
  reflexivity.
Qed.

(* Formal vs. Informal Proof *)

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

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

(* More Exercises *)

Theorem plus_swap : forall n m p : nat,
    n + (m + p) = m + (n + p).
Proof.
  intros.
  rewrite plus_assoc.
  assert (H: m + n = n + m).
  { rewrite plus_comm.
    reflexivity. }
  rewrite <- H.
  rewrite plus_assoc.
  reflexivity.
Qed.

Theorem mult_comm : forall m n : nat,
    m * n = n * m.
Proof.
Admitted.

Check leb.

Theorem leb_refl : forall n:nat,
    true = (n <=? n).
Proof.
Admitted.

Theorem zero_nbeq_S : forall n:nat,
    0 =? (S n) = false.
Proof.
Admitted.

Theorem andb_false_r : forall b:bool,
    andb b false = false.
Proof.
Admitted.

Theorem plus_ble_compat_l : forall n m p : nat,
    n <=? m = true -> (p + n) <=? (p + m) = true.
Proof.
Admitted.

Theorem S_nbeq_o : forall n:nat,
    (S n) =? 0 = false.
Proof.
Admitted.

Theorem mult_1_l : forall n:nat, 1 * n = n.
Proof.
Admitted.

Theorem mult_plus_distr_r : forall n m p : nat,
    (n + m) * p = (n * p) + (m * p).
Proof.
Admitted.

Theorem mult_assoc : forall n m p : nat,
    n * (m * p) = (n * m ) * p.
Proof.
Admitted.

Theorem eqb_refl : forall n:nat,
    true = (n =? n).
Proof.
Admitted.

Theorem plus_swap' : forall n m p : nat,
    n + (m + p) = m + (n + p).
Proof.
Admitted.

Fixpoint nat_to_bin (n:nat) : bin
. Admitted.

Theorem nat_bin_nat : forall n, bin_to_nat (nat_to_bin n) = n.
Proof.
Admitted.