How to do induction differently? - coq

I am doing an exercise in Coq and trying to prove if a list equals to its reverse, it's a palindrome. Here is how I define palindromes:
Inductive pal {X : Type} : list X -> Prop :=
| emptypal : pal []
| singlpal : forall x, pal [x]
| inducpal : forall x l, pal l -> pal (x :: l ++ [x]).
Here is the theorem:
Theorem palindrome3 : forall {X : Type} (l : list X),
l = rev l -> pal l.
According to my definition, I will need to do the induction my extracting the front and tail element but apparently coq won't let me do it, and if I force it to do so, it gives an induction result that definitely doesn't make any sense:
Proof.
intros X l H. remember (rev l) as rl. induction l, rl.
- apply emptypal.
- inversion H.
- inversion H.
- (* stuck *)
context:
1 subgoals
X : Type
x : X
l : list X
x0 : X
rl : list X
Heqrl : x0 :: rl = rev (x :: l)
H : x :: l = x0 :: rl
IHl : x0 :: rl = rev l -> l = x0 :: rl -> pal l
______________________________________(1/1)
pal (x :: l)
aparently the inductive context is terribly wrong. is there any way I can fix the induction?

The solution I propose here is probably not the shortest one, but I think it is rather natural.
My solution consists in defining an induction principle on list specialized to your problem.
Consider natural numbers. There is not only the standard induction nat_ind where you prove P 0 and forall n, P n -> P (S n). But there are other induction schemes, e.g., the strong induction lt_wf_ind, or the two-step induction where you prove P 0, P 1 and forall n, P n -> P (S (S n)). If the standard induction scheme is not strong enough to prove the property you want, you can try another one.
We can do the same for lists. If the standard induction scheme list_ind is not enough, we can write another one that works. In this idea, we define for lists an induction principle similar to the two-step induction on nat (and we will prove the validity of this induction scheme using the two-step induction on nat), where we need to prove three cases: P [], forall x, P [x] and forall x l x', P l -> P (x :: l ++ [x']). The proof of this scheme is the difficult part. Applying it to deduce your theorem is quite straightforward.
I don't know if the two-step induction scheme is part of the standard library, so I introduce it as an axiom.
Axiom nat_ind2 : forall P : nat -> Prop, P 0 -> P 1 ->
(forall n : nat, P n -> P (S (S n))) -> forall n : nat, P n.
Then we prove the induction scheme we want.
Lemma list_ind2 : forall {A} (P : list A -> Prop) (P_nil : P [])
(P_single : forall x, P [x])
(P_cons_snoc : forall x l x', P l -> P (x :: l ++ [x'])),
forall l, P l.
Proof.
intros. remember (length l) as n. symmetry in Heqn. revert dependent l.
induction n using nat_ind2; intros.
- apply length_zero_iff_nil in Heqn. subst l. apply P_nil.
- destruct l; [discriminate|]. simpl in Heqn. inversion Heqn; subst.
apply length_zero_iff_nil in H0. subst l. apply P_single.
- destruct l; [discriminate|]. simpl in Heqn.
inversion Heqn; subst. pose proof (rev_involutive l) as Hinv.
destruct (rev l). destruct l; discriminate. simpl in Hinv. subst l.
rewrite app_length in H0.
rewrite PeanoNat.Nat.add_comm in H0. simpl in H0. inversion H0.
apply P_cons_snoc. apply IHn. assumption.
Qed.
You should be able to conclude quite easily using this induction principle.
Theorem palindrome3 : forall {X : Type} (l : list X),
l = rev l -> pal l.

Related

Stuck proving lemma with unprovable subgoals

I'm trying to prove a lemma that's based on the following definitions.
Section lemma.
Variable A : Type.
Variable P : A -> Prop.
Variable P_dec : forall x, {P x}+{~P x}.
Inductive vector : nat -> Type :=
| Vnil : vector O
| Vcons : forall {n}, A -> vector n -> vector (S n).
Arguments Vcons {_} _ _.
Fixpoint countPV {n: nat} (v : vector n): nat :=
match v with
| Vnil => O
| Vcons x v' => if P_dec x then S (countPV v') else countPV v'
end.
The lemma I'm trying to prove is as follows
Lemma lem: forall (n:nat) (a:A) (v:vector n),
S n = countPV (Vcons a v) -> (P a /\ n = countPV v).
I've tried a lot of things and currently I'm at this point.
Proof.
intros n a v.
unfold not in P_dec.
simpl.
destruct P_dec.
- intros.
split.
* exact p.
* apply eq_add_S.
exact H.
- intros.
split.
The context at this point:
2 subgoals
A : Type
P : A -> Prop
P_dec : forall x : A, {P x} + {P x -> False}
n : nat
a : A
v : vector n
f : P a -> False
H : S n = countPV v
______________________________________(1/2)
P a
______________________________________(2/2)
n = countPV v
My issue is that I seem to be stuck with two subgoals that I can not prove and the available context does not seem to be helpful. Can anyone provide me with some pointers to move on?
EDIT:
I've proven the lemma by contradicting H:
assert (countPV v <= n).
* apply countNotBiggerThanConstructor.
* omega.
Qed.
where countNotBiggerThanConstructor is:
Lemma countNotBiggerThanConstructor: forall {n : nat} (v: vector n), countPV v <= n.
Proof.
intros n v.
induction v.
- reflexivity.
- simpl.
destruct P_dec.
+ apply le_n_S in IHv.
assumption.
+ apply le_S.
assumption.
Qed.
Notice that H can't possibly be true. That is a good thing, if you can prove False, you can prove anything. So I would do contradict H next (and you don't need that last split).
Overall your proof seems a little messy to me. I suggest thinking about how you would prove this lemma on paper and trying to do that in Coq. I am not an expert in Coq, but I think it would also help you realize, that you need to use contradiction in this case.
(Edit: BTW other answers suggesting that this lemma does not hold are wrong, but I can't comment with my 1 reputation)

Proving increasing iota in Coq

I am stuck on a goal.
Assume we have the following definition:
Fixpoint iota (n : nat) : list nat :=
match n with
| 0 => []
| S k => iota k ++ [k]
end.
And we want to prove:
Theorem t1 : forall n, In n (iota n) -> False.
So far, I have managed to the following:
Theorem t1 : forall n, In n (iota n) -> False.
Proof.
intros.
induction n.
- cbn in H. contradiction.
- cbn in H. apply app_split in H.
Focus 2. unfold not. intros.
unfold In in H0. destruct H0. assert (~(n = S n)) by now apply s_inj.
contradiction.
apply H0.
apply IHn.
I used these two lemmas, proofs omitted:
Axiom app_split : forall A x (l l2 : list A), In x (l ++ l2) -> not (In x l2) -> In x l.
Axiom s_inj : forall n, ~(n = S n).
However, I am completely stuck, I need to somehow show that: In n (iota n) assuming In (S n) (iota n).
As you've observed the fact that the n in In n and the one in iota n are in lockstep in your statement makes the induction hypothesis hard to invoke (if not completely useless).
The trick here is to prove a more general statement than the one you are actually interested in which breaks this dependency between the two ns. I would suggest:
Theorem t : forall n k, n <= k -> In k (iota n) -> False.
from which you can derive t1 as a corollary:
Corollary t1 : forall n, In n (iota n) -> False.
intro n; apply (t n n); reflexivity.
Qed.
If you want to peek at the proof of t, you can have a look at this self-contained gist

How do you determine which terms to call intros on in coq

I am a beginner with coq, so this may be a trivial question. Sometimes I can't figure out which terms I need to call intros on, when writing a Theorem. A simple example,
Theorem silly1 : forall (n m o p : nat),
n = m ->
[n;o] = [n;p] ->
[n;o] = [m;p].
Proof.
intros n m o p eq1 eq2.
rewrite <- eq1.
apply eq2. Qed.
I know based on the goal, that I will probably need to call intros on (n m o p), but why do I need to use it on eq1 and eq2.
Also, in some other Theorems, you may need to use intros on the type parameter, the hypothesis, or the inductive hypothesis. Example
Theorem trans_eq : forall (X:Type) (n m o : X),
n = m -> m = o -> n = o.
Proof.
intros X n m o eq1 eq2. rewrite -> eq1. rewrite -> eq2.
reflexivity. Qed.
Theorem silly3' : forall (n : nat),
(beq_nat n 5 = true -> beq_nat (S (S n)) 7 = true) ->
true = beq_nat n 5 ->
true = beq_nat (S (S n)) 7.
Proof.
intros n eq H.
symmetry in H. apply eq in H. symmetry in H.
apply H. Qed.
So I guess what I'm asking is...when I start proving a theorem, how should I go about reasoning through the goals, to determine which terms I need to call intros on?
An example of what gallais is refering to is this.
Theorem example_1 : forall A B, (A -> B) -> A -> B.
Proof. intros ? ? H1. apply H1. Qed.
Theorem example_2 : forall A B, (A -> B) -> A -> B.
Proof. intros ? ? H1 H2. apply H1. apply H2. Qed.
Print example_1.
Print example_2.
Another example of when it can be problematic is using introduction before using induction. This makes the induction hypothesis different.
Fixpoint reverse_helper {A : Type} (l1 l2 : list A) : list A :=
match l1 with
| nil => l2
| cons x l1 => reverse_helper l1 (cons x l2)
end.
Theorem example_3 : forall A (l1 l2 : list A), reverse_helper l1 l2 = app (reverse_helper l1 nil) l2.
Proof. intros. induction l1. simpl. reflexivity. simpl. try rewrite IHl1. Abort.
Theorem example_4 : forall A (l1 l2 : list A), reverse_helper l1 l2 = app (reverse_helper l1 nil) l2.
Proof. induction l1. intros. simpl. reflexivity. intros. simpl. rewrite (IHl1 (cons a l2)). rewrite (IHl1 (cons a nil)). Admitted.
Otherwise, you should use introduction whenever you can. You won't be able to use whatever is being quantified over or the antecedents of an implication until you do.
By the way
H1 : A1
...
Hn : An
___
B
is equivalent to
H1: A1, ..., Hn: An ⊢ B.
When you prove something interactively, you're using a sequent calculus starting from the conclusion and working your way back to the hypotheses.

Double induction in Coq

Basically, I would like to prove that following result:
Lemma nat_ind_2 (P: nat -> Prop): P 0 -> P 1 -> (forall n, P n -> P (2+n)) ->
forall n, P n.
that is the recurrence scheme of the so called double induction.
I tried to prove it applying induction two times, but I am not sure that I will get anywhere this way. Indeed, I got stuck at that point:
Proof.
intros. elim n.
exact H.
intros. elim n0.
exact H0.
intros. apply (H1 n1).
Actually, there's a much simpler solution. A fix allows recursion (aka induction) on any subterm while nat_rect only allows recursion on the immediate subterm of a nat. nat_rect itself is defined with a fix, and nat_ind is just a special case of nat_rect.
Definition nat_rect_2 (P : nat -> Type) (f1 : P 0) (f2 : P 1)
(f3 : forall n, P n -> P (S (S n))) : forall n, P n :=
fix nat_rect_2 n :=
match n with
| 0 => f1
| 1 => f2
| S (S m) => f3 m (nat_rect_2 m)
end.
#Rui's fix solution is quite general. Here is an alternative solution that uses the following observation: when proving this lemma mentally, you use somewhat stronger induction principle. For example if P holds for two consecutive numbers it becomes easy to make it hold for the next pair:
Lemma nat_ind_2 (P: nat -> Prop): P 0 -> P 1 -> (forall n, P n -> P (2+n)) ->
forall n, P n.
Proof.
intros P0 P1 H.
assert (G: forall n, P n /\ P (S n)).
induction n as [ | n [Pn PSn]]; auto.
split; try apply H; auto.
apply G.
Qed.
Here G proves something redundant, yet calling the induction tactic for it brings sufficient context for near-trivial proof.
I think well-founded induction is necessary for that.
Require Import Arith.
Theorem nat_rect_3 : forall P,
(forall n1, (forall n2, n2 < n1 -> P n2) -> P n1) ->
forall n, P n.
Proof.
intros P H1 n1.
apply Acc_rect with (R := lt).
info_eauto.
induction n1 as [| n1 H2].
apply Acc_intro. intros n2 H3. Check lt_n_0. Check (lt_n_0 _). Check (lt_n_0 _ H3). destruct (lt_n_0 _ H3).
destruct H2 as [H2]. apply Acc_intro. intros n2 H3. apply Acc_intro. intros n3 H4. apply H2. info_eauto with *.
Defined.
Theorem nat_rect_2 : forall P,
P 0 ->
P 1 ->
(forall n, P n -> P (S (S n))) ->
forall n, P n.
Proof.
intros ? H1 H2 H3.
induction n as [n H4] using nat_rect_3.
destruct n as [| [| n]].
info_eauto with *.
info_eauto with *.
info_eauto with *.
Defined.
A fun observation: Rui's answer is the fixpoint translation of NonNumeric's answer, which is the generalization of user1861759's answer for any n, not just n = 2.
In other words, these answers are all fine, and actually deeply related to one another, by the correspondence between terminating fixpoints and generalized induction.

Coq: Problems with List In inductive

I'm new to Coq, but with some effort I was able to prove various inductive lemmas. However I get stuck on all exercises that uses the following inductive definition:
Inductive In (A:Type) (y:A) : list A -> Prop :=
| InHead : forall xs:list A, In y (cons y xs)
| InTail : forall (x:A) (xs:list A), In y xs -> In y (cons x xs).
The furthest i got was with the following lemma:
Lemma my_In_rev : forall (A:Type) (x:A) (l:list A), In x l -> In x (rev l).
Proof.
induction l.
simpl.
trivial.
simpl.
intros.
The following two lemmas I cant get past the first steps, because I get stuck on the exists goal right after using intros.
Lemma my_In_map : forall (A B:Type) (y:B) (f:A->B) (l:list A), In y (map f l) -> exists x : A, In x l /\ y = f x.
Lemma my_In_split : forall (A:Type) (x:A) (l : list A), In x l -> exists l1, exists l2, l = l1 ++ (x::l2).
Proof.
Any help would be appreciated!
For your first lemma, I added two simple sublemmas (that you can find in the list library).
The two others are more straightforward.
Require Import List.
Lemma In_concat_l: forall (A: Type) (l1 l2: list A) (x:A),
In x l1 -> In x (l1 ++ l2).
Proof.
intros A.
induction l1 as [ | hd tl hi ]; intros l2 x hIn; simpl in *.
- contradiction.
- destruct hIn.
+ left; assumption.
+ right; now apply hi.
Qed.
Lemma In_concat_r: forall (A: Type) (l1 l2: list A) (x:A),
In x l2 -> In x (l1 ++ l2).
intros A.
induction l1 as [ | hd tl hi ]; intros l2 x hIn; simpl in *.
- assumption.
- right; now apply hi.
Qed.
Lemma my_In_rev : forall (A:Type) (x:A) (l:list A), In x l -> In x (rev l).
Proof.
intros A x l.
induction l as [ | hd tl hi ]; intros hIn; simpl in *.
- contradiction.
- destruct hIn.
+ apply In_concat_r.
rewrite H.
now constructor.
+ apply In_concat_l.
now apply hi.
Qed.
Lemma my_In_map : forall (A B:Type) (y:B) (f:A->B) (l:list A), In y (map f l) -> exists x : A, In x l /\ y = f x.
Proof.
intros A B y f l.
induction l as [ | hd tl hi]; intros hIn; simpl in *.
- contradiction.
- destruct hIn.
+ exists hd; split.
left; reflexivity.
symmetry; assumption.
+ destruct (hi H) as [x0 [ h1 h2]].
exists x0; split.
right; assumption.
assumption.
Qed.
Lemma my_In_split : forall (A:Type) (x:A) (l : list A), In x l -> exists l1, exists l2, l = l1 ++ (x::l2).
Proof.
intros A x l.
induction l as [ | hd tl hi]; intros hIn; simpl in *.
- contradiction.
- destruct hIn.
rewrite H.
exists nil; exists tl; simpl; reflexivity.
destruct (hi H) as [ l1 [ l2 h ]].
exists (hd :: l1); exists l2.
rewrite <- app_comm_cons; rewrite h.
reflexivity.
Qed.
I won't say it's less complex than Rui's answer, but I find this solution a little bit easier to understand. But in the end, they are relatively close.
Cheers,
V.
When the goal is existentially quantified, you have to give a concrete example of an object with the stated property, and when a hypothesis is existentially quantified, you're allowed to assume one such object exists and introduce it. See FAQs 47, 53, and 54. By the way, an In predicate is already defined in Coq.Lists.List. Check it out here. A reference for Coq tactics is here.
A proof of the first lemma:
Require Import Coq.Lists.List.
Require Import Coq.Setoids.Setoid.
Inductive In {A : Type} (y : A) : list A -> Prop :=
| InHead : forall xs : list A, In y (cons y xs)
| InTail : forall (x : A) (xs : list A), In y xs -> In y (cons x xs).
Lemma L1 : forall (t1 : Type) (l1 : list t1) (o1 o2 : t1),
In o1 (o2 :: l1) <-> o1 = o2 \/ In o1 l1.
Proof.
intros t1 l1 o1 o2. split.
intros H1. inversion H1 as [l2 [H3 H4] | o3 l2 H2 [H3 H4]].
left. reflexivity.
right. apply H2.
intros H1. inversion H1 as [H2 | H2].
rewrite H2. apply InHead.
apply InTail. apply H2.
Qed.
Lemma my_In_map : forall (A B : Type) (l : list A) (y : B) (f : A -> B),
In y (map f l) -> exists x : A, In x l /\ y = f x.
Proof.
intros A B. induction l as [| z l H1].
intros y f H2. simpl in *. inversion H2.
intros y f H2. simpl in *. rewrite L1 in H2. inversion H2 as [H3 | H3].
exists z. split.
apply InHead.
apply H3.
assert (H4 := H1 _ _ H3). inversion H4 as [x [H5 H6]]. exists x. split.
rewrite L1. right. apply H5.
apply H6.
Qed.