Library ex6
Blatt 6 —- Properties and Specifications
SemProg SoSe 2013, Set 6
Require Export Logic.
Exercise block: decidable equality
Definition DecidEquality (X: Type) := forall x y : X, (x = y) \/ ~(x = y).
From earlier chapters we also remember boolean equality for natural numbers beq_nat. Let us say that a type has boolean equality in such a situation.
Definition BoolEquality (X: Type) (f: X -> X -> bool) :=
forall x y, if (f x y) then (x = y) else ~(x = y).
Boolean equality should naturally imply decidable equality. Furthermore, our good friends like booleans and natsallow obvious boolean equality functions. This is an easy warm-up exercise.
Exercise 1: 5 points (natBooEq, booBooEq, Bool_Decid)
Theorem natBooEq: BoolEquality nat beq_nat.
Proof. Admitted.
Definition beq_bool (b c: bool) : bool := andb (orb (negb b) c) (orb (negb c) b).
Theorem booBooEq: BoolEquality bool beq_bool.
Proof. Admitted.
Theorem Bool_Decid: forall (X: Type) (f: X -> X -> bool), (BoolEquality X f) -> (DecidEquality X).
Proof. Admitted.
One would think that the converse implication will also be a piece of cake, i.e., decidable equality should yield a straightforward equality function:
Types with boolean/decidable equality can be used to construct new ones. The most obvious way of doing so is provided by lists.
Definition booleq_wrong (X: Type) (h: DecidEquality X) : X -> X -> bool :=
fun x y => match (h x y) with
| or_introl _ => true
| or_intror _ => false
end.
Try to uncomment this definition. What is the error message that you get? Why?
fun x y => match (h x y) with
| or_introl _ => true
| or_intror _ => false
end.
Exercise 2: 7.5 point (listDecEq, listBooEq)
Theorem listDecEq: forall X, DecidEquality X -> DecidEquality (list X).
Proof.
Admitted.
To do the same with boolean equality, we first need to define a helper function.
Fixpoint lliftb (X: Type) (f: X -> X -> bool) (l1 l2: list X) : bool := admit.
<- FILL THIS IN SO THAT YOU CAN PROVE
Theorem listBooEq: forall X f, (BoolEquality X f) -> BoolEquality (list X) (lliftb X f).
Proof. Admitted.
How about other types? Can you think of any candidates for boolean/decidable equality for:
- functions nat -> nat
- real numbers (however you want to implement them in Coq)
- Prop itself?
Exercise block: specifications for lists
Exercise 3: 5 points (good_subseq_extend, maximal_strengthening)
Inductive subseq' {X:Type} : list X -> list X -> Prop :=
| sub_nil : forall l, subseq' [] l
| sub_take : forall x l1 l2, subseq' l1 l2 -> subseq' (x :: l1) (x :: l2)
| sub_skip : forall x l1 l2, subseq' l1 l2 -> subseq' l1 (x :: l2).
Definition maximal {X:Type} (lmax : list X) (P : list X -> Prop) := P
lmax /\ forall l', P l' -> length l' <= length lmax.
A "good subsequence" for a given list l and a test is a
subsequence of l all of whose members evaluate to true under
the test.
Inductive all (X : Type) (P : X -> Prop) : list X -> Prop :=
| all_nil : all X P []
| all_cons : forall x l, P x -> all X P l -> all X P (x::l).
Fixpoint forallb {X : Type} (test : X -> bool) (l : list X) : bool :=
match l with
| [] => true
| x :: l' => andb (test x) (forallb test l')
end.
Definition good_subseq {X:Type} (test : X -> bool) (l lsub : list X) :=
subseq' lsub l /\ forallb test lsub = true.
Why, prove now the results we just stated w/o proof during the lecture...
Theorem good_subseq_extend : forall (X:Type) (test : X -> bool)
(l lsub : list X) (x : X),
good_subseq test l lsub ->
test x = true ->
good_subseq test (x::l) (x::lsub).
Proof. Admitted.
Theorem maximal_strengthening : forall (X:Type) (x:X)
(lmax l : list X)
(test : X -> bool),
maximal lmax (good_subseq test (x::l)) ->
test x = false ->
maximal lmax (good_subseq test l).
Proof. Admitted.
Exercise 4: 7.5 points (forallb_matches_spec)
Theorem forallb_matches_spec {X : Type} : forall (test : X -> bool), forall (l : list X),
if (forallb test l) then (all X (fun x => test x = true) l) else exists x : X, exists l1, exists l2, (test x = false) /\ l = l1 ++ x::l2.
Proof. Admitted.
Exercise 5: 7.5 points (repeatslog_equivalent_repeatsind)
The following inductively defined proposition...Inductive appears_in {X:Type} (a:X) : list X -> Prop :=
| ai_here : forall l, appears_in a (a::l)
| ai_later : forall b l, appears_in a l -> appears_in a (b::l).
...gives us a precise way of saying that a value a appears at
least once as a member of a list l.
Here are a few warm-ups about appears_in.
Lemma appears_in_app : forall {X:Type} (xs ys : list X) (x:X),
appears_in x (xs ++ ys) -> appears_in x xs \/ appears_in x ys.
Proof. Admitted.
Lemma app_nil : forall {X:Type} (xs: list X), xs ++ [] = xs.
Proof. Admitted.
Lemma app_appears_in_left : forall {X:Type} (xs ys : list X) (x:X),
appears_in x xs -> appears_in x (xs ++ ys).
Proof. Admitted.
Lemma app_appears_in_right : forall {X:Type} (xs ys : list X) (x:X),
appears_in x ys -> appears_in x (xs ++ ys).
Proof. Admitted.
Lemma app_appears_in : forall {X:Type} (xs ys : list X) (x:X),
appears_in x xs \/ appears_in x ys -> appears_in x (xs ++ ys).
Proof. Admitted.
We can use this property to definite inductively what it means that a list contains repeated occurrences of some element(s).
Inductive repeatsind (X:Type) : list X -> Prop :=
| rep_here : forall l x, (appears_in x l) -> repeatsind X (x :: l)
| rep_furth : forall l x, repeatsind X l -> repeatsind X (x::l).
However, there is an obvious alternative definition...
Definition repeatslog (X:Type) (l : list X) : Prop :=
exists x, exists l1, exists l2, exists l3, l = l1 ++ [x] ++ l2 ++ [x] ++ l3.
In fact, this definition is a good excuse to define our first nontrivial tactical. Do you understand what it does?
Tactic Notation "repeatslog" ident(hip) ident(x) ident(l1) ident(l2) ident(l3) ident(Hl3) :=
let Hx := fresh in inversion hip as [x Hx]; clear hip;
let Hl1 := fresh in inversion Hx as [l1 Hl1]; clear Hx; let Hl2 := fresh in inversion Hl1 as [l2 Hl2]; clear Hl1; inversion Hl2 as [l3 Hl3]; clear Hl2.
You will most likely find it quite helpful in the proof that the two definitions are equivalent:
Theorem repeatslog_equivalent_repeatsind {X: Type} : forall l, repeatslog X l <-> repeatsind X l.
Proof. Admitted.
As this is an equivalence, you may of course prove first each of directions in separate lemmas.
Evaluating and optimizing polymorphic string expressions.
Exercise 6: 7.5 points (optimizetrim_sound)
Inductive sexp (X : Type) : Type :=
| SNil : sexp X
| SSing : X -> sexp X
| SConc : sexp X -> sexp X -> sexp X
| STrimF : sexp X -> sexp X.
removal of the first char of the string, i.e., "trimming" the front
Define functions evaluting these string expressions to lists.
Fixpoint seval {X : Type} (s: sexp X) : list X := admit.
<- FILL THIS IN! See many examples below if you want to double-check how exactly this function should be defined.
Of course, there is a lot of redundancy in our expressions. Here is a function which tries to remove at least some. Do you understand what it does?
Fixpoint optimizetrim {X : Type} (s: sexp X) : sexp X :=
match s with
| SNil => SNil X
| SSing x => SSing X x
| SConc s' t => SConc X (optimizetrim s') (optimizetrim t)
| STrimF (SConc SNil t) => STrimF X (optimizetrim t)
| STrimF (SConc (SSing x) t) => (optimizetrim t)
| STrimF t => STrimF X (optimizetrim t)
end.
Just to double-check we understand what is going on there... a few examples with the intended output.
Definition testexp := SConc nat (SConc nat (SNil nat) (SConc nat (SSing nat 1) (SSing nat 2))) (SSing nat 1).
Definition trimtexp := (STrimF nat (STrimF nat testexp)).
Eval compute in seval(testexp).
[
[1, 2, 1] : list nat
]
Eval compute in seval(trimtexp).
= [1] : list nat
Eval compute in optimizetrim(trimtexp).
= STrimF nat
(STrimF nat
(SConc nat
(SConc nat (SNil nat) (SConc nat (SSing nat 1) (SSing nat 2)))
(SSing nat 1)))
: sexp nat
(STrimF nat
(SConc nat
(SConc nat (SNil nat) (SConc nat (SSing nat 1) (SSing nat 2)))
(SSing nat 1)))
: sexp nat
Definition testexp' := SConc nat (SSing nat 1) (SConc nat (SNil nat) (SConc nat (SSing nat 1) (SSing nat 2))) .
Definition trimtexp' := (STrimF nat (STrimF nat testexp')).
Eval compute in seval(testexp').
= [1, 1, 2]
: list nat
: list nat
Eval compute in seval(trimtexp').
= [2]
: list nat
: list nat
Eval compute in optimizetrim(trimtexp').
= STrimF nat
(SConc nat (SNil nat) (SConc nat (SSing nat 1) (SSing nat 2)))
: sexp nat
(SConc nat (SNil nat) (SConc nat (SSing nat 1) (SSing nat 2)))
: sexp nat
Eval compute in optimizetrim(optimizetrim(trimtexp')).
= STrimF nat (SConc nat (SSing nat 1) (SSing nat 2))
: sexp nat
: sexp nat
Eval compute in optimizetrim(optimizetrim(optimizetrim(trimtexp'))).
= SSing nat 2
: sexp nat
: sexp nat
Tactic Notation "sexp_cases" tactic(first) ident(c) :=
first;
[ Case_aux c "SNil" | Case_aux c "SSing"
| Case_aux c "SConc" | Case_aux c "STrimF" ].
Now we can prove a theorem that our optimization is indeed correct:
Theorem optimizetrim_sound : forall (X: Type) (s: sexp X),
seval (optimizetrim s) = seval s.
Proof. Admitted.
Of course, tacticals can be used to make the proof this theorem much shorter and you are well advised to try some mentioned at the end of the Wed lecture. With a clever use of these, you should not need more than 6-7 lines... okay, maybe a bit longish lines.
Explicit Proof Objects for Induction and Improved Proofs
Exercise 7: 5 points (gorgeous_is_trivial')
There's nothing magic about this induction lemma: it's just
another Coq lemma that requires a proof. Coq generates the proof
automatically too...
Print nat_ind.
We can read this as follows:
Suppose we have evidence f that P holds on 0, and
evidence f0 that forall n:nat, P n -> P (S n).
Then we can prove that P holds of an arbitrary nat n via
a recursive function F (here defined using the expression
form Fix rather than by a top-level Fixpoint
declaration). F pattern matches on n:
Those interested in functional programming may
notice that the match in F requires an annotation as n0
return (P n0) to help Coq's typechecker realize that the two arms
of the match actually return the same type (namely P n). This
is essentially like matching over a GADT (generalized algebraic
datatype) in Haskell. In fact, F has a dependent type: its
result type depends on its argument; GADT's can be used to
describe simple dependent types like this.
We can adapt this approach to proving nat_ind to help prove
non-standard induction principles too. Recall our desire to
prove that
forall n : nat, even n -> ev n.
Attempts to do this by standard induction on n fail, because the
induction principle only lets us proceed when we can prove that
even n -> even (S n) — which is of course never provable. What
we did earlier in this chapter was a bit of a hack:
Theorem even__ev : forall n : nat,
(even n -> ev n) /\ (even (S n) -> ev (S n)).
We can make a much better proof by defining and proving a
non-standard induction principle that goes "by twos":
- If it finds 0, F uses f to show that P n holds.
- If it finds S n0, F applies itself recursively on n0 to obtain evidence that P n0 holds; then it applies f0 on that evidence to show that P (S n) holds.
Definition nat_ind2 :
forall (P : nat -> Prop),
P 0 ->
P 1 ->
(forall n : nat, P n -> P (S(S n))) ->
forall n : nat , P n :=
fun P => fun P0 => fun P1 => fun PSS =>
fix f (n:nat) := match n return P n with
0 => P0
| 1 => P1
| S (S n') => PSS n' (f n')
end.
Once you get the hang of it, it is entirely straightforward to
give an explicit proof term for induction principles like this.
Proving this as a lemma using tactics is much less intuitive (try
it!).
The induction ... using tactic gives a convenient way to
specify a non-standard induction principle like this.
Lemma even__ev' : forall n, even n -> ev n.
Proof.
intros.
induction n as [ | |n'] using nat_ind2.
Case "even 0".
apply ev_0.
Case "even 1".
inversion H.
Case "even (S(S n'))".
apply ev_SS.
apply IHn'. unfold even. unfold even in H. simpl in H. apply H.
Qed.
Now, recall that in the previous Blatt we used a similar hack to show near-triviality of gorgeousness. Naturally, we can then hope to fix it in a similar way: defining an induction principle that goes "by three".
Definition nat_ind3 :
forall (P : nat -> Prop),
P 0 ->
P 1 ->
P 2 ->
(forall n : nat, P n -> P (S(S(S n)))) ->
forall n : nat , P n := admit.
<- FILL THIS IN!
Prove a similarly improved theorem using the new induction principle.
Theorem gorgeous_is_trivial' : forall n, gorgeous (n + 8).
Proof. Admitted.
This is the end of obligatory exercises.
BONUSAUFGABE (10 points)
Inductive no_repeats (X: Type) : list X -> Prop := .
<- Fill this in!
Check no_repeats_ind.
Eval compute in (no_repeats nat [1,2,3,4]).
Here is one more natural notion:
Definition disjoint {X:Type} (l1 l2: list X) := forall (x:X), appears_in x l1 -> ~ appears_in x l2.
Now prove the following theorem relating
disjoint, no_repeats and ++.
Theorem disjoint_no_repeats (X : Type) : forall l1 l2, (no_repeats X l1 /\ no_repeats X l2 /\ disjoint l1 l2) <-> no_repeats X (l1 ++ l2).
Proof. Admitted.