                   ***********************************
                   *                                 *
                   *  A   M a t i t a   p r i m e r  *
                   *                                 *
                   *   (with useful ??? exercises)   *
                   ***********************************

=================================
Learning to use the on-line help:
=================================
* Select the menu Help and then the menu Contents or press F1
* In the menu you can find the syntax for lambda terms and the syntax
  and semantics of every tactic and tactical available in the system

=================================
Learning to type Unicode symbols:
=================================
* Unicode symbols are written like this: \lambda \eta \leq ...
* Optional: to get the gliph corresponding to the Unicode symbol,
  type Alt+L (for ligature) just after the \something stuff
* Additional ligatures (use Alt+L to get the gliph)
  :=   for   \def
  ->   for   \to
  =>   for   \Rightarrow
  <=   for   \leq
  >=   for   \geq
* Commonly used Unicode symbols:
  \to     for  logical implication and function space
  \forall
  \exists
  \Pi     for dependent product
  \lambda
  \land   for logical and, both on propositions and booleans
  \lor    for logical or, both on propositions and booleans
  \lnot   for logical not, both on propositions and booleans

==============================
How to set up the environment:
==============================
* Every file must start with a line like this:

  set "baseuri" "cic:/matita/nat/plus/".

  that says that every definition and lemma in the current file
  will be put in the cic:/matita/nat/plus namespace.
  For an exercise put in a foo.ma file, use the namespace
  cic:/matita/foo/
* Files can start with inclusion lines like this:

  include "nat/plus.ma".

  This is required to activate the notation given in the nat/times.ma file.
  If you do not include "nat/times.ma", you will still be able to use all
  the definitions and lemmas given in "nat/plus.ma", but without the nice
  infix '+' notation for addition.

====================================
How to browse and search the library:
====================================
* Open the menu View and then New CIC Browser. You will get a browser-like
  window with integrated searching functionalities
* To explore the library, type the URI "cic:" in the URI field and start
  browsing. Definitions will be rendered as such. Theorems will be rendered
  in a declarative style even if initially produced in a procedural style.
* To get a nice notation for addition and natural numbers, put in your
  script  include "nat/plus.ma"  and execute it. Then use the browser to
  render cic:/matita/nat/plus/associative_plus.con. The declarative proof
  you see is not fully expanded. Every time you see a "Proof" or a
  "proof of xxx" you can click on it to expand the proof. Every constant and
  symbol is an hyperlink. Follow the hyperlinks to see the definition of
  natural numbers and addition.
* The home button visualizes in declarative style the proof under development.
  It shows nothin when the system is not in proof mode.
* Theorems and definitions can be looked for by name using wildcards. Write
  "*associative*" in the seach bar, select "Locate" and press Enter.
  You will see the result list of URIs. Click on them to follow the hyperlink.
* If you know the exact statement of a theorem, but not its name, you can write
  its statement in the search bar and select match. Try with
  "\forall n,m:nat. n + m = m + n". Sometimes you can find a theorem that is
  just close enough to what you were looking for. Try with
  "\forall n:nat. O = n + O"  (O is the letter O, not the number 0)
* Sometimes you may want to obtain an hint on what theorems can be applied
  to prove something. Write the statement to prove in the search bar and select
  Hint. Try with "S O + O = O + S O". As before, you can get some useful
  results that are not immediately usable in their current form.
* Sometimes you may want to look for the theorems and definitions that are
  instances of a given statement. Write the statement in the search bar
  using lambda abstractions in front to quantify the variables to be
  instantiated. Then use Instance. Try with "\lambda n.\forall x:nat.x+n=x".

=====================
How to define things:
=====================
* Look in the manual for Syntax and then Definitions and declarations.
  Often you can omit the types of binders if they can be inferred.
  Use question marks "?" to ask the system to infer an argument of an
  application. Non recursive definitions must be given using "definition".
  Structural recursive definitions must be given using "let rec".
  Try the following examples:

  axiom f: nat \to nat

  definition square := \lambda A:Type.\lambda f:A \to A. \lambda x. f (f x).

  definition square_f : nat \to nat \def square ? f.

  inductive tree (A:Type) : Type \def
     Empty: tree A
   | Cons: A \to tree A \to tree A \to tree A.

  let rec size (A:Type) (t: tree A) on t : nat \def
   match t with
    [ Empty \Rightarrow O
    | Cons _ l r \Rightarrow size ? l + size ? r
    ].

====================
How to prove things:
====================
* Elementary proofs can be done by directly writing the lambda-terms
  (as in Agda or Epigram). Try to complete the following proofs:

  lemma ex1:
   \forall A,B:Prop.
     ((\forall X:Prop.X \to  X) \to  A \to  B) \to  A \to  B \def 
    λA,B:Prop.λH. ...

  lemma ex2: \forall n,m. m + n = m + (n + O) \def 
    ...

  Hint: to solve ex2 use eq_f and plus_n_O. Look for their types using
  the browser.

* The usual way to write proofs is by using either the procedural style
  (as in Coq and Isabelle) or the still experimental declarative style
  (as in Isar and Mizar). Let's start with the declarative style.
  Look in the manual for the following declarative tactics:

    assume id:type.                            (* new assumption *)
    suppose formula (id).                      (* new hypothesis *)
    by lambda-term done.                       (* concludes the proof *)
    by lambda-term we proved formula (id).     (* intermediate step *)
    by _ done.                                 (* concludes the proof *)
    by _ we proved formula (id).               (* intermediate step *)

  Declarative tactics must always be terminated by a dot.
  When automation fails (last two tactics), you can always help the system
  by adding new intermediate steps or by writing the lambda-term by hand.

  Prove again ex1 and ex2 in declarative style. A proof in declarative
  style starts with

    lemma id: formula.
    theorem id: formula.

  (the two forms are totally equivalent) and ends with

    qed.

  Hint: you can select well-formed sub-formulae in the sequents window,
  copy them (using the Edit/Paste menu item or the contextual menu item)
  and paste them in the text (using the Edit/Copy menu item or the
  contextual menu item).

* The most used style to write proofs in Matita is the procedural one.
  In the rest of this tutorial we will only present the procedural style.
  Look in the manual for the following procedural tactics:

    intros
    apply lambda-term
    autobatch                (* in the manual autobatch is called auto *)

  Prove again ex1 and ex2 in procedural style. A proof in procedural style
  starts and ends as a proof in declarative style. The two styles can be
  mixed.

* Some tactics open multiple new goals. For instance, copy the following
  lemma:

  lemma ex3: \forall A,B:Prop. A \to B \to (A \land B) \land (A \land B).
   intros;
   split;

  Look for the split tactic in the manual. The split tactic of the previous
  script has created two new goals, both of type (A \land B). Notice that
  the labels ?8 and ?9 of both goals are now in bold. This means that both
  goals are currently active and that the next tactic will be applied to
  both goals. The ";" tactical used after "intros" and "split" has exactly
  this meaning: it activates all goals created by the previous tactic.
  Look for it in the manual, then execute "split;" again. Now you can see
  four active goals. The first and third one ask to prove A; the reamining
  ones ask to prove B. To apply different tactics to the selected goal, we
  need to branch over the selected goals. This is achieved by using the
  tactical "[" (branching). Now type "[" and exec it. Only the first goal
  is now active (in bold), and all the previously active goals have now
  subscripts ranging from 1 to 4. Use the "apply H;" tactic to solve the goal.
  No goals are now selected. Use the "|" (next goal) tactical to activate
  the next goal. Since we are able to solve the new active goal and the
  last goal at once, we want to select the two branches at the same time.
  Use the "2,4:" tactical to select the goals having as subscripts 2 and 4.
  Now solve the goals with "apply H1;" and select the last remaining goal
  with "|". Solve the goal with "apply H;". Finally, close the branching
  section using the tactical "]" and complete the proof with "qed.".
  Look for all this tacticals in the manual. The "*:" tactical is also
  useful: it is used just after a "[" or "|" tactical to activate all the
  remaining goals with a subscript (i.e. all the goals in the innermost
  branch).

  If a tactic "T" opens multiple goals, then "T;" activates all the new
  goals opened by "T". Instead "T." just activates the first goal opened
  by "T", postponing the remaining goals without marking them with subscripts.
  In case of doubt, always use "." in declarative scripts and only all the
  other tacticals in procedural scripts.

==========================
Computation and rewriting:
==========================
* State the following theorem:

  lemma ex4: \forall n,m. S (S n) + m = S (S (n + m)).

  and introduce the hypotheses with "intros". To complete the proof, we
  can simply compute "S (S n) + m" to obtain "S (S (n + m))". Using the
  browser (click on the "+" hyperlink), look at the definition of addition:
  since addition is defined by recursion on the first argument, and since
  the first argument starts with two constructors "S", computation can be
  made. Look for the "simplify" tactic in the manual and use it to
  obtain a trivial equality. Solve the equality using "reflexivity", after
  having looked for it in the manual.
* State the following theorem:

  lemma ex5: \forall n,m. n + S (S m) = S (S (n + m)).

  Try to use simplify to complete the proof as before. Why is "simplify"
  not useful in this case? To progress in the proof we need a lemma
  stating that "\forall n,m. S (n + m) = n + S m". Using the browser,
  look for its name in the library. Since the lemma states an equality,
  it is possible to use it to replace an instance of its left hand side
  with an instance of its right hand side (or the other way around) in the
  current sequent. Look for the "rewrite" tactic in the manual, and use
  it to solve the exercise. There are two possible solutions: one only
  uses rewriting from left to right ("rewrite >"), the other rewriting
  from right to left ("rewrite <"). Find both of them.
* It may happen that "simplify" fails to yield the simplified form you
  expect. In some situations, simplify can even make your goal more complex.
  In these cases you can use the "change" tactic to convert the goal into
  any other goal which is equivalent by computation only. State again
  exercise ex4 and solve the goal without using "simplify" by means of
  "change with (S (S (n + m)) = S (S (n + m))".
* Simplify does nothing to expand definitions that are not given by
  structural recursion. To expand definition "X" in the goal, use the
  "unfold X" tactic.

  State the following lemma and use "unfold Not" to unfold the definition
  of negation in terms of implication and False. Then complete the proof
  of the theorem.

  lemma ex6: \forall A:Prop. \lnot A \to A \to False.

* Sometimes you may be interested in simplifying, changing, unfolding or even
  substituting (by means of rewrite) only a sub-expression of the
  goal. Moreover, you may be interested in simplifying, changing, unfolding or
  substituting a (sub-)expression of one hypothesis. Look in the manual
  for these tactics: all of them have an optional argument that is a
  pattern. You can generate a pattern by: 1) selecting the sub-expression you
  want to act on in the sequent; 2) copying it (using the Edit/Copy menu
  item or the contextual menu); 3) pasting it as a pattern using the
  "Edit/Paste as pattern" menu item. Other tactics also have pattern arguments.
  State and solve the following exercise:

  lemma ex7: \forall n. (n + O) + (n + O) = n + (n + O).

  The proof of the lemma must rewrite the conclusion of the sequent to
  n + (n + O) = n + (n + O) and prove it by reflexivity.

  Hint: use the browser to look for the theorem that proves
   \forall n. n = n + O  and then use a pattern to control the behaviour
   of "rewrite <".

====================
Proofs by induction:
====================
* Functions can be defined by structural recursion over arguments whose
  type is inductive. To prove properties of these functions, a common
  strategy is to proceed by induction over the recursive argument of the
  function. To proceed by induction over an inductive argument "x", use
  the "elim x" tactic.

  Now include "nat/orders.ma" to activate the notation \leq.
  Then state and prove the following lemma by induction over n:

  lemma ex8: \forall n,m. m \leq n + m.

  Hint 1: use "autobatch" to automatically prove trivial facts
  Hint 2: "autobatch" never performs computations. In inductive proofs
   you often need to "simplify" the inductive step before using
   "autobatch". Indeed, the goal of proceeding by induction over the
   recursive argument of a structural recursive definition is exactly
   that of allowing computation both in the base and inductive cases.
* Using the browser, look at the definition of addition over natural
  numbers. You can notice that all the parameters are fixed during
  recursion, but the one we are recurring on. This is the reason why
  it is possible to prove a property of addition using a simple induction
  over the recursive argument. When other arguments of the structural
  recursive functions change in recursive calls, it is necessary to
  proceed by induction over generalized predicates where the additional
  arguments are universally quantified.

  Give the following tail recursive definition of addition between natural
  numbers:

  let rec plus' n m on n \def
   match n with
    [ O \Rightarrow m
    | S n' \Rightarrow plus' n' (S m)
    ].

  Note that both parameters of plus' change during recursion.
  Now state the following lemma, and try to prove it copying the proof
  given for ex8 (that started with "intros; elim n;")

  lemma ex9: \forall n,m. m \leq plus' n m.

  Why is it impossible to prove the goal in this way? 
  Now start the proof with "intros 1;", obtaining the generalized goal
  "\forall m. m \leq plus' n m", and proceed by induction on n using
  "elim n" as before. Complete the proof by means of simplification and
  autobatch. Why is it now possible to prove the goal in this way?
* Sometimes it is not possible to obtain a generalized predicate using the
  "intros n;" trick. However, it is always possible to generalize the
  conclusion of the goal using the "generalize" tactic. Look for it in the
  manual.

  State again ex9 and find a proof that starts with
  "intros; generalize in match m;".
* Some predicates can also be given as inductive predicates.
  In this case, remember that you can proceed by induction over the
  proof of the predicate. In particular, if H is a proof of
  False/And/Or/Exists, then "elim H" corresponds to False/And/Or/Exists
  elimination.

  State and prove the following lemma:

  lemma ex10: \forall A,B:Prop. A \lor (False \land B) \to A.

====================
Proofs by inversion:
====================
* Some predicates defined by induction are really defined as dependent
  families of predicates. For instance, the \leq relation over natural
  numbers is defined as follow:

  inductive le (n:nat) : nat \to Prop \def
     le_n: le n n
   | le_S: \forall m. le n m \to le n (S m).

  In Matita we say that the first parameter of le is a left parameter
  (since it is at the left of the ":" sign), and that the second parameter
  is a right parameter. Dependent families of predicates are inductive
  definitions having a right parameter.

  Now, consider a proof H of (le n E) for some expression E.
  Differently from what happens in Agda, proceeding by elimination of H
  (i.e. doing an "elim H") ignores the fact that the second argument of
  the type of H was E. Equivalently, eliminating H of type (le n E) and
  H' of type (le n E'), you obtain exactly the same new goals even if
  E and E' are different.

  State the following exercise and try to prove it by elimination of
  the first premise (i.e. by doing an "intros; elim H;").

  lemma ex11: \forall n. n \leq O \to n = O.

  Why cannot you solve the exercise?
  To exploit hypotheses whose type is inductive and whose right parameters
  are instantiated, you can sometimes use the "inversion" tactic. Look
  for it in the manual. Solve exercise ex11 starting with
  "intros; inversion H;". As usual, autobatch is your friend to automate
  the proof of trivial facts. However, autobatch never performs introduction
  of hypotheses. Thus you often need to use "intros;" just before "autobatch;".
  
  Note: most of the time the "inductive hypotheses" generated by inversion
  are completely useless. To remove a useless hypothesis H from the context
  you can use the "clear H" tactic. Look for it in the manual.
* The "inversion" tactic is based on the t_inv lemma that is automatically
  generated for every inductive family of predicates t. Look for the
  t_inv lemma using the browser and study the clever trick (a funny
  generalization) that is used to prove it. Brave students can try to
  prove t_inv using the tactics described so far.
  
=========================================================
Proofs by injectivity and discrimination of constructors:
=========================================================
* It is not unusual to obtain hypotheses of the form k1 args1 = k2 args2
  where k1 and k2 are either equal or different constructors of the same
  inductive type. If k1 and k2 are different constructors, the hypothesis
  k1 args1 = k2 args2 is contradictory (discrimination of constructors);
  otherwise we can derive the equality between corresponding arguments
  in args1 and args2 (injectivity of constructors). Both operations are
  performed by the "destruct" tactic. Look for it in the manual.

  State and prove the following lemma using the destruct tactic twice:

  lemma ex12: \forall n,m. \lnot (O = S n) \land (S (S n) = S (S m) \to n = m).
* The destruct tactic is able to prove things by means of a very clever trick
  you already saw in the course by Coquand. Using the browser, look at the
  proof of ex12. Brave students can try to prove ex12 without using the
  destruct tactic.

============================================
Conjecturing and proving intermediate facts:
============================================
* Look for the "cut" tactic in the manual. It is used to assume a new fact
  that needs to be proved later on in order to finish the goal. The name
  "cut" comes from the cut rule of sequent calculus. As you know from theory,
  the "cut" tactic is handy, but not necessary. Moreover, remember that you
  can use axioms at your own risk to assume that some facts are provable.
* Given a term "t" that proves an implication or universal quantification,
  it is possible to do forward reasoning in procedural style by means of
  the "lapply (t args)" tactic that introduces the instantiated version of
  the assumption in the context. Look for lapply in the manual. As the
  "cut" tactic, lapply is quite handy, but not a necessary tactic.

=====================================================
Overloading existent notations and creating new ones:
=====================================================
* Mathematical notation is highly overloaded and full of ambiguities.
  In Matita you can freely overload notations. The type system is used
  to efficiently disambiguate formulae written by the user. In case no
  interpretation of the formula makes sense, the user is faced with a set
  of errors, corresponding to the different interpretations. In case multiple
  interpretations make sense, the system asks the user a minimal amount of
  questions to understand the intended meaning. Finally, the system remembers
  the history of disambiguations and the answers of the user to 1) avoid
  asking the user the same questions the next time the script is executed
  2) avoid asking the user many questions by guessing the intended
  interpretation according to recent history.

  State the following lemma:

  lemma foo:
   \forall n,m:nat.
    n = m \lor  (\lnot  n = m \land  ((leb n m \lor  leb m n) = true)).

  Following the hyperlink, look at the type inferred for leb.
  What interpretation Matita choosed for the first and second \lor sign?
  Click on the hyperlinks of the two occurrences of \lor to confirm your answer.
* The basic idea behind overloading of mathematical notations is the following:
  1. during pretty printing of formulae, the internal logical representation
     of mathematical notions is mapped to MathML Content (an infinitary XML
     based standard for the description of abstract syntax tree of mathematical
     formulae). E.g. both Or (a predicate former) and orb (a function over
     booleans) are mapped to the same MathML Content symbol "'or".
  2. then, the MathML Content abstract syntax tree of a formula is mapped
     to concrete syntax in MathML Presentation (a finitary XML based standard
     for the description of concrete syntax trees of mathematical formulae).
     E.g. the "'or x y" abstract syntax tree is mapped to "x \lor y".
     The sequent window and the browser are based on a widget that is able
     to render and interact MathML Presentation.
  3. during parsing, the two phases are reversed: starting from the concrete
     syntax tree (which is in plain Unicode text), the abstract syntax tree
     in MathML Content is computed unambiguously. Then the abstract syntax tree
     is efficiently translated to every well-typed logical representation.
     E.g. "x \lor y" is first translated to "'or x y" and then interpreted as
     "Or x y" or "orb x y", depending on which interpretation finally yields
     well-typed lambda-terms.
* Using leb and cases analysis over booleans, define the two new non
  recursive predicates:

   min: nat \to nat \to nat
   max: nat \to nat \to nat

  Now overload the \land notation (associated to the "'and x y" MathML
  Content formula) to work also for min:

  interpretation "min of two natural numbers" 'and x y =
   (cic:/matita/exercise/min.con x y).

  Note: you have to substitute "cic:/matita/exercise/min.con" with the URI
  determined by the baseuri you picked at the beginning of the file.

  Overload also the notation for \lor (associated to "'or x y") in the
  same way.

  To check if everything works correctly, state the following lemma:

  lemma foo: \forall b,n. (false \land b) = false \land (O \land n) = O.

  How the system interpreted the instances of \land?

  Now try to state the following ill-typed statement:

  lemma foo: \forall b,n. (false \land O) = false \land (O \land n) = O.

  Click on the three error locations before trying to read the errors.
  Then click on the errors and read them in the error message window
  (just below the sequent window). Which error messages did you expect?
  Which ones make sense to you? Which error message do you consider to be
  the "right" one? In what sense?
* Defining a new notation (i.e. associating to a new MathML Content tree
  some MathML Presentation tree) is more involved.

  Suppose we want to use the "a \middot b" notation for multiplication
  between natural numbers. Type:

  notation "hvbox(a break \middot b)"
  non associative with precedence 55
  for @{ 'times $a $b }.

  interpretation "times over natural numbers" 'times x y =
   (cic:/matita/nat/times/times.con x y).

  To check if everything was correct, state the following lemma:

  lemma foo: \forall n. n \middot O = O.

  The "hvbox (a break \middot b)" contains more information than just
  "a \middot b". The "hvbox" tells the system to write "a", "\middot" and
  "b" in an horizontal row if there is enough space, or vertically otherwise.
  The "break" keyword tells the system where to break the formula in case
  of need. The syntax for defining new notations is not documented in the
  manual yet.

=====================================
Using notions without including them:
=====================================
* Using the browser, look for the "fact" function.
  Notice that it is defined in the "cic:/matita/nat/factorial" namespace
  that has not been included yet. Now state the following lemma:

  lemma fact_O_S_O: fact O = 1.

  Note that Matita automatically introduces in the script some informations
  to remember where "fact" comes from. However, you do not get the nice
  notation for factorial. Remove the lines automatically added by Matita
  and replace them with

  include "nat/factorial.ma"

  before stating again the lemma. Now the lines are no longer added and you
  get the nice notation. In the future we plan to activate all notation without
  the need of including anything.

=============================
A relatively simple exercise:
=============================
* Start from an empty .ma file, change the baseuri and include the following
  files for auxiliary notation:

  include "nat/plus.ma".
  include "nat/compare.ma".
  include "list/sort.ma".
  include "datatypes/constructors.ma".

  In particular, the following notations for lists and pairs are introduced:
   []                    is the empty list
   hd::tl                is the list obtained putting a new element hd in
                         front of the list tl
   @                     list concatenation
   \times                is the cartesian product
   \langle l,r \rangle   is the pair (l,r)
* Define an inductive data type of propositional formulae built from
  a denumerable set of atoms, conjunction, disjunction, negation, truth and
  falsity (no primitive implication).

  Hint: complete the following inductive definition.

  inductive Formula : Type \def 
   FTrue: Formula
 | FFalse: Formula
 | FAtom: nat \to  Formula
 | FAnd: Formula \to  Formula \to  Formula
 | ...
* Define a classical interpretation as a function from atom indexes to booleans:

  definition interp \def  nat \to  bool.
* Define by structural recursion over formulas an evaluation function
  parameterized over an interpretation.

  Hint: complete the following definition. The order of the
  different cases should be exactly the order of the constructors in the
  definition of the inductive type.

  let rec eval (i:interp) F on F : bool \def
   match F with
    [ FTrue \Rightarrow true
    | FFalse \Rightarrow false
    | FAtom n \Rightarrow interp n
    | ...
* We are interested in formulas in a particular normal form where only atoms
  can be negated. Define the "being in normal form" not_nf predicate as an
  inductive predicate with one right parameter.

  Hint: complete the following definition.

  inductive not_nf : Formula \to  Prop \def 
      NTrue: not_nf FTrue
    | NFalse: not_nf FFalse
    | NAtom: \forall n. not_nf (FAtom n)
    ...
    | NNot: \forall n. not_nf (FNot (FAtom n))
* We want to describe a procedure that reduces a formula to an equivalent
  not_nf normal form. Define two mutually recursive functions elim_not and
  negate over formulas that respectively 1: put the formula in normal form
  and 2: put the negated of a formula in normal form.

  Hint: complete the following definition.

  let rec negate F \def 
    match F with
     [ FTrue \Rightarrow  FFalse
     | FFalse \Rightarrow  FTrue
     | ...
     | FNot f \Rightarrow  elim_not f]
   and elim_not F \def 
    match F with
     [ FTrue \Rightarrow  FTrue
     | FFalse \Rightarrow  FFalse
     | ...
     | FNot f \Rightarrow  negate f
     ].

  Why is not possible to only define elim_not by changing the FNot case
  to "FNot f \Rightarrow elim_not (FNot f)"?
* Prove that the procedures just given correctly produce normal forms.
  I.e. prove the following theorem.

  theorem not_nf_elim_not:
   \forall F.not_nf (elim_not F) \land  not_nf (negate F).

  Why is not possible to prove that one function produces normal forms
  without proving the other part of the statement? Try and see what happens.

  Hint: use the "n1,...,nm:" tactical to activate similar cases and solve
  all of them at once.
* Finally prove that the procedures just given preserve the semantics of the
  formula. I.e. prove the following theorem.

  theorem eq_eval_elim_not_eval:
   \forall i,F.
    eval i (elim_not F) = eval i F \land  eval i (negate F) = eval i (FNot F).

  Hint: you may need to prove (or assume axiomatically) additional lemmas on
  booleans such as the two demorgan laws.

================================
A moderately difficult exercise:
================================
* Consider the inductive type of propositional formulae of the previous
  exercise. Describe with an inductive type the set of well types derivation
  trees for classical propositional sequent calculus without implication.
  
  Hint: complete the following definitions.

  definition sequent \def  (list Formula) × (list Formula).

  inductive derive: sequent \to  Prop \def 
     ExchangeL:
      \forall l,l1,l2,f. derive 〈f::l1@l2,l〉 \to  derive 〈l1 @ [f] @ l2,l〉
   | ExchangeR: ...
   | Axiom: \forall l1,l2,f. derive 〈f::l1, f::l2〉
   | TrueR: \forall l1,l2. derive 〈l1,FTrue::l2〉
   | ...
   | AndR: \forall l1,l2,f1,f2.
      derive 〈l1,f1::l2〉 \to  derive 〈l1,f2::l2〉 \to 
       derive 〈l1,FAnd f1 f2::l2〉
   | ...

  Note that while the exchange rules are explicit, weakening and contraction
  are embedded in the other rules.
* Define two functions that transform the left hand side and the right hand
  side of a sequent into a logically equivalent formula obtained by making
  the conjunction (respectively disjunction) of all formulae in the
  left hand side (respectively right hand side). From those, define a function
  that folds a sequent into a logically equivalent formula obtained by
  negating the conjunction of all formulae in the left hand side and putting
  the result in disjunction with the disjunction of all formuale in the
  right hand side.
* Define a predicate is_tautology for formulae.
* Prove the soundness of the sequent calculus. I.e. prove

  theorem soundness:
   \forall F. derive F \to  is_tautology (formula_of_sequent F).

  Hint: you may need to axiomatically assume or prove several lemmas on
  booleans that are missing from the library. You also need to prove some
  lemmas on the functions you have just defined.

==========================
A long and tough exercise:
==========================
* Prove the completeness of the sequent calculus studied in the previous
  exercise. I.e. prove

  theorem completeness:
   \forall S. is_tautology (formula_of_sequent S) \to  derive S.

  Hint: the proof is by induction on the size of the sequent, defined as the
  size of all formulae in the sequent. The size of a formula is the number of
  unary and binary connectives in the formula. In the inductive case you have
  to pick one formula with a positive size, bring it in front using the
  exchange rule, and construct the tree applying the appropriate elimination
  rules. The subtrees are obtained by inductive hypotheses. In the base case,
  since the formula is a tautology, either there is a False formula in the
  left hand side of the sequent, or there is a True formula in the right hand
  side, or there is a formula both in the left and right hand sides. In all
  cases you can construct a tree by applying once or twice the exchange rules
  and once the FalseL/TrueR/Axiom rule. The computational content of the proof
  is a search strategy.

  The main difficulty of the proof is to proceed by induction on something (the
  size of the sequent) that does not reflect the structure of the sequent (made
  of a pair of lists). Moreover, from the fact that the size of the sequent is
  greater than 0, you need to detect the exact positions of a non atomic
  formula in the sequent and this needs to be done by structural recursion
  on the appropriate side, which is a list. Finally, from the fact that a
  sequent of size 0 is a tautology, you need to detect the False premise or
  the True conclusion or the two occurrences of a formula that form an axiom,
  excluding all other cases. This last proof is already quite involved, and
  finding the right inductive predicate is quite challenging.
