(** * More On Induction *** Version of 10/13/2008 *) (* This chapter summarizes where we've been and takes the discussion of induction to a deeper level. *) Require Export func2. (* skip func3 *) (* Quick review... We've now seen a bunch of Coq's fundamental tactics -- enough, in fact, to do pretty much everything we'll want for a while. We'll introduce one or two more as we go along through the next few lectures, but basically this is the set we need. For quick reference, here's a summary: - intros move premises from goal to context - simpl simplify computations in the goal - simpl in H ... or a hypothesis - rewrite rewrite the goal - rewrite... in H ... or a hypothesis using an equational hypothesis or lemma - unfold replace a defined constant by its right-hand side in the goal - unfold... in H ... or a hypothesis - apply justify goal using a hypothesis, lemma, or constructor - apply... in H apply a hypothesis, lemma, or constructor to a hypothesis in the context (forward reasoning) - apply... with... explicitly specify values for variables that cannot be determined by pattern matching - destruct case analysis on values of inductively defined types - induction induction on values of inductively defined types - inversion reason by injectivity and distinctness of constructors - assert (e) as H introduce a "local lemma" e and call it H *) (* Note: many tactics (e.g., [apply], [reflexivity]) will automatically perform simplification before doing the rest of their work, so there is no need to put [simpl] before them explicitly. *) (* Quick summary of topics: What we've seen so far: - inductive definitions of datatypes - Fixpoints over inductive datatypes - higher-order functions (map, fold, filter, etc.) - polymorphism - basic Coq -- inductive proofs -- several fundamental tactics Still to come (before Midterm I): - subtleties of induction: generalizing IHs, induction principles - "programming with propositions" - logical connectives as inductive propositions - basic ideas of operational semantics *) (* ---------------------------------------------------------- *) (** * A closer look at induction *) (* The [induction] tactic actually does quite a bit of low-level bookkeeping for us. Recall the informal statement of the induction principle for natural numbers: If [P n] is some proposition involving a natural number n, and we want to show that P holds for ALL numbers n, we can reason like this: - show that [P O] holds - show that, if [P n] holds, then so does [P (S n)] - conclude that [P n] holds for all n. So, when we begin a proof with [intros n] and then [induction n], we are first telling Coq to consider PARTICULAR [n] (by introducing it into the context) and then telling it to prove something about ALL numbers (by using induction). What Coq actually does in this situation, internally, is to "re-generalize" the variable we perform induction on. For example, in the proof above that [plus] is associative... *) Lemma plus_assoc' : forall m n p : nat, plus m (plus n p) = plus (plus m n) p. Proof. (* ...we first introduce all 3 variables into the context, which amounts to saying "Choose an arbitrary [m], [n], and [p]..." *) intros m n p. (* ...We now use the [induction] tactic to prove [P m] (that is, [plus m (plus n p) = plus (plus m n) p]) for ALL [m], and hence also for the particular [m] that is in the context at the moment. *) induction m. Case "O". reflexivity. Case "S". (* In the second subgoal generated by [induction] -- the "inductive step" -- we must prove that [P m] implies [P (S m)] for all [m]. The [induction] tactic automatically introduces [m] and [P m] into the context for us, leaving just [P (S m)] as the goal. *) simpl. rewrite -> IHm. reflexivity. Qed. (* It also works to apply [induction] to a variable that is quantified in the goal. *) Lemma plus_commut' : forall m n : nat, plus m n = plus n m. Proof. induction m. Case "O". intros n. rewrite -> plus_0. reflexivity. Case "S". intros n. simpl. rewrite -> IHm. rewrite -> dist_succ_plus. reflexivity. Qed. (* Note that [induction m] leaves [n] still bound in the goal -- i.e., what we are proving inductively is a statement beginning with [forall n]. *) (* If we do [induction] on a variable that is quantified in the goal AFTER some other quantifiers, the [induction] tactic will automatically introduce these quantifiers into the context. *) Lemma plus_commut'' : forall m n : nat, plus m n = plus n m. Proof. (* Let's do induction on [n] this time, instead of [m]... *) induction n. Case "O". simpl. rewrite -> plus_0. reflexivity. Case "S". simpl. rewrite <- IHn. rewrite -> dist_succ_plus. reflexivity. Qed. (* --------------------------------------------------- *) (** * Generalizing induction hypotheses *) (* Last week's homework included a proof that the [double] function is injective. As many people discovered experimentally, the way we *start* this proof is a little bit delicate: if we begin it with [intros m. induction m.], all is well. But if we begin it with [intros m n. induction m.], we get stuck in the middle of the inductive case... *) Lemma double_injective_FAILED : forall m n, double m = double n -> m = n. Proof. intros m n. induction m. Case "O". simpl. intros eq. destruct n. SCase "O". reflexivity. SCase "S". inversion eq. Case "S". intros eq. destruct n. SCase "O". inversion eq. SCase "S". assert (m = n) as H. SSCase "Proof of assertion". (* Here we are stuck. We need the assertion in order to rewrite the final goal (subgoal 2 at this point) to an identity. But the induction hypothesis, [IHm], does not give us [m = n] -- there is an extra [S] in the way -- so the assertion is not provable. *) Admitted. (* What went wrong here? The problem is that, at the point we invoke the induction hypothesis, we have already introduced [n] into the context -- intuitively, we have told Coq, "Let's consider some particular [m] and [n]..." and we now have to prove that, if [double m = double n] for this *this particular* [m] and [n], then [m = n]. The next tactic, [induction m] says to Coq: We are going to show the goal by induction on [m]. That is, we are going to prove that P m = "if double m = double n, then m = n" holds for all [m] by showing - P O (i.e., "if double O = double n then O = n") - P m -> P (S m) (i.e., "if double m = double n then m = n" implies "if double (S m) = double n then S m = n"). If we look closely at the second statement, it is saying something rather strange: it says that, for any *particular* [n], if we know "if double m = double n then m = n" then we can prove "if double (S m) = double n then S m = n". To see why this is strange, let's think of a particular [n] -- say, [5]. The statement is then saying that, if we can prove Q = "if double m = 10 then m = 5" then we can prove R = "if double (S m) = 10 then S m = 5". But knowing Q doesn't give us any help with proving R! (If we tried to prove R from Q, we would say something like "Suppose [double (S m) = 10]..." but then we'd be stuck: knowing that [double (S m)] is [10] tells us nothing about whether [double m] is [10], so Q is useless at this point.) To summarize: Trying to carry out this proof by induction on [m] when [n] is already in the context doesn't work because we are trying to prove a relation involving *every* [m] but just a *single* [n]. *) (* The good proof of [double_injective] leaves [n] in the goal statement at the point where the [induction] tactic is invoked on [m]: *) Lemma double_injective' : forall m n, double m = double n -> m = n. Proof. intros m. induction m. Case "O". simpl. intros n eq. destruct n. SCase "O". reflexivity. SCase "S". inversion eq. Case "S". (* Notice that both the goal and the induction hypothesis have changed: the goal asks us to prove something more general (i.e., to prove the statement for *every* [n]), but the IH is correspondingly more flexible, allowing us to choose any [n] we like when we apply the IH. *) intros n eq. (* Now we choose a particular [n] and introduce the assumption that [double m = double n]. Since we are doing a case analysis on [m], we need a case analysis on [n] to keep the two "in sync". *) destruct n. SCase "O". inversion eq. (* The 0 case is trivial *) SCase "S". (* At this point, since we are in the second branch of the [destruct n], the [n] mentioned in the context at this point is actually the predecessor of the one we started out talking about. Since we are also in the [S] branch of the induction, this is perfect: if we instantiate the generic [n] in the IH with the [n] that we are talking about right now (this instantiation is performed automatically by [apply]), then [IHm] gives us exactly what we need to finish the proof. *) assert (m = n) as H. SSCase "Proof of assertion". apply IHm. inversion eq. reflexivity. rewrite -> H. reflexivity. Qed. (* So what we've learned is that we need to be careful about using induction to try to prove something too specific: If we're proving a property of [m] and [n] by induction on [m], we may need to leave [n] generic. However, this strategy doesn't always apply directly. Suppose, for example, that we had decided we wanted to prove [double_injective] by induction on [n] instead of [m]. *) Lemma double_injective_take2_FAILED : forall m n, double m = double n -> m = n. Proof. intros m n. induction n. Case "O". simpl. intros eq. destruct m. SCase "O". reflexivity. SCase "S". inversion eq. Case "S". intros eq. destruct m. SCase "O". inversion eq. SCase "S". assert (m = n) as H. SSCase "Proof of assertion". (* Here we are stuck again, just like before. *) Admitted. (* The problem is that, to do induction on [n], we must first introduce [m]. (If we simply say [induction n] without introducing anything first, Coq will automatically introduce [m] for us!) What can we do about this? One possibility is to rewrite the statement of the lemma so that [n] is quantified before [m]. This will work, but it's not nice: We don't want to have to mangle the statements of lemmas to fit the needs of a particular strategy for proving them -- we want to state them in the most clear and natural way. What we can do instead is to first introduce all the quantified variables and then RE-GENERALIZE one or more of them, taking them out of the context and putting them back at the beginning of the goal. The [generalize dependent] tactic does this. *) Lemma double_injective_take2 : forall m n, double m = double n -> m = n. Proof. intros m n. (* [m] and [n] are both in the context *) generalize dependent m. (* Now [m] is back in the goal and we can do induction on [n] and get a sufficiently general IH. *) induction n. Case "O". simpl. intros m eq. destruct m. SCase "O". reflexivity. SCase "S". inversion eq. Case "S". intros m eq. destruct m. SCase "O". inversion eq. SCase "S". assert (m = n) as H. SSCase "Proof of assertion". apply IHn. inversion eq. reflexivity. rewrite -> H. reflexivity. Qed. Lemma plus_m_m_injective_take2 : forall m n, plus m m = plus n n -> m = n. Proof. (* Carry out this proof by induction on [n]. *) (* SOLUTION *) intros m n. generalize dependent m. induction n. Case "0". intros m eq. destruct m. SCase "O". reflexivity. SCase "S". inversion eq. Case "S". intros m eq. destruct m. SCase "O". inversion eq. SCase "S". assert (m = n) as H. SSCase "Proof of assertion". apply IHn. simpl in eq. inversion eq. rewrite -> dist_succ_plus in eq. rewrite -> dist_succ_plus in eq. inversion eq. reflexivity. rewrite -> H. reflexivity. Qed. Lemma length_snoc''' : forall (n : nat) (X : Set) (v : X) (l : list X), length l = n -> length (snoc l v) = S n. Proof. (* Prove this by induction on [l]. *) (* SOLUTION *) intros n X v l. generalize dependent n. induction l. Case "nil". intros n eq. rewrite <- eq. reflexivity. Case "cons". intros n eq. simpl. destruct n. SCase "O". inversion eq. SCase "S". assert (length (snoc l v) = S n). SSCase "Proof of assertion". apply IHl. inversion eq. reflexivity. rewrite -> H. reflexivity. Qed. Lemma eqnat_false_S : forall m n, beq_nat m n = false -> beq_nat (S m) (S n) = false. Proof. (* Prove this by induction on [n]. *) (* SOLUTION *) intros m n. generalize dependent m. induction n. Case "O". intros m eq. destruct m. SCase "O". inversion eq. SCase "S". reflexivity. Case "S". intros m eq. destruct m. SCase "O". reflexivity. SCase "S". simpl. simpl in eq. apply eq. Qed. Lemma index_after_last : forall (n : nat) (X : Set) (l : list X), length l = n -> index (S n) l = None. Proof. (* Prove this by induction on [l] *) (* SOLUTION *) intros n X l. generalize dependent n. induction l. Case "nil". reflexivity. Case "cons". intros n eq. simpl. simpl in eq. destruct n. SCase "O". inversion eq. SCase "S". inversion eq. rewrite -> H0. apply IHl. apply H0. Qed. Lemma length_append_cons : forall (X : Set) (l1 l2 : list X) (x : X) (n : nat), length (l1 ++ (x :: l2)) = n -> S (length (l1 ++ l2)) = n. Proof. (* Prove this by induction on [l1], without using [length_append]. *) (* SOLUTION *) intros X l1 l2 x. induction l1. Case "nil". intros n eq. simpl. simpl in eq. rewrite -> eq. reflexivity. Case "cons". simpl. intros n eq. destruct n. SCase "O". inversion eq. SCase "S". assert (S (length (l1 ++ l2)) = n) as H. SSCase "Proof of assertion". apply IHl1. inversion eq. reflexivity. rewrite -> H. reflexivity. Qed.