Coq: How to prove "a=b -> nat_compare a b = Eq."? - coq

In an attempt to get a grasp what Coq is about, I ended up in a situation where I essentially need to prove that a=b -> nat_compare a b = Eq.
I can get a handy start by doing:
Coq < Theorem foo: forall (a:nat) (b:nat), a=b->nat_compare a b=Eq.
1 subgoal
============================
forall a b : nat, a = b -> nat_compare a b = Eq
foo < intros. rewrite H. destruct b.
which gives me:
2 subgoals
a : nat
H : a = 0
============================
nat_compare 0 0 = Eq
subgoal 2 is:
nat_compare (S b) (S b) = Eq
The first is trivial:
foo < simpl. reflexivity.
But the next goal stumps me:
1 subgoal
a : nat
b : nat
H : a = S b
============================
nat_compare (S b) (S b) = Eq
I can do
foo < rewrite <- H.
which gives:
1 subgoal
a : nat
b : nat
H : a = S b
============================
nat_compare a a = Eq
(I can also arrive to this exact point by simpl, which seems to make more sense.)
Now, with pen an paper, I'd just claim that here is my proof by induction..
Am I approaching this at all in a correct way? Where can I best learn how to do this properly?

I was able to prove this with
Theorem triv : forall a b, a = b -> nat_compare a b = Eq.
intros; subst; induction b; auto.
Qed.
The trick here is to leave the inductive hypothesis lying around. destruct is a weaker form of induction that doesn't give you an inductive hypothesis which you need for this proof.

Related

Coq's proof #Coq

I try to solve this proof but I don't find how to it.
I have two subgoals but I don't even know if it's correct.
Here the lemma that I trid to solve with this but I'm stuck :
2 subgoals
a, b : Nat
H : Equal (leB a b) True
______________________________________(1/2)
Equal match b with
| Z => False
| S m' => leB a m'
end (leB a b) / Equal (leB b (S a)) (leB a b)
______________________________________(2/2)
Equal (leB (S a) b) True / Equal (leB b (S a)) True
Inductive Bool : Type :=
True : Bool | False : Bool.
Definition Not(b : Bool) : Bool :=
Bool_rect (fun a => Bool)
False
True
b.
Lemma classic : forall b : Bool, Equal b (Not (Not b)).
Proof.
intro.
induction b.
simpl.
apply refl.
simpl.
apply refl.
Qed.
Definition Equal(T : Type)(x y : T) : Prop :=
forall P : T -> Prop, (P x) -> (P y).
Arguments Equal[T].
(* Avec certaines versions Arguments Equal[T] *)
Lemma refl : forall T : Type, forall x : T, Equal x x.
Proof.
intros.
unfold Equal.
intros.
assumption.
Qed.
Fixpoint leB n m : Bool :=
match n, m with
| Z, _ => True
| _, Z => False
| S n', S m' => leB n' m'
end.
First, don't introduce all variables in the beginning with intros. You will get a too weak induction hypothesis. Just introduce a.
Then in each branch, consider the different cases of b with the destruct tactic. It will simplify your goal and you can see if it is the left or the right side of goal that is true, and use your refl lemma to finish the goal.
The last case require that you use your induction hypothesis, and it is here that it is important that it holds for all b, not just one specific b.
Also, you didn't provide a definition for you Nat type, I guess it is something like this:
Inductive Nat := Z | S (n:Nat).
Here is a proof.
Lemma Linear : forall a b, (Equal (leB a b) True) \/ (Equal (leB b a) True).
Proof.
induction a.
- intros b. destruct b; simpl.
+ left. apply refl.
+ left. apply refl.
- intros b. destruct b; simpl.
+ right. apply refl.
+ destruct (IHa b) as [Hleft | Hright].
++ left. apply Hleft.
++ right. apply Hright.
Qed.
While it may not be as insightful, you can also use tactics that try these steps to get a shorter proof.
induction a; destruct b; firstorder.
will also prove your lemma.

Rel: le_antisymmetric comprehension

Rel chapter from Logical foundations. I was given a solution to the excersize that I'm trying to comprehend:
Definition antisymmetric {X: Type} (R: relation X) :=
forall a b : X, (R a b) -> (R b a) -> a = b.
Theorem le_antisymmetric :
antisymmetric le.
Proof.
unfold antisymmetric. intros a b [| b' H1] H2.
- reflexivity.
- absurd (S b' <= b').
+ apply le_Sn_n.
+ etransitivity ; eassumption.
I don't understand, how does intro pattern [| b' H1] work? After intros it shows:
2 subgoals (ID 43)
a, b : nat
H2 : a <= a
============================
a = a
subgoal 2 (ID 46) is:
a = S b'
2-nd subgoal:
a, b, b' : nat
H1 : a <= b'
H2 : S b' <= a
============================
a = S b'
I understand that it is equivalent to destruct, but what sort of destruct? It's definitely not a simple destruct b.
Also I'm trying to understand the logics behind using absurd (S b' <= b') tactics. Does it mean that if we prove that a = S b' in this context, it would imply that after we rewrite a to S b' in H1, we get: S b' <= b', which is a universally false statement (absurd)?
intros a b [| b' H1] H2 is equivalent to
intros a b H H2.
destruct H as [| b' H1].
destruct patterns generally follow the rules that if an inductive type has a single constructor (e.g. products) then for x in that inductive type, destruct x as [a b c...] does (non-recursive) induction and renames the new variables to a, b, c, etc. When the inductive type has more than one constructor, there are multiple cases. For that, you use destruct x as [a b c ... | d e f ... | ...]. These destruction patterns can be nested if needed, e.g. destruct x as [[a b] | [c [d | e]]]. Note that if a constructor takes no arguments, there is nothing to rename, so you can leave the pattern empty.
In particular, something like a natural number can be destructed with destruct n as [ | n']. This divides into the cases where n is zero (and the constructor has no arguments, so there's nothing to the left of the |) or n is the successor of n'. Similarly, a list could be destructed as destruct li as [ | a li'] to divide into the cases where the list is nil and where it is cons a li'. The space to the left of the | is unnecessary, so you could do destruct li as [| a li'].
What's happening in your case specifically is that le is defined as an inductive type something along the lines of
Inductive le (n : nat) : nat -> Prop :=
| le_n : le n n
| le_S : forall m : nat, le n m -> le n (S m).
So there are two cases: one with no arguments, and the successor case where the arguments are m: nat and H: le n m. Hence, the destruct pattern is [| m H].
To answer your second question, absurd (S b' <= b'). means that (we think) we're able to prove S b' <= b' in the current context. We can also prove (in the same context) that S b' <= b' is absurd, i.e. leads to a witness of False (more precisely, S b' <= b' -> False). Using the induction principle for False, this allows us to generate a witness for any type, and in particular for a = S b'.
You can see what kind of proof term absurd produces with a minimal experiment.
Goal forall A B: Type, A.
Proof.
intros A B.
absurd B.
Show Proof.
This should print (fun A B : Type => False_rect A (?Goal ?Goal0)). The first goal (?Goal) has type ~B (i.e. B -> False). The second goal (?Goal0) has type B. If we can provide both, then ?Goal ?Goal0 has type False, so False_rect A (?Goal ?Goal0) produces a witness of type A.

Understanding specialize tactic

Trying to comprehend the answer of #keep_learning I walked through this code step by step:
Inductive nostutter {X:Type} : list X -> Prop :=
| ns_nil : nostutter []
| ns_one : forall (x : X), nostutter [x]
| ns_cons: forall (x : X) (h : X) (t : list X), nostutter (h::t) -> x <> h -> nostutter (x::h::t).
Example test_nostutter_4: not (nostutter [3;1;1;4]).
Proof.
intro.
inversion_clear H.
inversion_clear H0.
unfold not in H2.
(* We are here *)
specialize (H2 eq_refl).
apply H2.
Qed.
Here is what we have before excuting specialize
H1 : 3 <> 1
H : nostutter [1; 4]
H2 : 1 = 1 -> False
============================
False
Here is eq Prop whose constructor eq_refl is used in specialize:
Inductive eq (A:Type) (x:A) : A -> Prop :=
eq_refl : x = x :>A
where "x = y :> A" := (#eq A x y) : type_scope.
I can't explain, how this command works:
specialize (H2 eq_refl).
I read about specialize in reference manual, but the explanation there is too broad. As far as I understand it sees that "1 = 1" expression in H2 satisfies eq_refl constructor and therefore eq proposition is True. Then it simplifies the expression:
True -> False => False
And we get
H1 : 3 <> 1
H : nostutter [1; 4]
H2 : False
============================
False
Can somebody provide me a minimal example with explanation of what is specialize doing, so I could freely use it?
Update
Trying to imitate how specialize works using apply I did the following:
Example specialize {A B: Type} (H: A -> B) (a: A): B.
Proof.
apply H in a.
This gives:
A : Type
B : Type
H : A -> B
a : B
============================
B
Almost the same as specialize, only different hypothesis name.
In test_nostutter_4 theorem I tried this and it worked:
remember (#eq_refl nat 1) as Heq.
apply H2 in Heq as H3.
It gives us:
H1 : 3 <> 1
H : nostutter [1; 4]
H2 : 1 = 1 -> False
Heq : 1 = 1
H3 : False
HeqHeq : Heq = eq_refl
============================
False
This one was more complex, we had to introduce a new hypothesis Heq. But we got what we need - H3 at the end.
Does specialize internally use something like remember? Or is it possible to solve it with apply but without remember?
specialize, in its simplest form, simply replaces a given hypothesis with that hypothesis applied to some other term.
In this proof,
Example specialize {A B: Type} (H: A -> B) (a: A): B.
Proof.
specialize (H a).
exact H.
Qed.
we initially have the hypothesis H: A -> B. When we call specialize (H a), we apply H to a (apply as in function application). This gives us something of type B. specialize then gets rid of the old H for us and replaces it with the result of the application. It gives the new hypothesis the same name: H.
In your case, we have H2: 1 = 1 -> False, which is a function from the type 1 = 1 to the type False. That means that H2 applied to eq_refl is of type False, i.e. H2 eq_refl: False. When we use the tactic specialize (H2 eq_refl)., the old H2 is cleared and replaced by a new term (H2 eq_refl) whose type is False. It keeps the old name H2, though.
specialize is useful when you're sure that you're only going to use a hypothesis once, since it automatically gets rid of the old hypothesis. One disadvantage is that the old name may not fit the meaning of the new hypothesis. However, in your case and in my example, H is a generic enough name that it works either way.
To your update...
specialize is a core tactic defined directly in the ltac plugin. It doesn't use any other tactic internally, since it is its internals.
If you want to keep a hypothesis, you can use the as modifier, which works for both specialize and apply. In the proof
Example specialize {A B: Type} (H: A -> B) (a: A): B.
Proof.
if you do specialize (H a) as H0., instead of clearing H, it'll introduce a new hypothesis H0: B. apply H in a as H0. has the same effect.

How to unfold a Coq fixpoint by one iteration

I have the following in my proof environment:
1 subgoal
a, b : nat
H : (fix loop (m : nat) : nat :=
match (m - a) with
| 0 => m
| S m' => loop m'
end) b = 0
G : (b - a) = 0
Clearly, H is equivalent to
match (b - a) with
| 0 => b
| S m' => loop m'
end = 0
Which would then allow me to rewrite using G.
But since it is trapped in there, represented as (m - a), I cannot rewrite using G.
How do I unfold a fixpoint out by one iteration?
Edit: The following code will set up the proof environment. Just ignore the admit statements. Your goal is not to prove the statement (which is trivial) but to "unfold" the fixpoint.
From mathcomp Require Import all.
Goal forall a b : nat,
modn b a = 0 -> True.
Proof.
intros a b H.
unfold modn in H.
destruct a.
+ admit.
+ simpl in H.
assert ((b - a) = 0) as G.
- admit.
- unfold modn_rec in H.
To unfold a fixpoint you need to destruct its decreasing argument.
destruct b; simpl in H.
If you want to keep a single case, you'll have to prove the equality you mention in a separate lemma or assertion.
assert (Hfix : (fix loop m := match ... end) b = match ... end)

coq induction with passing in equality

I have a list with a known value and want to induct on it, keeping track of what the original list was, and referring to it by element. That is, I need to refer to it by l[i] with varying i instead of just having (a :: l).
I tried to make an induction principle to allow me to do that. Here is a program with all of the unnecessary Theorems replaced with Admitted, using a simplified example. The objective is to prove allLE_countDown using countDown_nth, and have list_nth_rect in a convenient form. (The theorem is easy to prove directly without any of those.)
Require Import Arith.
Require Import List.
Definition countDown1 := fix f a i := match i with
| 0 => nil
| S i0 => (a + i0) :: f a i0
end.
(* countDown from a number to another, excluding greatest. *)
Definition countDown a b := countDown1 b (a - b).
Theorem countDown_nth a b i d (boundi : i < length (countDown a b))
: nth i (countDown a b) d = a - i - 1.
Admitted.
Definition allLE := fix f l m := match l with
| nil => true
| a :: l0 => if Nat.leb a m then f l0 m else false
end.
Definition drop {A} := fix f (l : list A) n := match n with
| 0 => l
| S a => match l with
| nil => nil
| _ :: l2 => f l2 a
end
end.
Theorem list_nth_rect_aux {A : Type} (P : list A -> list A -> nat -> Type)
(Pnil : forall l, P l nil (length l))
(Pcons : forall i s l d (boundi : i < length l), P l s (S i) -> P l ((nth i l d) :: s) i)
l s i (size : length l = i + length s) (sub : s = drop l i) : P l s i.
Admitted.
Theorem list_nth_rect {A : Type} (P : list A -> list A -> nat -> Type)
(Pnil : forall l, P l nil (length l))
(Pcons : forall i s l d (boundi : i < length l), P l s (S i) -> P l ((nth i l d) :: s) i)
l s (leqs : l = s): P l s 0.
Admitted.
Theorem allLE_countDown a b : allLE (countDown a b) a = true.
remember (countDown a b) as l.
refine (list_nth_rect (fun l s _ => l = countDown a b -> allLE s a = true) _ _ l l eq_refl Heql);
intros; subst; [ apply eq_refl | ].
rewrite countDown_nth; [ | apply boundi ].
pose proof (Nat.le_sub_l a (i + 1)).
rewrite Nat.sub_add_distr in H0.
apply leb_correct in H0.
simpl; rewrite H0; clear H0.
apply (H eq_refl).
Qed.
So, I have list_nth_rect and was able to use it with refine to prove the theorem by referring to the nth element, as desired. However, I had to construct the Proposition P myself. Normally, you'd like to use induction.
This requires distinguishing which elements are the original list l vs. the sublist s that is inducted on. So, I can use remember.
Theorem allLE_countDown a b : allLE (countDown a b) a = true.
remember (countDown a b) as s.
remember s as l.
rewrite Heql.
This puts me at
a, b : nat
s, l : list nat
Heql : l = s
Heqs : l = countDown a b
============================
allLE s a = true
However, I can't seem to pass the equality as I just did above. When I try
induction l, s, Heql using list_nth_rect.
I get the error
Error: Abstracting over the terms "l", "s" and "0" leads to a term
fun (l0 : list ?X133#{__:=a; __:=b; __:=s; __:=l; __:=Heql; __:=Heqs})
(s0 : list ?X133#{__:=a; __:=b; __:=s; __:=l0; __:=Heql; __:=Heqs})
(_ : nat) =>
(fun (l1 l2 : list nat) (_ : l1 = l2) =>
l1 = countDown a b -> allLE l2 a = true) l0 s0 Heql
which is ill-typed.
Reason is: Illegal application:
The term
"fun (l l0 : list nat) (_ : l = l0) =>
l = countDown a b -> allLE l0 a = true" of type
"forall l l0 : list nat, l = l0 -> Prop"
cannot be applied to the terms
"l0" : "list nat"
"s0" : "list nat"
"Heql" : "l = s"
The 3rd term has type "l = s" which should be coercible to
"l0 = s0".
So, how can I change the induction principle
such that it works with the induction tactic?
It looks like it's getting confused between
the outer variables and the ones inside the
function. But, I don't have a way to talk
about the inner variables that aren't in scope.
It's very strange, since invoking it with
refine works without issues.
I know for match, there's as clauses, but
I can't figure out how to apply that here.
Or, is there a way to make list_nth_rect use
P l l 0 and still indicate which variables correspond to l and s?
First, you can prove this result much more easily by reusing more basic ones. Here's a version based on definitions of the ssreflect library:
From mathcomp
Require Import ssreflect ssrfun ssrbool ssrnat eqtype seq.
Definition countDown n m := rev (iota m (n - m)).
Lemma allLE_countDown n m : all (fun k => k <= n) (countDown n m).
Proof.
rewrite /countDown all_rev; apply/allP=> k; rewrite mem_iota.
have [mn|/ltnW] := leqP m n.
by rewrite subnKC //; case/andP => _; apply/leqW.
by rewrite -subn_eq0 => /eqP ->; rewrite addn0 ltnNge andbN.
Qed.
Here, iota n m is the list of m elements that counts starting from n, and all is a generic version of your allLE. Similar functions and results exist in the standard library.
Back to your original question, it is true that sometimes we need to induct on a list while remembering the entire list we started with. I don't know if there is a way to get what you want with the standard induction tactic; I didn't even know that it had a multi-argument variant. When I want to prove P l using this strategy, I usually proceed as follows:
Find a predicate Q : nat -> Prop such that Q (length l) implies P l. Typically, Q n will have the form n <= length l -> R (take n l) (drop n l), where R : list A -> list A -> Prop.
Prove Q n for all n by induction.
I do not know if this answers your question, but induction seems to accept with clauses. Thus, you can write the following.
Theorem allLE_countDown a b : allLE (countDown a b) a = true.
remember (countDown a b) as s.
remember s as l.
rewrite Heql.
induction l, s, Heql using list_nth_rect
with (P:=fun l s _ => l = countDown a b -> allLE s a = true).
But the benefit is quite limited w.r.t. the refine version, since you need to specify manually the predicate.
Now, here is how I would have proved such a result using objects from the standard library.
Require Import List. Import ListNotations.
Require Import Omega.
Definition countDown1 := fix f a i := match i with
| 0 => nil
| S i0 => (a + i0) :: f a i0
end.
(* countDown from a number to another, excluding greatest. *)
Definition countDown a b := countDown1 b (a - b).
Theorem countDown1_nth a i k d (boundi : k < i) :
nth k (countDown1 a i) d = a + i -k - 1.
Proof.
revert k boundi.
induction i; intros.
- inversion boundi.
- simpl. destruct k.
+ omega.
+ rewrite IHi; omega.
Qed.
Lemma countDown1_length a i : length (countDown1 a i) = i.
Proof.
induction i.
- reflexivity.
- simpl. rewrite IHi. reflexivity.
Qed.
Theorem countDown_nth a b i d (boundi : i < length (countDown a b))
: nth i (countDown a b) d = a - i - 1.
Proof.
unfold countDown in *.
rewrite countDown1_length in boundi.
rewrite countDown1_nth.
replace (b+(a-b)) with a by omega. reflexivity. assumption.
Qed.
Theorem allLE_countDown a b : Forall (ge a) (countDown a b).
Proof.
apply Forall_forall. intros.
apply In_nth with (d:=0) in H.
destruct H as (n & H & H0).
rewrite countDown_nth in H0 by assumption. omega.
Qed.
EDIT:
You can state an helper lemma to make an even more concise proof.
Lemma Forall_nth : forall {A} (P:A->Prop) l,
(forall d i, i < length l -> P (nth i l d)) ->
Forall P l.
Proof.
intros. apply Forall_forall.
intros. apply In_nth with (d:=x) in H0.
destruct H0 as (n & H0 & H1).
rewrite <- H1. apply H. assumption.
Qed.
Theorem allLE_countDown a b : Forall (ge a) (countDown a b).
Proof.
apply Forall_nth.
intros. rewrite countDown_nth. omega. assumption.
Qed.
The issue is that, for better or for worse, induction seems to assume that its arguments are independent. The solution, then, is to let induction automatically infer l and s from Heql:
Theorem list_nth_rect {A : Type} {l s : list A} (P : list A -> list A -> nat -> Type)
(Pnil : P l nil (length l))
(Pcons : forall i s d (boundi : i < length l), P l s (S i) -> P l ((nth i l d) :: s) i)
(leqs : l = s): P l s 0.
Admitted.
Theorem allLE_countDown a b : allLE (countDown a b) a = true.
remember (countDown a b) as s.
remember s as l.
rewrite Heql.
induction Heql using list_nth_rect;
intros; subst; [ apply eq_refl | ].
rewrite countDown_nth; [ | apply boundi ].
pose proof (Nat.le_sub_l a (i + 1)).
rewrite Nat.sub_add_distr in H.
apply leb_correct in H.
simpl; rewrite H; clear H.
assumption.
Qed.
I had to change around the type of list_nth_rect a bit; I hope I haven't made it false.