(** * Testcases for [take.v]
Authors:
    - Lulof Pirée (1363638)
    - Cosmin Manea (1298542)
    - Jelle Wemmenhove
Creation date: 17 May 2021

Testcases for the [Take] tactic.
Tests pass if they can be run without unhandled errors.
--------------------------------------------------------------------------------

This file is part of Waterproof-lib.

Waterproof-lib is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Waterproof-lib is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Waterproof-lib.  If not, see <https://www.gnu.org/licenses/>.
*)

From Ltac2 Require Import Ltac2.
From Ltac2 Require Option.

Require Import Waterproof.message.

Require Import Waterproof.test_auxiliary.
Load take.

(** Test 0: This should work fine *)
Goal forall n : nat, n <= 2*n.
    Take n : nat.
Abort.

(** Test 1: Also this should work fine *)
Goal forall n : nat, n <= 2*n.
    Take x : nat.
Abort.

(** Test 2: This should raise an error, because the type does not match*)
Goal forall n : nat, n <= 2*n.
    assert_raises_error (fun () => Take n : bool).
Abort.

(** Test 3: This should raise an error,
    because [Take] solves forall-quatifiers *)
Goal exists n : nat, n <= 2*n.
    assert_raises_error (fun() => Take n : nat).
Abort.

(** Test 4: Multi argument testcase *)
Goal forall n : nat, forall m : nat, n + m <= 2*n + m.
    Take n : nat and m : nat.
Abort.

(** Test 5: Two sets of multiple variables of the same type. *)
Goal forall (n m k: nat) (b1 b2: bool), Nat.odd (n + m + k) = andb b1 b2.
    Take n, m, k : nat and b1, b2: bool.
Abort.

(** Test 5.1: Too few variables of the same type. *)
Goal forall (n m k: nat) (b1 b2: bool), Nat.odd (n + m + k) = andb b1 b2.
    Take n, m : nat.
Abort.

(** Test 5.2: Too many variables of the same type. *)
Goal forall (n m k: nat) (b1 b2: bool), Nat.odd (n + m + k) = andb b1 b2.
    Fail Take n, m, k, l : nat.
Abort.

(** Test 5.3: Too many variables (of the same type). *)
Goal forall (n m k: nat), Nat.odd (n + m + k) = false.
    Fail Take n, m, k, l : nat.
Abort.

(** Test 5.4: Too many variables (of different types). *)
Goal forall (n m k: nat), Nat.odd (n + m + k) = false.
    Fail Take n, m, k : nat and l : bool.
Abort.

(** Test 6: Two sets of multiple variables of the same type,
    but with different names *)
Goal forall (n m k: nat) (b1 b2: bool), Nat.odd (n + m + k) = andb b1 b2.
    Take a, b, c : nat and d, e: bool.
Abort.

(** Test 7: not allowed to introduce so many bools *)
Goal forall (n m k: nat) (b1 b2: bool), Nat.odd (n + m + k) = andb b1 b2.
    assert_raises_error (fun () => Take a, b, c, d, e: bool).
Abort.

(** Test 8: look how crazy many vars we can introduce*)
Goal forall (a b c d e f g: nat) (b1 b2: bool),
    Nat.odd (a + b + c + d + e + f + g) = andb b1 b2.
    Take a, b, c, d, e, f, g : nat and b1, b2: bool.
Abort.

(** Test 9: This should give a helpful error.
    (Note that two variables have the same name "a"!)

    Currently it raises "Internal (err:(a is already used.))"
    which seems clear enough :)
    *)
Goal forall (a b c d e f g: nat) (b1 b2: bool),
    Nat.odd (a + b + c + d + e + f + g) = andb b1 b2.
    assert_raises_error (fun() => Take a, b, c, d, e, f, g : nat and a, h: bool).
Abort.

(** Test 10: Two sets of multiple variables of the same type.
    But in a *different order* with different names.

    This should raise an error, as the order of introducing variables is different.
*)
Goal forall (n m k: nat)  (b1 b2: bool), Nat.odd (n + m + k) = andb b1 b2.
    assert_raises_error (fun() => Take y, u: bool and a, b, c : nat).
Abort.

(** Test 11: Attempting to show an implication should be rejected. *)
Goal (0 = 1) -> False.
  Fail Take p : (0 = 1).
Abort.

(** Test 12: Attempting to show a negation should be rejected. *)
Goal not (0 = 1).
  Fail Take p : (0 = 1).
Abort.

Require Import Reals.
(** Test 13: Introducing too many variables when
    the for all statement is followed by an implication.
*)
Goal forall (n : nat) (x y : R), (0 < x < y)%R -> (x^n < y^n)%R.
Proof.
  Fail Take n : nat and x, y, z : R.
  Fail Take n : nat and x, y : R and f : (R -> R).
  Take n : nat and x, y : R.
Abort.


(*|
##########################################################
Why would the tactic ``exact`` be complete for Coq proofs?
##########################################################

:Link: https://stackoverflow.com/q/46386249
|*)

(*|
Question
********

In the question `Is there a minimal complete set of tactics in Coq?
<https://stackoverflow.com/questions/32682544/is-there-a-minimal-complete-set-of-tactics-in-coq>`__,
the answers mentioned that ``exact`` would be enough to prove all
goals. Could someone explain and give an example? For example, how
would the goal ``A \/ B -> B \/ A`` with ``A``, ``B`` being ``Prop``
be proven by merely a bunch of ``exact``? If you have other better
examples, please don't hesitate to answer as well. The point is to
give some explanation on this issue and give a non-trivial example.
|*)

(*|
Answer (ejgallego)
******************

Recall that proofs in Coq are just terms in the (lambda) Calculus of
Inductive Constructions. Thus, your lemma is proven as:
|*)

Lemma test A B : A \/ B -> B \/ A.
Proof.
  exact (fun x => match x with
                  | or_introl p => or_intror p
                  | or_intror p => or_introl p
                  end).
Qed.

(*| which is almost the same as: |*)

Definition test' A B : A \/ B -> B \/ A :=
  fun x => match x with
           | or_introl p => or_intror p
           | or_intror p => or_introl p
           end.

(*|
[they differ in "opacity", don't worry about that, but Coq 8.8 will
likely support the ``Lemma foo := term`` syntax, closer to ``exact
term``.]

A more convenient tactic to build proofs is ``refine``, which allows
you to specify partial terms:
|*)

Lemma test'' A B : A \/ B -> B \/ A.
Proof.
  refine (fun x => _).
  refine (match x with | or_introl _ => _ | or_intror _ => _ end).
  - refine (or_intror a).
  - refine (or_introl b).
Qed.

(*|
In fact, ``refine`` is the basic tactic of the Coq proof engine;
``exact T`` basically executes ``refine T`` and checks that no goals
remain open.
|*)

(*|
Answer (Yves)
*************

Because of its theoretical foundations, the logic of Coq does not rely
on tactics as a primitive way to construct proofs. In fact, you could
use Coq and construct what would be considered as legitimate proofs
without ever using any tactic by using the idiom.
|*)

Lemma test3 A B : A \/ B -> B \/ A.
Proof.
  exact (fun x => match x with
                  | or_introl p => or_intror p
                  | or_intror p => or_introl p
                  end).
Qed.

(*|
So the question of "complete set of tactics" is not completely
meaningful.

On the other hand, tactics have been introduced to make the work
easier. So it is useful to know a *reasonably complete set of tactics*
that makes it possible to perform proofs without having a thorough
knowledge of Coq's theoretical foundations. My favorite set of tactics
is:

- ``intros``, ``apply`` (to deal with universal quantification and
  implication);
- ``destruct`` (to deal with logical connectives ``and``, also written
  ``/\`` and ``or``, also written ``\/``, and existential
  quantification, when in hypotheses);
- ``split`` to deal with ``and`` when in a goal's conclusion;
- ``left``, ``right`` to deal with ``or`` when in a goal's conclusion;
- ``exists`` to deal with an existential quantification when in the
  conclusion;
- ``assert`` to establish intermediate facts (not needed for
  completeness, but it really helps you write more readable proofs);
- ``exact`` and ``assumption`` when what you want to prove is really
  obvious from the context.

When reasoning about natural numbers, you will undoubtedly define
functions by pattern-matching and recursively and reason about their
behavior, so it is important to also know the tactics ``change``,
``simpl``, ``case``, ``case_eq``, ``injection``, and ``discriminate``.
Last, when you start making proofs that are advanced enough you need
tools for automated proofs like ``ring`` and ``lia``.
|*)


(*|
############################################
Using typeclass instances within typeclasses
############################################

:Link: https://stackoverflow.com/q/44638611
|*)

(*|
Question
********

I am trying to define two instances of a type class, one of which will
use the other's instance. However, unless I bind the function's name
outside of the second definition Coq is unable to determine it should
use the type class instance from ``bexp`` (take a look at the comment
for dirty hack). Is there a way to avoid this sort of hack in Coq?
|*)

Class Compilable (A : Type) := { compile : A -> bool }.

Definition bexp := False. (* .none *)
Inductive cexp : Type :=
| CAnd :  cexp -> cexp -> cexp
| COr  :  cexp -> cexp -> cexp
| CProp : bexp -> cexp.

Instance : Compilable bexp :=
  { compile :=
    fix compile b :=
      match b with
        (* elided *)
      end
  }.

Definition compile2 := compile.

Require Import Coq.Bool.Bool. (* .none *)
Instance: Compilable cexp :=
  { compile :=
    fix compile c :=
      match c with
      | CAnd x y => compile x && compile y
      | COr x y => compile x || compile y
      | CProp e => compile2 e (* <-- dirty hack *)
      end
  }.

(*|
Answer
******

This can be fixed if we replace ``compile`` with some other name
(``rec``) like so:
|*)

Instance : Compilable cexp :=
  { compile :=
    fix rec c :=
      match c with
      | CAnd x y => rec x && rec y
      | COr x y => rec x || rec y
      | CProp e => compile e
      end
  }.

(*|
In this comment the OP pointed out that Haskell easily deals with this
situation. To understand the reason why Coq does not do it let us take
a look at the type of ``compile``:
|*)

About compile. (* .unfold *)

(*|
We can see that Coq is more explicit about how typeclasses work. When
you call ``compile e`` Coq sort of inserts placeholders standing for
the implicit arguments like so ``@compile _ _ e`` (see `these slides
<https://www.cis.upenn.edu/~bcpierce/courses/670Fall12/slides.pdf>`__,
pages 21-25 for more detail). But with ``fix compile c`` you shadowed
the previous binding, hence the type error.
|*)


(*|
#########################################
Fail to use let-destruct for tuple in Coq
#########################################

:Link: https://stackoverflow.com/q/30197166
|*)

(*|
Question
********

I'm a new user for Coq. I have defined some functions:
|*)

Definition p (a : nat) := (a + 1, a + 2, a + 3).

Fail Definition q :=
  let (s, r, t) := p 1 in
  s + r + t.

Definition q' :=
  match p 1 with
  | (s, r, t) => s + r + t
  end.

(*|
I'm trying to destruct the result of ``p`` into a tuple
representation. However coqc complains on ``q``:
|*)

Fail Definition q :=
  let (s, r, t) := p 1 in
  s + r + t. (* .unfold .messages *)

(*|
while ``q'`` can pass the compilation. If I change ``p`` to return a
pair ``(a + 1, a + 2)``, the corresponding ``q`` and ``q'`` both work
fine.

**Why let-destruct only allows pair? Or have I made any error in
syntax?** I've checked with Coq manual but found no clue.
|*)

(*|
Answer
******

What is a bit confusing in Coq is that there are *two* different forms
of destructing let. The one you are looking for needs a quote before
the pattern:
|*)

Reset Initial. (* .none *)
Definition p (a : nat) := (a + 1, a + 2, a + 3).

Definition q :=
  let '(s, r, t) := p 1 in
  s + r + t.

(*|
Prefixing the pattern with a quote allows you to use nested patterns
and use user-defined notations in them. The form without the quote
only works with one-level patterns, and doesn't allow you to use
notations, or refer to constructor names in your patterns.

----

**Q:** So the 3-member tuple should be regarded as destructing again
on the first member of pair, then I have to use 'quote'?
|*)


(**
<<
                   Lecture 2: Fundamentals of Coq
      -------------------------------------------------------------
             Anders Mörtberg (Carnegie Mellon University)
>>
The Coq proof assistant is a programming language for writing
proofs: https://coq.inria.fr/

It has been around since 1984 and was initially implemented by Thierry
Coquand and his PhD supervisor Gérard Huet. The first version, called
CoC, implemented the Calculus of Constructions as presented in:

"The Calculus of Constructions".
Thierry Coquand, Gérard Huet,
Information and Computation (1988). 76(2–3): 95–120.

It was later extended to the Calculus of Inductive Constructions and
the name changed to Coq. These type theories are similar to Martin-Löf
type theory, but there are some differences that I won't have time to
get into. As Coq is supposed to be a programming language for
developing proofs all programs have to be terminating, so it can be
seen as a "total" programming language.

The Coq system has a very long and detailed reference manual:

https://coq.inria.fr/distrib/current/refman/

However, UniMath is only using a subset of Coq. So there is a lot of
information that is not relevant for UniMath development in there. The
fragment of Coq that UniMath uses consists of:

- Pi-types (with judgmental eta)
- Sigma-types (with judgmental eta/surjective pairing)
- Some inductive types: natural numbers, booleans, unit, empty
- Coproduct types
- A universe UU

The reason we only have one universe is that there is no support for
universe resizing axioms in Coq yet. Because of this we assume the
inconsistent rule UU : UU, but we are working on improving this so
that we can make use of Coq's universe consistency
checker. Furthermore, the reason UniMath only relies on this subset is
that everything in UniMath has to be motivated by the model in
simplicial sets.

In this lecture I will show some things than can be done in UniMath if
it is viewed as a programming language. In the later lectures we will
also see how to actually prove properties about definitions and
programs. The UniMath library can be found at:

https://github.com/UniMath/UniMath

This whole file is a Coq file and it hence has the file ending ".v" (v
for vernacular) and can be processed using:

- emacs with proof-general: https://proofgeneral.github.io/

- Coq IDE

I use emacs as it is also an extremely powerful text editor, but if
you never saw emacs before it might be quite overwhelming. If so I
think Coq IDE (interactive development environment) might be easier to
get started with and it has some features that emacs doesn't have so
some Coq experts use it as well. But in this talk I will use emacs.

In order to make emacs interact with Coq one should install Proof
General. This is an extension to emacs that enables it to interact
with a range of proof assistants, including Coq. For UniMath
development one should also install agda-mode in order to be able to
properly insert unicode characters.

The basic keybinding that I use when working in Proof General are:
<<
C-c C-n      -- Process one line
C-c C-u      -- Unprocess a line
C-c C-ENTER  -- Process the whole buffer until where the cursor is
C-c C-b      -- Process the whole file
C-c C-r      -- Unprocess (retract) the whole file
C-x C-c      -- Kill Coq process
>>
For more commands see:

https://proofgeneral.github.io/doc/master/userman/Basic-Script-Management/#Script-processing-commands
https://proofgeneral.github.io/doc/master/userman/Coq-Proof-General/#Coq_002dspecific-commands

Here C = CTRL and M = ALT on a standard emacs setup, so "C-c C-n"
means "press c while holding CTRL and then press n while still holding
CTRL" (note that the keybindings are case sensitive). ENTER is the
"return" key on my keyboard.

Various basic emacs commands:
<<
C-x C-f      -- Find file
C-x C-s      -- Save file
C-x C-x      -- Exit emacs
C-x b        -- Kill buffer
C-_          -- Undo
C-x 1        -- Maximize buffer
C-x 2        -- Split buffer horizontally
C-x 3        -- Split buffer vertically
M-%          -- Search-and-replace
M-;          -- Comment region
>>
For more commands see: https://www.gnu.org/software/emacs/ (or just
Google "emacs tutorial")

*)

(** To import a file write:
<<
Require Import Dir.Filename.
>>
*)

(**

This will import the file Filename in the directory Dir. This assumes
that directory Dir is visible to Coq, that is, in the list of
directories in which Coq looks for files to import. If you are working
in the UniMath directory this Coq will be able to find all of the
files in the UniMath library, but if you are in another directory you
need to the update your LoadPath by running (where "/path/to/UniMath/"
is replaced to the path where UniMath is located on your computer):
<<
Add LoadPath "/path/to/UniMath".
>>

Coq has a quite fancy module system which allows you to import and
export definitions in various complicated ways. We extremely rarely
use this in UniMath so you can safely ignore it for now. The only
thing you need to know is that there is an alternative to Require
Import:
<<
Require Export Dir.Filename.
>>
that is used in some files. This will make the file your working on
import all of the definitions in Dir.Filename and then export them
again so that any file that imports this file also gets the
definitions in Dir.Filename. I personally only use Require Import.

*)

Require Import UniMath.Foundations.Preamble.

(** * Definitions, lambda terms *)

(** The polymorphic identity function *)
Definition idfun : forall (A : UU), A -> A := fun (A : UU) (a : A) => a.

(** forall = Pi *)
(** fun = lambda *)

Check idfun.
Print idfun.

Definition idfun' (A : UU) (a : A) : A := a.

Check idfun'.
Print idfun'.

(** The constant function that returns its first argument *)
Definition const (A B : UU) (a : A) (b : B) : A := a.

(** The booleans come from the Coq standard library *)
About bool.
Print bool.

(** These are generated by true and false *)
Check true.
Check false.

(** They also come with an induction principle *)
About bool_rect.

(** Using this we can define a "recursion" principle *)
Definition ifbool (A : UU) (x y : A) : bool -> A :=
  bool_rect (fun _ : bool => A) x y.

(** We can define boolean negation: *)
Definition negbool : bool -> bool :=
  ifbool bool false true.

(** and compute with it *)
Eval compute in negbool true.

(** Coq supports many evaluation strategies, like cbn and cbv, for
details see:

https://coq.inria.fr/refman/tactics.html#Conversion-tactics

*)

(** boolean and *)
Definition andbool (b : bool) : bool -> bool :=
  ifbool (bool -> bool) (idfun bool) (const bool bool false) b.

Eval compute in andbool true true.
Eval compute in andbool true false.
Eval compute in andbool false true.
Eval compute in andbool false false.

(** Exercise: define boolean or:

Definition orbool : bool -> bool -> bool :=

*)

(** The natural numbers do no longer come from the Coq standard library (as of 2018) *)
About nat.
Print nat.

(** They are defined as Peano numbers, but we can use numerals *)
Check idfun nat 3.

(** This type also comes with an induction principle *)
Check nat_rect.

(** We can define the recursor: *)
Definition nat_rec (A : UU) (a : A) (f : nat -> A -> A) : nat -> A :=
  nat_rect (fun _ : nat => A) a f.

(**

This can be understood as a function defined by the following clauses:
<<
nat_rec m f 0 = m
nat_rec m f (S n) = f n (nat_rec m f n)
>>
*)

(** We can look at the normal form of the recursor by *)
Eval compute in nat_rec.

(** This contains a both a "fix" and "match _ with _". These primitives
of Coq are not allowed in UniMath. Instead we write everything using
recursors. The reason is that everything we write in UniMath has to be
justified by the simplicial set model and the situation for general
inductive types with match is unclear. *)

(** If you are used to regular programming languages it might seem very
strange to only program with the recursors, but it's actually not too
bad and we can do many programs quite directly *)

(** truncated predecessor *)
Definition pred : nat -> nat := nat_rec nat 0 (fun x _ => x).

Eval compute in pred 3.

(** We can test if a number is even by: *)
Definition even : nat -> bool := nat_rec bool true (fun _ b => negbool b).

Eval compute in even 3.
Eval compute in even 4.

(** Exercise: define a function odd that tests if a number is odd

Definition odd : nat -> bool :=

*)

(** Addition *)
Definition add (m : nat) : nat -> nat := nat_rec nat m (fun _ y => S y).

Eval compute in add 2 3.

(** We can define a nice notation for addition by: *)
Notation "x + y" := (add x y).

Eval compute in 4 + 9.

(** To find information about a notation we can write: *)
Locate "+".

(** Coq allows us to write very sophisticated notations, for details
see: https://coq.inria.fr/refman/syntax-extensions.html *)

(** In UniMath we use a lot of unicode. To input a unicode character
write \ and then the name of the symbol. For example \alpha gives
α. To get information about a unicode character, including how to
write it, put the cursor on top of the symbol and type: M-x
describe-char

In fact one can write:
<<
  λ x, e        instead of   fun x => e
  ∏ (x : A), T  instead of   forall (x : A), T
  A → B        instead of    A -> B
>>
*)

(** Iterate a function many times *)
Definition iter (A : UU) (a : A) (f : A → A) : nat → A :=
  nat_rec A a (λ _ y, f y).

(** A more fancy notation where the ̂ symbol is the unicode one (written \^ ) *)
Notation "f ̂ n" := (λ x, iter _ x f n) (at level 10).

Eval compute in pred̂2 5.

(** Exercise: define a notation "myif b then x else y" for "ifbool _ x y b"
and rewrite negbool and andbool using this notation.

Note that we cannot introduce the notation "if b then x else y" as
this is already used.

*)

Definition sub (m n : nat) : nat := pred̂n m.

Eval compute in sub 15 4.
Eval compute in sub 11 15.

(** Exercise: define addition using iter and S *)

Definition is_zero : nat → bool := nat_rec bool true (λ _ _, false).

Eval compute in is_zero 3.
Eval compute in is_zero 0.

Definition eqnat (m n : nat) : bool :=
  andbool (is_zero (sub m n)) (is_zero (sub n m)).

Notation "m == n" := (eqnat m n) (at level 30).

Eval compute in 5 == 5.
Eval compute in 5 == 3.
Eval compute in 9 == 5.

(** Exercises: define <, >, ≤, ≥ *)

(** Note that I could omit the A from the definition of f ̂ n and just
replace it by _. The reason is that Coq very often can infer what
arguments are so that we can omit them. *)

(** For example if I omit nat in *)
Check idfun _ 3.
(** Coq will figure it out for us *)

(** In fact we can tell Coq to always put _ for an argument by using
curly braces in the definition: *)

Definition idfun'' {A : UU} (a : A) : A := a.

Check idfun'' 3.
Check idfun'' true.

(** Such an argument is called an "implicit argument" and these are
very useful in order for us to get definitions that don't take too
many unnecessary arguments. However it is sometimes necessary to be
able to give all the arguments explicitly, this is achieved by putting
an @ in front of the function *)

Check @idfun'' nat 3.

(** * More inductive types *)

(** The inductive types we have seen so far come from the standard
library, but Coq allows us to define any inductive types we want. In
UniMath we don't allow this and the additional inductive types we have
are defined in Foundations. *)

(** One of these is the coproduct (disjoint union) of types *)
About coprod.
Print coprod.
Check inl.
Check inr.
Locate "ii1".
Locate "ii2".

Check (λ A B C : UU, @coprod_rect A B (λ _, C)).

Definition coprod_rec {A B C : UU} (f : A → C) (g : B → C) : A ⨿ B → C :=
  @coprod_rect A B (λ _, C) f g.

(** Define integers as a coproduct of nat with itself *)
(** +1 = inl 1 *)
(** 0 = inl 0 *)
(** -1 = inr 0 *)
(** Note that we get the negative numbers "off-by-one". We could also
have inl 0 and inr 0 representing zero, but this becomes quite strange
as we actually want to work with a quotient type where "inl 0 = inr
0". *)

Definition Z : UU := nat ⨿ nat.

Notation "⊹ x" := (inl x) (at level 20).
Notation "─ x" := (inr x) (at level 40).

Definition Z1 : Z := ⊹ 1.
Definition Z0 : Z := ⊹ 0.
Definition Zn3 : Z := ─ 2.

(** We can define a function by case on whether the number is ⊹ n or ─ n *)
Definition Zcase (A : UU) (fpos : nat → A) (fneg : nat → A) : Z → A :=
  coprod_rec fpos fneg.

Definition negate : Z → Z :=
  Zcase Z (λ x, ifbool Z Z0 (─ pred x) (is_zero x)) (λ x, ⊹ S x).

Eval compute in negate Z0.
Eval compute in negate Z1.
Eval compute in negate Zn3.

(** Exercise (harder): define addition for Z. *)

(** We also have two more inductive types: *)

(** The unit type with one inhabitant tt *)
About unit.
Print unit.

(** The empty type with no inhabitants *)
About empty.
Print empty.

(** * Sigma types *)

(** Sigma types are called total2 and this is the only example of a
Record in the whole UniMath library. The reason we use a record is so
that we can get surjective pairing. *)

About total2.
Print total2.

(** We have a notation ∑ (x : A), B for Sigma types *)

(** To form a pair use *)
Check tpair.
Locate ",,".

(** and to project out of a pair use *)
Check pr1.
Check pr2.

(** In PartA.v we define the direct product as a Sigma type *)
Definition dirprod (X Y : UU) := ∑ x : X, Y.

Notation "A × B" := (dirprod A B) (at level 75, right associativity) : type_scope.

Definition dirprod_pr1 {X Y : UU} : X × Y -> X := pr1.
Definition dirprod_pr2 {X Y : UU} : X × Y -> Y := pr2.

Definition dirprodf {X Y X' Y' : UU} (f : X -> Y) (f' : X' -> Y')
  (xx' : X × X') : Y × Y' := (f (pr1 xx'),,f' (pr2 xx')).

Definition swap {X Y : UU} (xy : X × Y) : Y × X :=
  dirprodf dirprod_pr2 dirprod_pr1 (xy,,xy).

(** We can define the type of even numbers as a Sigma-type: *)
Definition even_nat : UU :=
  ∑ (n : nat), ifbool _ unit empty (even n).

Definition test20 : even_nat := (20,,tt).

(** This Definition does not work *)
Fail Definition test3 : even_nat := (3,,tt).

(** Section's can be very useful to only write parameters that are used
many times once and for all. They are also useful for organizing your
files into logical sections. *)
Section curry.

Variables A B C : UU.

(** To make these arguments implicit use:
<<
Context {A B C : UU}.
>>
*)

Definition curry : (A × B → C) → (A → B → C) :=
  λ (f : A × B → C) (a : A) (b : B), f (a,,b).

Definition uncurry : (A → B → C) → (A × B → C) :=
  λ (f : A → B → C) (ab : A × B), f (pr1 ab) (pr2 ab).

End curry.

Check curry.
Print curry.
Check uncurry.
Print uncurry.

(** These were all the basics for interacting with Coq in this
lecture. The UniMath library is a very large library and so far we
have only seen parts of Preamble.v. When writing programs and proofs
in UniMath it is very important to be able to search the library so
that you don't do something someone else has already done. *)

(** To find everything about nat type: *)
Search nat.

(** To search for all lemmas involving the pattern *)
(* Search _ (_ -> _ <= _). *)

(** To find information about a notation *)
Locate "+".

(** More information about searching can be found in the reference
manual. *)

(**

One can also generate HTML documentation for UniMath that is a bit
easier to browse than reading text files. This is done by typing:
<<
> make html
>>
You can then open this documentation in your browser:
<<
> firefox html/toc.html
>>
In order to have your comments display properly in the HTML use the
following format:

(** This comment will be displayed *)
(** * This will be displayed as a section header *)
(** ** This will be a subsection *)

For more details see:

https://coq.inria.fr/distrib/current/refman/tools.html#coqdoc

*)

(**

The UniMath library is organized into a collection of packages that
can be found in the UniMath folder. The library is then compiled using
a Makefile which specifies in which order things has to be compiled
and how they should be compiled.

In order to have your file get compiled when you type "make" add it to
the .package/files file of the package where it belongs. See for
example: UniMath/UniMath/Foundations/.package/files

If you're working on a new package then you need to add it to the
Makefile by writing:

PACKAGES += MyPackage

*)

               (*⋆⋆⋆⋆⋆⋆ THE END ⋆⋆⋆⋆⋆⋆*)


(** * UseTactics: Tactic Library for Coq: A Gentle Introduction *)

(* Chapter written and maintained by Arthur Chargueraud *)

(** Coq comes with a set of builtin tactics, such as [reflexivity],
    [intros], [inversion] and so on. While it is possible to conduct
    proofs using only those tactics, you can significantly increase
    your productivity by working with a set of more powerful tactics.
    This chapter describes a number of such useful tactics, which, for
    various reasons, are not yet available by default in Coq.  These
    tactics are defined in the [LibTactics.v] file. *)

Set Warnings "-notation-overridden,-parsing".

From Coq Require Import Arith.Arith.

From PLF Require Maps.
From PLF Require Stlc.
From PLF Require Types.
From PLF Require Smallstep.
From PLF Require LibTactics.
From PLF Require Equiv.
From PLF Require References.
From PLF Require Hoare.
From PLF Require Sub.

Import LibTactics.

(** Remark: SSReflect is another package providing powerful tactics.
    The library "LibTactics" differs from "SSReflect" in two respects:
        - "SSReflect" was primarily developed for proving mathematical
          theorems, whereas "LibTactics" was primarily developed for proving
          theorems on programming languages. In particular, "LibTactics"
          provides a number of useful tactics that have no counterpart in the
          "SSReflect" package.
        - "SSReflect" entirely rethinks the presentation of tactics,
          whereas "LibTactics" mostly stick to the traditional
          presentation of Coq tactics, simply providing a number of
          additional tactics.  For this reason, "LibTactics" is
          probably easier to get started with than "SSReflect". *)

(** This chapter is a tutorial focusing on the most useful features
    from the "LibTactics" library. It does not aim at presenting all
    the features of "LibTactics". The detailed specification of tactics
    can be found in the source file [LibTactics.v]. Further documentation
    as well as demos can be found at https://www.chargueraud.org/softs/tlc/. *)

(** In this tutorial, tactics are presented using examples taken from
    the core chapters of the "Software Foundations" course. To illustrate
    the various ways in which a given tactic can be used, we use a
    tactic that duplicates a given goal. More precisely, [dup] produces
    two copies of the current goal, and [dup n] produces [n] copies of it. *)

(* ################################################################# *)
(** * Tactics for Naming and Performing Inversion *)

(** This section presents the following tactics:
    - [introv], for naming hypotheses more efficiently,
    - [inverts], for improving the [inversion] tactic. *)

(* ================================================================= *)
(** ** The Tactic [introv] *)

Module IntrovExamples.

Import Maps.
Import Imp.
Import Equiv.
Import Stlc.

(** The tactic [introv] allows to automatically introduce the
    variables of a theorem and explicitly name the hypotheses
    involved. In the example shown next, the variables [c],
    [st], [st1] and [st2] involved in the statement of determinism
    need not be named explicitly, because their name where already
    given in the statement of the lemma. On the contrary, it is
    useful to provide names for the two hypotheses, which we
    name [E1] and [E2], respectively. *)

Theorem ceval_deterministic: forall c st st1 st2,
  st =[ c ]=> st1 ->
  st =[ c ]=> st2 ->
  st1 = st2.
Proof.
  introv E1 E2. (* was [intros c st st1 st2 E1 E2] *)
Abort.

(** When there is no hypothesis to be named, one can call
    [introv] without any argument. *)

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.
  introv. (* was [intros X P Q] *)
Abort.

(** The tactic [introv] also applies to statements in which
    [forall] and [->] are interleaved. *)

Theorem ceval_deterministic': forall c st st1,
  (st =[ c ]=> st1) ->
  forall st2,
  (st =[ c ]=> st2) ->
  st1 = st2.
Proof.
  introv E1 E2. (* was [intros c st st1 E1 st2 E2] *)
Abort.

(** Like the arguments of [intros], the arguments of [introv]
    can be structured patterns. *)

Theorem exists_impl: forall X (P : X -> Prop) (Q : Prop) (R : Prop),
  (forall x, P x -> Q) ->
  ((exists x, P x) -> Q).
Proof.
  introv [x H2]. eauto.
  (* same as [intros X P Q R H1 [x H2].], which is itself short
     for [intros X P Q R H1 H2. destruct H2 as [x H2].] *)
Qed.

(** Remark: the tactic [introv] works even when definitions
    need to be unfolded in order to reveal hypotheses. *)

End IntrovExamples.

(* ================================================================= *)
(** ** The Tactic [inverts] *)

Module InvertsExamples.
Import Maps.
Import Imp.
Import Equiv.

(** The [inversion] tactic of Coq is not very satisfying for
    three reasons. First, it produces a bunch of equalities
    which one typically wants to substitute away, using [subst].
    Second, it introduces meaningless names for hypotheses.
    Third, a call to [inversion H] does not remove [H] from the
    context, even though in most cases an hypothesis is no longer
    needed after being inverted. The tactic [inverts] address all
    of these three issues. It is intented to be used in place of
    the tactic [inversion]. *)

(** The following example illustrates how the tactic [inverts H]
    behaves mostly like [inversion H] except that it performs
    some substitutions in order to eliminate the trivial equalities
    that are being produced by [inversion]. *)

Theorem skip_left: forall c,
  cequiv <{skip; c}> c.
Proof.
  introv. split; intros H.
  dup. (* duplicate the goal for comparison *)
  (* was... *)
  - inversion H. subst. inversion H2. subst. assumption.
  (* now... *)
  - inverts H. inverts H2. assumption.
Abort.

(** A slightly more interesting example appears next. *)

Theorem ceval_deterministic: forall c st st1 st2,
  st =[ c ]=> st1  ->
  st =[ c ]=> st2 ->
  st1 = st2.
Proof.
  introv E1 E2. generalize dependent st2.
  induction E1; intros st2 E2.
  admit. admit. (* skip some basic cases *)
  dup. (* duplicate the goal for comparison *)
  (* was: *)
  - inversion E2. subst. admit.
  (* now: *)
  - inverts E2. admit.
Abort.

(** The tactic [inverts H as.] is like [inverts H] except that the
    variables and hypotheses being produced are placed in the goal
    rather than in the context. This strategy allows naming those
    new variables and hypotheses explicitly, using either [intros]
    or [introv]. *)

Theorem ceval_deterministic': forall c st st1 st2,
  st =[ c ]=> st1  ->
  st =[ c ]=> st2 ->
  st1 = st2.
Proof.
  introv E1 E2. generalize dependent st2.
  induction E1; intros st2 E2;
    inverts E2 as.
  - (* E_Skip *) reflexivity.
  - (* E_Ass *)
    (* Observe that the variable [n] is not automatically
       substituted because, contrary to [inversion E2; subst],
       the tactic [inverts E2] does not substitute the equalities
       that exist before running the inversion. *)
    (* new: *) subst n.
    reflexivity.
  - (* E_Seq *)
    (* Here, the newly created variables can be introduced
       using intros, so they can be assigned meaningful names,
       for example [st3] instead of [st'0]. *)
    (* new: *) intros st3 Red1 Red2.
    assert (st' = st3) as EQ1.
    { (* Proof of assertion *) apply IHE1_1; assumption. }
    subst st3.
    apply IHE1_2. assumption.
  (* E_IfTrue *)
  - (* b1 reduces to true *)
    (* In an easy case like this one, there is no need to
       provide meaningful names, so we can just use [intros] *)
    (* new: *) intros.
    apply IHE1. assumption.
  - (* b1 reduces to false (contradiction) *)
    (* new: *) intros.
    rewrite H in H5. inversion H5.
  (* The other cases are similiar *)
Abort.

(** In the particular case where a call to [inversion] produces
    a single subgoal, one can use the syntax [inverts H as H1 H2 H3]
    for calling [inverts] and naming the new hypotheses [H1], [H2]
    and [H3]. In other words, the tactic [inverts H as H1 H2 H3] is
    equivalent to [inverts H as; introv H1 H2 H3]. An example follows. *)

Theorem skip_left': forall c,
  cequiv <{ skip ; c}> c.
Proof.
  introv. split; intros H.
  inverts H as U V. (* new hypotheses are named [U] and [V] *)
  inverts U. assumption.
Abort.

End InvertsExamples.

(** A more involved example appears next. In particular, this example
    shows that the name of the hypothesis being inverted can be reused. *)

Module InvertsExamples1.
Import Types.
Import Stlc.
Import STLC.
Import Maps.

Example typing_nonexample_1 :
  ~ exists T,
      empty |-
        \x:Bool,
            \y:Bool,
               (x y) \in
        T.
Proof.
  dup 3.

  (* The old proof: *)
  -  intros Hc. destruct Hc as [T Hc].
  inversion Hc; subst; clear Hc.
  inversion H4; subst; clear H4.
  inversion H5; subst; clear H5 H4.
  inversion H2; subst; clear H2.
  discriminate H1.

  (* The new proof: *)
  - intros C. destruct C.
  inverts H as H1.
  inverts H1 as H2.
  inverts H2 as H3 H4.
  inverts H3 as H5.
  inverts H5.

  (* The new proof, alternative: *)
  - intros C. destruct C.
  inverts H as H.
  inverts H as H.
  inverts H as H1 H2.
  inverts H1 as H1.
  inverts H1.
Qed.

End InvertsExamples1.

(** Note: in the rare cases where one needs to perform an inversion
    on an hypothesis [H] without clearing [H] from the context,
    one can use the tactic [inverts keep H], where the keyword [keep]
    indicates that the hypothesis should be kept in the context. *)

(* ################################################################# *)
(** * Tactics for N-ary Connectives *)

(** Because Coq encodes conjunctions and disjunctions using binary
    constructors [/\] and [\/], working with a conjunction or a
    disjunction of [N] facts can sometimes be quite cumbursome.
    For this reason, "LibTactics" provides tactics offering direct
    support for n-ary conjunctions and disjunctions. It also provides
    direct support for n-ary existententials. *)

(** This section presents the following tactics:
    - [splits] for decomposing n-ary conjunctions,
    - [branch] for decomposing n-ary disjunctions *)

Module NaryExamples.
  Import References.
  Import Smallstep.
  Import STLCRef.

(* ================================================================= *)
(** ** The Tactic [splits] *)

(** The tactic [splits] applies to a goal made of a conjunction
    of [n] propositions and it produces [n] subgoals. For example,
    it decomposes the goal [G1 /\ G2 /\ G3] into the three subgoals
    [G1], [G2] and [G3]. *)

Lemma demo_splits : forall n m,
  n > 0 /\ n < m /\ m < n+10 /\ m <> 3.
Proof.
  intros. splits.
Abort.

(* ================================================================= *)
(** ** The Tactic [branch] *)

(** The tactic [branch k] can be used to prove a n-ary disjunction.
    For example, if the goal takes the form [G1 \/ G2 \/ G3],
    the tactic [branch 2] leaves only [G2] as subgoal. The following
    example illustrates the behavior of the [branch] tactic. *)

Lemma demo_branch : forall n m,
  n < m \/ n = m \/ m < n.
Proof.
  intros.
  destruct (lt_eq_lt_dec n m) as [ [H1|H2]|H3].
  - branch 1. apply H1.
  - branch 2. apply H2.
  - branch 3. apply H3.
Qed.

End NaryExamples.

(* ################################################################# *)
(** * Tactics for Working with Equality *)

(** One of the major weakness of Coq compared with other interactive
    proof assistants is its relatively poor support for reasoning
    with equalities. The tactics described next aims at simplifying
    pieces of proof scripts manipulating equalities. *)

(** This section presents the following tactics:
    - [asserts_rewrite] for introducing an equality to rewrite with,
    - [cuts_rewrite], which is similar except that its subgoals are swapped,
    - [substs] for improving the [subst] tactic,
    - [fequals] for improving the [f_equal] tactic,
    - [applys_eq] for proving [P x y] using an hypothesis [P x z],
      automatically producing an equality [y = z] as subgoal. *)

Module EqualityExamples.

(* ================================================================= *)
(** ** The Tactics [asserts_rewrite] and [cuts_rewrite] *)

(** The tactic [asserts_rewrite (E1 = E2)] replaces [E1] with [E2] in
    the goal, and produces the goal [E1 = E2]. *)

Theorem mult_0_plus : forall n m : nat,
  (0 + n) * m = n * m.
Proof.
  dup.
  (* The old proof: *)
  intros n m.
  assert (H: 0 + n = n). reflexivity. rewrite -> H.
  reflexivity.

  (* The new proof: *)
  intros n m.
  asserts_rewrite (0 + n = n).
    reflexivity. (* subgoal [0+n = n] *)
    reflexivity. (* subgoal [n*m = m*n] *)
Qed.

(** Remark: the syntax [asserts_rewrite (E1 = E2) in H] allows
     rewriting in the hypothesis [H] rather than in the goal. *)

(** The tactic [cuts_rewrite (E1 = E2)] is like
    [asserts_rewrite (E1 = E2)], except that the equality [E1 = E2]
    appears as first subgoal. *)

Theorem mult_0_plus' : forall n m : nat,
  (0 + n) * m = n * m.
Proof.
  intros n m.
  cuts_rewrite (0 + n = n).
    reflexivity. (* subgoal [n*m = m*n] *)
    reflexivity. (* subgoal [0+n = n] *)
Qed.

(** More generally, the tactics [asserts_rewrite] and [cuts_rewrite]
    can be provided a lemma as argument. For example, one can write
    [asserts_rewrite (forall a b, a*(S b) = a*b+a)].
    This formulation is useful when [a] and [b] are big terms,
    since there is no need to repeat their statements. *)

Theorem mult_0_plus'' : forall u v w x y z: nat,
  (u + v) * (S (w * x + y)) = z.
Proof.
  intros. asserts_rewrite (forall a b, a*(S b) = a*b+a).
    (* first subgoal:  [forall a b, a*(S b) = a*b+a] *)
    (* second subgoal: [(u + v) * (w * x + y) + (u + v) = z] *)
Abort.

(* ================================================================= *)
(** ** The Tactic [substs] *)

(** The tactic [substs] is similar to [subst] except that it
    does not fail when the goal contains "circular equalities",
    such as [x = f x]. *)

Lemma demo_substs : forall x y (f:nat->nat),
  x = f x ->
  y = x ->
  y = f x.
Proof.
  intros. substs. (* the tactic [subst] would fail here *)
  assumption.
Qed.

(* ================================================================= *)
(** ** The Tactic [fequals] *)

(** The tactic [fequals] is similar to [f_equal] except that it
    directly discharges all the trivial subgoals produced. Moreover,
    the tactic [fequals] features an enhanced treatment of equalities
    between tuples. *)

Lemma demo_fequals : forall (a b c d e : nat) (f : nat->nat->nat->nat->nat),
  a = 1 ->
  b = e ->
  e = 2 ->
  f a b c d = f 1 2 c 4.
Proof.
  intros. fequals.
  (* subgoals [a = 1], [b = 2] and [c = c] are proved, [d = 4] remains *)
Abort.

(* ================================================================= *)
(** ** The Tactic [applys_eq] *)

(** The tactic [applys_eq] is a variant of [eapply] that introduces
    equalities for subterms that do not unify. For example, assume
    the goal is the proposition [P x y] and assume we have the
    assumption [H] asserting that [P x z] holds. We know that we can
    prove [y] to be equal to [z]. So, we could call the tactic
    [assert_rewrite (y = z)] and change the goal to [P x z], but
    this would require copy-pasting the values of [y] and [z].
    With the tactic [applys_eq], we can call [applys_eq H 1], which
    proves the goal and leaves only the subgoal [y = z]. The value [1]
    given as argument to [applys_eq] indicates that we want an equality
    to be introduced for the first argument of [P x y] counting from
    the right. The three following examples illustrate the behavior
    of a call to [applys_eq H 1], a call to [applys_eq H 2], and a
    call to [applys_eq H 1 2]. *)

Axiom big_expression_using : nat->nat. (* Used in the example *)

Lemma demo_applys_eq_1 : forall (P:nat->nat->Prop) x y z,
  P x (big_expression_using z) ->
  P x (big_expression_using y).
Proof.
  introv H. dup.

  (* The old proof: *)
  assert (Eq: big_expression_using y = big_expression_using z).
    admit. (* Assume we can prove this equality somehow. *)
  rewrite Eq. apply H.

  (* The new proof: *)
  applys_eq H 1.
    admit. (* Assume we can prove this equality somehow. *)
Abort.

(** If the mismatch was on the first argument of [P] instead of
    the second, we would have written [applys_eq H 2]. Recall
    that the occurences are counted from the right. *)

Lemma demo_applys_eq_2 : forall (P:nat->nat->Prop) x y z,
  P (big_expression_using z) x ->
  P (big_expression_using y) x.
Proof.
  introv H. applys_eq H 2.
Abort.

(** When we have a mismatch on two arguments, we want to produce
    two equalities. To achieve this, we may call [applys_eq H 1 2].
    More generally, the tactic [applys_eq] expects a lemma and a
    sequence of natural numbers as arguments. *)

Lemma demo_applys_eq_3 : forall (P:nat->nat->Prop) x1 x2 y1 y2,
  P (big_expression_using x2) (big_expression_using y2) ->
  P (big_expression_using x1) (big_expression_using y1).
Proof.
  introv H. applys_eq H 1 2.
  (* produces two subgoals:
     [big_expression_using x1 = big_expression_using x2]
     [big_expression_using y1 = big_expression_using y2] *)
Abort.

End EqualityExamples.

(* ################################################################# *)
(** * Some Convenient Shorthands *)

(** This section of the tutorial introduces a few tactics
    that help make proof scripts shorter and more readable:
    - [unfolds] (without argument) for unfolding the head definition,
    - [false] for replacing the goal with [False],
    - [gen] as a shorthand for [dependent generalize],
    - [admits] for naming an addmited fact,
    - [admit_rewrite] for rewriting using an admitted equality,
    - [admit_goal] to set up a proof by induction by skipping the
      justification that some order decreases,
    - [sort] for re-ordering the proof context by moving moving all
      propositions at the bottom. *)

(* ================================================================= *)
(** ** The Tactic [unfolds] *)

Module UnfoldsExample.
  Import Hoare.

(** The tactic [unfolds] (without any argument) unfolds the
    head constant of the goal. This tactic saves the need to
    name the constant explicitly. *)

Lemma bexp_eval_true : forall b st,
  beval st b = true ->
  (bassn b) st.
Proof.
  intros b st Hbe. dup.

  (* The old proof: *)
  unfold bassn. assumption.

  (* The new proof: *)
  unfolds. assumption.
Qed.

(** Remark: contrary to the tactic [hnf], which may unfold several
    constants, [unfolds] performs only a single step of unfolding. *)

(** Remark: the tactic [unfolds in H] can be used to unfold the
    head definition of the hypothesis [H]. *)

End UnfoldsExample.

(* ================================================================= *)
(** ** The Tactics [false] and [tryfalse] *)

(** The tactic [false] can be used to replace any goal with [False].
    In short, it is a shorthand for [exfalso].
    Moreover, [false] proves the goal if it contains an absurd
    assumption, such as [False] or [0 = S n], or if it contains
    contradictory assumptions, such as [x = true] and [x = false]. *)

Lemma demo_false : forall n,
  S n = 1 ->
  n = 0.
Proof.
  intros. destruct n. reflexivity. false.
Qed.

(** The tactic [false] can be given an argument: [false H] replace
    the goals with [False] and then applies [H]. *)

Lemma demo_false_arg :
  (forall n, n < 0 -> False) ->
  3 < 0 ->
  4 < 0.
Proof.
  intros H L. false H. apply L.
Qed.

(** The tactic [tryfalse] is a shorthand for [try solve [false]]:
    it tries to find a contradiction in the goal. The tactic
    [tryfalse] is generally called after a case analysis. *)

Lemma demo_tryfalse : forall n,
  S n = 1 ->
  n = 0.
Proof.
  intros. destruct n; tryfalse. reflexivity.
Qed.

(* ================================================================= *)
(** ** The Tactic [gen] *)

(** The tactic [gen] is a shortand for [generalize dependent]
    that accepts several arguments at once. An invocation of
    this tactic takes the form [gen x y z]. *)

Module GenExample.
  Import Stlc.
  Import STLC.
  Import Maps.

Lemma substitution_preserves_typing : forall Gamma x U t v T,
  x |-> U ; Gamma |- t \in T ->
  empty |- v \in U   ->
  Gamma |- [x:=v]t \in T.
Proof.
  dup.

  (* The old proof: *)
  - intros Gamma x U t v T Ht Hv.
  generalize dependent Gamma. generalize dependent T.
  induction t; intros T Gamma H;
    inversion H; clear H; subst; simpl; eauto.
  admit. admit.

  (* The new proof: *)
  - introv Ht Hv. gen Gamma T.
  induction t; intros S Gamma H;
    inversion H; clear H; subst; simpl; eauto.
  admit. admit.
Abort.

End GenExample.

(* ================================================================= *)
(** ** The Tactics [admits], [admit_rewrite] and [admit_goal] *)

(** Temporarily admitting a given subgoal is very useful when
    constructing proofs. Several tactics are provided as
    useful wrappers around the builtin [admit] tactic. *)

Module SkipExample.

(** The tactic [admits H: P] adds the hypothesis [H: P] to the context,
    without checking whether the proposition [P] is true.
    It is useful for exploiting a fact and postponing its proof.
    Note: [admits H: P] is simply a shorthand for [assert (H:P). admit.] *)

Theorem demo_admits : True.
Proof.
  admits H: (forall n m : nat, (0 + n) * m = n * m).
Abort.

(** The tactic [admit_rewrite (E1 = E2)] replaces [E1] with [E2] in
    the goal, without checking that [E1] is actually equal to [E2]. *)

Theorem mult_plus_0 : forall n m : nat,
  (n + 0) * m = n * m.
Proof.
  dup 3.

  (* The old proof: *)
  intros n m.
  assert (H: n + 0 = n). admit. rewrite -> H. clear H.
  reflexivity.

  (* The new proof: *)
  intros n m.
  admit_rewrite (n + 0 = n).
  reflexivity.

  (* Remark: [admit_rewrite] can be given a lemma statement as argument,
   like [asserts_rewrite]. For example: *)
  intros n m.
  admit_rewrite (forall a, a + 0 = a).
  reflexivity.
Admitted.

(** The tactic [admit_goal] adds the current goal as hypothesis.
    This cheat is useful to set up the structure of a proof by
    induction without having to worry about the induction hypothesis
    being applied only to smaller arguments. Using [skip_goal], one
    can construct a proof in two steps: first, check that the main
    arguments go through without waisting time on fixing the details
    of the induction hypotheses; then, focus on fixing the invokations
    of the induction hypothesis. *)

Import Imp.

Theorem ceval_deterministic: forall c st st1 st2,
  st =[ c ]=> st1 ->
  st =[ c ]=> st2 ->
  st1 = st2.
Proof.
  (* The tactic [admit_goal] creates an hypothesis called [IH]
     asserting that the statment of [ceval_deterministic] is true. *)
  admit_goal.
  (* Of course, if we call [assumption] here, then the goal is solved
     right away, but the point is to do the proof and use [IH]
     only at the places where we need an induction hypothesis. *)
  introv E1 E2. gen st2.
  induction E1; introv E2; inverts E2 as.
  - (* E_Skip *) reflexivity.
  - (* E_Ass *)
    subst n.
    reflexivity.
  - (* E_Seq *)
    intros st3 Red1 Red2.
    assert (st' = st3) as EQ1.
    { (* Proof of assertion *)
      (* was: [apply IHE1_1; assumption.] *)
      (* new: *) eapply IH. eapply E1_1. eapply Red1. }
    subst st3.
    (* was: apply IHE1_2. assumption.] *)
    (* new: *) eapply IH. eapply E1_2. eapply Red2.
  (* The other cases are similiar. *)
Abort.

End SkipExample.

(* ================================================================= *)
(** ** The Tactic [sort] *)

Module SortExamples.
  Import Imp.

(** The tactic [sort] reorganizes the proof context by placing
    all the variables at the top and all the hypotheses at the
    bottom, thereby making the proof context more readable. *)

Theorem ceval_deterministic: forall c st st1 st2,
  st =[ c ]=> st1 ->
  st =[ c ]=> st2 ->
  st1 = st2.
Proof.
  intros c st st1 st2 E1 E2.
  generalize dependent st2.
  induction E1; intros st2 E2; inverts E2.
  admit. admit. (* Skipping some trivial cases *)
  sort. (* Observe how the context is reorganized *)
Abort.

End SortExamples.

(* ################################################################# *)
(** * Tactics for Advanced Lemma Instantiation *)

(** This last section describes a mechanism for instantiating a lemma
    by providing some of its arguments and leaving other implicit.
    Variables whose instantiation is not provided are turned into
    existentential variables, and facts whose instantiation is not
    provided are turned into subgoals.

    Remark: this instantion mechanism goes far beyond the abilities of
    the "Implicit Arguments" mechanism. The point of the instantiation
    mechanism described in this section is that you will no longer need
    to spend time figuring out how many underscore symbols you need to
    write. *)

(** In this section, we'll use a useful feature of Coq for decomposing
    conjunctions and existentials. In short, a tactic like [intros] or
    [destruct] can be provided with a pattern [(H1 & H2 & H3 & H4 & H5)],
    which is a shorthand for [[H1 [H2 [H3 [H4 H5]]]]]]. For example,
    [destruct (H _ _ _ Htypt) as [T [Hctx Hsub]].] can be rewritten in
    the form [destruct (H _ _ _ Htypt) as (T & Hctx & Hsub).] *)

(* ================================================================= *)
(** ** Working of [lets] *)

(** When we have a lemma (or an assumption) that we want to exploit,
    we often need to explicitly provide arguments to this lemma,
    writing something like:
    [destruct (typing_inversion_var _ _ _ Htypt) as (T & Hctx & Hsub).]
    The need to write several times the "underscore" symbol is tedious.
    Not only we need to figure out how many of them to write down, but
    it also makes the proof scripts look prettly ugly. With the tactic
    [lets], one can simply write:
    [lets (T & Hctx & Hsub): typing_inversion_var Htypt.]

    In short, this tactic [lets] allows to specialize a lemma on a bunch
    of variables and hypotheses. The syntax is [lets I: E0 E1 .. EN],
    for building an hypothesis named [I] by applying the fact [E0] to the
    arguments [E1] to [EN]. Not all the arguments need to be provided,
    however the arguments that are provided need to be provided in the
    correct order. The tactic relies on a first-match algorithm based on
    types in order to figure out how the to instantiate the lemma with
    the arguments provided. *)

Module ExamplesLets.
  Import Sub.

(* To illustrate the working of [lets], assume that we want to
   exploit the following lemma. *)

Import Maps.
Import Sub.
Import String.

Axiom typing_inversion_var : forall Gamma (x:string) T,
  Gamma |- x \in T ->
  exists S,
    Gamma x = Some S /\ S <: T.

(** First, assume we have an assumption [H] with the type of the form
    [has_type G (var x) T]. We can obtain the conclusion of the
    lemma [typing_inversion_var] by invoking the tactics
    [lets K: typing_inversion_var H], as shown next. *)

Lemma demo_lets_1 : forall (G:context) (x:string) (T:ty),
  G |- x \in T ->
  True.
Proof.
  intros G x T H. dup.

  (* step-by-step: *)
  lets K: typing_inversion_var H.
  destruct K as (S & Eq & Sub).
  admit.

  (* all-at-once: *)
  lets (S & Eq & Sub): typing_inversion_var H.
  admit.
Abort.

(** Assume now that we know the values of [G], [x] and [T] and we
    want to obtain [S], and have [has_type G (var x) T] be produced
    as a subgoal. To indicate that we want all the remaining arguments
    of [typing_inversion_var] to be produced as subgoals, we use a
    triple-underscore symbol [___]. (We'll later introduce a shorthand
    tactic called [forwards] to avoid writing triple underscores.) *)

Lemma demo_lets_2 : forall (G:context) (x:string) (T:ty), True.
Proof.
  intros G x T.
  lets (S & Eq & Sub): typing_inversion_var G x T ___.
Abort.

(** Usually, there is only one context [G] and one type [T] that are
    going to be suitable for proving [has_type G (tm_var x) T], so
    we don't really need to bother giving [G] and [T] explicitly.
    It suffices to call [lets (S & Eq & Sub): typing_inversion_var x].
    The variables [G] and [T] are then instantiated using existential
    variables. *)

Lemma demo_lets_3 : forall (x:string), True.
Proof.
  intros x.
  lets (S & Eq & Sub): typing_inversion_var x ___.
Abort.

(** We may go even further by not giving any argument to instantiate
    [typing_inversion_var]. In this case, three unification variables
    are introduced. *)

Lemma demo_lets_4 : True.
Proof.
  lets (S & Eq & Sub): typing_inversion_var ___.
Abort.

(** Note: if we provide [lets] with only the name of the lemma as
    argument, it simply adds this lemma in the proof context, without
    trying to instantiate any of its arguments. *)

Lemma demo_lets_5 : True.
Proof.
  lets H: typing_inversion_var.
Abort.

(** A last useful feature of [lets] is the double-underscore symbol,
    which allows skipping an argument when several arguments have
    the same type. In the following example, our assumption quantifies
    over two variables [n] and [m], both of type [nat]. We would like
    [m] to be instantiated as the value [3], but without specifying a
    value for [n]. This can be achieved by writting [lets K: H __ 3]. *)

Lemma demo_lets_underscore :
  (forall n m, n <= m -> n < m+1) ->
  True.
Proof.
  intros H.

  (* If we do not use a double underscore, the first argument,
     which is [n], gets instantiated as 3. *)
  lets K: H 3. (* gives [K] of type [forall m, 3 <= m -> 3 < m+1] *)
    clear K.

  (* The double underscore preceeding [3] indicates that we want
     to skip a value that has the type [nat] (because [3] has
     the type [nat]). So, the variable [m] gets instiated as [3]. *)
  lets K: H __ 3. (* gives [K] of type [?X <= 3 -> ?X < 3+1] *)
    clear K.
Abort.

(** Note: one can write [lets: E0 E1 E2] in place of [lets H: E0 E1 E2].
    In this case, the name [H] is chosen arbitrarily.

    Note: the tactics [lets] accepts up to five arguments. Another
    syntax is available for providing more than five arguments.
    It consists in using a list introduced with the special symbol [>>],
    for example [lets H: (>> E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 10)]. *)

End ExamplesLets.

(* ================================================================= *)
(** ** Working of [applys], [forwards] and [specializes] *)

(** The tactics [applys], [forwards] and [specializes] are
    shorthand that may be used in place of [lets] to perform
    specific tasks.

    - [forwards] is a shorthand for instantiating all the arguments
    of a lemma. More precisely, [forwards H: E0 E1 E2 E3] is the
    same as [lets H: E0 E1 E2 E3 ___], where the triple-underscore
    has the same meaning as explained earlier on.

    - [applys] allows building a lemma using the advanced instantion
    mode of [lets], and then apply that lemma right away. So,
    [applys E0 E1 E2 E3] is the same as [lets H: E0 E1 E2 E3]
    followed with [eapply H] and then [clear H].

    - [specializes] is a shorthand for instantiating in-place
    an assumption from the context with particular arguments.
    More precisely, [specializes H E0 E1] is the same as
    [lets H': H E0 E1] followed with [clear H] and [rename H' into H].

    Examples of use of [applys] appear further on. Several examples of
    use of [forwards] can be found in the tutorial chapter [UseAuto]. *)
(* ################################################################# *)
(** * Summary *)

(** In this chapter we have presented a number of tactics that help make
    proof script more concise and more robust on change.

    - [introv] and [inverts] improve naming and inversions.

    - [false] and [tryfalse] help discarding absurd goals.

    - [unfolds] automatically calls [unfold] on the head definition.

    - [gen] helps setting up goals for induction.

    - [cases] and [cases_if] help with case analysis.

    - [splits] and [branch], to deal with n-ary constructs.

    - [asserts_rewrite], [cuts_rewrite], [substs] and [fequals] help
      working with equalities.

    - [lets], [forwards], [specializes] and [applys] provide means
      of very conveniently instantiating lemmas.

    - [applys_eq] can save the need to perform manual rewriting steps
      before being able to apply lemma.

    - [admits], [admit_rewrite] and [admit_goal] give the flexibility to
      choose which subgoals to try and discharge first.

    Making use of these tactics can boost one's productivity in Coq proofs.

    If you are interested in using [LibTactics.v] in your own developments,
    make sure you get the lastest version from:
    https://www.chargueraud.org/softs/tlc/.

*)

(* 2020-09-09 21:08 *)


(** * Induction: Proof by Induction *)

(** The next line imports all of our definitions from the
    previous chapter. *)

Require Export Basics.

(** For it to work, you need to use [coqc] to compile [Basics.v]
    into [Basics.vo].  (This is like making a .class file from a .java
    file, or a .o file from a .c file.)

    Here are two ways to compile your code:

     - CoqIDE:

         Open [Basics.v].
         In the "Compile" menu, click on "Compile Buffer".

     - Command line:

         Run [coqc Basics.v]

    *)

(* ###################################################################### *)
(** * Naming Cases *)

(** The fact that there is no explicit command for moving from
    one branch of a case analysis to the next can make proof scripts
    rather hard to read.  In larger proofs, with nested case analyses,
    it can even become hard to stay oriented when you're sitting with
    Coq and stepping through the proof.  (Imagine trying to remember
    that the first five subgoals belong to the inner case analysis and
    the remaining seven cases are what remains of the outer one...)
    Disciplined use of indentation and comments can help, but a better
    way is to use the [Case] tactic. *)

(** [Case] is not built into Coq: we need to define it ourselves.
    There is no need to understand how it works -- you can just skip
    over the definition to the example that follows.  It uses some
    facilities of Coq that we have not discussed -- the string
    library (just for the concrete syntax of quoted strings) and the
    [Ltac] command, which allows us to declare custom tactics.  Kudos
    to Aaron Bohannon for this nice hack! *)

Require String. Open Scope string_scope.

Ltac move_to_top x :=
  match reverse goal with
  | H : _ |- _ => try move x after H
  end.

Tactic Notation "assert_eq" ident(x) constr(v) :=
  let H := fresh in
  assert (x = v) as H by reflexivity;
  clear H.

Tactic Notation "Case_aux" ident(x) constr(name) :=
  first [
    set (x := name); move_to_top x
  | assert_eq x name; move_to_top x
  | fail 1 "because we are working on a different case" ].

Tactic Notation "Case" constr(name) := Case_aux Case name.
Tactic Notation "SCase" constr(name) := Case_aux SCase name.
Tactic Notation "SSCase" constr(name) := Case_aux SSCase name.
Tactic Notation "SSSCase" constr(name) := Case_aux SSSCase name.
Tactic Notation "SSSSCase" constr(name) := Case_aux SSSSCase name.
Tactic Notation "SSSSSCase" constr(name) := Case_aux SSSSSCase name.
Tactic Notation "SSSSSSCase" constr(name) := Case_aux SSSSSSCase name.
Tactic Notation "SSSSSSSCase" constr(name) := Case_aux SSSSSSSCase name.
(** Here's an example of how [Case] is used.  Step through the
   following proof and observe how the context changes. *)

Theorem andb_true_elim1 : forall b c : bool,
  andb b c = true -> b = true.
Proof.
  intros b c H.
  destruct b.
  Case "b = true".  (* <----- here *)
    reflexivity.
  Case "b = false".  (* <---- and here *)
    rewrite <- H.
    reflexivity.
Qed.

(** [Case] does something very straightforward: It simply adds a
    string that we choose (tagged with the identifier "Case") to the
    context for the current goal.  When subgoals are generated, this
    string is carried over into their contexts.  When the last of
    these subgoals is finally proved and the next top-level goal
    becomes active, this string will no longer appear in the context
    and we will be able to see that the case where we introduced it is
    complete.  Also, as a sanity check, if we try to execute a new
    [Case] tactic while the string left by the previous one is still
    in the context, we get a nice clear error message.

    For nested case analyses (e.g., when we want to use a [destruct]
    to solve a goal that has itself been generated by a [destruct]),
    there is an [SCase] ("subcase") tactic. *)

(** **** Exercise: 2 stars (andb_true_elim2) *)
(** Prove [andb_true_elim2], marking cases (and subcases) when
    you use [destruct]. *)

Theorem andb_true_elim2 : forall b c : bool,
  andb b c = true -> c = true.
Proof.
  intros b c H.
  destruct c.
  Case "c = true".
    reflexivity.
  Case "c = false".
    rewrite <- H.
    destruct b.
    SCase "b = true".
      reflexivity.
    SCase "b = false".
      reflexivity.
Qed.
(** [] *)

(** There are no hard and fast rules for how proofs should be
    formatted in Coq -- in particular, where lines should be broken
    and how sections of the proof should be indented to indicate their
    nested structure.  However, if the places where multiple subgoals
    are generated are marked with explicit [Case] tactics placed at
    the beginning of lines, then the proof will be readable almost no
    matter what choices are made about other aspects of layout.

    This is a good place to mention one other piece of (possibly
    obvious) advice about line lengths.  Beginning Coq users sometimes
    tend to the extremes, either writing each tactic on its own line
    or entire proofs on one line.  Good style lies somewhere in the
    middle.  In particular, one reasonable convention is to limit
    yourself to 80-character lines.  Lines longer than this are hard
    to read and can be inconvenient to display and print.  Many
    editors have features that help enforce this. *)

(* ###################################################################### *)
(** * Proof by Induction *)

(** We proved in the last chapter that [0] is a neutral element
    for [+] on the left using a simple argument.  The fact that it is
    also a neutral element on the _right_... *)

Theorem plus_0_r_firsttry : forall n:nat,
  n + 0 = n.

(** ... cannot be proved in the same simple way.  Just applying
  [reflexivity] doesn't work: the [n] in [n + 0] is an arbitrary
  unknown number, so the [match] in the definition of [+] can't be
  simplified.  *)

Proof.
  intros n.
  simpl. (* Does nothing! *)
Abort.

(** *** *)

(** And reasoning by cases using [destruct n] doesn't get us much
   further: the branch of the case analysis where we assume [n = 0]
   goes through, but in the branch where [n = S n'] for some [n'] we
   get stuck in exactly the same way.  We could use [destruct n'] to
   get one step further, but since [n] can be arbitrarily large, if we
   try to keep on like this we'll never be done. *)

Theorem plus_0_r_secondtry : forall n:nat,
  n + 0 = n.
Proof.
  intros n. destruct n as [| n'].
  Case "n = 0".
    reflexivity. (* so far so good... *)
  Case "n = S n'".
    simpl.       (* ...but here we are stuck again *)
Abort.

(** *** *)

(** To prove such facts -- indeed, to prove most interesting
    facts about numbers, lists, and other inductively defined sets --
    we need a more powerful reasoning principle: _induction_.

    Recall (from high school) the principle of induction over natural
    numbers: If [P(n)] is some proposition involving a natural number
    [n] and we want to show that P holds for _all_ numbers [n], we can
    reason like this:
         - show that [P(O)] holds;
         - show that, for any [n'], if [P(n')] holds, then so does
           [P(S n')];
         - conclude that [P(n)] holds for all [n].

    In Coq, the steps are the same but the order is backwards: we
    begin with the goal of proving [P(n)] for all [n] and break it
    down (by applying the [induction] tactic) into two separate
    subgoals: first showing [P(O)] and then showing [P(n') -> P(S
    n')].  Here's how this works for the theorem we are trying to
    prove at the moment: *)

(** *** *)

Theorem plus_0_r : forall n:nat, n + 0 = n.
Proof.
  intros n. induction n as [| n'].
  Case "n = 0".     reflexivity.
  Case "n = S n'".  simpl. rewrite -> IHn'. reflexivity.  Qed.

(** Like [destruct], the [induction] tactic takes an [as...]
    clause that specifies the names of the variables to be introduced
    in the subgoals.  In the first branch, [n] is replaced by [0] and
    the goal becomes [0 + 0 = 0], which follows by simplification.  In
    the second, [n] is replaced by [S n'] and the assumption [n' + 0 =
    n'] is added to the context (with the name [IHn'], i.e., the
    Induction Hypothesis for [n']).  The goal in this case becomes [(S
    n') + 0 = S n'], which simplifies to [S (n' + 0) = S n'], which in
    turn follows from the induction hypothesis. *)

Theorem minus_diag : forall n,
  minus n n = 0.
Proof.
  (* WORKED IN CLASS *)
  intros n. induction n as [| n'].
  Case "n = 0".
    simpl. reflexivity.
  Case "n = S n'".
    simpl. rewrite -> IHn'. reflexivity.  Qed.

(** **** Exercise: 2 stars (basic_induction) *)

(** Prove the following lemmas using induction. You might need
    previously proven results. *)

Theorem mult_0_r : forall n:nat,
  n * 0 = 0.
Proof.
  induction n as [|n'].
  Case "n = 0". reflexivity.
  Case "n = S n'". simpl. rewrite IHn'. reflexivity.
Qed.

Theorem plus_n_Sm : forall n m : nat,
  S (n + m) = n + (S m).
Proof.
  intros n m.
  (* Completed in Basics.v for Binary *)
  rewrite <- plus_S_assoc. reflexivity.
Qed.

Theorem plus_comm : forall n m : nat,
  n + m = m + n.
Proof.
  intros n m. rewrite <- plus_comm. reflexivity.
Qed.

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

(** [] *)

(** **** Exercise: 2 stars (double_plus) *)

(** Consider the following function, which doubles its argument: *)

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

(** Use induction to prove this simple fact about [double]: *)

Lemma double_plus : forall n, double n = n + n .
Proof.
  intros n. induction n as [|n'].
  Case "n = 0". reflexivity.
  Case "n = S n'". simpl. rewrite <- plus_n_Sm. rewrite IHn'. reflexivity.
Qed.
(** [] *)

(** **** Exercise: 1 star (destruct_induction) *)
(** Briefly explain the difference between the tactics
    [destruct] and [induction].

Induction introduces an induction hypothesis in addition to performing case analysis.

*)
(** [] *)

(* ###################################################################### *)
(** * Proofs Within Proofs *)

(** In Coq, as in informal mathematics, large proofs are very
    often broken into a sequence of theorems, with later proofs
    referring to earlier theorems.  Occasionally, however, a proof
    will need some miscellaneous fact that is too trivial (and of too
    little general interest) to bother giving it its own top-level
    name.  In such cases, it is convenient to be able to simply state
    and prove the needed "sub-theorem" right at the point where it is
    used.  The [assert] tactic allows us to do this.  For example, our
    earlier proof of the [mult_0_plus] theorem referred to a previous
    theorem named [plus_O_n].  We can also use [assert] to state and
    prove [plus_O_n] in-line: *)

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

(** The [assert] tactic introduces two sub-goals.  The first is
    the assertion itself; by prefixing it with [H:] we name the
    assertion [H].  (Note that we could also name the assertion with
    [as] just as we did above with [destruct] and [induction], i.e.,
    [assert (0 + n = n) as H].  Also note that we mark the proof of
    this assertion with a [Case], both for readability and so that,
    when using Coq interactively, we can see when we're finished
    proving the assertion by observing when the ["Proof of assertion"]
    string disappears from the context.)  The second goal is the same
    as the one at the point where we invoke [assert], except that, in
    the context, we have the assumption [H] that [0 + n = n].  That
    is, [assert] generates one subgoal where we must prove the
    asserted fact and a second subgoal where we can use the asserted
    fact to make progress on whatever we were trying to prove in the
    first place. *)

(** Actually, [assert] will turn out to be handy in many sorts of
    situations.  For example, suppose we want to prove that [(n + m)
    + (p + q) = (m + n) + (p + q)]. The only difference between the
    two sides of the [=] is that the arguments [m] and [n] to the
    first inner [+] are swapped, so it seems we should be able to
    use the commutativity of addition ([plus_comm]) to rewrite one
    into the other.  However, the [rewrite] tactic is a little stupid
    about _where_ it applies the rewrite.  There are three uses of
    [+] here, and it turns out that doing [rewrite -> plus_comm]
    will affect only the _outer_ one. *)

Theorem plus_rearrange_firsttry : forall n m p q : nat,
  (n + m) + (p + q) = (m + n) + (p + q).
Proof.
  intros n m p q.
  (* We just need to swap (n + m) for (m + n)...
     it seems like plus_comm should do the trick! *)
  rewrite -> plus_comm.
  (* Doesn't work...Coq rewrote the wrong plus! *)
Abort.

(** To get [plus_comm] to apply at the point where we want it, we can
    introduce a local lemma stating that [n + m = m + n] (for
    the particular [m] and [n] that we are talking about here), prove
    this lemma using [plus_comm], and then use this lemma to do the
    desired rewrite. *)

Theorem plus_rearrange : forall n m p q : nat,
  (n + m) + (p + q) = (m + n) + (p + q).
Proof.
  intros n m p q.
  assert (H: n + m = m + n).
    Case "Proof of assertion".
    rewrite -> plus_comm. reflexivity.
  rewrite -> H. reflexivity.  Qed.

(** **** Exercise: 4 stars (mult_comm) *)
(** Use [assert] to help prove this theorem.  You shouldn't need to
    use induction. *)

Theorem plus_swap : forall n m p : nat,
  n + (m + p) = m + (n + p).
Proof.
  intros n m p.
  rewrite plus_assoc.  rewrite plus_assoc.
  assert (H: n + m = m + n).
    Case "Proof of assertion". rewrite plus_comm. reflexivity.
  rewrite H. reflexivity.
Qed.

(** Now prove commutativity of multiplication.  (You will probably
    need to define and prove a separate subsidiary theorem to be used
    in the proof of this one.)  You may find that [plus_swap] comes in
    handy. *)

Theorem mult_S_r : forall m n : nat,
  m * S n = m * n + m.
Proof.
  intros m n.
  induction m as [|m'].
  Case "m = 0". reflexivity.
  Case "m = S m'".
    simpl. rewrite IHm'.
    rewrite plus_n_Sm. rewrite plus_n_Sm.
    rewrite plus_assoc. reflexivity.
Qed.

Theorem mult_comm : forall m n : nat,
 m * n = n * m.
Proof.
  intros n m.
  induction n as [|n'].
  Case "n = 0". rewrite mult_0_l. rewrite mult_0_r. reflexivity.
  Case "n = S n'".
    simpl. rewrite mult_S_r. rewrite IHn'. rewrite plus_comm. reflexivity.
Qed.

(** [] *)

(** **** Exercise: 2 stars, optional (evenb_n__oddb_Sn) *)

(** Prove the following simple fact: *)

Theorem evenb_n__oddb_Sn : forall n : nat,
  evenb n = negb (evenb (S n)).
Proof.
  induction n as [|n'].
  Case "n = 0". reflexivity.
  Case "n = S n'".
    simpl. rewrite IHn'. rewrite negb_involutive. reflexivity.
Qed.
(** [] *)

(* ###################################################################### *)
(** * More Exercises *)

(** **** Exercise: 3 stars, optional (more_exercises) *)
(** Take a piece of paper.  For each of the following theorems, first
    _think_ about whether (a) it can be proved using only
    simplification and rewriting, (b) it also requires case
    analysis ([destruct]), or (c) it also requires induction.  Write
    down your prediction.  Then fill in the proof.  (There is no need
    to turn in your piece of paper; this is just to encourage you to
    reflect before hacking!) *)

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

Theorem zero_nbeq_S : forall n:nat,
  beq_nat 0 (S n) = false.
Proof.
  reflexivity.
Qed.

Theorem andb_false_r : forall b : bool,
  andb b false = false.
Proof.
  destruct b.
  Case "b = true". reflexivity.
  Case "b = false". reflexivity.
Qed.

Theorem plus_ble_compat_l : forall n m p : nat,
  ble_nat n m = true -> ble_nat (p + n) (p + m) = true.
Proof.
  intros n m p H.
  induction p as [|p'].
  Case "p = 0". simpl. rewrite H. reflexivity.
  Case "p = p'". simpl. rewrite IHp'. reflexivity.
Qed.

Theorem S_nbeq_0 : forall n:nat,
  beq_nat (S n) 0 = false.
Proof.
  reflexivity.
Qed.

Theorem mult_1_l : forall n:nat, 1 * n = n.
Proof.
  intros n. simpl. rewrite plus_0_r. reflexivity.
Qed.

Theorem all3_spec : forall b c : bool,
    orb
      (andb b c)
      (orb (negb b)
               (negb c))
  = true.
Proof.
  intros b c.
  destruct b.
  Case "b = true". simpl. destruct c.
    SCase "c = true". reflexivity.
    SCase "c = false". reflexivity.
  Case "b = false". reflexivity.
Qed.

Theorem mult_plus_distr_r : forall n m p : nat,
  (n + m) * p = (n * p) + (m * p).
Proof.
  induction p as [|p'].
  Case "p = 0". rewrite mult_0_r. rewrite mult_comm. simpl. rewrite mult_0_r. reflexivity.
  Case "p = S p'".
    rewrite mult_S_r.
    rewrite mult_S_r.
    rewrite mult_S_r.
    rewrite <- plus_assoc.
    assert (H: n + (m * p' + m) = (m * p') + (n + m)).
    SCase "Proof of assertion".
      rewrite plus_comm. rewrite <- plus_assoc.
      assert (H': m + n = n + m).
        SSCase "Proof of subassertion". rewrite plus_comm. reflexivity.
      rewrite H'. reflexivity.
    rewrite H.
    assert (H1: n * p' + (m * p' + (n + m)) = (n * p' + m * p') + (n + m)).
      SCase "Proof of subassertion".
      rewrite plus_assoc. reflexivity.
    rewrite H1.
    rewrite IHp'.
    reflexivity.
(* Sigh, too long *)
Qed.

Theorem mult_assoc : forall n m p : nat,
  n * (m * p) = (n * m) * p.
Proof.
  intros.
  induction n as [|n'].
  Case "n = 0".
    reflexivity.
  Case "n = S n'".
    simpl. rewrite IHn'. rewrite mult_plus_distr_r. reflexivity.
Qed.
(** [] *)

(** **** Exercise: 2 stars, optional (beq_nat_refl) *)
(** Prove the following theorem.  Putting [true] on the left-hand side
of the equality may seem odd, but this is how the theorem is stated in
the standard library, so we follow suit.  Since rewriting
works equally well in either direction, we will have no
problem using the theorem no matter which way we state it. *)

Theorem beq_nat_refl : forall n : nat,
  true = beq_nat n n.
Proof.

  induction n as [|n'].
  Case "n = 0".
    reflexivity.
  Case "n = S n".
    simpl. rewrite IHn'. reflexivity.
Qed.
(** [] *)

(** **** Exercise: 2 stars, optional (plus_swap') *)
(** The [replace] tactic allows you to specify a particular subterm to
   rewrite and what you want it rewritten to.  More precisely,
   [replace (t) with (u)] replaces (all copies of) expression [t] in
   the goal by expression [u], and generates [t = u] as an additional
   subgoal. This is often useful when a plain [rewrite] acts on the wrong
   part of the goal.

   Use the [replace] tactic to do a proof of [plus_swap'], just like
   [plus_swap] but without needing [assert (n + m = m + n)].
*)

Theorem plus_swap' : forall n m p : nat,
  n + (m + p) = m + (n + p).
Proof.
  intros.
  rewrite plus_assoc. rewrite plus_assoc.
  replace (n + m) with (m + n).
  reflexivity.
  Case "replace". rewrite plus_comm. reflexivity.
Qed.

(** [] *)

(** **** Exercise: 3 stars (binary_commute) *)
(** Recall the [increment] and [binary-to-unary] functions that you
    wrote for the [binary] exercise in the [Basics] chapter.  Prove
    that these functions commute -- that is, incrementing a binary
    number and then converting it to unary yields the same result as
    first converting it to unary and then incrementing.

    (Before you start working on this exercise, please copy the
    definitions from your solution to the [binary] exercise here so
    that this file can be graded on its own.  If you find yourself
    wanting to change your original definitions to make the property
    easier to prove, feel free to do so.) *)

(* Redefining *)
Fixpoint bin_to_nat (n : bin) : nat :=
  match n with
  | B   => O
  | T n => (bin_to_nat n) + (bin_to_nat n)
  | M n => (bin_to_nat n) + (bin_to_nat n) + 1
  end.

Theorem inc_bin_to_nat_commute:
  forall (b : bin),
  bin_to_nat (inc b) = S (bin_to_nat b).
Proof.
  induction b as [|b'|b'].
  Case "b = B". reflexivity.
  Case "b = M b'".
    simpl. rewrite IHb'. simpl. replace (S (bin_to_nat b')) with (bin_to_nat b' + 1).
    rewrite plus_assoc. reflexivity.
    SCase "rewrite". rewrite plus_comm. reflexivity.
  Case "b = T b'".
    simpl. rewrite <- plus_comm. reflexivity.
Qed.
(** [] *)

(** **** Exercise: 5 stars, advanced (binary_inverse) *)
(** This exercise is a continuation of the previous exercise about
    binary numbers.  You will need your definitions and theorems from
    the previous exercise to complete this one.

    (a) First, write a function to convert natural numbers to binary
        numbers.  Then prove that starting with any natural number,
        converting to binary, then converting back yields the same
        natural number you started with.

    (b) You might naturally think that we should also prove the
        opposite direction: that starting with a binary number,
        converting to a natural, and then back to binary yields the
        same number we started with.  However, it is not true!
        Explain what the problem is.

    (c) Define a function [normalize] from binary numbers to binary
        numbers such that for any binary number b, converting to a
        natural and then back to binary yields [(normalize b)].  Prove
        it.

    Again, feel free to change your earlier definitions if this helps
    here.
*)
Fixpoint nat_to_bin (n : nat) : bin :=
  match n with
  | O    => B
  | S n' => inc (nat_to_bin n')
  end.

Theorem nat_to_bin_bin_to_nat_commute:
  forall (n : nat),
  bin_to_nat (nat_to_bin n) = n.
Proof.
  induction n as [|n'].
  Case "n = 0". reflexivity.
  Case "n = S n'". simpl. rewrite inc_bin_to_nat_commute. rewrite IHn'. reflexivity.
Qed.

(* (b) There are multiple valid representations of the same nat number in the binary system *)
(* (c) *)
Fixpoint normalize_bin (b : bin) : bin :=
  match b with
  | B    => B
  | T b' => match (normalize_bin b') with
            | B   => B
            | b'' => T b''
            end
  | M b' => M (normalize_bin b')
  end.

Theorem M_nat_to_bin_commute : forall (n : nat),
  M (nat_to_bin n) = nat_to_bin (n + n + 1).
Proof.
  induction n; intros.
  simpl. reflexivity.
  simpl.
    assert (n + S n + 1 = S (n + n + 1)).
    rewrite plus_S_assoc. rewrite plus_0_r. rewrite plus_S_assoc.
    assert (n + n + 1 = S (n + n)).
      rewrite plus_comm. reflexivity.
    rewrite H. reflexivity.
    rewrite H. simpl. rewrite <- IHn. reflexivity.
Qed.

Theorem bin_to_nat_nat_to_bin_normalized_commute:
  forall (b : bin),
  nat_to_bin (bin_to_nat b) = normalize_bin b.
Proof.
  induction b as [|b'|b'].
  Case "b = B". reflexivity.
  Case "b = M b'". simpl. rewrite <- IHb'. rewrite M_nat_to_bin_commute. reflexivity.
  Case "b = T b'". simpl. rewrite <- IHb'.
Abort.

(** [] *)

(* ###################################################################### *)
(** * Advanced Material *)

(** ** Formal vs. Informal Proof *)

(** "Informal proofs are algorithms; formal proofs are code." *)

(** The question of what, exactly, constitutes a "proof" of a
    mathematical claim has challenged philosophers for millennia.  A
    rough and ready definition, though, could be this: a proof of a
    mathematical proposition [P] is a written (or spoken) text that
    instills in the reader or hearer the certainty that [P] is true.
    That is, a proof is an act of communication.

    Now, acts of communication may involve different sorts of readers.
    On one hand, the "reader" can be a program like Coq, in which case
    the "belief" that is instilled is a simple mechanical check that
    [P] can be derived from a certain set of formal logical rules, and
    the proof is a recipe that guides the program in performing this
    check.  Such recipes are _formal_ proofs.

    Alternatively, the reader can be a human being, in which case the
    proof will be written in English or some other natural language,
    thus necessarily _informal_.  Here, the criteria for success are
    less clearly specified.  A "good" proof is one that makes the
    reader believe [P].  But the same proof may be read by many
    different readers, some of whom may be convinced by a particular
    way of phrasing the argument, while others may not be.  One reader
    may be particularly pedantic, inexperienced, or just plain
    thick-headed; the only way to convince them will be to make the
    argument in painstaking detail.  But another reader, more familiar
    in the area, may find all this detail so overwhelming that they
    lose the overall thread.  All they want is to be told the main
    ideas, because it is easier to fill in the details for themselves.
    Ultimately, there is no universal standard, because there is no
    single way of writing an informal proof that is guaranteed to
    convince every conceivable reader.  In practice, however,
    mathematicians have developed a rich set of conventions and idioms
    for writing about complex mathematical objects that, within a
    certain community, make communication fairly reliable.  The
    conventions of this stylized form of communication give a fairly
    clear standard for judging proofs good or bad.

    Because we are using Coq in this course, we will be working
    heavily with formal proofs.  But this doesn't mean we can ignore
    the informal ones!  Formal proofs are useful in many ways, but
    they are _not_ very efficient ways of communicating ideas between
    human beings. *)

(** For example, here is a proof that addition is associative: *)

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

(** Coq is perfectly happy with this as a proof.  For a human,
    however, it is difficult to make much sense of it.  If you're used
    to Coq you can probably step through the tactics one after the
    other in your mind and imagine the state of the context and goal
    stack at each point, but if the proof were even a little bit more
    complicated this would be next to impossible.  Instead, a
    mathematician might write it something like this: *)
(** - _Theorem_: For any [n], [m] and [p],
      n + (m + p) = (n + m) + p.
    _Proof_: By induction on [n].

    - First, suppose [n = 0].  We must show
        0 + (m + p) = (0 + m) + p.
      This follows directly from the definition of [+].

    - Next, suppose [n = S n'], where
        n' + (m + p) = (n' + m) + p.
      We must show
        (S n') + (m + p) = ((S n') + m) + p.
      By the definition of [+], this follows from
        S (n' + (m + p)) = S ((n' + m) + p),
      which is immediate from the induction hypothesis. [] *)

(** The overall form of the proof is basically similar.  This is
    no accident: Coq has been designed so that its [induction] tactic
    generates the same sub-goals, in the same order, as the bullet
    points that a mathematician would write.  But there are
    significant differences of detail: the formal proof is much more
    explicit in some ways (e.g., the use of [reflexivity]) but much
    less explicit in others (in particular, the "proof state" at any
    given point in the Coq proof is completely implicit, whereas the
    informal proof reminds the reader several times where things
    stand). *)

(** Here is a formal proof that shows the structure more
    clearly: *)

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

(** **** Exercise: 2 stars, advanced (plus_comm_informal) *)
(** Translate your solution for [plus_comm] into an informal proof. *)

(** Theorem: Addition is commutative.

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

(** **** Exercise: 2 stars, optional (beq_nat_refl_informal) *)
(** Write an informal proof of the following theorem, using the
    informal proof of [plus_assoc] as a model.  Don't just
    paraphrase the Coq tactics into English!

    Theorem: [true = beq_nat n n] for any [n].

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

(* $Date: 2014-02-19 21:36:35 -0500 (Wed, 19 Feb 2014) $ *)


(*|
###############################
Coq coercions and goal matching
###############################

:Link: https://stackoverflow.com/q/45482430
|*)

(*|
Question
********

Assume I have the following setup:
|*)

Inductive exp: Set :=
| CE: nat -> exp.

Inductive adt: exp -> Prop :=
| CA: forall e, adt e.

Coercion nat_to_exp := CE.

Ltac my_tactic := match goal with
                  | [ |- adt (CE ?N) ] => apply (CA (CE N))
                  end.

(*| And I try to prove a simple theorem using the custom tactic: |*)

Theorem silly: adt 0.
Proof.
  Fail my_tactic. (* .unfold .fails .no-goals *)
Abort.

(*|
This fails, because the goal is not of the form ``adt (CE ?N)`` but of
the form ``adt (nat_to_exp ?N)`` (This is shown explicitly when using
``Set Printing Coercions``).

Trying to prove a slightly different theorem works:
|*)

Theorem silly: adt (CE 0).
Proof.
  my_tactic.
Qed.

(*|
Possible workarounds I know of:

- Not using coercions.
- Unfolding coercions in the tactic (with ``unfold nat_to_exp``). This
  alleviates the problem slightly, but fails as soon as a new coercion
  is introduced the tactic doesn't know about.

Ideally, I would like the pattern matching to succeed if the pattern
matches after unfolding all definitions (The definitions should not
stay unfolded, of course).

Is this possible? If not, are there reasons why it is not possible?
|*)

(*|
Answer
******

You can directly declare the constructor ``CE`` as a coercion rather
than wrapping it as ``nat_to_exp`` like so:
|*)

Coercion CE : nat >-> exp.

(*|
The proof then goes through without any issue. If you insist on naming
your coercion (e.g. because it's a compound expression rather than a
single constructor), you can change your tactics so that it handles
non unfolded coercions explicitly:
|*)

Reset my_tactic. (* .none *)
Ltac my_tactic := match goal with
                  | [ |- adt (CE ?N) ] => apply (CA (CE N))
                  | [ |- adt (nat_to_exp ?N) ] => apply (CA (CE N))
                  end.

(*|
----

**A:** There is also syntactic sugar for the case when constructors
are coercions:
|*)

Reset exp. (* .none *)
Inductive exp: Set := | CE :> nat -> exp.

(*|
automatically declares ``CE`` as a coercion (notice that I used ``:>``
instead of ``:`` for ``CE``).

**A:** You can also use e.g. ``cbv delta.`` before pattern-matching on
the goal to unfold transparent definitions. Then your original
``my_tactic`` would work.
|*)