From 9689433ff808cc4b09a566eaf80eaa0f63afb68d Mon Sep 17 00:00:00 2001
From: Shachar Itzhaky <corwin.amber@gmail.com>
Date: Tue, 18 May 2021 00:34:28 +0300
Subject: [PATCH] [feature] jscoqdoc.

A useful utility for injecting jsCoq into documents generated by `coqdoc`.
Adapted from a hack I used for Software Foundations.

To demonstrate its use, `nahas_tutorial.v` is used as an example.
---
 examples/.gitignore       |    2 +
 examples/Makefile         |    9 +
 examples/nahas_tutorial.v | 2903 +++++++++++++++++++++++++++++++++++++
 ui-css/jscoq.css          |  183 ---
 ui-css/jscoqdoc.css       |  632 ++++++++
 ui-js/jscoq-agent.js      |   83 ++
 ui-js/jscoqdoc.js         |   54 +
 7 files changed, 3683 insertions(+), 183 deletions(-)
 create mode 100644 examples/.gitignore
 create mode 100644 examples/Makefile
 create mode 100644 examples/nahas_tutorial.v
 delete mode 100644 ui-css/jscoq.css
 create mode 100644 ui-css/jscoqdoc.css
 create mode 100644 ui-js/jscoq-agent.js
 create mode 100755 ui-js/jscoqdoc.js

diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 00000000..352a3f46
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1,2 @@
+_build
+nahas_tutorial.html
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644
index 00000000..5aa7e5d6
--- /dev/null
+++ b/examples/Makefile
@@ -0,0 +1,9 @@
+.PHONY: all
+
+all: nahas_tutorial.html
+
+nahas_tutorial.html: nahas_tutorial.v
+	mkdir -p _build && cp $< _build/
+	coqc _build/$<
+	JSCOQ_URL=.. ../ui-js/jscoqdoc.js --no-index --no-lib-name --parse-comments _build/$<
+	@rm coqdoc.css  # urghh
diff --git a/examples/nahas_tutorial.v b/examples/nahas_tutorial.v
new file mode 100644
index 00000000..a220ea82
--- /dev/null
+++ b/examples/nahas_tutorial.v
@@ -0,0 +1,2903 @@
+(**
+Mike Nahas's Coq Tutorial
+
+Started 2012-Nov-06
+Version 1.2, 2019-Jan-22
+Tested with Coq version 8.10+alpha
+
+------------------------
+Dedicated to Kernighan and Ritchie,
+who wrote a magnificent introduction to a programming language.
+*)
+
+
+(** * Introduction *)
+(**
+Coq is a proof assistant.  Coq helps you write formal proofs.
+
+A "formal proof" is a mathematical proof that is written in a language
+similar to a programming language.  (Actually, Coq's language _is_ a
+programming language, but we'll get to that later.)  Formal proofs are
+harder for a human to read, but easier for a program to read.  The
+advantage is that a formal proof can be verified by a program,
+eliminating human error.
+
+(NOTE: Yes, a human error could exist in the verifying program, the
+operating system, the computer, etc..  The risk in the verifying
+program is kept small by keeping the program very simple and small.
+As for the other parts, checking a proof on multiple systems make it
+much more likely that any errors are in the proofs rather than in the
+verification of the proof.)
+
+This tutorial will teach you the basics of using Coq to write formal
+proofs.  My aim is to teach you a powerful subset of Coq's commands by
+showing you lots of actual proofs.  This tutorial will not teach you
+all of the Coq commands, but you will learn enough to get started
+using Coq.
+
+*)
+
+
+(** ** Prerequisites *)
+(**
+This tutorial will teach you how to use Coq to write formal proofs.  I
+assume you already know how to write a proof and know about formal
+logic.  I tried to make this tutorial easy to read, so if you don't
+know those things, keep reading and you _might_ be able to pick them
+up.
+
+I also assume you understand a programming language.  It doesn't
+matter which programming language.
+
+If you feel unprepared, the "further reading" section at the end has
+useful links.
+*)
+
+
+(** ** Installing Coq *)
+(**
+
+The easiest thing is to install "CoqIDE", the graphical version of
+Coq.  Windows and MacOS installers and the latest source code are
+available at http://coq.inria.fr/download If you're running Linux,
+your package manager probably has a recent-ish version.
+(Debian/Ubuntu/Mint: "sudo apt install coqide", Fedora/CentOS might be
+'su -c "yum install coq-coqide"'.  Arch might be "sudo pacman -S
+coqide")
+
+If you like the Emacs text editor, the alternative is to run "coqtop",
+the command-line version of Coq.  It is also available at
+http://coq.inria.fr/download The Linux package is usually called
+"coq".  To use it in Emacs, you have to also download and install the
+"Proof General" Emacs mode.  Is is available at
+https://proofgeneral.github.io/ Linux package managers include some
+version of it.  (Debian/Ubuntu/Mint calls it "proofgeneral".
+Fedora/CentOS might call it "emacs-common-proofgeneral".  Arch might
+call it "proofgeneral".)
+
+*)
+
+
+(** ** Loading this file *)
+(**
+
+This file (yes, the one you're reading right now) is a Coq file.  Coq
+file's names usually end in ".v".  If this file's name currently ends
+in ".txt", you'll want to change it to ".v" before loading it into
+CoqIDE or Emacs.  (If this file's name ends in ".html" or ".pdf",
+someone used Coq to generate this document, in which case you need to
+find the original ".v" file they used.  If you can't find it, the
+author hosts a recent version at
+https://mdnahas.github.io/doc/nahas_tutorial.v)
+
+Once you've made sure the file's name ends in ".v", start CoqIDE or
+Emacs and load the file.
+
+You need to know that this file was designed to work in a specific
+version of Coq.  Coq is a research tool and the developers
+occasionally make small changes to its file format, so it is possible
+that this file is for a different version of Coq.  As you progress
+through the tutorial, you may find a proof that your version of Coq
+doesn't like.  You can probably make it work, but if you can't, you
+can try installing the latest version of CoqIDE and downloading the
+latest version of the tutorial from the author's website.
+(https://mdnahas.github.io/doc/nahas_tutorial.v)
+
+*)
+
+(** * Comments *)
+(**
+Coq will ignore everything that starts with a '(' followed by a '*'
+and end with a '*' followed by a ')'.  These are called comments.
+*)
+
+(* This is a comment. *)
+
+(** * Your First Proof! *)
+(**
+We'll begin by proving the proposition:
+<<
+  "for all things you could prove,
+   if you have a proof of it, then you have a proof of it."
+>>
+
+Okay, that's not that exciting but we can't print "Hello, World" in Coq...
+*)
+
+Theorem my_first_proof : (forall A : Prop, A -> A).
+Proof.
+  intros A.
+  intros proof_of_A.
+  exact proof_of_A.
+Qed.
+
+(** ** Dissection of your first proof *)
+(**
+
+A Coq proof starts by stating what you're trying to prove.  That is
+done by the command "Theorem".  After the word "Theorem" comes the
+name of the theorem: "my_first_proof".  If you want use this theorem
+later, you'll refer to it by that name.  Then comes a colon, the
+statement of what you're trying to prove, and a period ('.').
+
+As you can see, every Coq command ends with a period.
+
+Let's skip over how we express what you want to prove and go on to the
+actual proof itself.  That starts, unsurprisingly, with the command
+"Proof" (with its command-terminating period).  Then comes the actual
+proof, which takes 3 steps.  Lastly, the "Qed" command ends the proof.
+
+(NOTE: Instead of "Theorem", you may also see proofs that start with
+"Lemma", "Remark", "Fact", "Corollary", and "Proposition", which all
+mean the _SAME_ thing.  In my proofs, I only use "Theorem".  Instead
+of "Qed", you may also see proofs that end with "Admitted" or
+"Defined", but these mean _DIFFERENT_ things.  Use "Qed" for now.)
+
+
+Coq uses 3 different "languages" and you can see them all here in this
+proof.
+  - The "vernacular" language manages definitions and top-level
+    interactions.  Each of its commands starts with a capital letter:
+    "Theorem", "Proof", and "Qed".
+  - The "tactics" language is used to write proofs.  Its commands start
+    with a lower-case letter: "intros" and "exact".
+  - The unnamed language of Coq terms is used to express what you want
+    to prove.  Its expressions use lots of operators and
+    parentheses: "(forall A : Prop, A -> A)".  (Technically, this
+    language is a subset of the vernacular language, but it is useful to
+    think of it as its own thing.)
+
+Now, let's take a look inside this proof!  Since it probably has
+scrolled off the screen, I'll reproduce it.
+*)
+
+Theorem my_first_proof__again : (forall A : Prop, A -> A).
+Proof.
+  intros A.
+  intros proof_of_A.
+  exact proof_of_A.
+Qed.
+
+(** ** Seeing where you are in a proof *)
+(**
+
+CoqIDE and Proof General are valuable because they show you the state
+in the middle of your proof.  They show what you have proven and what
+you still need to prove.
+
+Let's see the different states inside your first proof.  Move your
+cursor (by clicking your mouse or using the arrow keys) over any line
+between "Proof." and "Qed.".  Now let's see the state at that point.
+
+In CoqIDE, there are three ways to do it:
+  1. From the menu bar, open the "Navigation" menu and select "go to"
+  2. In the tool bar, click on the 5th icon (a green arrow pointing at a yellow ball)
+  3. Use a keyboard combo.  On my Mac, it's control-option-rightarrow.
+
+In Proof General:
+  Do "C-c C-Enter" (press control-c and then control-Enter)
+
+In a different part of the screen you should see something like:
+
+<<
+  A : Prop
+  proof_of_A : A
+  ============================
+   A
+>>
+
+Everything above the bar is what you know to exist or have assumed to
+exist.  These are called "hypotheses" and we refer to everything above
+the bar as "the context".  Below the bar is what we are trying to
+prove.  It is called "the current subgoal".
+
+"The goal" is the theorem we're trying to prove.  A "subgoal" is what
+we are trying to prove at any point during the proof.  We say "current
+subgoal" because during a proof we may have multiple things remaining
+to prove.  For example, during a proof by induction, we will have one
+subgoal for the "base case" and another subgoal for the "inductive
+case".  But we (usually) only work on one subgoal at a time and the
+one we're working on is called "the current subgoal".
+
+
+Now I want to explain each tactic and how it helps prove this theorem.
+The proof has probably scrolled off your screen again, so here it is a
+third time.  *)
+
+Theorem my_first_proof__again__again : (forall A : Prop, A -> A).
+Proof.
+  intros A.
+  intros proof_of_A.
+  exact proof_of_A.
+Qed.
+
+(** ** Your first Tactic! *)
+(**
+Our starting state is:
+
+<<
+
+  ============================
+   forall A : Prop, A -> A
+>>
+
+Our goal (and current subgoal) starts with "forall A : Prop,...".  In
+English, this would be "For all A such that A is a Prop, ...".  One way
+to prove a statement like "For all x such that x is an integer, ..."
+is to assume we have an arbitrary integer x and show that the rest of
+the statement holds for x. The first tactic "intros" does exactly that.
+
+Thus, every time I see "intros A.", I think "assume A".
+
+The tactic "intros" takes "forall" off the front of the subgoal,
+changes its variable into a hypothesis in the context, and names the
+hypothesis.  Recall, the context holds all things we've proven or, as
+in this case, assumed to be proven.  We named the new hypothesis "A",
+which is the same as the name of the variable removed from the
+subgoal.  You should always keep the same name, if possible.
+
+_RULE_: If the subgoal starts with "(forall <name> : <type>, ..."
+     Then use tactic "intros <name>.".
+
+The state after "intros A." is:
+
+<<
+  A : Prop
+  ============================
+   A -> A
+>>
+
+In Coq, "A : Prop" means you have something named "A" of type "Prop".
+In the future you will see "0 : nat" which means "0" of type "nat"
+(natural numbers) and you will see "true : bool" which means "true" of
+type "bool" (boolean or true-false values).  In some places, you will
+see "A B C : Prop", which means "A", "B", and "C" all have type
+"Prop".
+
+The type "Prop" is easier to explain after we've executed the next
+command.  The next tactic is "intros", which we said works when
+"forall" is at the front of the subgoal.  It works here because "->"
+is actually shorthand for "forall".  "B -> C" really means "(forall
+something_of_type_B : B, C)".  So, "A->A" is "(forall
+something_of_type_A : A, A)".  The result is that "intros proof_of_A."
+removes that hidden "forall" and moves the unnamed variable of type A
+to the hypothesis named "proof_of_A".
+
+_RULE_: If the subgoal starts with "<type> -> ..."
+     Then use tactic "intros <name>.".
+
+The state after the second "intros" command, looks like:
+
+<<
+  A : Prop
+  proof_of_A : A
+  ============================
+   A
+>>
+
+Now, we can talk about "Prop".  "proof_of_A" is a proof.  It has type
+"A", which means that "A" is something that could have a proof.  In
+logic, that is called a "proposition".  Since the proposition "A" has
+type "Prop", "Prop" must be the type of propositions.
+
+Prop is an important concept, so let's see some propositions:
+  - (forall x : nat, (x < 5) -> (x < 6))
+  - (forall x y : nat, x + y = y + x)
+  - (forall A : Prop, A -> A)
+
+All of these have type "Prop".  They can all can have proofs.  That
+last Prop should look familiar - it's what we're trying to prove right
+now!
+
+IT IS VITALLY IMPORTANT THAT YOU DO NOT THINK OF A Prop AS BEING
+EITHER "TRUE" OR "FALSE".  A Prop either has a proof or it does not
+have a proof.  Godel shattered mathematics by showing that some true
+propositions can never proven.  Tarski went further and showed that
+some propositions cannot even be said to be true or false!!!  Coq deals
+with these obstacles in modern mathematics by restricting Prop to
+being either proven or unproven, rather than true or false.
+
+Now that I've got that across, let's go and finish our first proof.
+After the second "intros" tactic, our subgoal is "A", which means "we
+need something of type A" or, because A is a proposition, "we need a
+proof of A".  Now, the previous command moved a proof of A into the
+context and called it "proof_of_A".  So, an hypothesis in our context
+(which are all things we know to exist) has a type that matches our
+subgoal (which is what we want to create), so we have an exact match.
+The tactic "exact proof_of_A" solves the subgoal (and the proof).
+
+Ta-da!  Your first proof!
+
+_RULE_: If the subgoal matches an hypothesis,
+     Then use tactic "exact <hyp_name>.".
+
+
+Okay, let's try something more complicated!
+*)
+
+
+(** * Proofs with -> *)
+(** ** Proof going forward *)
+
+Theorem forward_small : (forall A B : Prop, A -> (A->B) -> B).
+Proof.
+ intros A.
+ intros B.
+ intros proof_of_A.
+ intros A_implies_B.
+ pose (proof_of_B := A_implies_B proof_of_A).
+ exact proof_of_B.
+Qed.
+
+(**
+Look at the tactics used in this proof.  (The tactics are the commands
+between "Proof." and "Qed.")  You should be familiar with "intros" and
+"exact".  The new one is "pose".  We're going to use "pose" in a
+forward proof.  A "forward proof" creates larger and more complex
+hypotheses in the context until one matches the goal.  Later, we'll
+see a "backward proof", which breaks the goal into smaller and simpler
+subgoals until they're trivial.
+
+In our current proof, the state right before the "pose" command is:
+
+<<
+  A : Prop
+  B : Prop
+  proof_of_A : A
+  A_implies_B : A -> B
+  ============================
+   B
+>>
+
+The subgoal is "B", so we're trying to construct a proof of B.
+
+In the context we have "A_implies_B : A->B".  "A->B", if you recall,
+is equivalent to "(forall proof_of_A : A, B)", that is, for every
+proof of A, we have a proof of B.
+
+It happens that we have a proof of A in our context and it's called
+(unoriginally) "proof_of_A".  The expression "A_implies_B
+proof_of_A" computes the proof of B that is associated with that
+particular proof of A.
+
+The command "pose" assigns the result of "A_implies_B proof_of_A" to
+the new hypothesis "proof_of_B".  (Note the annoying extra set of
+parentheses that are necessary around the arguments to the "pose"
+command.)
+
+_RULE_: If you have an hypothesis
+          "<hyp_name>: <type1> -> <type2> -> ... -> <result_type>"
+     OR an hypothesis
+            "<hyp_name>: (forall <obj1>:<type1>, (forall <obj2>:<type2>, ... <result_type> ...))"
+     OR any combination of "->" and "forall",
+     AND you have hypotheses of type "type1", "type2"...,
+     Then use tactic "pose" to create something of type "result_type".
+
+The proof ends with the "exact" command.  We could have ended with
+"exact (A_implies_B proof_of_A)", but I think it is easier to read
+when it ends with with "exact proof_of_B."
+
+This was a forward proof.  Let's see a backward one.
+*)
+
+(** ** Proof going backward *)
+
+Theorem backward_small : (forall A B : Prop, A -> (A->B)->B).
+Proof.
+ intros A B.
+ intros proof_of_A A_implies_B.
+ refine (A_implies_B _).
+   exact proof_of_A.
+Qed.
+
+(**
+Notice that we're trying to prove the exact same theorem as before.
+However, I'm going to show a "backward proof" that breaks the goal
+into smaller and simpler subgoals.  We will start this proof trying to
+find a proof of B, but then change that to find the simpler proof of
+A.
+
+First, notice that the 4 "intros" commands from our last proof have
+changed to just 2.  The "intros" command can take any number of
+arguments, each argument stripping a forall (or ->) off the front of
+the current subgoal.  (WARNING: Don't use "intros" with no arguments
+at all - it doesn't do what you'd expect!)  We could have combined all
+the "intros" into a single command, but I think it is cleaner to
+introduce the Props and the derived values separately.
+
+The state right before the "refine" command is:
+
+<<
+  A : Prop
+  B : Prop
+  proof_of_A : A
+  A_implies_B : A -> B
+  ============================
+   B
+>>
+
+The subgoal is the Prop "B", so we're trying to construct a proof of
+B.
+
+We know that "A_implies_B" can create a proof of B, given a proof of
+A.  We saw the syntax for it was "A_implies_B something_of_type_A".
+The command "refine (A_implies_B _)." let's us create the proof of B
+without specifying the argument of type A.  (The parentheses are
+necessary for it to parse correctly.)  This solves our current subgoal
+of type B and the unspecified argument - represented by the underscore
+("_") - become a new "child" subgoal.
+
+<<
+  A : Prop
+  B : Prop
+  proof_of_A : A
+  A_implies_B : A -> B
+  ============================
+   A
+>>
+
+In this case, the child subgoal has us trying to find a proof of A.
+Since it's a child subgoal, we indent the tactics used to solve it.
+And the tactic to solve it is our well worn "exact".
+
+_RULE_: If you have subgoal "<goal_type>"
+     AND have hypothesis
+          "<hyp_name>: <type1> -> <type2> -> ... -> <typeN> -> <goal_type>",
+     Then use tactic "refine (<hyp_name> _ _ ...)." with N underscores.
+
+The important thing to take away from this proof is that we changed
+the subgoal.  That's what happens in a backward proof.  You keep
+changing the subgoal to make it smaller and simpler.  "A" doesn't seem
+much smaller or simpler than "B", but it was.
+
+Now, let's rev this up...
+*)
+
+(** ** Proof going backward (large) *)
+
+Theorem backward_large : (forall A B C : Prop, A -> (A->B) -> (B->C) -> C).
+Proof.
+ intros A B C.
+ intros proof_of_A A_implies_B B_implies_C.
+ refine (B_implies_C _).
+   refine (A_implies_B _).
+     exact proof_of_A.
+Qed.
+
+(**
+Look at the sequence of tactics.  It starts with a pair of "intros"s.
+That's followed by the new kid on the block "refine".  We finish with
+"exact".  That pattern will start to look familiar very soon.
+
+The state before the first "refine" is:
+
+<<
+  A : Prop
+  B : Prop
+  C : Prop
+  proof_of_A : A
+  A_implies_B : A -> B
+  B_implies_C : B -> C
+  ============================
+   C
+>>
+
+Our current subgoal is "C" and "C" is at the end of "B -> C", so we
+can do "refine (B_implies_C _)".  That command creates a new child
+subgoal "B".
+
+Then, our subgoal is "B" and "B" is at the end of "A -> B", so "refine
+(A_implies_B _)" creates a new child subgoal of "A".
+
+Then we finish with "exact proof_of_A.".  Easy as pie.
+
+
+Let's do a really big example!
+*)
+
+(** ** Proof going backward (huge) *)
+
+Theorem backward_huge : (forall A B C : Prop, A -> (A->B) -> (A->B->C) -> C).
+Proof.
+ intros A B C.
+ intros proof_of_A A_implies_B A_imp_B_imp_C.
+ refine (A_imp_B_imp_C _ _).
+   exact proof_of_A.
+
+   refine (A_implies_B _).
+     exact proof_of_A.
+Qed.
+
+(**
+Yes, something is different here!  We have our starting "intros"s.  We
+have a "refine".  But then the commands are indented and we have two
+"exact"s!
+
+The state before the first refine is:
+
+<<
+  A : Prop
+  B : Prop
+  C : Prop
+  proof_of_A : A
+  A_implies_B : A -> B
+  A_imp_B_imp_C : A -> B -> C
+  ============================
+   C
+>>
+
+Now, we have a subgoal "C" and "C" is at the end of "A->B->C", so we
+can do "refine (A_imp_B_imp_C _ _)".  Notice that "A_imp_B_imp_C" has
+two implication arrows (the "->"s), so refine requires two underscores
+and creates two subgoals - one for something of type A and another for
+something of type B.
+
+I hinted that a formal proof is actually written in a programming
+language.  We see that very clearly here.  "A_imp_B_imp_C" is a
+function that takes two arguments, one of type A and one of type B,
+and returns a value of type C.  The type of the function is "A->B->C"
+and a call to it looks like "A_imp_B_imp_C something_of_type_A
+something_of_type_B".  Notice that there are no parentheses needed -
+you just put the arguments next to the name of the function.  For
+example, "function arg1 arg2". This is a style common in functional
+programming languages.  For those familiar with imperative languages,
+like C, C++, and Java, it will feel odd not to have parentheses and
+commas to denote the arguments.
+
+The first "refine" created two subgoals.  CoqIDE and Proof General
+will tell you that two subgoals exist, but they will only show you the
+context for the current subgoal.
+
+In the text of the proof, we represent that "refine" created multiple
+subgoals by formatting them like an "if-then-else" or "switch/match"
+statement in a programming language.  The proofs for each subgoal get
+indented and we use a blank line to separate them.
+
+The proof of the first subgoal is trivial.  We need an "A" and we have
+"proof_of_A : A".  "exact proof_of_A" completes the subgoal.  We now
+put the blank line in the proof to show we're moving on to the next
+subgoal.
+
+The rest of the proof we've seen before.  The proof for it is indented
+to show it's the result of a "branch" in the proof created by "refine
+(A_imp_B_impC _ _)".
+
+
+Having seen this complex theorem proved with a backward proof, let's
+see what it would look like with a forward one.
+*)
+
+(** ** Proof going forward (huge) *)
+
+Theorem forward_huge : (forall A B C : Prop, A -> (A->B) -> (A->B->C) -> C).
+Proof.
+ intros A B C.
+ intros proof_of_A A_implies_B A_imp_B_imp_C.
+ pose (proof_of_B := A_implies_B proof_of_A).
+ pose (proof_of_C := A_imp_B_imp_C proof_of_A proof_of_B).
+ exact proof_of_C.
+Show Proof.
+Qed.
+
+(**
+This is the same theorem as before, except it has a forward proof
+instead of a backward one.
+
+In this proof, we can see the programming language underneath the
+proof.  "A_implies_B" is a function with type "A->B", so when it is
+call with argument "proof_of_A", it generates a proof of B.  The
+"pose" command assigns the resulting value to the (unimaginative) name
+"proof_of_B".
+
+Likewise, "A_imp_B_imp_C" is a function with type "A->B->C".  Called
+with "proof_of_A" and "proof_of_B", it produces a proof of C which
+becomes the hypothesis "proof_of_C".
+
+There is a new vernacular command at the end: "Show Proof".  If you
+put your cursor right after it and press "control-option-right_arrow"
+(in CoqIDE) or "C-C C-Enter" (in Proof General), you'll see the actual
+code for the proof.  It looks like this:
+
+<<
+  (fun (A B C : Prop)
+       (proof_of_A : A) (A_implies_B : A -> B) (A_imp_B_imp_C : A -> B -> C)
+   =>
+     let proof_of_B := A_implies_B proof_of_A in
+     let proof_of_C := A_imp_B_imp_C proof_of_A proof_of_B in
+       proof_of_C)
+>>
+
+I formatted this version so that the correspondence to the proof is
+clearer.  The "intros" commands declare the parameters for the
+function.  "pose" declares constant values in the function.  Lastly,
+the "exact" command is used to return the result of the function.  As
+we go, you'll see the tight relationship in Coq between proofs and
+code.
+
+
+At this point, I want to emphasize that Coq proofs are not normally
+this tedious or verbose.  The proofs I've shown and the proofs I will
+show are demonstrating the mechanics of how Coq works.  I'm using
+simple familiar types to make the mechanics' operations clear to you.
+Coq's tactic language contains commands for automatic theorem proving
+and for defining macros.  Almost all the proofs in this tutorial are
+simple enough to dispatch with a single Coq tactic.  But when you
+prove more complicated statements, you'll need all the commands I'm
+teaching in this tutorial.
+
+This helps explain why most Coq proofs are backwards.  Once a
+goal has been transformed into simple enough subgoals, those
+subgoals can each be proved by automation.
+
+
+So far, we've only worked with proofs, propositions, and Prop.  Let's
+add some more types!
+*)
+
+
+
+(** * true and false vs. True and False *)
+(**
+The vernacular command "Inductive" lets you create a new type.  I
+wanted to introduce the boolean type, which has two values: "true" and
+"false".  The problem is that in addition to "true" and "false", Coq
+has two other entities named "True" and "False", where the first
+letter is capitalized.  To keep the differences straight in your mind,
+I'm going to introduce them to you all at once.
+
+<<
+Inductive False : Prop := .
+
+Inductive True : Prop :=
+  | I : True.
+
+Inductive bool : Set :=
+  | true : bool
+  | false : bool.
+>>
+
+- Capital-F "False" is a Prop with no proofs.
+- Capital-T "True" is a Prop that has a single proof called "I".
+   (That's a capital-I, not a number 1.)
+- Lastly, "bool" is a Set and "bool" has two elements:
+  lower-case-t "true" and lower-case-f "false".
+
+I find these names confusing.  Recall that "Prop"s are things that may
+have a proof.  So I think capital-T "True" and capital-F "False" should
+have been named "Provable" and "Unprovable" (or "AlwaysProvable"
+and "NeverProvable").  The lower-case ones act like what you're
+accustomed to.
+
+Since we've been playing with Props, let's do some proofs with (the
+badly named) "True" and "False" and we'll come back to the lower-case
+"true" and "false" later.  *)
+
+(** ** Capital-T True and Capital-F False *)
+(** *** True is provable *)
+
+Theorem True_can_be_proven : True.
+  exact I.
+Qed.
+
+(**
+If we look at the state before the first (and only) line of the proof,
+we see:
+
+<<
+
+  ============================
+   True
+>>
+
+There are no hypotheses; the context is empty.  We are trying to find
+a proof of True.  By its definition, True has a single proof called
+"I".  So, "exact I." solves this proof.
+
+_RULE_: If your subgoal is "True",
+     Then use tactic "exact I.".
+
+Now, we turn to False.
+*)
+
+
+(** *** Unprovability *)
+(**
+I wrote earlier that a proposition either has a proof or it does not
+(yet) have a proof.  In some cases, we can prove that a proposition
+can never have a proof.  We do that by showing that for every possible
+proof of a proposition, we could generate a proof of False.  Because
+False has no proofs (by its definition), we know that the proposition
+has no proofs.
+
+So, to show that "A : Prop" has no proofs, we need to show
+  - (forall proof_of_A: A, False)
+Or, equivalently
+  - A -> False
+
+We do this so often that there is an operator "~" to represent it.
+
+<<
+Definition not (A:Prop) := A -> False.
+
+Notation "~ x" := (not x) : type_scope.
+>>
+
+"Definition" is a vernacular command that says two things are
+interchangeable.  So, "(not A)" and "A -> False" are interchangeable.
+
+"Notation" is a vernacular command that creates an operator, in this
+case "~", and defines it as an alternate notation for an expression,
+in this case "(not _)".  Since "not" takes a Prop, "~" can only be
+applied to a Prop.
+
+(NOTE: The Notation command is how the operator "->" was created
+for "(forall ...)".)
+
+
+Let's try to prove some things are unprovable!
+*)
+
+(** *** False is unprovable *)
+
+Theorem False_cannot_be_proven : ~False.
+Proof.
+  unfold not.
+  intros proof_of_False.
+  exact proof_of_False.
+Qed.
+
+(**
+The only new tactic here is "unfold".  We said "Definition" says two
+expressions are interchangeable.  Well, "unfold" and "fold" will
+interchange them.  After the "unfold", we have
+
+<<
+
+  ============================
+   False -> False
+>>
+
+The "unfold" exposes that "~" is really an "->".  And we're very
+familiar with using the tactic "intros" to remove "->" at the front of
+the subgoal.  The command "intros proof_of_False" does just that.
+
+
+After that, it goes as usual.  "intros"s at the start and "exact" at
+the end.  It feels weird to have an hypothesis labeled
+"proof_of_False" doesn't it?  It's weird because we know False has no
+proofs, so that hypothesis can never exist.  Wouldn't it be better if
+we could say that directly?...
+*)
+
+Theorem False_cannot_be_proven__again : ~False.
+Proof.
+  intros proof_of_False.
+  case proof_of_False.
+Qed.
+
+(**
+This is the same theorem as before, but two things have changed in
+this proof.
+
+First, it doesn't have "unfold not.".  Since we know "~" is shorthand
+for "->", we can skip over the unfold tactic and go straight to using
+"intros".
+
+_RULE_: If your subgoal is "~<type>" or "~(<term>)" or "(not <term>)",
+     Then use tactic "intros".
+
+The second change is that we found a new way to finish a proof!
+Instead of "exact", we use the "case" tactic.  "case" is
+powerful: it creates subgoals for every possible construction of its
+argument.  Since there is no way to construct a False, "case"
+creates no subgoals!  Without a subgoal, we're done!
+
+_RULE_: If any hypothesis is "<name> : False",
+     Then use tactic "case <name>.".
+*)
+
+(** *** -> Examples *)
+(**
+We can use True and False to see that Coq's "->" operator really does
+act like "implication" from logic.
+*)
+
+Theorem thm_true_imp_true : True -> True.
+Proof.
+  intros proof_of_True.
+  exact I.  (** "exact proof_of_True." also works. *)
+Qed.
+
+Theorem thm_false_imp_true : False -> True.
+Proof.
+  intros proof_of_False.
+  exact I.  (** "case proof_of_False." also works. *)
+Qed.
+
+Theorem thm_false_imp_false : False -> False.
+Proof.
+  intros proof_of_False.
+  case proof_of_False.  (** "exact proof_of_False." works, but is not recommended. *)
+Qed.
+
+(** True->False can never be proven.  We demonstrate that by
+proving ~(True->False). *)
+Theorem thm_true_imp_false : ~(True -> False).
+Proof.
+  intros T_implies_F.
+  refine (T_implies_F _).
+    exact I.
+Qed.
+
+(**
+All of the above proofs should be obvious to you by now.
+*)
+
+
+(** *** Reducto ad absurdium *)
+(**
+Below is another staple of logic: reduction to absurdity.  If a
+proposition has a proof and you prove that it cannot have a proof, then
+you can conclude anything.
+*)
+
+Theorem absurd2 : forall A C : Prop, A -> ~ A -> C.
+Proof.
+  intros A C.
+  intros proof_of_A proof_that_A_cannot_be_proven.
+  unfold not in proof_that_A_cannot_be_proven.
+  pose (proof_of_False := proof_that_A_cannot_be_proven proof_of_A).
+  case proof_of_False.
+Qed.
+
+(**
+
+This is a tricky proof.  Since our subgoal "C" doesn't appear in our
+hypotheses, we cannot end the proof with "exact something_of_type_C".
+The only other option we know (so far) is "case" on a proof of False.
+
+The tactic "unfold ... in" is used to interchange the definition of
+"not" in a hypothesis.  That exposes that the type "~A" is really
+"A -> False", that is, a function from a proof of A to a proof of False.
+
+With that knowledge, we can call the function with "proof_of_A" to get
+a proof of False.  And we end the proof with "case" on a proof of
+False!
+
+
+That was a serious proof!  But we've done about as much as we can do
+with Props, so let's get back to bools!
+*)
+
+
+(** ** The return of lower-case true and lower-case false *)
+(**
+Now, remember when I introduced capital-T True and capital-F False of
+type Prop, I also introduced the type "bool".  Type "bool" has two
+different constructors: lower-case-t true and lower-case-f false.
+
+<<
+Inductive bool : Set :=
+  | true : bool
+  | false : bool.
+>>
+
+"true" and "false" have type "bool" and "bool" has type "Set".  "Set"
+is the type of normal datatypes, like "bool" and natural numbers and
+most of the other types you'll see.  The exception is propositions,
+which have type "Prop".
+
+Let's load some helper functions for the type "bool".
+*)
+
+Require Import Bool.
+
+(**
+"Require Import" is a vernacular command that loads definitions from a
+library.  In this case, the library is named "Bool" and there is a
+file named "Bool.v" that contains its definitions, proofs, etc..
+
+Two of the functions are:
+<<
+Definition eqb (b1 b2:bool) : bool :=
+  match b1, b2 with
+    | true, true => true
+    | true, false => false
+    | false, true => false
+    | false, false => true
+  end.
+
+Definition Is_true (b:bool) :=
+  match b with
+    | true => True
+    | false => False
+  end.
+>>
+
+The first function "eqb" returns true if the two arguments match.
+("eqb" is short for "equal for type bool".)
+
+The second function "Is_true" converts a bool into a Prop.  In the
+future, you can use "(<name> = true)", but we haven't described "="
+yet.  The operator "=" is REALLY cool, but you have to understand more
+basic types, like bool, first.
+
+Let's do some proofs with these functions.
+*)
+
+(** *** Is_true true is True *)
+
+Theorem true_is_True: Is_true true.
+Proof.
+  simpl.
+  exact I.
+Qed.
+
+(**
+
+"Is_true" is a function, so "Is_true true" is that function called
+with the argument "true".  Since "Is_true"'s type is "bool->Prop", we
+know that the function call returns a "Prop", which is something that
+can be proven.  So, this proof demonstrates that there exists a proof
+for "Is_true true".
+
+Admittedly, "Is_true true" seems like a dumb thing to prove.  Later,
+we'll replace the argument "true" with more useful boolean
+expressions, like "4 < 5" and other comparisons.
+
+The proof contains a new tactic: "simpl".  It is short for "simplify".
+If you have a function call and you have the definition of the
+function, the tactic "simpl" will execute the function on the
+arguments.  In this case, the function returns something of type Prop
+and that Prop happens to be "True".  (Yes, the function returned a
+type.  This is common in Coq.)
+
+"True" becomes our new subgoal.  And we've seen how to prove a subgoal
+of "True" and that's "exact I" because "True" was defined to have a
+single proof named "I".
+
+
+_RULE_: If the current subgoal contains a function call with all its arguments,
+     Then use the tactic "simpl.".
+
+
+I promised that you would see "Is_true" with a more complex argument.
+So, let me show you that.
+*)
+
+(** *** Is_true called with a complex constant. *)
+
+Theorem not_eqb_true_false: ~(Is_true (eqb true false)).
+Proof.
+  simpl.
+  exact False_cannot_be_proven.
+Qed.
+
+(**
+
+The tactic "simpl" executes the functions to produce a subgoal of
+"~False".  That should look familiar, because we've proved it before!
+We could copy-and-paste the proof _OR_ we could just say the proof
+already exists!  We gave the proof of "~False" the name
+"False_cannot_be_proven", so the tactic "exact False_cannot_be_proven"
+finishes the proof immediately!  Sweet, isn't it?
+
+
+Now, let's look at a more complex call to Is_true, where we'll get to
+see the tactic "case" show its power!
+*)
+
+(** *** case with bools *)
+
+Theorem eqb_a_a : (forall a : bool, Is_true (eqb a a)).
+Proof.
+  intros a.
+  case a.
+    (** suppose a is true *)
+    simpl.
+    exact I.
+
+    (** suppose a is false *)
+    simpl.
+    exact I.
+Qed.
+
+(**
+Take a look at the state after "case a.".  There's two subgoals!  I
+said before that "case" creates subgoals for every possible
+construction of its argument.  So far, we've only used "case" with
+hypotheses of type "False".  False has no constructors, so it
+generates no subgoals and ends our proofs.
+
+Hypotheses of type "bool" have two possible constructors "true" and
+"false".  Thus, "case a" creates two new subgoals - one where "a" has
+been replaced by "true" and a second where "a" has been replaced by
+"false".
+
+NOTE: The replacement of "a" by "true" (or "false") only happens
+inside the subgoal.  It does NOT happen in the hypotheses.  It is
+sometimes important to control where "a" is before calling "case"!
+
+I believe it is good style to label the two different cases.  I do
+that with the comment "suppose <hyp> is <constructor>".  Since the
+definition of "bool" listed "true" before "false", the subgoal where
+"a" is "true" becomes the current subgoal.  Once we prove it, the case
+where "a" is "false" will become the current subgoal.
+
+
+_RULE_: If there is a hypothesis "<name>" of a created type
+     AND that hypothesis is used in the subgoal,
+     Then you can try the tactic "case <name>.".
+
+
+Let's do one more example.
+*)
+
+Theorem thm_eqb_a_t: (forall a:bool, (Is_true (eqb a true)) -> (Is_true a)).
+Proof.
+  intros a.
+  case a.
+    (** suppose a is true *)
+    simpl.
+    intros proof_of_True.
+    exact I.
+
+    (** suppose a is false *)
+    simpl.
+    intros proof_of_False.
+    case proof_of_False.
+Qed.
+
+(**
+In this example, I had to control the location of "a" when using the
+tactic "case".
+
+Usually, we would do "intros a" followed by another "intros" to move
+everything before the "->" into a hypothesis.  If we did this, one
+usage of "a" would move from the subgoal into a hypothesis and "case
+a" would NOT replace it with "true" or "false".  (The "case" tactic
+only changes the subgoal.)  And we'd be unable to prove the theorem.
+
+Instead, I left "a" in the subgoal and delayed the second "intros"
+until after the "case" command.  (In fact, after the "simpl" too.)  As
+a result, all usages of "a" were replaced by "true" or "false" and the
+theorem was provable.
+*)
+
+(** * And, Or *)
+(**
+One of the most amazing things about Coq is its fundamental rules are
+so simple that even things like "and" and "or" can be defined in terms
+of them.  I'll start with "or" because it shows some special features.
+*)
+(** ** Or *)
+(**
+Before showing you the definition for "or", I want you to see some
+examples so that the definition will make sense.  In the examples
+below, imagine you have a hypothesis with a natural number "x".
+  - (or (x < 5) (x = 7))
+  - (or True False)
+  - (or (x = 0) (x = 1))
+As you can see, "or" is a function.  Each of its arguments are
+propositions - each of them are things that could be proved.  And we
+know that the "or" itself can be proved, so the result of call to "or"
+must be a proposition.  Since a proposition has type "Prop", the type
+of "or" is:
+  - or (A B:Prop) : Prop
+That is, a function that it takes two propositions, A and B, and
+returns a proposition.
+
+I said the result of "or" is a proposition - something that might have
+a proof.  So how do we create a proof of "or"?  For that, we need to
+see the definition...
+
+<<
+Inductive or (A B:Prop) : Prop :=
+  | or_introl : A -> A \/ B
+  | or_intror : B -> A \/ B
+where "A \/ B" := (or A B) : type_scope.
+>>
+
+This vernacular command does four things.
+  - declares "or",
+     a function that takes two Props and produces a Prop
+  - declares "or_introl",
+     a constructor that takes a proof of "A" and returns a proof of "(or A B)"
+  - declares "or_intror",
+     a constructor that takes a proof of "B" and returns a proof of "(or A B)"
+  - declares "\/",
+     an operator that is interchangeable with "or"
+
+One way to think of this is to say "(or A B)" creates a type and
+"(or_introl proof_of_A)" and "(or_intror proof_of_B)" are instances of
+that type.  In fact, the ONLY way to create things of type "or" is by
+using the constructors "or_introl" and "or_intror".
+
+It is important to note that constructors, like "or_introl" and
+"or_intror", are functions that can be called, but do not have any
+definitions.  They are not made of calls to other functions.  They
+cannot be executed and the result looked at.  The tactic "simpl" will
+not remove them.  They are opaque constants.
+
+The vernacular command "Inductive" creates new types.  It's called
+"Inductive" since it lets you create inductive types, although none of
+our types so far has been inductive.  (That will change!)  It is the
+same command we saw earlier to declare other new types with their
+constants.
+
+<<
+Inductive False : Prop := .
+
+Inductive True : Prop :=
+  I : True.
+
+Inductive bool : Set :=
+  | true : bool
+  | false : bool.
+>>
+
+Right now, we want to play with "or", so let's get proving!
+*)
+
+Theorem left_or : (forall A B : Prop, A -> A \/ B).
+Proof.
+  intros A B.
+  intros proof_of_A.
+  pose (proof_of_A_or_B := or_introl proof_of_A : A \/ B).
+  exact proof_of_A_or_B.
+Qed.
+
+(**
+This proof should have gone pretty much as you expected.  The only
+complication is that the "pose" command could not infer the type of B
+from "or_introl proof_of_A", so the type had to be given explicitly by
+putting ": A \/ B" at the end.  "or_introl proof_of_A" by itself is a
+proof of "A \/ anything".
+
+The proof is shorter without the "pose", since Coq knows the type from
+the subgoal we're trying to prove.  You can see this in a similar proof
+using "or_intror" below.
+*)
+
+Theorem right_or : (forall A B : Prop, B -> A \/ B).
+Proof.
+  intros A B.
+  intros proof_of_B.
+  refine (or_intror _).
+    exact proof_of_B.
+Qed.
+
+(**
+An even shorter proof would have just "exact (or_intror proof_of_B)".
+But if you don't know all the constructor's arguments, using "refine"
+is a fine approach.
+
+_RULE_: If the subgoal's top-most term is a created type,
+     Then use "refine (<name_of_constructor> _ _ ...).".
+
+Let's prove something a little more complicated...
+*)
+
+(** *** Or commutes *)
+
+Theorem or_commutes : (forall A B, A \/ B -> B \/ A).
+Proof.
+  intros A B.
+  intros A_or_B.
+  case A_or_B.
+    (** suppose A_or_B is (or_introl proof_of_A) *)
+    intros proof_of_A.
+    refine (or_intror _).
+      exact proof_of_A.
+
+    (** suppose A_or_B is (or_intror proof_of_B) *)
+    intros proof_of_B.
+    refine (or_introl _).
+      exact proof_of_B.
+Qed.
+
+(**
+This proof uses the tactic "case" to consider the two different ways
+we might have constructed the hypothesis "A \/ B".  One possibility
+was using "(or_introl proof_of_A)", which would have meant there was a
+proof of "A".  For that case, the tactic puts "A ->" at the front of the
+subgoal and we use "intros proof_of_A." to move the proof into the context.
+Notice that "case" changed the subgoal and not the context.  "case"
+never changes the context - that's a useful property to know (and look
+out for) when working with it.
+
+The other possibility for creating the hypothesis "A \/ B" was that
+the constructor "or_intror" was used.  For it to be used, there had to
+exist a proof of "B".  So, for the second case created by "case", the
+subgoal has "B ->" at the front and we use "intros proof_of_B." to move
+it into the context.
+
+Once the "proof_of_A" or "proof_of_B" is in the context, the proofs of
+each subgoal follow the pattern we've seen before.
+
+
+Now that we've seen how "or" works, let's take a look at "and".
+*)
+
+(** ** And *)
+
+(**
+I think we can go straight to the definition.
+
+<<
+Inductive and (A B:Prop) : Prop :=
+  conj : A -> B -> A /\ B
+
+where "A /\ B" := (and A B) : type_scope.
+>>
+
+As you could expect, "and" is a function that takes two Props and
+returns a Prop.  That's the exact same as we saw with "or".  However,
+while "or" had two constructors that each took one argument, "and" has
+a single constructor that takes two arguments.  So, the one and only
+way to create something of type "(and A B)" is to use "(conj
+proof_of_A proof_of_B)".
+
+Let's go straight to a proof!
+*)
+
+Theorem both_and : (forall A B : Prop, A -> B -> A /\ B).
+Proof.
+  intros A B.
+  intros proof_of_A proof_of_B.
+  refine (conj _ _).
+    exact proof_of_A.
+
+    exact proof_of_B.
+Qed.
+
+(**
+Pretty simple proof.  There is only one constructor for things of type
+"(and A B)", which is "conj".  It takes two arguments, which after
+"refine", become two subgoals.  The proofs of each are trivial.
+
+After seeing "or", "and" should be easy, so let's jump to the complex
+proof.
+*)
+(** *** And commutes *)
+
+Theorem and_commutes : (forall A B, A /\ B -> B /\ A).
+Proof.
+  intros A B.
+  intros A_and_B.
+  case A_and_B.
+    (** suppose A_and_B is (conj proof_of_A proof_of_B) *)
+    intros proof_of_A proof_of_B.
+    refine (conj _ _).
+      exact proof_of_B.
+
+      exact proof_of_A.
+Qed.
+
+(**
+The "case" tactic looked at all the different ways to construct an
+"and" in the hypothesis and found there was only one: "(conj
+proof_of_A proof_of_B)".  So, the tactic created one subgoal with both
+of those required values at the front of the subgoal.
+
+Similarly, when we need to build an "and" to satisfy the subgoal,
+there is only one way to construct an "and": "conj" with two
+arguments.  Thus, after "refine" two subgoals were created, one
+requiring something of type "B" and the other requiring something of
+type "A".
+
+*)
+
+(** *** destruct tactic *)
+
+(**
+
+The previous proof used "case" to create subgoals for all constructors
+for something of type "A /\ B".  But there was only one constructor.
+So, we really didn't need to indent.  Nor should we waste a line with
+a comment saying "suppose".  And it'd be really nice if we didn't have
+to write the "intros" command.
+
+Luckily, Coq provides a tactic called "destruct" that is a little more
+versatile than "case".  I recommend using it for types that have a
+single constructor.  The format is:
+
+<<
+  destruct <hyp> as [ <arg1> <arg2> ... ].
+>>
+
+I put a comment with the name of the constructor inside the square
+braces.
+
+The result is a much shorter and cleaner proof.
+*)
+
+Theorem and_commutes__again : (forall A B, A /\ B -> B /\ A).
+Proof.
+  intros A B.
+  intros A_and_B.
+  destruct A_and_B as [(**conj*) proof_of_A proof_of_B].
+  refine (conj _ _).
+    exact proof_of_B.
+
+    exact proof_of_A.
+Qed.
+
+(**
+_RULE_: If a hypothesis "<name>" is a created type with only one constructor,
+      Then use "destruct <name> as [ <arg1> <arg2> ... ]" to extract its arguments.
+*)
+
+(** *** functions and inductive types *)
+(**
+
+We just saw that "and" and "or" work with Props and are inductive
+types.  In this section, I'll introduce "andb" and "orb" for working
+with "bool"s.  Rather than inductive types, "andb" and "orb" are
+functions.
+
+To be clear, "and" and "or" are "inductive types" - that is, types we
+defined where instances can only be produced by calling opaque
+functions called constructors.  Those constructors are things like
+"or_introl", "or_intror", and "conj".
+
+In fact, the type "bool" is also inductive type.  It has two
+constructors: "true" and "false".  These constructors take no
+arguments, so they are closer to constants than "opaque functions".
+
+To manipulate "bool"s, we could use an inductive type, but it's much
+easier just to define a function.  We've already seen one function on
+bools: "eqb", which did equality.  Some other functions that Coq defines are:
+
+- andb (b1 b2:bool) : bool
+- orb (b1 b2:bool) : bool
+- negb (b:bool) : bool
+
+These are "and", "or", and "not" (but called "negation" for some reason).
+Like with "eqb", the names all ended in "b" to indicate they work with
+"bool"s.  Two of these functions also come with operators:
+
+<<
+Infix "&&" := andb : bool_scope.
+Infix "||" := orb : bool_scope.
+>>
+
+We'll get some good practice proving that these functions on bools are
+equivalent to our inductive types on Props.  As part of this, we'll
+need a form of equality for propositions that is called "if and only
+if" or "double implication".
+
+<<
+Definition iff (A B:Prop) := (A -> B) /\ (B -> A).
+
+Notation "A <-> B" := (iff A B) : type_scope.
+>>
+
+You should know everything you see in the proofs below.  Remember,
+"simpl" is used to execute a function and "case" and "destruct" are
+used on inductive types.
+*)
+Theorem orb_is_or : (forall a b, Is_true (orb a b) <-> Is_true a \/ Is_true b).
+Proof.
+  intros a b.
+  unfold iff.
+  refine (conj _ _).
+    (** orb -> \/ *)
+    intros H.
+    case a, b.
+      (** suppose a,b is true, true *)
+      simpl.
+      refine (or_introl _).
+        exact I.
+
+      (** suppose a,b is true, false *)
+      simpl.
+      exact (or_introl I).
+
+      (** suppose a,b is false, true *)
+      exact (or_intror I).
+
+      (** suppose a,b is false, false *)
+      simpl in H.
+      case H.
+
+    (** \/ -> orb *)
+    intros H.
+    case a, b.
+      (** suppose a,b is true, true *)
+      simpl.
+      exact I.
+
+      (** suppose a,b is true, false *)
+      exact I.
+
+      (** suppose a,b is false, true *)
+      exact I.
+
+      (** suppose a,b is false, false *)
+      case H.
+         (** suppose H is (or_introl A) *)
+         intros A.
+         simpl in A.
+         case A.
+
+         (** suppose H is (or_intror B) *)
+         intros B.
+         simpl in B.
+         case B.
+Qed.
+
+(**
+Wow!  We're not in Kansas any more!
+
+First, I used "case" to split the "\/" hidden inside the "<->".
+
+Second, inside each of those branches, I did "case a,b", which created
+4 subgoals, for every possible combination of the two constructors.  I
+thought the proof was clearer that way, even though doing "case a"
+followed by "case b" might have been shorter.
+
+The proofs for when "a" or "b" were true were easy.  They were all the
+same proof.  You can see that I make the proof shorter as I went along
+- combining "refine" and "exact" and skipping right over "simpl".
+
+When both "a" and "b" were false, I decided it was best to do "case"
+on an instance of False.  For "orb", that was pretty easy.  I did
+"simpl in H", which executed functions calls inside a hypothesis.
+That gave me an hypothesis of type False, and "case H" ended the
+subgoal.
+
+However, for the inductive type "or", it was more complicated.  I used
+"case H" to consider the different ways of constructing the "or".
+Both constructors - "or_introl" and "or_intror" - lead to a False
+hypothesis.
+
+That was a real proof.  Welcome to the big leagues.
+
+
+_RULE_ If a hypothesis "<name>" contain a function call with all its arguments,
+      Then use the tactic "simpl in <name>.".
+
+
+Let's try it with "andb" and "/\".
+*)
+
+Theorem andb_is_and : (forall a b, Is_true (andb a b) <-> Is_true a /\ Is_true b).
+Proof.
+  intros a b.
+  unfold iff.
+  refine (conj _ _).
+    (** andb -> /\ *)
+    intros H.
+    case a, b.
+      (** suppose a,b is true,true *)
+      simpl.
+      exact (conj I I).
+
+      (** suppose a,b is true,false *)
+      simpl in H.
+      case H.
+
+      (** suppose a,b is false,true *)
+      simpl in H.
+      case H.
+
+      (** suppose a,b is false,false *)
+      simpl in H.
+      case H.
+
+    (** /\ -> andb *)
+    intros H.
+    case a,b.
+      (** suppose a,b is true,true *)
+      simpl.
+      exact I.
+
+      (** suppose a,b is true,false *)
+      simpl in H.
+      destruct H as [(**conj*) A B].
+      case B.
+
+      (** suppose a,b is false,true *)
+      simpl in H.
+      destruct H as [(**conj*) A B].
+      case A.
+
+      (** suppose a,b is false,false *)
+      simpl in H.
+      destruct H as [(**conj*) A B].
+      case A.
+Qed.
+
+(** *** admit tactic *)
+(**
+
+It should be easy for you to proof that "not" and "notb" are
+equivalent.  Below is a statement of the theorem; fill it in
+if you think you can.
+
+I did not include a proof, so where you would normally see a proof,
+you'll see the tactic "admit" and the vernacular command "Admitted".
+The tactic "admit" is a cheat.  It ends a subgoal without solving it.
+A proof containing an "admit" is not a real proof, so Coq forces you
+to end it with "Admitted" instead of "Qed".  I use these commands
+below so that Coq's parser won't get hung up because I didn't include
+a proof of the theorem.
+
+The "admit" tactic has real uses.  When there are multiple subgoals
+and you want to skip over the easy ones to work on the hard one first,
+the "admit" tactic can be used to get the easy subgoals out of the
+way.  Or, if you are only part way done a proof but you want send
+someone a Coq file that parses, "admit" can be used to fill in your
+blanks.
+
+
+So, take a swing at proving this theorem.
+ - HINT: Remember "~A" is "A -> False".
+ - HINT: You can make use of the proof "False_cannot_be_proven"
+ - HINT: When you get stuck, look at "thm_true_imp_false".
+*)
+
+Theorem negb_is_not : (forall a, Is_true (negb a) <-> (~(Is_true a))).
+Proof.
+  admit.  (** delete "admit" and put your proof here. *)
+Admitted. (** when done, replace "Admitted." with "Qed." *)
+
+(**
+_RULE_: If you have a subgoal that you want to ignore for a while,
+      Then use the tactic "admit.".
+*)
+
+(** * Existence and Equality *)
+(**
+Like "and" and "or", the concepts of "there exists" and "equality" are
+not fundamental to Coq.  They are concepts defined inside of it. Only
+"forall" and the notion of creating a type with constructors is
+fundamental to Coq.
+*)
+
+(** ** Existence *)
+(**
+
+In Coq, you cannot just declare that something exists.  You must prove
+it.
+
+For example, we might want to prove that "there exists a bool 'a' such
+that (Is_true (andb a true))".  We cannot just state that the
+"bool" exists.  You need to produce a value for "a" - called the
+witness - and then prove that the statement holds for the witness.
+
+The definition and operator are:
+<<
+Inductive ex (A:Type) (P:A -> Prop) : Prop :=
+  ex_intro : forall x:A, P x -> ex (A:=A) P.
+
+Notation "'exists' x .. y , p" := (ex (fun x => .. (ex (fun y => p)) ..))
+  (at level 200, x binder, right associativity,
+   format "'[' 'exists'  '/  ' x  ..  y ,  '/  ' p ']'")
+  : type_scope.
+>>
+
+The proposition "ex P" should be read: "P is a function returning a
+Prop and there exists an argument to that function such that (P arg)
+has been proven".  The function "P" is known as "the predicate".  The
+constructor for "ex P" takes the predicate "P" , the witness (called
+"x" here) and a proof of "P x" in order to return something of type
+"ex P".
+
+"exists ..., ..." is an operator to provide a friendly notation.  For
+"and" and "or", we did the same with "/\" and "/\".  For existence,
+the usually operator is a capital E written backwards, but that's
+difficult to type, so Coq uses the word "exists".
+
+Let's test this out with the easy theorem I already mentioned.
+*)
+
+Definition basic_predicate
+:=
+  (fun a => Is_true (andb a true))
+.
+
+Theorem thm_exists_basics : (ex basic_predicate).
+Proof.
+  pose (witness := true).
+  refine (ex_intro basic_predicate witness _).
+    simpl.
+    exact I.
+Qed.
+
+(**
+
+I start by defining the predicate: a function that takes a single
+argument "a" (which Coq can determine is a "bool").  The key part of
+the proof is the tactic "refine (ex_intro ...)". The arguments to
+"ex_intro" are:
+  - the predicate
+  - the witness
+  - a proof of the predicated called with the witness
+
+In this usage, I passed "ex_intro" the predicate, the witness, and
+use an "_" to create a new subgoal for the proof.  The tactics "simpl"
+and "exist" are used to solve the proof.
+
+I'll prove the same theorem, but this time I'll use "exists" operator
+to show you how much cleaner it looks.
+*)
+Theorem thm_exists_basics__again : (exists a, Is_true (andb a true)).
+Proof.
+  pose (witness := true).
+  refine (ex_intro _ witness _).
+    (** Coq automatically determines the predicate! *)
+    (** We're left to prove that the witness satisfies the function. *)
+    simpl.
+    exact I.
+Qed.
+
+(**
+_RULE_: If the current subgoal starts "exists <name>, ..."
+     Then create a witness and use "refine (ex_intro _ witness _)"
+*)
+
+(** *** More existence *)
+(**
+We often use "exists" and "forall" at the same time.  Thus, we end up
+with proofs like the following.
+*)
+
+Theorem thm_forall_exists : (forall b, (exists a, Is_true(eqb a b))).
+Proof.
+  intros b.
+  case b.
+    (** b is true *)
+    pose (witness := true).
+    refine (ex_intro _ witness _).
+      simpl.
+      exact I.
+
+    (** b is false *)
+    pose (witness := false).
+    refine (ex_intro _ witness _).
+      simpl.
+      exact I.
+Qed.
+
+(**
+If you look at the proof above, the witness was always equal to "b".
+So, let's try simplifying the proof.
+*)
+
+Theorem thm_forall_exists__again : (forall b, (exists a, Is_true(eqb a b))).
+Proof.
+  intros b.
+  refine (ex_intro _ b _). (** witness is b *)
+  exact (eqb_a_a b).
+Qed.
+
+(**
+We used "b" as the witness and ended up with the state:
+
+<<
+  b : bool
+  ============================
+   Is_true (eqb b b)
+>>
+
+Now, we have already proved:
+<<
+Theorem eqb_a_a : (forall a : bool, Is_true (eqb a a)).
+>>
+
+I've told you that "->" is the type of a function and that "->" is
+shorthand for "(forall ...)", so it's not a far leap to see that our
+theorem "eqb_a_a" is a function from any bool (called "a" in the
+statement) to a proof of "Is_true (eqb a a)".
+
+In our current proof, we needed that statement to hold for our
+particular hypothesis of "b".  That is done by the function call
+"eqb_a_a b", which substitutes the specific "b" into the place of the
+generic "a" in the body of the "forall".  The result is "Is_true (eqb
+b b)", which solves our proof.
+*)
+
+(** *** Exists and Forall *)
+(**
+Here is a classic theorem of logic, showing the relationship between
+"forall" and "exists".  It gives us a chance to "destruct" an instance
+of type "ex".
+*)
+
+Theorem forall_exists : (forall P : Set->Prop,  (forall x, ~(P x)) -> ~(exists x, P x)).
+Proof.
+  intros P.
+  intros forall_x_not_Px.
+  unfold not.
+  intros exists_x_Px.
+  destruct exists_x_Px as [(**ex_intro P*) witness proof_of_Pwitness].
+  pose (not_Pwitness := forall_x_not_Px witness).
+  unfold not in not_Pwitness.
+  pose (proof_of_False := not_Pwitness proof_of_Pwitness).
+  case proof_of_False.
+Qed.
+
+(**
+The proof requires some explanation.
+  - we use intros and unfold until we have everything in the context.
+  - we use "destruct" to extract the witness and the proof of "P witness".
+  - we call "(forall x, ~(P x))" with the witness, to generate "(P witness) -> False"
+  - we call "P witness -> False" with "P witness" to get a proof of "False".
+  - we use the tactic "case" on "proof_of_False"
+
+This proof is hard to read.  If you have difficulty reading a proof,
+you can always use CoqIDE or Proof General to step through the proof
+and look at the state after each tactic.
+
+Another good example of "exists" is proving that the implication goes
+the other way too.
+*)
+
+Theorem exists_forall : (forall P : Set->Prop,  ~(exists x, P x) -> (forall x, ~(P x))).
+Proof.
+  intros P.
+  intros not_exists_x_Px.
+  intros x.
+  unfold not.
+  intros P_x.
+  unfold not in not_exists_x_Px.
+  refine (not_exists_x_Px _).
+    exact (ex_intro P x P_x).
+Qed.
+
+(**
+Again, this isn't a very readable proof.  It goes by:
+  - we use intros and unfold until our subgoal is "False".
+  - we use "refine" to call a function whose type ends in "-> False"
+  - we create something of type "exists" by calling "ex_intro"
+*)
+
+(** *** Calculating witnesses *)
+(**
+For some predicates, the witness is a single value or is easy to
+generate.  Other times, it isn't.  In those cases, we can write a
+function to calculate the witness.
+
+Right now, we can't write very complicated functions, since we only
+know a few types.  When we have lists and natural numbers, you'll see
+some examples.
+
+*)
+
+(** ** Equality *)
+(**
+Now we come to the big prize: equality!  Equality is a derived concept
+in Coq.  It's an inductive type, just like "and", "or", and "ex"
+("exists").  When I found that out, I was shocked and fascinated!
+
+It's defined as:
+<<
+Inductive eq (A:Type) (x:A) : A -> Prop :=
+    eq_refl : x = x :>A
+
+where "x = y :> A" := (@eq A x y) : type_scope.
+
+Notation "x = y" := (x = y :>_) : type_scope.
+>>
+
+The "Inductive" statement creates a new type "eq" which is a function
+of a type A and 2 values of type A to Prop.  (NOTE: One value of type
+A is written (x:A) before the ":" and the other is written "A ->"
+after.  This is done so Coq infers the type "A" from the first
+value and not the second.)  Calling "eq" with all its arguments
+returns a proposition (with type Prop).  A proof of "eq x y" means
+that "x" and "y" both have the same type and that "x" equals "y".
+
+The only way to create a proof of type "eq" is to use the only
+constructor "eq_refl".  It takes a value of "x" of type "A" and
+returns "@eq A x x", that is, that "x" is equal to itself.  (The "@"
+prevents Coq from inferring values, like the type "A".)  The name
+"eq_refl" comes from the reflexive property of equality.
+
+Lastly, comes two operators.  The less commonly used one is "x = y :>
+A" which let's you say that "x" and "y" are equal and both have type
+"A".  The one you'll use most of the time, "x = y", does the same but
+let's Coq infer the type "A" instead of forcing you to type it.
+
+Now, if you we paying attention, you saw that "eq_refl" is the only
+constructor.  We can only create proofs of "x = x"!  That doesn't seem
+useful at all!
+
+What you don't see is that Coq allows you to execute a function call
+and substitute the result for the function call.  For example, if we had
+a function "plus" that added natural numbers (which have type "nat"),
+we could use "eq_refl (plus 1 1)" to create a proof of
+"eq nat (plus 1 1) (plus 1 1)".  Then, if we execute the second function
+call, we get "eq nat (plus 1 1) 2", that is, "1 + 1 = 2"!
+
+The concept of substituting a function call with its result or
+substituting the result with the function call is called
+"convertibility".  One tactic we've seen, "simpl", replaces
+convertible values.  We'll see more tactics in the future.
+
+
+Now that we have a concept of what "=" means in Coq, let's use it!
+*)
+
+(** *** Equality is symmetric *)
+
+Theorem thm_eq_sym : (forall x y : Set, x = y -> y = x).
+Proof.
+  intros x y.
+  intros x_y.
+  destruct x_y as [(*eq_refl*)].
+  exact (eq_refl x).
+Qed.
+
+(**
+I use the "destruct" tactic on the type "eq" because it only has one
+constructor.  Let's look at the state before and after that call.
+
+Before, it is:
+<<
+  x : Set
+  y : Set
+  x_y : x = y
+  ============================
+   y = x
+>>
+
+And after:
+<<
+  x : Set
+  ============================
+   x = x
+>>
+
+By destructing the "eq_refl", Coq realizes that "x" and "y" are
+convertible and wherever the second name is, it can be replaced by
+the first.  So, "y" disappears and is replaced by "x".  (NOTE:
+"destruct" unlike "case", does change the context, so the hypotheses
+"y" and "x_y" also disappear.)
+
+After "destruct", we're left with a subgoal of "x = x", which is
+solved by calling "eq_refl".
+
+Just to become familiar, let's prove the other big property of
+equality: transitivity.
+*)
+
+(** *** Equality is transitive *)
+
+Theorem thm_eq_trans : (forall x y z: Set, x = y -> y = z -> x = z).
+Proof.
+  intros x y z.
+  intros x_y y_z.
+  destruct x_y as [(*eq_refl*)].
+  destruct y_z as [(*eq_refl*)].
+  exact (eq_refl x).
+Qed.
+
+(**
+Seems pretty easy, doesn't it?
+
+Rather than using "destruct", most proofs using equality use the
+tactics "rewrite" and "rewrite <-".  If "x_y" has type "x = y", then
+"rewrite x_y" will replace "x" with "y" in the subgoal and "rewrite <-
+x_y" will go the other way, replacing "y" with "x".
+
+Let's see these tactics by proving transitivity again.
+*)
+
+Theorem thm_eq_trans__again : (forall x y z: Set, x = y -> y = z -> x = z).
+Proof.
+  intros x y z.
+  intros x_y y_z.
+  rewrite x_y.
+  rewrite <- y_z.
+  exact (eq_refl y).
+Qed.
+
+(**
+That is much cleaner.
+
+_RULE_: If you have a hypothesis "<name> : <a> = <b>"
+     AND "<a>" in your current subgoal
+     Then use the tactic "rewrite <name>.".
+
+_RULE_: If you have a hypothesis "<name> : <a> = <b>"
+     AND "<b>" in your current subgoal
+     Then use the tactic "rewrite <- <name>.".
+
+Let's try something that explicitly relies on convertibility.  Recall,
+"andb" is and for "bools" and that it is represented by the operator
+"&&".
+*)
+
+Theorem andb_sym : (forall a b, a && b = b && a).
+Proof.
+  intros a b.
+  case a, b.
+    (** suppose a,b is true,true *)
+    simpl.
+    exact (eq_refl true).
+
+    (** suppose a,b is true,false *)
+    simpl.
+    exact (eq_refl false).
+
+    (** suppose a,b is false,true *)
+    simpl.
+    exact (eq_refl false).
+
+    (** suppose a,b is false,false *)
+    simpl.
+    exact (eq_refl false).
+Qed.
+
+(**
+So, for this proof, we divided it into 4 cases and used convertibility
+to show that equality held in each.  Pretty simple.
+
+I should do an example with inequality too.
+*)
+
+(** *** Inequality with discriminate *)
+
+(**
+Coq uses the operator "<>" for inequality, which really means
+"equality is unprovable" or "equality implies False".
+
+<<
+Notation "x <> y  :> T" := (~ x = y :>T) : type_scope.
+Notation "x <> y" := (x <> y :>_) : type_scope.
+>>
+
+Let's start with a simple example.  Recall that "negb" is the
+"not" operation for bools.
+*)
+Theorem neq_nega: (forall a, a <> (negb a)).
+Proof.
+  intros a.
+  unfold not.
+  case a.
+    (**suppose a is true*)
+    intros a_eq_neg_a.
+    simpl in a_eq_neg_a.
+    discriminate a_eq_neg_a.
+
+    (**suppose a is false*)
+    intros a_eq_neg_a.
+    simpl in a_eq_neg_a.
+    discriminate a_eq_neg_a.
+Qed.
+(**
+To prove this, once again I had to delay calling "intros" until after
+"case".  If I did not, "a" in "a_eq_neg_a" would not get replaced
+"true" or "false".  I also used a new tactic: "discriminate".
+
+"discriminate" operates on a hypothesis where values of inductive type
+are compared using equality.  If the constructors used to generate the
+type are the different, like here where we have "true = false", then
+Coq knows that situation can never happen.  It's like a proof of
+False.  In that case, "discriminate <hypname>." ends the subgoal.
+
+When working with inductive types, you will use "discriminate" to
+eliminate a lot of cases that can never happen.
+
+_RULE_: If you have a hypothesis
+          "<name> : (<constructor1> ...) = (<constructor2> ...)
+     OR "<name> : <constant1> = <constant2>
+     Then use the tactic "discriminate <name>.".
+
+*)
+
+
+(**
+To really show off equality, we'll need something more numerous than
+just 2 booleans.  Next up is natural numbers - an infinite set of
+objects - and induction - which allows us to prove properties about
+infinite sets of objects!
+*)
+
+
+(** * Natural Numbers and Induction *)
+(** ** Peano Arithmetic *)
+(**
+The natural numbers are 0, 1, 2, 3, 4, ... .  They are the simplest
+mathematical concept that contain an unbounded number of values.
+
+In 1889, Peano published a simple set of axioms (fundamental rules)
+that defined the natural numbers and how to prove things about them.
+Peano's axioms have become the standard way in logic of talking about
+and manipulating the natural numbers.  More complex schemes - like the
+decimal numbers we usually use - are justified by proving them
+equivalent to Peano's axioms.
+
+Peano's definition of natural numbers is:
+  - 0 is a natural number.
+  - For every natural number n, (S n) is a natural number.
+
+"(S n)" is spoken as "the successor of n" but you should think about
+it as "1 + n".  If we were to list the first few of Peano's natural
+numbers, they would be:
+  - 0 is 0
+  - 1 is (S 0) = (1 + 0)
+  - 2 is (S (S 0)) = (1+ (1 + 0))
+  - 3 is (S (S (S 0)) = (1 + (1 + (1 + 0)))
+  - ...
+
+Peano's natural numbers are represented in Coq by the type "nat".
+
+<<
+Inductive nat : Set :=
+  | O : nat
+  | S : nat -> nat.
+>>
+
+_WARNING_: The "O" here is the capital-letter O, _not_ the number zero.
+
+So, Coq's natural numbers exactly matches the definition of Peano.
+
+If we want to do something with "nat"s, we need some functions.  Here
+is how Peano's definition of addition looks in Coq.
+
+<<
+Fixpoint plus (n m:nat) : nat :=
+  match n with
+  | O => m
+  | S p => S (p + m)
+  end
+
+where "n + m" := (plus n m) : nat_scope.
+>>
+
+Previously, we used the vernacular command "Definition" to define a
+function.  The command here is "Fixpoint".  "Fixpoint" must be used
+any time you define a recursive function.  It's hard to see that
+"plus" calls itself because the recursive call is hidden by the "+" in
+"p + m".
+
+We know that (2 + 3) = 5, so let's see how that property holds in Coq.
+Below, the difference between lines is one execution of a function
+call to "plus".
+
+-       (plus (S (S O)) (S (S (S O))))
+-    (S (plus    (S O)  (S (S (S O)))))
+- (S (S (plus       O   (S (S (S O))))))
+- (S (S (S (S (S O))))))
+
+Each execution of "plus" strips an "S" off the front of the first
+number and puts it in front of the recursive call to "plus".  When the
+first parameter is "O", "plus" returns the second argument.
+
+Remember that "O" and "S" are constructors - they're opaque functions
+that were created when we declared "nat".  There is no way to execute
+them.  Therefore, "(S (S (S (S (S O))))))" does not have any function
+calls that can be executed and, therefore, is the canonical form of
+the number 5.
+
+The proof of "2 + 3 = 5" only need one call to "simpl" to get it to
+the canonical form.
+*)
+
+Theorem plus_2_3 : (S (S O)) + (S (S (S O))) = (S (S (S (S (S O))))).
+Proof.
+  simpl.
+  exact (eq_refl 5).
+Qed.
+
+(**
+As you can see by "eq_refl 5", Coq will translate decimal numbers into
+terms of "nat" automatically.  This means capital-letter "O" and the
+digit "0" are really the same term, which prevents a lot of mistakes.
+There are ways to instruct Coq to treat decimals as other types, such
+as integers, rationals, etc.
+
+We can convince ourselves that the function "plus" has all the
+properties associated with addition, by proving those properties.  So,
+let's prove them!
+*)
+
+Theorem plus_O_n : (forall n, O + n = n).
+Proof.
+  intros n.
+  simpl.
+  exact (eq_refl n).
+Qed.
+
+(** ** Induction *)
+(**
+
+It was easy to prove "(forall n, O + n = n)", but it's much harder to
+prove "(forall n, n + O = n)".  The reason is that the function "plus"
+executes "match" on its first parameter.  If the first parameter is a
+constant, such as "O", we can use "simpl" to execute "plus" and get
+the answer.  But, if the first argument is a variable, like "n", we
+can't.
+
+To solve this, we need to do the proof by induction on "n".  Peano's
+axiom for induction is:
+<<
+  If "P" is a predicate such that
+     (P 0) is proven, and
+     for all n, (P n) -> (P (S n)) is proven,
+  Then (P n) is proven for all n.
+>>
+
+The proof of "(P O)" is known as the "base case".  The proof of
+"(forall n:nat, P n -> P (S n))" is known as the "inductive case" and,
+inside that case, "P n" is known as the "inductive hypothesis".
+
+When "nat" was declared, Coq created a function that exactly
+represents induction on "nat"s.  It is called "nat_ind" and you can
+see its type with the vernacular command "Check".
+*)
+Check nat_ind.
+(**
+Its output should look like this:
+<<
+nat_ind
+     : forall P : nat -> Type,
+         P O -> (forall n : nat, P n -> P (S n)) -> forall n : nat, P n
+>>
+
+Now, we _could_
+  - define a function "P n" that represents "O + n = n"
+  - prove that the function holds for "P O"
+  - prove that the function holds for "P (S n)" assuming "P n"
+  - and then call the function nat_ind with those 3 values.
+
+But that's a lot of work.  Luckily, Coq defines tactics that do most
+of the work for us!  These tactics take a subgoal and infer the
+predicate P, and then generates child subgoals for the other pieces.
+
+One tactic, "elim" works very similarly to "case".
+*)
+
+Theorem plus_n_O : (forall n, n + O = n).
+Proof.
+  intros n.
+  elim n.
+    (** base case *)
+    simpl.
+    exact (eq_refl O).
+
+    (** inductive case *)
+    intros n'.
+    intros inductive_hypothesis.
+    simpl.
+    rewrite inductive_hypothesis.
+    exact (eq_refl (S n')).
+Qed.
+
+(**
+It's obvious how the tactic "elim" generates the base case and
+inductive case.  It was important to me to show that the "n" in the
+inductive case was different from the original "n" in our goal.  I
+chose to name it "n'" which is spoken "n prime".
+
+I used the "rewrite" tactic with the inductive hypothesis to
+substitute "n' + O" in the subgoal.  If you're using the "elim"
+tactic, you should always use the inductive hypothesis.  If you don't,
+you should use the "case" tactic instead of "elim".
+
+
+_RULE_: If there is a hypothesis "<name>" of a created type
+     AND that hypothesis is used in the subgoal,
+     AND the type has a recursive definition,
+     Then you can try the tactic "elim <name>.".
+*)
+
+
+(** *** Induction tactic *)
+(**
+Just as the "case" tactic has a similar "destruct" tactic, the "elim"
+tactic has a similar "induction" tactic.  Let's look at the previous
+proof, but using this new tactic.
+*)
+Theorem plus_n_O__again : (forall n, n + O = n).
+Proof.
+  intros n.
+  induction n as [|n' inductive_hypothesis].
+    (** base case *)
+    simpl.
+    exact (eq_refl O).
+
+    (** inductive case *)
+    simpl.
+    rewrite inductive_hypothesis.
+    exact (eq_refl (S n')).
+Qed.
+(**
+So, the "induction" tactic does the same thing as "elim.", except the
+names of the created variables are listed in the tactic, rather than
+being assigned later using "intros" in the inductive case of the proof.
+
+The "induction" command creates 2 subgoals, one for the base case and
+another for the inductive case, and after the "as" keyword, there are
+2 lists of variable names, one for the base case and one for the
+inductive case.  Those lists are separated by verical bars ('|')
+inside the square brackets.  The base case doesn't create any
+variables, so its list is empty.  The inductive case creates two
+variables, and they are named "n'" and "inductive_hypothesis".
+
+I said the "induction" command was similar to "destruct" and, if the
+type "destruct"ed has more than one constructor, the "destruct"
+command will create a subgoals for each constructor and the command
+needs a list of variable names for each constructor.  For example, the
+type "or" has two constructors.  Recall that something of type "or A
+B" can be created by "or_introl proof_of_A" or "or_intror proof_of_B".
+If I "destruct" an "or A B", it will create two subgoals and the
+"destruct" needs to have a list of variables for each.  To
+demonstrate this, I'll redo the proof that or commutes.
+
+*)
+
+Theorem or_commutes__again : (forall A B, A \/ B -> B \/ A).
+Proof.
+  intros A B.
+  intros A_or_B.
+  destruct A_or_B as [proof_of_A | proof_of_B].
+    (** suppose A_or_B is (or_introl proof_of_A) *)
+    refine (or_intror _).
+      exact proof_of_A.
+
+    (** suppose A_or_B is (or_intror proof_of_B) *)
+    refine (or_introl _).
+      exact proof_of_B.
+Qed.
+
+(**
+In my proofs, I like to use "case" and "elim" and only use "destruct"
+for types with a single constructor.  However, some people prefer to
+use "destruct" and "induction" for every proof.  The writers of Coq
+are talking about removing this duplication and may remove "case" and
+"elim" in the future.
+
+If you want to use "destruct" and "induction", it is helpful to use
+the Vernacular command "Print", which prints out a definition and
+shows you how many constructors there are and which variables you need
+to name for each one.
+*)
+Print or.
+Print nat_ind.
+(**
+
+Now we return to proofs on induction using "nat".  And let's do a
+difficult proof.  Let's prove that "n + m = m + n".
+*)
+(** *** Addition is Symmetric *)
+Theorem plus_sym: (forall n m, n + m = m + n).
+Proof.
+  intros n m.
+  elim n.
+    (** base case for n *)
+    elim m.
+      (** base case for m *)
+      exact (eq_refl (O + O)).
+
+      (** inductive case for m *)
+      intros m'.
+      intros inductive_hyp_m.
+      simpl.
+      rewrite <- inductive_hyp_m.
+      simpl.
+      exact (eq_refl (S m')).
+
+    (** inductive case for n *)
+    intros n'.
+    intros inductive_hyp_n.
+    simpl.
+    rewrite inductive_hyp_n.
+    elim m.
+      (** base case for m *)
+      simpl.
+      exact (eq_refl (S n')).
+
+      intros m'.
+      intros inductive_hyp_m.
+      simpl.
+      rewrite inductive_hyp_m.
+      simpl.
+      exact (eq_refl (S (m' + S n'))).
+Qed.
+
+(**
+That is a _hard_ proof.  I've seen an expert in Coq get stuck on it
+for 10 minutes.  It uses a lot of the features that I've demonstrated
+in this tutorial.  I encourage you to open a new file and try to prove
+it yourself.  If you get stuck on part of it, you should use the "admit" tactic
+and move onto another part.  If you get really stuck, open up this
+file and look at how I did it in my proof.  You should keep practicing
+on that proof until you can do it automatically.
+*)
+
+(** *** Common nat operators *)
+(**
+"nat" is a commonly used type in Coq and there are a lot of operators
+defined for it.  Having seen how "plus" was defined, you can probably
+guess the definition for "mult".
+
+<<
+Fixpoint mult (n m:nat) : nat :=
+  match n with
+  | O => 0
+  | S p => m + p * m
+  end
+
+where "n * m" := (mult n m) : nat_scope.
+>>
+
+Also defined are:
+  - "n - m" := (minus n m)
+  - "n <= m" := (le n m)
+  - "n <  m" := (lt n m)
+  - "n >= m" := (ge n m)
+  - "n >  m" := (gt n m)
+
+Along with:
+  - "x <= y <= z" := (x <= y /\ y <= z)
+  - "x <= y < z"  := (x <= y /\ y <  z)
+  - "x < y < z"   := (x <  y /\ y <  z)
+  - "x < y <= z"  := (x <  y /\ y <= z)
+  - "max n m"
+  - "min n m"
+
+The dangerous one in this group is "minus".  The natural numbers don't
+go lower than 0, so "(1 - 2)" cannot return a negative number.  Since
+Coq must return something of type "nat", it returns "O" instead.  So
+you can do crazy proofs like this without intending!
+*)
+
+Theorem minus_is_funny : (0 - 1) = (0 - 2).
+Proof.
+  simpl.
+  exact (eq_refl 0).
+Qed.
+
+(**
+Later, with lists, I'll show you different ways to implement "minus"
+without this flaw.
+*)
+
+
+(** * Data types*)
+(**
+You've learned the basics of Coq.  This section introduces some
+commonly used data types, so that you get to see them and get some
+more experience reading proofs.
+*)
+
+Require Import List.
+
+(** ** Lists and Option *)
+(**
+
+Lists are a common data structure in functional programming.
+(Imperative programs use more arrays.)  The definition of a
+singly-linked list in Coq is:
+
+<<
+Inductive list (A : Type) : Type :=
+ | nil : list A
+ | cons : A -> list A -> list A.
+>>
+
+I'll address the type "Type" in a second, but for the moment know that
+a list takes a type called "A" and is either empty - constructed using
+"nil" - or a node containing a value of type "A" and a link to another
+list.
+
+In a number of earlier places, I've ignored the type "Type".  It hides
+some magic in Coq.  We saw early on that "proof_of_A" was a proof
+and had type "A", which was a proposition.  "A", since it was a
+proposition, had type "Prop".  But if types can have types, what type
+does "Prop" have?  The answer is "Type(1)".  The answer to your next N
+questions is that "Type(1)" has type "Type(2)".  "Type(2)" has type
+"Type(3)".  Etc.
+
+Similarly, "true" had type "bool".  "bool" had type "Set".  And "Set" has
+type "Type(1)" (just like "Prop").
+
+Coq hides this infinite hierarchy from the user with the magic type
+"Type".  When you use "Type", Coq will determine if the value lies in
+"Prop", "Set", or any of the the "Type(...)" types.
+
+Going back to lists, there is an operator for building a list.
+<<
+Infix "::" := cons (at level 60, right associativity) : list_scope.
+>>
+So, the value "5 :: 4 :: nil" would be a "list nat" containing the
+values 5 and 4.
+
+A simple function that works on all types of lists is "length":
+<<
+Definition length (A : Type) : list A -> nat :=
+  fix length l :=
+  match l with
+   | nil => O
+   | _ :: l' => S (length l')
+  end.
+>>
+
+Just to get started, let's prove that adding an element to a list
+increases it's length by 1.
+*)
+Theorem cons_adds_one_to_length :
+   (forall A:Type,
+   (forall (x : A) (lst : list A),
+   length (x :: lst) = (S (length lst)))).
+Proof.
+  intros A.
+  intros x lst.
+  simpl.
+  exact (eq_refl (S (length lst))).
+Qed.
+(**
+The proof is pretty simple, but the statement of what we want to prove
+is not!  First, we need "A", which is the type stored in our lists.
+Then we need an instance of A and a instance of a list of A.  Only
+then can we state that putting an element on the front of a list
+increases its length by 1.
+*)
+
+(** *** The three forms of hd *)
+(**
+Now, the simplest function you can imagine for a linked list is "hd",
+which returns the first value in the list.  But there's a problem:
+what should we return if the list is empty?
+
+In some programming languages, you might throw an exception - either
+explicitly or through a memory violation.  In others, you might assume
+the program crashes.  But if you to prove a program correct, these
+aren't choices you can make.  The program needs to be predictable.
+
+There are choices you can make:
+  - have "hd" take an additional parameter, which it returns if the list is empty
+  - have "hd" return a new data structure that may or may not contain the value
+  - pass "hd" a proof that the list is not empty
+
+You didn't think of that last one, did you?!  I'll show you each of these
+approaches so that you can see what they look like.
+*)
+(** **** hd with additional parameter *)
+
+Definition hd (A : Type) (default : A) (l : list A)
+:=
+  match l with
+    | nil => default
+    | x :: _ => x
+  end.
+
+(**
+This is the default version of hd in Coq and it looks like you would
+expect.  This version is more useful than you might think thanks to
+"partial evaluation".
+
+"partial evaluation" is passing only _some_ of the arguments to a
+function.  In Coq, and many functional languages, the result is a new
+function where the supplied arguments are now treated like constants
+in the function and the remaining parameters are can still be passed
+in.
+
+Thus, we can do:
+*)
+Definition my_hd_for_nat_lists
+:=
+  hd nat 0.
+(**
+The remaining parameter - the list - can still be passed to
+"my_hd_for_nat_lists", but the "Type" parameter has been bound to
+"nat" and the parameter "default" has been bound to zero.
+
+We can confirm that it works correctly by using the vernacular command
+"Compute", which executes all the function calls and prints the result.
+*)
+Compute my_hd_for_nat_lists nil.
+(**
+Prints "= 0 : nat".
+*)
+Compute my_hd_for_nat_lists (5 :: 4 :: nil).
+(**
+Prints "= 5 : nat".
+
+We can also prove it correct.
+*)
+Theorem correctness_of_hd :
+   (forall A:Type,
+   (forall (default : A) (x : A) (lst : list A),
+   (hd A default nil) = default /\ (hd A default (x :: lst)) = x)).
+Proof.
+  intros A.
+  intros default x lst.
+  refine (conj _ _).
+    simpl.
+    exact (eq_refl default).
+
+    simpl.
+    exact (eq_refl x).
+Qed.
+
+(** **** hd returns option *)
+(**
+The second approach is to have "hd" return another data structure that
+either contains the first value in the list or contains nothing.  Coq
+defines a generic data structure for that, called "option".
+
+<<
+Inductive option (A : Type) : Type :=
+  | Some : A -> option A
+  | None : option A.
+>>
+
+"option" is a type with two constructors.  "Some" takes a value and
+"None" does not.  To get the value out of an "option", a proof will
+have to branch using "case" and look at the branch for the constructor
+"Some".  Likewise, a program will have to use "match" and look at the
+branch for "Some".
+
+Coq includes a version of "hd" that returns an "option".  It's called
+"hd_error".
+*)
+Definition hd_error (A : Type) (l : list A)
+:=
+  match l with
+    | nil => None
+    | x :: _ => Some x
+  end.
+(**
+Again, using "Compute" we can examine what it returns.
+*)
+Compute hd_error nat nil.
+(**
+Prints "= None : option nat".
+*)
+Compute hd_error nat (5 :: 4 :: nil).
+(**
+Prints "= Some 5 : option nat".
+
+We can also prove it correct.
+*)
+Theorem correctness_of_hd_error :
+   (forall A:Type,
+   (forall (x : A) (lst : list A),
+   (hd_error A nil) = None /\ (hd_error A (x :: lst)) = Some x)).
+Proof.
+  intros A.
+  intros x lst.
+  refine (conj _ _).
+    simpl.
+    exact (eq_refl None).
+
+    simpl.
+    exact (eq_refl (Some x)).
+Qed.
+
+(** **** hd with a proof that the list is non-empty *)
+(**
+Here's the option that's not available to any other programming
+language you've seen: we can pass to "hd" a proof that the list is not
+empty, so "hd" can never fail.
+*)
+Definition hd_never_fail (A : Type) (lst : list A) (safety_proof : lst <> nil)
+  : A
+:=
+  (match lst as b return (lst = b -> A) with
+    | nil =>    (fun foo : lst = nil =>
+                   match (safety_proof foo) return A with
+                   end
+                )
+    | x :: _ => (fun foo : lst = x :: _ =>
+                   x
+                )
+  end) eq_refl.
+
+(**
+I don't expect you to understand this function, let alone write
+it.  It took me 30 minutes to write it, mostly by copying from code
+printed using "Show Proof." from the complicated proofs above.
+
+But I do expect you to understand that it _can_ be written in Coq.
+And, I expect you to be able to read a proof that the function does
+what I said it does.
+*)
+Theorem correctness_of_hd_never_fail :
+   (forall A:Type,
+   (forall (x : A) (rest : list A),
+   (exists safety_proof : ((x :: rest) <> nil),
+      (hd_never_fail  A (x :: rest) safety_proof) = x))).
+Proof.
+  intros A.
+  intros x rest.
+  assert (witness : ((x :: rest) <> nil)).
+    unfold not.
+    intros cons_eq_nil.
+    discriminate cons_eq_nil.
+  refine (ex_intro _ witness _).
+    simpl.
+    exact (eq_refl x).
+Qed.
+(**
+The proof is pretty simple.  We create a witness for the exists.
+Since the witness involves an inequality, we end its proof with
+"discriminate".  Then we use "simpl" to show that the function returns
+what the right value.
+
+In this proof, I used the tactic "assert" instead of "pose".  I wanted
+to write:
+<<
+  pose (witness := _ : ((x :: rest) <> nil)).
+>>
+But Coq does not allow that for some reason.  "pose" cannot have "_"
+for the complete value.  If you want to use "_" for the whole value of
+pose, you have to use the tactic "assert".
+*)
+
+(** *** Tail of lists *)
+(**
+"hd" extracts the first value in a list and its partner "tl" returns
+the list after that first element.  In Coq, it's definition is:
+*)
+Definition tl (A : Type) (l:list A) :=
+  match l with
+    | nil => nil
+    | a :: m => m
+  end.
+(**
+If you want to test your skills, it would be good practice to write
+three different versions of "tl", just like I did for "hd", and prove
+them correct.
+<<
+Definition tl_error (A : Type) (l : list A)
+  : option list A
+:=
+  ...
+Definition tl_never_fail (A : Type) (lst : list A) (safety_proof : lst <> nil)
+  : list A
+:=
+  ...
+>>
+
+Here's just a simple proof that the "hd" and "tl" of a non-empty list
+can be used to reconstruct the list.
+*)
+Theorem hd_tl :
+   (forall A:Type,
+   (forall (default : A) (x : A) (lst : list A),
+   (hd A default (x::lst)) :: (tl A (x::lst)) = (x :: lst))).
+Proof.
+  intros A.
+  intros default x lst.
+  simpl.
+  exact (eq_refl (x::lst)).
+Qed.
+
+(** *** Appending lists *)
+(**
+We often want to do more than just add one element at the front of a
+list.  Coq includes the function "app", short for "append", to
+concatenate two lists.
+
+<<
+Definition app (A : Type) : list A -> list A -> list A :=
+  fix app l m :=
+  match l with
+   | nil => m
+   | a :: l1 => a :: app l1 m
+  end.
+
+Infix "++" := app (right associativity, at level 60) : list_scope.
+>>
+
+Here are some nice theorems you can try to prove about appending lists.
+Since append is defined recursively, many of these theorems must be
+proved with induction using the "elim" tactic.
+*)
+
+Theorem app_nil_l : (forall A:Type, (forall l:list A, nil ++ l = l)).
+Proof.
+  admit.  (** delete "admit" and put your proof here. *)
+Admitted. (** when done, replace "Admitted." with "Qed." *)
+
+Theorem app_nil_r : (forall A:Type, (forall l:list A, forall l:list A, l ++ nil = l)).
+Proof.
+  admit.  (** delete "admit" and put your proof here. *)
+Admitted. (** when done, replace "Admitted." with "Qed." *)
+
+Theorem app_comm_cons : forall A (x y:list A) (a:A), a :: (x ++ y) = (a :: x) ++ y.
+Proof.
+  admit.  (** delete "admit" and put your proof here. *)
+Admitted. (** when done, replace "Admitted." with "Qed." *)
+
+Theorem app_assoc : forall A (l m n:list A), l ++ m ++ n = (l ++ m) ++ n.
+Proof.
+  admit.  (** delete "admit" and put your proof here. *)
+Admitted. (** when done, replace "Admitted." with "Qed." *)
+
+Theorem app_cons_not_nil : forall A (x y:list A) (a:A), nil <> x ++ a :: y.
+Proof.
+  admit.  (** delete "admit" and put your proof here. *)
+Admitted. (** when done, replace "Admitted." with "Qed." *)
+
+
+(** * Conclusion *)
+(**
+
+I designed this tutorial to give you just enough to get started in
+Coq.  You know enough concepts to have an idea of how things work in
+Coq and how things relate.  You may only know a small number of simple
+tactics, but they're powerful enough that you can prove real theorems.
+
+As you get more experience, you'll see more complex concepts and use
+more tactics.  You may even get to use SSReflect, which is a separate
+tactics language!  As you read more Coq proofs, you'll also see
+different styles for writing proofs.  (Any expert will tell you that
+this tutorial's simple style is strikingly different from that used in
+their proofs.)
+
+I hope you've seen some of the power and possibilities of Coq.  Many
+expert provers consider Coq proofs a puzzle and I wish you luck in
+solving your puzzles!
+*)
+
+
+(** * Postscripts *)
+
+(** ** Contact *)
+(**
+HTML and Coq versions of the tutorial are hosted on the author's professional website.
+
+https://mdnahas.github.io/doc/nahas_tutorial.html
+https://mdnahas.github.io/doc/nahas_tutorial.v
+
+The author, Michael Nahas, can be reached at michael@nahas.com
+*)
+
+(** ** Further Reading *)
+(**
+Coq uses a lot of concepts that I didn't explain.  Below are some good
+sources on those topic.
+
+For learning about proofs, I recommend the Pulitzer Prize winning book
+"Godel, Escher, Bach", Chapters 7 and 8.  (Pages 181 to 230 in my
+copy.)
+
+For learning about some of the ideas used in formal proofs, you can
+read about Intuitionistic logic, the Curry-Howard correspondence, and
+the BHK-Interpretation.
+
+http://en.wikipedia.org/wiki/Intuitionistic_logic
+
+http://en.wikipedia.org/wiki/Curry-Howard_correspondence
+
+http://en.wikipedia.org/wiki/BHK_interpretation
+
+If you have a deep interest in logic, I highly recommend Gentzen's
+"Investigations into Logical Deductions".  It's not necessary at all,
+but it is a beautiful paper.
+
+If you need to learn a programming language, OCaml is the one that
+would help you with Coq the most.  Coq is written in OCaml and it
+borrows a lot of OCaml's syntax and style.
+
+https://ocaml.org
+
+To learn more about Coq, I found the textbook "Software Foundations"
+readable.  It focuses on proving programs correct.  You can also look
+at Coq's documentation webpage.
+
+https://softwarefoundations.cis.upenn.edu/
+
+https://coq.inria.fr/documentation
+*)
+
+
+(** ** Vernacular commands *)
+(**
+  - "Theorem" starts a proof.
+  - "Qed" ends a proof.
+  - "Admitted" ends an incomplete proof.
+  - "Definition" declares a function.
+  - "Fixpoint" declares a recursive function.
+  - "Inductive" declares data types.
+  - "Notation" creates new operators.
+  - "Infix" also creates new operators.
+  - "Show Proof" prints out the current state of the proof.
+  - "Require Import" reads in definitions from a file.
+  - "Check" prints out a description of a type.
+  - "Compute" prints out the result of a function call.
+*)
+
+(** ** The tactic guide *)
+
+(**
+_RULE_: If the subgoal starts with "(forall <name> : <type>, ..."
+      Then use tactic "intros <name>.".
+
+_RULE_: If the subgoal starts with "<type> -> ..."
+      Then use tactic "intros <name>.".
+
+_RULE_: If the subgoal matches an hypothesis,
+      Then use tactic "exact <hyp_name>.".
+
+_RULE_: If you have an hypothesis
+          "<hyp_name>: <type1> -> <type2> -> ... -> <result_type>"
+     OR an hypothesis
+            "<hyp_name>: (forall <obj1>:<type1>, (forall <obj2>:<type2>, ... <result_type> ...))"
+     OR any combination of "->" and "forall",
+     AND you have hypotheses of type "type1", "type2"...,
+     Then use tactic "pose" to create something of type "result_type".
+
+_RULE_: If you have subgoal "<goal_type>"
+     AND have hypothesis
+          "<hyp_name>: <type1> -> <type2> -> ... -> <typeN> -> <goal_type>",
+     Then use tactic "refine (<hyp_name> _ _ ...)." with N underscores.
+
+_RULE_: If your subgoal is "True",
+     Then use tactic "exact I.".
+
+_RULE_: If your subgoal is "~<type>" or "~(<term>)" or "(not <term>)",
+     Then use tactic "intros".
+
+_RULE_: If any hypothesis is "<name> : False",
+     Then use tactic "case <name>.".
+
+_RULE_: If the current subgoal contains a function call with all its arguments,
+     Then use the tactic "simpl.".
+
+_RULE_: If there is a hypothesis "<name>" of a created type
+     AND that hypothesis is used in the subgoal,
+     Then you can try the tactic "case <name>.".
+
+_RULE_: If the subgoal's top-most term is a created type,
+      Then use "refine (<name_of_constructor> _ _ ...).".
+
+_RULE_: If a hypothesis "<name>" is a created type with only one
+constructor,
+     Then use "destruct <name> as [ <arg1> <arg2> ... ]." to extract its arguments.
+
+_RULE_: If a hypothesis "<name>" contain a function call with all its arguments,
+     Then use the tactic "simpl in <name>.".
+
+_RULE_: If you have a subgoal that you want to ignore for a while,
+     Then use the tactic "admit.".
+
+_RULE_: If the current subgoal starts "exists <name>, ..."
+     Then create a witness and use "refine (ex_intro _ witness _).".
+
+_RULE_: If you have a hypothesis "<name> : <a> = <b>"
+     AND "<a>" in your current subgoal
+     Then use the tactic "rewrite <name>.".
+
+_RULE_: If you have a hypothesis "<name> : <a> = <b>"
+     AND "<b>" in your current subgoal
+     Then use the tactic "rewrite <- <name>.".
+
+_RULE_: If you have a hypothesis
+          "<name> : (<constructor1> ...) = (<constructor2> ...)
+     OR "<name> : <constant1> = <constant2>
+     Then use the tactic "discriminate <name>.".
+
+_RULE_: If there is a hypothesis "<name>" of a created type
+     AND that hypothesis is used in the subgoal,
+     AND the type has a recursive definition
+     Then you can try the tactic "elim <name>.".
+*)
diff --git a/ui-css/jscoq.css b/ui-css/jscoq.css
deleted file mode 100644
index 47c586a2..00000000
--- a/ui-css/jscoq.css
+++ /dev/null
@@ -1,183 +0,0 @@
-code, kbd, pre, samp {
-    font-family: Menlo,Monaco,Consolas,monospace;
-}
-
-body,html {
-    height: 100%;
-    background-color:#eee;
-}
-
-#toplevel-container {
-width: 50%;
-background-color: black;
-color: #ccc;
-overflow: auto;
-overflow-x: hidden;
-height: 100%;
-float:left;
-padding:10px;
-padding-top: 20px;
-}
-
-#toplevel-container pre#output {
-padding: 0px;
-}
-
-#toplevel-container #output {
-background-color:transparent;
-color: #ccc;
-border: none;
-line-height:18px;
-font-size: 12px;
-margin-bottom: 0px;
-}
-
-#toplevel-container textarea {
-width:90%;
-line-height:18px;
-font-size: 12px;
-background-color: transparent;
-color: #fff;
-border: 0;
-resize: none;
-outline: none;
-font-family: Menlo,Monaco,Consolas,monospace;
-font-weight: bold;
-float:left;
-margin: 0px;
-padding:0px;
-}
-#toplevel-container #sharp {
-float: left;
-line-height:18px;
-font-size: 12px;
-font-family: Menlo,Monaco,Consolas,monospace;
-white-space: pre;
-}
-.sharp:before{
-content:"# ";
-line-height:18px;
-font-size: 12px;
-font-family: Menlo,Monaco,Consolas,monospace;
-}
-.caml{
-color:rgb(110, 110, 201);
-}
-#toplevel-side {
-    position:relative;
-    width:45%;
-    height: 100%;
-    overflow: auto;
-    text-align:justify;
-    float:left;
-    margin-left:30px;
-}
-
-.stderr {
-    color: #d9534f;
-}
-.stdout {
-
-}
-
-#goal-info-area {
-    overflow: auto;
-    height: 30%;
-    float: left;
-    margin-bottom: 0.2em;
-}
-
-#toplevel-info ul{
-    padding: 0px;
-    list-style-type: none;
-}
-
-div.msg-area {
-    padding-top: 0.2em;
-    border-top: 2px solid black;
-    overflow: auto;
-    resize: vertical;
-    float: left;
-    width: 100%;
-}
-
-.msg-area span {
-    display: block;
-    padding: 2px;
-    margin-top: 2px;
-    margin-bottom: 0px;
-    border-left: 2px solid red;
-    border-top: 1px dashed red;
-    /* border-bottom: 1px solid red; */
-    border-right: 1px solid red;
-    width: 100%;
-}
-
-#goal-info-area {
-    height: 50%;
-}
-
-#goal-info-area span {
-    border: 0px;
-}
-
-#query-info-area {
-    height: 13%;
-}
-
-#jscoq-log-area {
-    height: 30%;
-}
-
-.errorloc{
-    border-bottom-width: 3px;
-    border-bottom-style: solid;
-    border-bottom-color: red;
-}
-canvas {
-    border: 1px dashed black;
-    float: left;
-    margin: 7px;
-}
-#output canvas {
-    background-color: #464646;
-    float: none;
-    display: block;
-    border: 1px dashed while;
-    margin: 7px;
-}
-#output img {
-    display:block;
-}
-#toplevel-examples {
-    width: 270px;
-    float: left;
-}
-#toplevel-examples .list-group-item{
-    padding: 5px 15px;
-}
-#btn-share {
-    float:right;
-    margin-top:-20px;
-    background-color:rgb(92, 129, 184);
-    border-color: rgb(70, 75, 128);
-    padding: 1px 5px;
-    display:none;
-}
-.clear { clear:both; }
-
-.sharp .id { color: #59B65C ; font-style: italic }
-.sharp .kw0 { color: rgb(64, 75, 190); font-weight: bold ;}
-.sharp .kw1 { color: rgb(150, 0, 108); font-weight: bold ;}
-.sharp .kw2 { color: rgb(23, 100, 42); font-weight: bold ;}
-.sharp .kw3 { color: #59B65C; font-weight: bold ;}
-.sharp .kw4 { color: #59B65C; font-weight: bold ;}
-.sharp .comment { color: green ; font-style: italic ; }
-.sharp .string { color: #6B6B6B; font-weight: bold ; }
-.sharp .text { }
-.sharp .numeric { color: #729AAF; }
-.sharp .directive { font-style: italic ; color : #EB00FF; } ;
-.sharp .escape { color: #409290 ; }
-.sharp .symbol0 { color: orange ; font-weight: bold ; }
-.sharp .symbol1 { color: #993300 ; font-weight: bold ; }
-.sharp .constant { color: rgb(0, 152, 255); }
diff --git a/ui-css/jscoqdoc.css b/ui-css/jscoqdoc.css
new file mode 100644
index 00000000..f92514f0
--- /dev/null
+++ b/ui-css/jscoqdoc.css
@@ -0,0 +1,632 @@
+body {
+  overscroll-behavior-y: none;
+  --header-bg: #4b525f87;
+  --heading-bg: #91a1d1;
+}
+
+@media (max-width: 1564px) {  /* 860 / 0.55 */
+
+  /* this is a hack that's needed because changing padding causes reflow */
+  /* and loses the current scroll position */
+  .terse#ide-wrapper #main {
+    padding-right: 10px;
+    padding-left: 10px;
+  }
+  .terse#ide-wrapper #page {
+    padding-left: 80px;
+    transition: padding 0.1s ease;
+    transition-delay: 0.5s;
+  }
+  .terse#ide-wrapper:not(.toggled) #page {
+    padding-left: 0;
+  }
+  #ide-wrapper #main {
+    transition: padding 0.1s ease;
+    transition-delay: 0.3s;
+  }
+  .full#ide-wrapper:not(.toggled) #main {
+    padding-right: 10px;
+    transition: padding 0.1s ease;
+  }
+  #ide-wrapper:not(.toggled) #page {
+    transition-delay: 0s;
+  }
+  .terse#ide-wrapper #panel-wrapper {
+    transition-delay: 0.1s;
+  }
+
+}
+
+#ide-wrapper.layout-flex #panel-wrapper {
+  max-width: 38em !important;  /* jsCoq's default is 48em */
+}
+
+.doc div.code {
+  display: block;
+  overflow-x: auto;
+}
+
+*:focus {
+  outline: none;
+}
+
+#jscoq-plug {
+  position: absolute;
+  height: 32px;
+  right: 0;
+  top: 40px;
+  width: 40px !important;
+  background: url(../ui-images/jscoq-splash.png);  
+  background-size: cover;
+  cursor: pointer;
+  /*transition: visibility 0s linear 1s, width 0.2s ease; */
+  transition: width 0.2s ease;
+}
+
+#jscoq-plug:hover {
+  width: 60px !important;
+}
+
+body.goals-active #jscoq-plug {
+  width: 0 !important;
+}
+
+#panel-wrapper button.close {
+  position: absolute;
+  padding: 0;
+  border: none;
+  top:       0;     left:        0;
+  width:     15px;  height:      15px;
+  font-size: 16px;  line-height: 10px;
+}
+
+#panel-wrapper button.close:hover {
+  background: #ccc;
+}
+
+#panel-wrapper #toolbar {
+  padding-left: 15px;
+}
+
+/*
+ * Larger fonts in presentation mode (SF's `terse`)
+ */
+
+body.terse #panel-wrapper .flex-panel {
+  font-size: 125%;
+}
+body.terse #panel-wrapper .exits.right {
+  display: none;
+}
+
+body.terse .CodeMirror.jscoq {
+  font-size: 22px;
+  line-height: 34px;
+}
+
+body.terse #goal-panel div.contextual-info {
+  font-size: 100%; /* overrides jsCoq's style */
+}
+
+body.terse #query-panel .Error,
+body.terse #query-panel .Warning,
+body.terse #query-panel .Notice,
+body.terse #query-panel .Info,
+body.terse #query-panel .Debug {
+  background-size: 18px;
+  background-position-y: 3px;
+  padding-left: 22px;
+}
+
+#footer hr {
+  border-top: 1px solid black;
+  opacity: 0.2;
+}
+
+
+/* ---------------------------------------------------------------- */
+
+/**
+ * Document body styles;
+ * Based on Software Foundations 
+ */
+
+body {
+  padding: 0px 0px;
+  margin: 0px 0px;
+  padding-left: 1em;
+  background-color: white;
+  font-family: Arial, Helvetica, sans-serif;
+  background-repeat: no-repeat;
+  background-size: 100%;
+}
+
+#page {
+  display: block;
+  padding: 0px;
+  margin: 0px;
+}
+
+#header {
+  min-height: 30px;
+  max-width: 760px;
+  margin: 0 auto;
+  padding-left: 80px;
+  padding-right: 80px;
+  padding-top: 30px;
+  background-color: var(--header-bg);
+}
+
+#header h1 {
+  padding: 0;
+  margin: 0;
+}
+
+/* Menu */
+ul#menu {
+  padding: 0;
+  display: block;
+  margin: auto;
+}
+
+ul#menu li, div.button {
+  display: inline-block;
+  background-color: white;
+  padding: 5px;
+  font-size: .70em;
+  text-transform: uppercase;
+  width: 30%;
+  text-align: center;
+  font-weight: 600;
+}
+
+div.button {
+      margin-top:5px;
+  width: 40%;
+}
+
+#button_block {margin-top:50px;}
+
+ul#menu a.hover li  {
+  background-color: red;
+}
+
+/* Contents */
+
+#main, #main_home {
+  display: block;
+  padding: 80px;
+  padding-top: 60px; /* BCP */
+  font-size: 100%;
+  line-height: 100%;
+  max-width: 760px;
+  background-color: #ffffff;
+  margin: 0 auto;
+}
+
+#main_home {
+  background-color: rgba(255, 255, 255, 0.5);
+}
+
+#index_content div.intro p {
+  font-size: 12px;
+}
+
+#main  h1 {
+  /* line-height: 80%; */
+  line-height: normal;
+  padding-top: 3px;
+  padding-bottom: 4px;
+  /* Demitri: font-size: 22pt; */
+  font-size: 200%; /* BCP */
+}
+
+/* allow for multi-line headers */
+#main  a.idref:visited {color : #416DFF; text-decoration : none; }
+#main  a.idref:link {color : #416DFF; text-decoration : none; }
+#main  a.idref:hover {text-decoration : none; }
+#main  a.idref:active {text-decoration : none; }
+
+#main  a.modref:visited {color : #416DFF; text-decoration : none; }
+#main  a.modref:link {color : #416DFF; text-decoration : none; }
+#main  a.modref:hover {text-decoration : none; }
+#main  a.modref:active {text-decoration : none; }
+
+#main .keyword { color : #697f2f }
+#main { color: black }
+
+/* General section class - applies to all .section IDs */
+.section {
+  padding-top: 12px;
+  padding-bottom: 11px;
+  padding-left: 8px;
+  margin-top: 5px;
+  margin-bottom: 3px;
+  margin-top: 18px;
+  font-size : 125%;
+  color: #ffffff;
+}
+
+.section, ul#menu li.section_name, div.button, td.logical_tab, .ui-state-active  {
+  background-color: var(--heading-bg);
+}
+
+/* Book title in header */
+.booktitleinheader {
+  color: #000000;
+  text-transform: uppercase;
+  font-weight: 300;
+  letter-spacing: 1px;
+  font-size: 125%;
+  margin-left: 0px;
+  margin-bottom: 22px;
+}
+
+/* Chapter titles */
+.libtitle {
+  max-width: 860px;
+  text-transform: uppercase;
+  margin: 5px auto;
+  font-weight: 500;
+  padding-bottom: 2px;
+  font-size: 120%;
+  letter-spacing: 3px;
+}
+
+.subtitle {
+  display: block;
+  padding-top: 10px;
+  font-size: 70%;
+  line-height: normal;
+}
+
+h2.section {
+  color: #2a2c71;
+  background-color: transparent;
+  padding-left: 0px;
+  padding-top: 0px;
+  padding-bottom: 0px;
+  /* margin-top: 0px; */
+  margin-top: 9px; /* BCP 2/20 */
+  font-size : 135%; }
+
+h3.section {
+  /* background-color: rgb(90%,90%,100%); */
+  background-color: white;
+  /* padding-left: 8px; */
+  padding-left: 0px;
+  /* padding-top: 7px;  */
+  padding-top: 0px;
+  /* padding-bottom: 0px; */
+  padding-bottom: 0.5em;
+  /* margin-top: 9px; */
+  /* margin-top: 4px;  (BCP 2/20) */
+  margin-top: 9px;   /* BCP 2/20 */
+  font-size : 115%;
+  color:black;
+}
+
+h4.section {
+  margin-top: .2em;
+  background-color: white;
+  color: #2a2c71;
+  padding-left: 0px;
+  padding-top: 0px;
+  padding-bottom: 0.5em; /* 0px;  */
+  font-size : 100%;
+  font-style : bold;
+  text-decoration : underline;
+}
+
+#header p {
+  font-size: 13px;
+}
+
+/* Sets up Main ID and margins */
+
+#main .doc {
+  margin: 0px auto;
+  font-size: 14px;
+  line-height: 22px;
+  /* max-width: 570px; */
+  color: black;
+  /* text-align: justify; */
+  border-style: plain;
+  /* This might work better after changing back to standard coqdoc: */
+  padding-top: 10px;
+  /* padding-top: 18px; */
+}
+
+.quote {
+  margin-left: auto;
+  margin-right: auto;
+  width: 40em;
+  color: darkred;
+}
+
+.loud {
+  color: darkred;
+}
+
+pre {
+  margin-top: 0px;
+  margin-bottom: 0px;
+}
+
+.inlinecode {
+  display: inline;
+  /* font-size: 125%; */
+  color: #444444;
+  font-family: monospace }
+
+.doc .inlinecode {
+  display: inline;
+  font-size: 105%;
+  color: rgb(35%,35%,70%);
+  font-family: monospace }
+
+h1 .inlinecode .id, h1.section span.inlinecode {
+      color: #ffffff;
+}
+
+.inlinecodenm {
+  display: inline;
+  /* font-size: 125%; */
+  color: #444444;
+}
+
+.doc .inlinecodenm {
+  display: inline;
+  color: rgb(35%,35%,70%);
+}
+
+.doc .inlinecodenm .id {
+  color: rgb(35%,35%,70%);
+}
+
+.doc .code {
+  display: inline;
+  font-size: 110%;
+  color: rgb(35%,35%,70%);
+  font-family: monospace;
+  padding-left: 0px;
+}
+
+.comment {
+  display: inline;
+  font-family: monospace;
+  color: rgb(50%,50%,80%);
+}
+
+.inlineref {
+  display: inline;
+  /* font-family: monospace; */
+  color: rgb(50%,50%,80%);
+}
+
+.show::before {
+  /* content: "more"; */
+  content: "+";
+}
+
+.show {
+  background-color: rgb(93%,93%,93%);
+  display: inline;
+  font-family: monospace;
+  font-size: 60%;
+  padding-top: 1px;
+  padding-bottom: 2px;
+  padding-left: 4px;
+  padding-right: 4px;
+  color: rgb(60%,60%,60%);
+}
+
+/*
+.show {
+  display: inline;
+  font-family: monospace;
+  font-size: 60%;
+  padding-top: 0px;
+  padding-bottom: 0px;
+  padding-left: 10px;
+  border: 1px;
+  border-style: solid;
+  color: rgb(75%,75%,85%);
+}
+*/
+
+.proofbox {
+  font-size: 90%;
+  color: rgb(75%,75%,75%);
+}
+
+#main .less-space {
+  margin-top: -12px;
+}
+
+/* Inline quizzes */
+.quiz:before {
+  color: rgb(40%,40%,40%);
+  /* content: "- Quick Check -" ; */
+  display: block;
+  text-align: center;
+  margin-bottom: 5px;
+}
+.quiz {
+  border: 4px;
+  border-color: rgb(80%,80%,80%);
+  /*
+  margin-left: 40px;
+  margin-right: 100px;
+ */
+  padding: 5px;
+  padding-left: 8px;
+  padding-right: 8px;
+  margin-top: 10px;
+  margin-bottom: 15px;
+  border-style: solid;
+}
+
+/* For textual ones... */
+.show-old {
+  display: inline;
+  font-family: monospace;
+  font-size: 80%;
+  padding-top: 0px;
+  padding-bottom: 0px;
+  padding-left: 3px;
+  padding-right: 3px;
+  border: 1px;
+  margin-top: 5px;   /* doesn't work?! */
+  border-style: solid;
+  color: rgb(75%,75%,85%);
+}
+
+.largebr {
+  margin-top: 10px;
+}
+
+.code {
+  padding-left: 20px;
+  font-size: 14px;
+  font-family: monospace;
+  line-height: 17px;
+  margin-top: 9px;
+}
+
+/* Working:
+.code {
+  display: block;
+  padding-left: 0px;
+  font-size: 110%;
+  font-family: monospace;
+}
+*/
+
+.code-space {
+  max-width: 50em;
+  margin-top: 0em;
+  /* margin-bottom: -.5em; */
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.code-tight {
+  max-width: 50em;
+  margin-top: .6em;
+  /* margin-bottom: -.7em; */
+  margin-left: auto;
+  margin-right: auto;
+}
+
+hr.doublespaceincode {
+  height: 1pt;
+  visibility: hidden;
+  font-size: 10px;
+}
+
+/*
+code.br {
+height: 5em;
+}
+*/
+
+#main .citation {
+  color: rgb(70%,0%,0%);
+  text-decoration: underline;
+}
+
+table.infrule {
+  border: 0px;
+  margin-left: 50px;
+  margin-top: .5em;
+  margin-bottom: 1.2em;
+}
+
+td.infrule {
+  font-family: monospace;
+  text-align: center;
+  /*    color: rgb(35%,35%,70%);  */
+  padding: 0px;
+  line-height: 100%;
+}
+
+tr.infrulemiddle hr {
+  margin: 1px 0 1px 0;
+}
+
+.infrulenamecol {
+  color: rgb(60%,60%,60%);
+  font-size: 80%;
+  padding-left: 1em;
+  padding-bottom: 0.1em
+}
+
+#footer {
+  font-size: 65%;
+  font-family: sans-serif;
+}
+
+.id { display: inline; }
+
+.id[title="constructor"] {
+  color: #697f2f;
+}
+
+.id[title="var"],
+.id[title="variable"] {
+  color: rgb(40%,0%,40%);
+}
+
+.id[title="definition"] {
+  color: rgb(0%,40%,0%);
+}
+
+.id[title="abbreviation"] {
+  color: rgb(0%,40%,0%);
+}
+
+.id[title="lemma"] {
+  color: rgb(0%,40%,0%);
+}
+
+.id[title="instance"] {
+  color: rgb(0%,40%,0%);
+}
+
+.id[title="projection"] {
+  color: rgb(0%,40%,0%);
+}
+
+.id[title="method"] {
+  color: rgb(0%,40%,0%);
+}
+
+.id[title="inductive"] {
+  color: #034764;
+}
+
+.id[title="record"] {
+  color: rgb(0%,0%,80%);
+}
+
+.id[title="class"] {
+  color: rgb(0%,0%,80%);
+}
+
+.id[title="keyword"] {
+  color : #697f2f;
+  /*     color: black; */
+}
+
+.inlinecode .id {
+  color: rgb(0%,0%,0%);
+}
+
+.nowrap {
+  white-space: nowrap;
+}
+
+.HIDEFROMHTML {
+  display: none;
+}
diff --git a/ui-js/jscoq-agent.js b/ui-js/jscoq-agent.js
new file mode 100644
index 00000000..1c78f420
--- /dev/null
+++ b/ui-js/jscoq-agent.js
@@ -0,0 +1,83 @@
+/**
+ * Injects jsCoq into an existing page.
+ * This script has to be at the end of the body so that it runs after
+ * the page DOM has loaded.
+ */
+
+function jsCoqInject() {
+    b = document.body;
+    b.setAttribute('id', 'ide-wrapper');
+    b.classList.add('toggled');
+    b.classList.add(isTerse() ? 'terse' : 'full');
+
+    var plug = document.createElement('div');
+    plug.setAttribute('id', 'jscoq-plug');
+    plug.addEventListener('click', jsCoqStart);
+    b.append(plug);
+}
+
+var jsCoqShow = location.search === '?jscoq=on' ||
+                location.search !== '?jscoq=off' && localStorage.jsCoqShow === 'true';
+
+var jscoq_ids  = ['#main > div.code, #main > div.HIDEFROMHTML > div.code'];
+var jscoq_opts = {
+    layout:    'flex',
+    show:      jsCoqShow,
+    focus:     false,
+    replace:   true,
+    editor:    { mode: { 'company-coq': true }, className: 'jscoq code-tight' },
+    init_pkgs: ['init'],
+    all_pkgs:  { '+': ['coq'] },
+    init_import: ['utf8'],
+    implicit_libs: true
+};
+
+async function jsCoqLoad() {
+    // - remove empty code fragments (coqdoc generates some spurious ones)
+    for (let code of document.querySelectorAll('#main > div.code')) {
+        if (code.textContent.match(/^\s*$/)) code.remove();
+    }
+
+    // - make page div focusable so that keyboard scrolling works
+    var page = document.querySelector('#page');
+    page.setAttribute('tabindex', -1);
+    page.focus();
+
+    // - set presenter keyboard bindings to page-up/page-down to allow editing
+    if (typeof KEYS !== 'undefined')
+        Object.assign(KEYS, {
+            next: 34,        // PageDown
+            prev: 33         // PageUp
+        });
+
+    // - load and start jsCoq
+    await JsCoq.load(jscoq_opts.base_path);
+
+    Deprettify.REPLACES.push(   // LF,PLF define their own versions (for Imp)
+        [/∨/g, '\\/'], [/∧/g, '/\\'], [/↔/g, '<->'], [/≤/g, '<='], [/≠/g, '<>'],
+        [/∈/g, '\\in']);
+
+    var coq = await JsCoq.start(jscoq_ids, jscoq_opts);
+    window.coq = coq;
+    window.addEventListener('beforeunload', () => { localStorage.jsCoqShow = coq.layout.isVisible(); })
+
+    // - close button (replaces jsCoq's bulky power button)
+    //$('#panel-wrapper #toolbar').prepend($('<button>').addClass('close').text('×')
+    //    .on('click', () => coq.layout.hide()));
+}
+
+function jsCoqStart() {
+    coq.layout.show();
+}
+
+/** SF-style terse mode */
+function isTerse() {
+    return !!document.querySelector('[src$="/slides.js"]');
+}
+
+if (location.search !== '?jscoq=no') {
+    window.addEventListener('DOMContentLoaded', () => {
+        jsCoqInject();
+        jsCoqLoad();
+    });
+}
diff --git a/ui-js/jscoqdoc.js b/ui-js/jscoqdoc.js
new file mode 100755
index 00000000..b7d6cb9b
--- /dev/null
+++ b/ui-js/jscoqdoc.js
@@ -0,0 +1,54 @@
+#!/usr/bin/env node
+
+const fs = require('fs'),
+      path = require('path'),
+      chld = require('child-process-promise');
+
+
+const JSCOQ_URL = process.env['JSCOQ_URL'] || '.',
+      SCRIPTS = ["ui-js/jscoq-loader.js", "ui-js/jscoq-agent.js"],
+      STYLES = ["ui-css/jscoqdoc.css"];
+
+function treatText(text) {
+    return text
+        .replace(/(<link href=")coqdoc[.]css(".*>)/, (_, pref, suf) =>
+            [...SCRIPTS.map(mkScript), ...STYLES.map(mkStyle)].join('\n'));
+}
+
+function treatFile(fn) {
+    var text = fs.readFileSync(fn, 'utf-8');
+    fs.writeFileSync(fn, treatText(text));
+}
+
+function url(fn) {
+    return `${JSCOQ_URL}/${fn}`;
+}
+
+function mkScript(fn) {
+    return `<script src="${url(fn)}"></script>`;
+}
+function mkStyle(fn) {
+    return `<link href="${url(fn)}" rel="stylesheet" type="text/css" />`;
+}
+
+async function main() {
+    var args = process.argv.slice(2),
+        coqdocArgs = args.filter(x => !x.endsWith('.html')),
+        html = args.map(a => {
+            if (a.endsWith('.html')) return a;
+            else if (a.endsWith('.v'))
+                return path.basename(a).replace(/[.]v$/, '.html')
+        }).filter(x => x);
+
+    if (coqdocArgs.length > 0) {
+        await chld.spawn('coqdoc', coqdocArgs, {stdio: 'inherit'});
+    }
+
+    for (let fn of html)
+        treatFile(fn);
+}
+
+main().catch(e => {
+    console.log(`error: ${e.message}`);
+    process.exit(1);
+})
-- 
GitLab