From elpi Require Import elpi. From HB Require Import structures. From mathcomp Require Import all_ssreflect. Set Implicit Arguments. Unset Strict Implicit. Unset Printing Implicit Defensive. (** #
# ** Recap of lesson 1 Proof language - [move=>] [name] [/view] [//] [/=] [[..|..]] to name/change the goal - [: name], [: (lem arg)] - [rewrite lem -lem] - [apply: lem] - [apply/view] Library - naming convention: [addnC], [eqP], [orbN], [orNb], ... - notations: [.+1], [if-is-then-else] - [Search (_ + _) inside ssrnat] - [Search addn "C" inside ssrnat] - Use the HTML doc! Approach - boolean predicates - [reflect P b] to link [bool] with [Prop] #
# -------------------------------------------------------- #
# ** Today - the SSReflect proof language (part 2) - stack model and views - [rewrite] idioms and patterns - forward reasoning with [have] - spec lemmas - [reflect] - [fooP] - idioms #
# -------------------------------------------------------- #
# *** Bookkeeping 101 - the goal is a stack - defective case/elim (the _top_ implicit tactic argument) - tactical [=>] for post processing - tactical [:] for pre processing - "rename", "reorder" #
# *) Lemma negb_and : forall b c, ~~ (b && c) = ~~ b || ~~ c. Proof. (* move=> b. move=> c. move: b. move: c. (* up and down *) move=> c b. move: b c. (* reverse order, it's a stack *) move=> b; case: b. move=> c; case: c. (* no need to name _top_, it's the default subject *) *) by case; case. Qed. (** #
# #
(notes)
# We say that the goal (under the horizontal bar) is a stack, since items can only be accessed accorrding to a stack discipline. If the goal is [forall x y, x = 1 + 2 * y -> odd x] one has to deal with [x] and [y] before accessing [x = 1 + 2 * y]. #
# #
# -------------------------------------------------------- #
# *** Induction - [elim] with generalization - [in x *] alternative - [rewrite (lem args)] to specialize a lemma #
# *) Lemma induction_fold (l : seq nat) x : foldl addn x l = x + foldl addn 0 l. Proof. (*# elim: l => [|y ys IH] /=. (* first attempt *) by rewrite addn0. (* we are stuck *) #*) elim: l x => [|y ys IH] x /=. (* better attempt, generalize and re-introduce x *) by rewrite addn0. by rewrite IH (IH y) addnA. (* IH is now quantified *) Qed. (** #
# #
(notes)
# Recall that [elim] (or [case]) actually operate on the top of the stack, which is the last item generalized by [:]. #
# #
# ------------------------------------------------------------ #
# ** Views are just lemmas (plus some automatic adaptors) - lemmas like [A -> B] can be used as views too - boolean connectives have associated views ("P" suffix) #
# *) Lemma test_leqW i j k : (i <= k) && (k.+1 <= j) -> i <= j.+1. Proof. (*# move=> /andP H; case: H. move=> /leqW leq_ik1. #*) move=> /andP[/leqW leq_ik1 /leqW leq_k1j1]. (* process fully, then name *) by apply: leq_trans leq_ik1 leq_k1j1. Qed. (** #
# #
(notes)
# There is nothin special with the [/andP[H1 H2]] we did in lesson 1, it is just the composition of [/view] followed by a case split [[]] (in this case only one branch) #
# #
# -------------------------------------- #
# ** [rewrite], one command to rule them all - 1/3 of the lines of Math Comp proofs are [rewrite] - side conditions handling via [//] and [?] - rewrite a boolean predicate ([is_true] hides an eqaution) #
# *) Lemma test_leq_cond p : prime p -> p.-1.+1 + p = p.*2. Proof. (*# move=> pr_p. Search predn inside ssrnat. rewrite prednK. (* main step *) by rewrite addnn. (* side condition *) Search prime leq 0. by apply: prime_gt0. (* conclusion *) #*) (* idiomatic use of conditional rewrite rules *) by move=> pr_p; rewrite prednK ?addnn // prime_gt0. Qed. (** #
# #
# -------------------------------------------------------- #
# ** [rewrite] and subterm selection - keyed matching drives the search - specialization via argument passing - specialization via pattern - localization via contextual pattern (approximate or precise) - LHS and RHS notations #
#*) Lemma subterm_selection n m : n + (m * 2).+1 = n * (m + m.+1). Proof. rewrite addnC. rewrite (addnC m). rewrite [_ + m]addnC. rewrite [in n * _]addnC. rewrite [X in _ = _ * X]addnC. rewrite [in RHS]addnC. Abort. Lemma occurrence_selection n m : n + m = n + m. Proof. rewrite addnC. (* all occurrecens of the rule are replaced *) rewrite [in RHS]addnC. (* limit to RHS of the goal *) Abort. Lemma no_pattern_from_the_rewrite_rule n : n + 0 = n. Proof. rewrite -[n in RHS]addn0. (* precise patterns for ambiguous rules *) Abort. (** #
# #
(notes)
# The details can be found in the reference #manual# or in the #paper# #
# #
# ------------------------------------------------------------ ------------------------------------------------------------ #
# ** The reflect predicate - [reflect P b] is the preferred way to state that the predicate [P] (in [Prop]) is logically equivalent to [b = true] #
# *) Module reflect_for_eqP. Print reflect. (* we use this boolean predicate in the examples below *) Fixpoint eqn m n := match m, n with | 0, 0 => true | j.+1,k.+1 => eqn j k | _, _ => false end. Arguments eqn !m !n. (** #
# #
# ---------------------------------------------------------- #
# ** Proving the reflection lemma for [eqn] - the convenience lemma [iffP] - the [congr] tactic - trivial branches [=> //] - rewrite on the fly [->] - [first by] - [congr] #
# *) Lemma myeqP m n : reflect (m = n) (eqn m n). Proof. (*# apply: (iffP idP) => [|->]; last by elim: n. elim: m n; first by case. move=> n IHn m eq_n1m. case: m eq_n1m => // m eq_n1m1. (* case with generalization *) congr (_.+1). (* cleanup of the goal *) by apply: IHn. #*) apply: (iffP idP) => [|->]; last by elim: n. by elim: m n => [|m IHm] // [|n] // /IHm->. Qed. Lemma test_myeqP n m : (eqn n m) -> m = n. Proof. by move=> /myeqP ->. Qed. End reflect_for_eqP. (** #
# #
# -------------------------------------------------------- #
# ** Spec lemmas - Inductive predicates to drive the proof: - you chose how many branches - you chose which equations are automatically applied - you chose which extra assumption a branch has - [of] syntax for inductives #
#*) Inductive leq_xor_gtn m n : bool -> bool -> Prop := | LeqNotGtn of m <= n : leq_xor_gtn m n true false | GtnNotLeq of n < m : leq_xor_gtn m n false true. Axiom leqP : forall m n : nat, leq_xor_gtn m n (m <= n) (n < m). (** #
# #
# -------------------------------------------------------- #
# ** Let's try out [leqP] on an ugly goal - matching of indexes uses the same discipline of [rewrite] Bonus (time permitting): - generalization of unresolved implicits after [/leq_trans] - specialization of the top stack item via [/(_ arg)] #
#*) Lemma test_leqP m n1 n2 : (m <= (if n1 < n2 then n1 else n2)) = (m <= n1) && (m <= n2) && ((n1 < n2) || (n2 <= n1)). Proof. (* the indexes of [leqP] give rise to patterns, which are matched right to left. So the first one is [_ < _] which finds [n1 < n2] and replaces it with [false] in the first branch and [true] in the second. Then it is the turn of [n2 <= n1]. use: Set Debug "ssreflect". for a slow motion *) case: leqP => [leqn21 | /ltnW ltn12 ]; rewrite /= andbT. by rewrite andb_idl // => /leq_trans /(_ leqn21). by rewrite andb_idr // => /leq_trans->. Qed. (** #
# #
(notes)
# While I presonally find [/leq_trans] too clever and likely unnecessary, it is used in the library, hence this slide #
# #
# -------------------------------------------------------- #
# ** Another commodity: [ifP] - a spec lemma for if-then-else - handy with case, since matching spares you to write the expressions involved - remark how the goal is used as a work space #
#*) Lemma test_ifP n m : if n <= m then 0 <= m - n else m - n == 0. Proof. case: ifP => //. (* MC idiom: don't introduce hyps if not necessary *) by move=> /negbT; rewrite subn_eq0 leqNgt negbK=> /ltnW. Qed. (** #
# #
# -------------------------------------------------------- -------------------------------------------------------- #
# ** Forward reasoning - [have : statement.] - [have := proof] - [have /view ... : .. := ..] and variations Definition of all << Fixpoint all a s := if s is x :: s' then a x && all a s' else true. >> Definition of count << Fixpoint count a s := if s is x :: s' then a x + count s' else 0. >> A lemma linking the two concepts #
# *) Lemma all_count (T : Type) (a : pred T) s : all a s = (count a s == size s). Proof. (* common start *) elim: s => //= x s. (* first try at using EM *) have EM_a : a x || ~~ a x. by apply: orbN. move: EM_a => /orP EM_a. case: EM_a => [-> | /negbTE-> ] //= _. (*# (* second try using views and have *) have /orP[ ax | /negbTE n_ax ] : a x || ~~ a x by case: (a x). by rewrite ax. rewrite n_ax /= => ?. #*) (*# (* this is the best way of doing it *) have [ax//|n_ax ?] := boolP (a x). #*) (* common conclusion *) by rewrite add0n eqn_leq ltnNge count_size /= andbC. Qed. (** #
# #
# -------------------------------------------------------- #
# ** References for this lesson: - SSReflect #manual# - documentation of the #library# - in particular #seq# #
# -------------------------------------------------------- #
# ** Demo (time permitting, or as an exercise) - you should be now able to read this proof #
#*) Lemma dvdn_fact m n : 0 < m <= n -> m %| n`!. Proof. case: m => //= m; elim: n => //= n IHn; rewrite ltnS leq_eqVlt. by move=> /orP[ /eqP-> | /IHn]; [apply: dvdn_mulr | apply: dvdn_mull]. Qed. Lemma prime_above m : {p | m < p & prime p}. Proof. (*# Check pdivP. #*) have /pdivP[p pr_p p_dv_m1]: 1 < m`! + 1 by rewrite addn1 ltnS fact_gt0. exists p => //; rewrite ltnNge; apply: contraL p_dv_m1 => p_le_m. (*# Check dvdn_addr. #*) by rewrite dvdn_addr ?dvdn_fact ?prime_gt0 // gtnNdvd ?prime_gt1. Qed. (** #
# #
(notes)
# This proof is also explained in the #Math Comp Book# #
# #
# *)