Proving that a reversible list is a palindrome in Coq - coq

Here is my inductive definition of palindromes:
Inductive pal { X : Type } : list X -> Prop :=
| pal0 : pal []
| pal1 : forall ( x : X ), pal [x]
| pal2 : forall ( x : X ) ( l : list X ), pal l -> pal ( x :: l ++ [x] ).
And the theorem I want to prove, from Software Foundations:
Theorem rev_eq_pal : forall ( X : Type ) ( l : list X ),
l = rev l -> pal l.
My informal outlines of the proof are as follows:
Suppose l0 is an arbitrary list such that l0 = rev l0. Then one of the following three cases must hold. l0 has:
(a) zero elements, in which case it is a palindrome by definition.
(b) one element, in which case it is also a palindrome by definition.
(c) two elements or more, in which case l0 = x :: l1 ++ [x] for some element x and some list l1 such that l1 = rev l1.
Now, since l1 = rev l1, one of the following three cases must hold...
The recursive case analysis will terminate for any finite list l0 because the length of the list analyzed decreases by 2 through each iteration. If it terminates for any list ln, all of its outer lists up to l0 are also palindromes, since a list constructed by appending two identical elements at either end of a palindrome is also a palindrome.
I think the reasoning is sound, but I'm not sure how to formalize it. Can it be turned into a proof in Coq? Some explanations of how the tactics used work would be especially helpful.

This is a nice example where "direct" induction does not work well at all because you don't directly make the recursive call on the tail, but on part of the tail. In such cases, I usually advice to state your lemma with the length of the list, not on the list itself. You can then specialize it. That would be something like:
Lemma rev_eq_pal_length: forall (X: Type) (n: nat) (l: list X), length l <= n -> l = rev l -> pal l.
Proof.
(* by induction on [n], not [l] *)
Qed.
Theorem rev_eq_pal: forall (X: Type) (l: list X), l = rev l -> pal l.
Proof.
(* apply the previous lemma with n = length l *)
Qed.
I can help you in more detail if necessary, just leave a comment.
Good luck !
V.
EDIT: just to help you, I needed the following lemmas to make this proof, you might need them too.
Lemma tool : forall (X:Type) (l l': list X) (a b: X),
a :: l = l' ++ b :: nil -> (a = b /\ l = nil) \/ exists k, l = k ++ b :: nil.
Lemma tool2 : forall (X:Type) (l1 l2 : list X) (a b: X),
l1 ++ a :: nil = l2 ++ b :: nil -> a = b /\ l1 = l2.

You could also derive your induction principle from a form of well-founded induction.
Notation " [ ] " := nil : list_scope.
Notation " [ x1 ; .. ; x2 ] " := (cons x1 .. (cons x2 nil) ..) : list_scope.
Open Scope list_scope.
Conjecture C1 : forall t1 f1 p1, (forall x1, (forall x2, f1 x2 < f1 x1 -> p1 x2) -> p1 x1) -> forall x1 : t1, p1 x1.
Conjecture C2 : forall t1 p1, p1 [] -> (forall x1 l1, p1 ([x1] ++ l1)) -> forall l1 : list t1, p1 l1.
Conjecture C3 : forall t1 p1, p1 [] -> (forall x1 l1, p1 (l1 ++ [x1])) -> forall l1 : list t1, p1 l1.
Conjecture C4 : forall t1 (x1 x2 : t1) l1, length l1 < length ([x1] ++ l1 ++ [x2]).
Theorem T1 : forall t1 p1,
p1 [] ->
(forall x1, p1 [x1]) ->
(forall x1 x2 l1, p1 l1 -> p1 ([x1] ++ l1 ++ [x2])) ->
forall l1 : list t1, p1 l1.
Proof.
intros t1 p1 h1 h2 h3.
induction l1 as [l1 h4] using (C1 (list t1) (#length t1)).
induction l1 as [| x1 l1] using C2.
eapply h1.
induction l1 as [| x2 l1] using C3.
simpl.
eapply h2.
eapply h3.
eapply h4.
eapply C4.
Qed.
You can prove conjecture C1 by first applying the hypothesis to the conclusion, then using structural induction on f1 x1, and then using some facts about <.
To prove C3, which has no induction hypothesis, you first use case analysis on is_empty l1, and then use the facts is_empty l1 = true -> l1 = [] and is_empty l1 = false -> l1 = delete_last l1 ++ [get_last l1] (get_last will need a default value).

Related

Solving a split proof in Coq

I am currently working on the volume 3 of the Software Foundations' textbook Verified Functional Algorithm and I am stuck on the proof of one exercise.
You can find the chapter about Mergesort which I am dealing with at the moment here: https://softwarefoundations.cis.upenn.edu/vfa-current/Merge.html
This is where I am stuck so far:
Lemma split_perm : forall {X:Type} (l l1 l2: list X),
split l = (l1,l2) -> Permutation l (l1 ++ l2).
Proof.
induction l as [| x | x1 x2 l1' IHl'] using list_ind2; intros.
- inversion H. simpl. reflexivity.
- inversion H. simpl. reflexivity.
- inversion H. destruct (split l1'). inversion H1. simpl. apply perm_skip.
apply Permutation_cons_app.
And this is the result from my last tactic "apply Permutation_cons_app."
1 subgoal
X : Type
x1, x2 : X
l1', l, l0 : list X
IHl' : forall l1 l2 : list X,
(l, l0) = (l1, l2) -> Permutation l1' (l1 ++ l2)
l1, l2 : list X
H : split (x1 :: x2 :: l1') = (l1, l2)
H1 : (x1 :: l, x2 :: l0) = (l1, l2)
H2 : x1 :: l = l1
H3 : x2 :: l0 = l2
______________________________________(1/1)
Permutation (x2 :: l1') (l ++ x2 :: l0)
Any leads on how I should continue to prove my goal ?
Well, actually right now you have result from apply perm_skip. After you have applyed Permutations_cons_app, you need pay attention on your induction hypothises.

Solving a mergesort split proof in Coq

I am currently working on the volume 3 of the Software Foundations' textbook Verified Functional Algorithm and I am stuck on the proof of one exercise.
You can find the chapter about Mergesort which I am dealing with at the moment here: https://softwarefoundations.cis.upenn.edu/vfa-current/Merge.html
This is where I am stuck so far:
(** **** Exercise: 3 stars, standard (split_perm) *)
(** Here's another fact about [split] that we will find useful later on.
*)
Lemma split_perm : forall {X:Type} (l l1 l2: list X),
split l = (l1,l2) -> Permutation l (l1 ++ l2).
Proof.
induction l as [| x | x1 x2 l1' IHl'] using list_ind2; intros.
- inv H. simpl. reflexivity.
- inv H. simpl. reflexivity.
- inv H.
And this is the result from my last tactic "inv H."
1 subgoal
X : Type
x1, x2 : X
l1' : list X
IHl' : forall l1 l2 : list X, split l1' = (l1, l2) -> Permutation l1' (l1 ++ l2)
l1, l2 : list X
H1 : (let (l1, l2) := split l1' in (x1 :: l1, x2 :: l2)) = (l1, l2)
______________________________________(1/1)
Permutation (x1 :: x2 :: l1') (l1 ++ l2)
Any leads on how I should continue to prove my goal ? Permutation (x1 :: x2 :: l1') (l1 ++ l2)
I think a first step is to get rid of split l', so destruct (split l') and then an inversion on H1 should simplify your goal

Coq induction hypothesis is wrong

I'm trying to prove a simple induction on two lists,
and for some reason Coq writes the induction hypothesis wrong.
Here is my proof:
Lemma eqb_list_true_iff_left_to_right :
forall A (eqb : A -> A -> bool),
(forall a1 a2, eqb a1 a2 = true <-> a1 = a2) ->
forall l1 l2, eqb_list eqb l1 l2 = true -> l1 = l2.
Proof.
intros A eqb H1.
induction l1 as [|a1 l1' IHl1'] eqn:E1.
- induction l2 as [|a2 l2' IHl2'] eqn:E2.
+ reflexivity.
+ intros H2. simpl in H2. discriminate H2.
- (* where did l1 = l1' come from ??? *)
And here are the hypotheses and goals when reaching the last (commented) line:
1 subgoal
A : Type
eqb : A -> A -> bool
H1 : forall a1 a2 : A, eqb a1 a2 = true <-> a1 = a2
l1 : list A
a1 : A
l1' : list A
E1 : l1 = a1 :: l1'
IHl1' : l1 = l1' ->
forall l2 : list A, eqb_list eqb l1' l2 = true -> l1' = l2
______________________________________(1/1)
forall l2 : list A, eqb_list eqb (a1 :: l1') l2 = true -> a1 :: l1' = l2
Obviously, IHl1' involves a false -> _ so it's useless. Where did the l1 = l1' come from??? What am I missing here??? Thanks!!
Short answer: remove the eqn:E1 in the call to induction l1.
This directive asks that the induction tactic adds an equality in the statement to be proved by induction. But if you add such an equality, then it appears in the statement to be proved by induction and this messes up the induction proof.

How to do induction differently?

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.

How to give a counterxample in Coq?

Is it possible to give a counterexample for a statement which doesn't hold in general? Like, for example that the all quantor does not distribute over the connective "or". How would you state that to begin with?
Parameter X : Set.
Parameter P : X -> Prop.
Parameter Q : X -> Prop.
(* This holds in general *)
Theorem forall_distributes_over_and
: (forall x:X, P x /\ Q x) -> ((forall x:X, P x) /\ (forall x:X, Q x)).
Proof.
intro H. split. apply H. apply H.
Qed.
(* This doesn't hold in general *)
Theorem forall_doesnt_distributes_over_or
: (forall x:X, P x \/ Q x) -> ((forall x:X, P x) \/ (forall x:X, Q x)).
Abort.
Here is a quick and dirty way to prove something similar to what you want:
Theorem forall_doesnt_distributes_over_or:
~ (forall X P Q, (forall x:X, P x \/ Q x) -> ((forall x:X, P x) \/ (forall x:X, Q x))).
Proof.
intros H.
assert (X : forall x : bool, x = true \/ x = false).
destruct x; intuition.
specialize (H _ (fun b => b = true) (fun b => b = false) X).
destruct H as [H|H].
now specialize (H false).
now specialize (H true).
Qed.
I have to quantify X P and Q inside the negation in order to be able to provide the one I want. You couldn't quite do that with your Parameters as they somehow fixed an abstract X, P and Q, thus making your theorem potentially true.
In general, if you want to produce a counterexample, you can state the negation of the formula and then prove that this negation is satisfied.