Related
I'm trying to write a proof in the Isabelle "structured style" and I'm not sure how to specify the value of existential variables. Specifically, I'm trying to expand the sorrys in this proof:
lemma division_theorem: "lt Zero n ⟹ ∃ q r. lt r n ∧ m = add (mul q n) r"
proof (induct m)
case Zero
then show ?case
by (metis add_zero_right mul.simps(1))
next
case (Suc m)
then show ?case
proof (cases "Suc r = n")
case True
then show ?thesis sorry
next
case False
then show ?thesis sorry
qed
qed
Zero, add, and mul are defined on a nat-like class that I made just for the purposes of writing simple number theory proofs, hopefully that is intuitive. I have done this in the "apply" style, so I'm familiar with how the proof is supposed to go, I'm just not understanding how to turn it into "structured" style.
So the goals generated by these cases are:
1. (lt Zero n ⟹ ∃q r. lt r n ∧ m = add (mul q n) r) ⟹
lt Zero n ⟹ cnat.Suc r = n ⟹ ∃q r. lt r n ∧ cnat.Suc m = add (mul q n) r
2. (lt Zero n ⟹ ∃q r. lt r n ∧ m = add (mul q n) r) ⟹
lt Zero n ⟹ cnat.Suc r ≠ n ⟹ ∃q r. lt r n ∧ cnat.Suc m = add (mul q n) r
At a high level, for that first goal, I want to grab the q and r from the first existential, specify q' = Suc q and r' = Zero for the second existential, and let sledgehammer bash out precisely what mix of arithmetic lemmas to use to prove that it works. And then do that same for q' = q and r' = Suc r for the second case.
How can I do this? I have tried various mixes of obtain, rule exI, but I feel like I'm not understanding some basic mechanism here. Using the apply style this works when I apply subgoal_tac but it seems like that is unlikely to be the ideal method of solution here.
As you can see in the two goals generated by the command cases "Suc r = n", the occurrences of variable r in both the expressions cnat.Suc r = n and cnat.Suc r ≠ n are actually free and thus not related to the existentially quantified formula whatsoever. In order to "grab" the q and r from the induction hypothesis you need to use the obtain command. As a side remark, I suggest to use the induction method instead of the induct method so you can refer to the induction hypothesis as Suc.IH instead of Suc.hyps. Once you "grab" q and r from the induction hypothesis, you just need to prove that
lt r' n, and that
Suc m = add (mul q' n) r'
with q' and r' as defined for each of your two cases. Here is a (slightly incomplete) proof of your division theorem:
lemma division_theorem: "lt Zero n ⟹ ∃ q r. lt r n ∧ m = add (mul q n) r"
proof (induction m)
case Zero
then show ?case
by (metis add_zero_right mul.simps(1))
next
case (Suc m)
(* "Grab" q and r from IH *)
from ‹lt Zero n› and Suc.IH obtain q and r where "lt r n ∧ m = add (mul q n) r"
by blast
show ?case
proof (cases "Suc r = n")
case True
(* In this case, we use q' = Suc q and r' = Zero as witnesses *)
from ‹Suc r = n› and ‹lt r n ∧ m = add (mul q n) r› have "Suc m = add (mul (Suc q) n) Zero"
using add_comm by auto
with ‹lt Zero n› show ?thesis
by blast
next
case False
(* In this case, we use q' = q and r' = Suc r as witnesses *)
from ‹lt r n ∧ m = add (mul q n) r› have "Suc m = add (mul q n) (Suc r)"
by simp
moreover have "lt (Suc r) n"
sorry (* left as exercise :) *)
ultimately show ?thesis
by blast
qed
qed
I'm an undergraduate student trying to prove correctness and termination of imperative version of Euclidean gcd and Euclidean extended gcd algorithm. I used IMP language to implement the first one and Hoare logic to prove correctness and termination:
lemma "⊢{λs. s ''a'' = n ∧ s ''b'' = m ∧ n > 0 ∧ m > 0 ∧ (gcd (s ''a'') (s ''b'') = gcd (n) (m))}
WHILE (Or (Less (V ''b'') (V ''a'')) (Less (V ''a'') (V ''b'')))
DO (IF (Less (V ''b'') (V ''a'')) THEN
(''a'' ::= Sub (V ''a'') (V ''b''))
ELSE
(''b'' ::= Sub (V ''b'') (V ''a'')))
{λs. s ''a'' = gcd (s ''A'') (s ''B'')}"
apply (rule While'[where P = "λs. s ''a'' = n ∧ s ''b'' = m ∧ 0 < n ∧ 0 < m ∧ gcd (s ''a'') (s ''b'') = gcd n m"])
apply auto
apply (rule Assign')
apply auto
prefer 2
apply (rule Assign')
apply auto
remaining sub goals are:
proof (prove)
goal (3 subgoals):
1. ⋀s. 0 < s ''a'' ⟹ m = s ''b'' ⟹ n = s ''a'' ⟹ s ''a'' < s ''b'' ⟹ False
2. ⋀s. 0 < s ''b'' ⟹ m = s ''b'' ⟹ n = s ''a'' ⟹ s ''b'' < s ''a'' ⟹ False
3. ⋀s. n = s ''a'' ⟹ m = s ''a'' ⟹ 0 < s ''a'' ⟹ s ''b'' = s ''a'' ⟹ s ''a'' = gcd (s ''A'') (s ''B'')
and I don't now how to finish the proof. The gcd function here is default gcd from GCD library. I also tried this definition from Arith2 library:
definition cd :: "[nat, nat, nat] ⇒ bool"
where "cd x m n ⟷ x dvd m ∧ x dvd n"
definition gcd :: "[nat, nat] ⇒ nat"
where "gcd m n = (SOME x. x>0 ∧ cd x m n & (∀y.(cd y m n) ⟶ y dvd x))"
Is what I wrote correct and how should I continue? Should I use these definitions instead or should I write recursive version of gcd function myself? Is this approach correct?
First of all, you have a type in one place, where you talk about s ''A'' and s ''B'' instead of s ''a'' and s ''b''. But that is of course not the problem you were asking about.
The problem here is that the precondition is too strong to work with the WHILE rule. It contains the conditions s ''a'' = n and s ''b'' = m, which clearly do not work as a loop invariant since the loop modifies the variables a and b, so after one loop iteration, one of the conditions s ''a'' = n and s ''b'' = m will no longer hold.
You need to figure out a proper invariant that is weaker than what you have now. What you have to do is to kick out the s ''a'' = n and s ''b'' = m. Then your proof goes through.
You can then recover the statement you actually want to show with the strengthen_pre rule.
So the start of your proof would look something like this:
lemma "⊢{λs. s ''a'' = n ∧ s ''b'' = m ∧ n ≥ 0 ∧ m ≥ 0 ∧ (gcd (s ''a'') (s ''b'') = gcd (n) (m))}
WHILE (Or (Less (V ''b'') (V ''a'')) (Less (V ''a'') (V ''b'')))
DO (IF (Less (V ''b'') (V ''a'')) THEN
(''a'' ::= Sub (V ''a'') (V ''b''))
ELSE
(''b'' ::= Sub (V ''b'') (V ''a'')))
{λs. s ''a'' = gcd (s ''a'') (s ''b'')}"
apply (rule strengthen_pre)
defer
apply (rule While'[where P = "λs. s ''a'' ≥ 0 ∧ s ''b'' ≥ 0 ∧ gcd (s ''a'') (s ''b'') = gcd n m"])
To avoid this awkward manual use of strengthen_pre, other versions of IMP allow annotating the invariants of WHILE loops directly in the algorithm itself, so that a VCG (verification condition generator) can automatically give you all the things you have to prove and you don't have to apply Hoare rules manually.
Addendum: Note however that there is also a problem with your postcondition:
{λs. s ''a'' = gcd (s ''a'') (s ''b'')}
This is not what you want to show! This just means that the value of a after the execution is the GCD of the values of a and b after the execution. This also happens to be true because a and b are always equal after the algorithm has finished – but what you really want to know is that the value of a after the execution is equal to the GCD of a and b before the execution, i.e. equal to gcd n m. You therefore have to change your postcondition to
{λs. s ''a'' = gcd n m}
Having gone through most exercises and also solved/proved in LEAN the first five propositional validities/properties at the end of chapter 3 in the LEAN manual, I still have trouble with the following implication (one of the implications needed for the proof of property 6):
theorem Distr_or_L (p q r : Prop) : (p ∨ q) ∧ (p ∨ r) → p ∨ (q ∧ r) :=
begin
intros pqpr,
have porq : p ∨ q, from pqpr.left,
have porr : p ∨ r, from pqpr.right,
sorry
end
The difficulty I face is mainly due to the case when p is not true, as I don't know how to combine, using LEAN tools, the two sides of the and in the hypothesis to obtain the fact that both q and r must hold under that scenario. I would greatly appreciate any help here; please help me understand how to construct this proof in the above setting without importing any other tactics except those in standard LEAN. For completeness, here is my proof of the other direction:
theorem Distr_or_R (p q r : Prop) : p ∨ (q ∧ r) → (p ∨ q) ∧ (p ∨ r) :=
begin
intros pqr,
exact or.elim pqr
( assume hp: p, show (p ∨ q) ∧ (p ∨ r), from
and.intro (or.intro_left q hp) (or.intro_left r hp) )
( assume hqr : (q ∧ r), show (p ∨ q) ∧ (p ∨ r), from
and.intro (or.intro_right p hqr.left) (or.intro_right p hqr.right) )
end
Hint. Try case splitting on both porq and porr.
Here's a solution
theorem Distr_or_L (p q r : Prop) : (p ∨ q) ∧ (p ∨ r) → p ∨ (q ∧ r) :=
begin
intros pqpr,
have porq : p ∨ q, from pqpr.left,
have porr : p ∨ r, from pqpr.right,
{ cases porq with hp hq,
{ exact or.inl hp },
{ cases porr with hp hr,
{ exact or.inl hp },
{ exact or.inr ⟨hq, hr⟩ } } }
end
Here is an asnwer for say problem without using cases. (explained later in tbe mentioned book)
example : p ∨ (q ∧ r) ↔ (p ∨ q) ∧ (p ∨ r) :=
Iff.intro
(fun hpqr => Or.elim hpqr
(fun hp: p =>
show (p ∨ q) ∧ (p ∨ r) from And.intro (Or.inl hp) (Or.inl hp))
(fun hqr: q ∧ r =>
show (p ∨ q) ∧ (p ∨ r) from And.intro (Or.inr hqr.left) (Or.inr hqr.right)))
(fun hpqpr: (p ∨ q) ∧ (p ∨ r) =>
have hpq: (p ∨ q) := hpqpr.left;
have hpr: (p ∨ r) := hpqpr.right;
Or.elim hpq
(fun hp: p => show p ∨ (q ∧ r) from Or.inl hp)
(fun hq: q => Or.elim hpr
(fun hp: p => show p ∨ (q ∧ r) from Or.inl hp)
(fun hr: r => show p ∨ (q ∧ r) from Or.inr (And.intro hq hr))))
You're using the information in the right part of the conjunction, otherwise, the left part doesn't give any info regarding r.
Given the procedure even, I want to prove that even (n * (S n)) = true for all natural numbers n.
Using induction, this is easily seen to be true for the case n = 0. However, the case (S n) * (S (S n)) is hard to simplify.
I've considered proving the lemma that even (m * n) = even m /\ even n, but this doesn't seem to be easier.
Also, it is easy to see that if even n = true iff. even (S n) = false.
Fixpoint even (n: nat) : bool :=
match n with
| O => true
| 1 => false
| S (S n') => even n'
end.
Can someone give a hint on how to prove this using a "beginner's" subset of Coq?
This is a case where a more advanced induction principle can be useful. It is briefly described in this answer.
Require Import Coq.Arith.Arith.
Require Import Coq.Bool.Bool.
Lemma pair_induction (P : nat -> Prop) :
P 0 -> P 1 -> (forall n, P n -> P (S n) -> P (S (S n))) ->
forall n, P n.
Proof.
intros ? ? ? n. enough (P n /\ P (S n)) by tauto.
induction n; intuition.
Qed.
Now, let's define several helper lemmas. They are obvious and can be easily proved using the pair_induction principle and some proof automation.
Lemma even_mul2 : forall n, Nat.even (2 * n) = true.
Proof.
induction n; auto.
now replace (2 * S n) with (2 + 2 * n) by ring.
Qed.
Lemma even_add_even : forall m n,
Nat.even m = true ->
Nat.even (m + n) = Nat.even n.
Proof.
now induction m using pair_induction; auto.
Qed.
Lemma even_add_mul2 : forall m n,
Nat.even (2 * m + n) = Nat.even n.
Proof.
intros; apply even_add_even, even_mul2.
Qed.
Lemma even_S : forall n,
Nat.even (S n) = negb (Nat.even n).
Proof.
induction n; auto.
simpl (Nat.even (S (S n))). (* not necessary -- just to make things clear *)
apply negb_sym. assumption.
Qed.
The following lemma shows how to "distribute" even over multiplication. It plays an important role in the proof of our main goal. As almost always generalization helps a lot.
Lemma even_mult : forall m n,
Nat.even (m * n) = Nat.even m || Nat.even n.
Proof.
induction m using pair_induction; simpl; auto.
intros n. replace (n + (n + m * n)) with (2 * n + m * n) by ring.
now rewrite even_add_mul2.
Qed.
Now, the proof of the goal is trivial
Goal forall n, Nat.even (n * (S n)) = true.
intros n. now rewrite even_mult, even_S, orb_negb_r.
Qed.
Can someone give a hint on how to prove this using a "beginner's" subset of Coq?
You can consider this a hint, since it reveals the general structure of a possible proof. The automatic tactics may be replaced by the "manual" tactics like with rewrite, apply, destruct and so on.
I'd like to contribute a shorter proof using the mathcomp lib:
From mathcomp Require Import all_ssreflect all_algebra.
Lemma P n : ~~ odd (n * n.+1).
Proof. by rewrite odd_mul andbN. Qed.
odd_mul is proved by simple induction, as well as odd_add.
Another version, in the spirit of #ejgallego's answer.
Let's give another definition for the even predicate. The purpose of this is to make proofs by simple induction easy, so there is no need of using pair_induction. The main idea is that we are going to prove some properties of even2 and then we'll use the fact that Nat.even and even2 are extensionally equal to transfer the properties of even2 onto Nat.even.
Require Import Coq.Bool.Bool.
Fixpoint even2 (n : nat) : bool :=
match n with
| O => true
| S n' => negb (even2 n')
end.
Let's show that Nat.even and even2 are extensionally equal.
Lemma even_S n :
Nat.even (S n) = negb (Nat.even n).
Proof. induction n; auto. apply negb_sym; assumption. Qed.
Lemma even_equiv_even2 n :
Nat.even n = even2 n.
Proof. induction n; auto. now rewrite even_S, IHn. Qed.
Some distribution lemmas for even2:
Lemma even2_distr_add m n :
even2 (m + n) = negb (xorb (even2 m) (even2 n)).
Proof.
induction m; simpl.
- now destruct (even2 n).
- rewrite IHm. now destruct (even2 m); destruct (even2 n).
Qed.
Lemma even2_distr_mult m n :
even2 (m * n) = even2 m || even2 n.
Proof.
induction m; auto; simpl.
rewrite even2_distr_add, IHm.
now destruct (even2 m); destruct (even2 n).
Qed.
Finally, we are able to prove our goal, using the equality between Nat.even and even2.
Goal forall n, Nat.even (n * (S n)) = true.
intros n.
now rewrite even_equiv_even2, even2_distr_mult, orb_negb_r.
Qed.
A short version that makes use of the standard library:
Require Import Coq.Arith.Arith.
Goal forall n, Nat.even (n * (S n)) = true.
intros n.
now rewrite Nat.even_mul, Nat.even_succ, Nat.orb_even_odd.
Qed.
For what it's worth, here is my take on the solution. The essential idea is, instead of proving a predicate P n, prove instead P n /\ P (S n), which is equivalent, but the second formulation allows to use simple induction.
Here is the complete proof:
Require Import Nat.
Require Import Omega.
Definition claim n := even (n * (S n)) = true.
(* A technical Lemma, needed in the proof *)
Lemma tech: forall n m, even n = true -> even (n + 2*m) = true.
Proof.
intros. induction m.
* simpl. replace (n+0) with n; intuition.
* replace (n + 2 * S m) with (S (S (n+2*m))); intuition.
Qed.
(* A simple identity, that Coq needs help to prove *)
Lemma ident: forall n,
(S (S n) * S (S (S n))) = (S n * S( S n) + 2*(S (S n))).
(* (n+2)*(n+3) = (n+1)*(n+2) + 2*(n+2) *)
Proof.
intro.
replace (S (S (S n))) with ((S n) + 2) by intuition.
remember (S (S n)) as m.
replace (m * (S n + 2)) with ((S n + 2) * m) by intuition.
intuition.
Qed.
(* The claim to be proved by simple induction *)
Lemma nsn: forall n, claim n /\ claim (S n).
Proof.
intros.
unfold claim.
induction n.
* intuition.
* intuition. rewrite ident. apply tech; auto.
Qed.
(* The final result is now a simple corollary *)
Theorem thm: forall n, claim n.
Proof.
apply nsn.
Qed.
I've two Fibonacci implementations, seen below, that I want to prove are functionally equivalent.
I've already proved properties about natural numbers, but this exercise requires another approach that I cannot figure out.
The textbook I'm using have introduced the following syntax of Coq, so it should be possible to prove equality using this notation:
<definition> ::= <keyword> <identifier> : <statement> <proof>
<keyword> ::= Proposition | Lemma | Theorem | Corollary
<statement> ::= {<quantifier>,}* <expression>
<quantifier> ::= forall {<identifier>}+ : <type>
| forall {({<identifier>}+ : <type>)}+
<proof> ::= Proof. {<tactic>.}* <end-of-proof>
<end-of-proof> ::= Qed. | Admitted. | Abort.
Here are the two implementations:
Fixpoint fib_v1 (n : nat) : nat :=
match n with
| 0 => O
| S n' => match n' with
| O => 1
| S n'' => (fib_v1 n') + (fib_v1 n'')
end
end.
Fixpoint visit_fib_v2 (n a1 a2 : nat) : nat :=
match n with
| 0 => a1
| S n' => visit_fib_v2 n' a2 (a1 + a2)
end.
It is pretty obvious that these functions compute the same value for the base case n = 0, but the induction case is much harder?
I've tried proving the following Lemma, but I'm stuck in induction case:
Lemma about_visit_fib_v2 :
forall i j : nat,
visit_fib_v2 i (fib_v1 (S j)) ((fib_v1 j) + (fib_v1 (S j))) = (fib_v1 (add_v1 i (S j))).
Proof.
induction i as [| i' IHi'].
intro j.
rewrite -> (unfold_visit_fib_v2_0 (fib_v1 (S j)) ((fib_v1 j) + (fib_v1 (S j)))).
rewrite -> (add_v1_0_n (S j)).
reflexivity.
intro j.
rewrite -> (unfold_visit_fib_v2_S i' (fib_v1 (S j)) ((fib_v1 j) + (fib_v1 (S j)))).
Admitted.
Where:
Fixpoint add_v1 (i j : nat) : nat :=
match i with
| O => j
| S i' => S (add_v1 i' j)
end.
A note of warning: in what follows I'll to try to show the main idea of such a proof, so I'm not going to stick to some subset of Coq and I won't do arithmetic manually. Instead I'll use some proof automation, viz. the ring tactic. However, feel free to ask additional questions, so you could convert the proof to somewhat that would suit your purposes.
I think it's easier to start with some generalization:
Require Import Arith. (* for `ring` tactic *)
Lemma fib_v1_eq_fib2_generalized n : forall a0 a1,
visit_fib_v2 (S n) a0 a1 = a0 * fib_v1 n + a1 * fib_v1 (S n).
Proof.
induction n; intros a0 a1.
- simpl; ring.
- change (visit_fib_v2 (S (S n)) a0 a1) with
(visit_fib_v2 (S n) a1 (a0 + a1)).
rewrite IHn. simpl; ring.
Qed.
If using ring doesn't suit your needs, you can perform multiple rewrite steps using the lemmas of the Arith module.
Now, let's get to our goal:
Definition fib_v2 n := visit_fib_v2 n 0 1.
Lemma fib_v1_eq_fib2 n :
fib_v1 n = fib_v2 n.
Proof.
destruct n.
- reflexivity.
- unfold fib_v2. rewrite fib_v1_eq_fib2_generalized.
ring.
Qed.
#larsr's answer inspired this alternative answer.
First of all, let's define fib_v2:
Require Import Coq.Arith.Arith.
Definition fib_v2 n := visit_fib_v2 n 0 1.
Then, we are going to need a lemma, which is the same as fib_v2_lemma in #larsr's answer. I'm including it here for consistency and to show an alternative proof.
Lemma visit_fib_v2_main_property n: forall a0 a1,
visit_fib_v2 (S (S n)) a0 a1 =
visit_fib_v2 (S n) a0 a1 + visit_fib_v2 n a0 a1.
Proof.
induction n; intros a0 a1; auto with arith.
change (visit_fib_v2 (S (S (S n))) a0 a1) with
(visit_fib_v2 (S (S n)) a1 (a0 + a1)).
apply IHn.
Qed.
As suggested in the comments by larsr, the visit_fib_v2_main_property lemma can be also proved by the following impressive one-liner:
now induction n; firstorder.
Because of the nature of the numbers in the Fibonacci series it's very convenient to define an alternative induction principle:
Lemma pair_induction (P : nat -> Prop) :
P 0 ->
P 1 ->
(forall n, P n -> P (S n) -> P (S (S n))) ->
forall n, P n.
Proof.
intros H0 H1 Hstep n.
enough (P n /\ P (S n)) by tauto.
induction n; intuition.
Qed.
The pair_induction principle basically says that if we can prove some property P for 0 and 1 and if for every natural number k > 1, we can prove P k holds under the assumption that P (k - 1) and P (k - 2) hold, then we can prove forall n, P n.
Using our custom induction principle, we get the proof as follows:
Lemma fib_v1_eq_fib2 n :
fib_v1 n = fib_v2 n.
Proof.
induction n using pair_induction.
- reflexivity.
- reflexivity.
- unfold fib_v2.
rewrite visit_fib_v2_main_property.
simpl; auto.
Qed.
Anton's proof is very beautiful, and better than mine, but it might be useful, anyway.
Instead of coming up with the generalisation lemma, I strengthened the induction hypothesis.
Say the original goal is Q n. I then changed the goal with
cut (Q n /\ Q (S n))
from
Q n
to
Q n /\ Q (S n)
This new goal trivially implies the original goal, but with it the induction hypothesis becomes stronger, so it becomes possible to rewrite more.
IHn : Q n /\ Q (S n)
=========================
Q (S n) /\ Q (S (S n))
This idea is explained in Software Foundations in the chapter where one does proofs over even numbers.
Because the propostion often is very long, I made an Ltac tactic that names the long and messy term.
Ltac nameit Q :=
match goal with [ _:_ |- ?P ?n] => let X := fresh Q in remember P as X end.
Require Import Ring Arith.
(Btw, I renamed vistit_fib_v2 to fib_v2.)
I needed a lemma about one step of fib_v2.
Lemma fib_v2_lemma: forall n a b, fib_v2 (S (S n)) a b = fib_v2 (S n) a b + fib_v2 n a b.
intro n.
pattern n.
nameit Q.
cut (Q n /\ Q (S n)).
tauto. (* Q n /\ Q (S n) -> Q n *)
induction n.
split; subst; simpl; intros; ring. (* Q 0 /\ Q 1 *)
split; try tauto. (* Q (S n) *)
subst Q. (* Q (S (S n)) *)
destruct IHn as [H1 H2].
assert (L1: forall n a b, fib_v2 (S n) a b = fib_v2 n b (a+b)) by reflexivity.
congruence.
Qed.
The congruence tactic handles goals that follow from a bunch of A = B assumptions and rewriting.
Proving the theorem is very similar.
Theorem fib_v1_fib_v2 : forall n, fib_v1 n = fib_v2 n 0 1.
intro n.
pattern n.
nameit Q.
cut (Q n /\ Q (S n)).
tauto. (* Q n /\ Q (S n) -> Q n *)
induction n.
split; subst; simpl; intros; ring. (* Q 0 /\ Q 1 *)
split; try tauto. (* Q (S n) *)
subst Q. (* Q (S (S n)) *)
destruct IHn as [H1 H2].
assert (fib_v1 (S (S n)) = fib_v1 (S n) + fib_v1 n) by reflexivity.
assert (fib_v2 (S (S n)) 0 1 = fib_v2 (S n) 0 1 + fib_v2 n 0 1) by
(pose fib_v2_lemma; congruence).
congruence.
Qed.
All the boiler plate code could be put in a tactic, but I didn't want to go crazy with the Ltac, since that was not what the question was about.
This proof script only shows the proof structure. It could be useful to explain the idea of the proof.
Require Import Ring Arith Psatz. (* Psatz required by firstorder *)
Theorem fibfib: forall n, fib_v2 n 0 1 = fib_v1 n.
Proof with (intros; simpl in *; ring || firstorder).
assert (H: forall n a0 a1, fib_v2 (S n) a0 a1 = a0 * (fib_v1 n) + a1 * (fib_v1 (S n))).
{ induction n... rewrite IHn; destruct n... }
destruct n; try rewrite H...
Qed.
There is a very powerful library -- math-comp written in the Ssreflect formal proof language that is in its turn based on Coq. In this answer I present a version that uses its facilities. It's just a simplified piece of this development. All credit goes to the original author.
Let's do some imports and the definitions of our two functions, math-comp (ssreflect) style:
From mathcomp
Require Import ssreflect ssrnat ssrfun eqtype ssrbool.
Fixpoint fib_rec (n : nat) {struct n} : nat :=
if n is n1.+1 then
if n1 is n2.+1 then fib_rec n1 + fib_rec n2
else 1
else 0.
Fixpoint fib_iter (a b n : nat) {struct n} : nat :=
if n is n1.+1 then
if n1 is n2.+1
then fib_iter b (b + a) n1
else b
else a.
A helper lemma expressing the basic property of Fibonacci numbers:
Lemma fib_iter_property : forall n a b,
fib_iter a b n.+2 = fib_iter a b n.+1 + fib_iter a b n.
Proof.
case=>//; elim => [//|n IHn] a b; apply: IHn.
Qed.
Now, let's tackle equivalence of the two implementations.
The main idea here, that distinguish the following proof from the other proofs has been presented as of time of this writing, is that we perform
kind of complete induction, using elim: n {-2}n (leqnn n). This gives us the following (strong) induction hypothesis:
IHn : forall n0 : nat, n0 <= n -> fib_rec n0 = fib_iter 0 1 n0
Here is the main lemma and its proof:
Lemma fib_rec_eq_fib_iter : fib_rec =1 fib_iter 0 1.
Proof.
move=>n; elim: n {-2}n (leqnn n)=> [n|n IHn].
by rewrite leqn0; move/eqP=>->.
case=>//; case=>// n0; rewrite ltnS=> ltn0n.
rewrite fib_iter_property.
by rewrite <- (IHn _ ltn0n), <- (IHn _ (ltnW ltn0n)).
Qed.
Here is yet another answer, similar to the one using mathcomp, but this one uses "vanilla" Coq.
First of all, we need some imports, additional definitions, and a couple of helper lemmas:
Require Import Coq.Arith.Arith.
Definition fib_v2 n := visit_fib_v2 n 0 1.
Lemma visit_fib_v2_property n: forall a0 a1,
visit_fib_v2 (S (S n)) a0 a1 =
visit_fib_v2 (S n) a0 a1 + visit_fib_v2 n a0 a1.
Proof. now induction n; firstorder. Qed.
Lemma fib_v2_property n:
fib_v2 (S (S n)) = fib_v2 (S n) + fib_v2 n.
Proof. apply visit_fib_v2_property. Qed.
To prove the main lemma we are going to use the standard well-founded induction lt_wf_ind principle for natural numbers with the < relation (a.k.a. complete induction):
This time we need to prove only one subgoal, since the n = 0 case for complete induction is always vacuously true. Our induction hypothesis, unsurprisingly, looks like this:
IH : forall m : nat, m < n -> fib_v1 m = fib_v2 m
Here is the proof:
Lemma fib_v1_eq_fib2 n :
fib_v1 n = fib_v2 n.
Proof.
pattern n; apply lt_wf_ind; clear n; intros n IH.
do 2 (destruct n; trivial).
rewrite fib_v2_property.
rewrite <- !IH; auto.
Qed.