*********************************** * * * 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.