Mefresa.Batteries
Batteries is a general purpose library for Coq development.
It is essentially aimed at improving Coq Proof scripts' readability
and maintainability.
As of now, it features the following:
- An easy way to annotate/comment proof goals. This is achieved
by using the Case command. For instance,
- Case "Goal 1 of 5"
- A simple tactic(al) - thus - that fails if it doesn't solve the current goal. This is useful for maintaining your scripts. If you keep the rationale of always using this tactic to conclude your goals, whenever updating your Coq scripts, you will know exactly where it breaks.
Annotating your Proofs
The following definitions will allow us to annotate our proof scripts. These were
taken from Software Foundations.
Ltac move_to_top x :=
match reverse goal with
| H : _ |- _ => try move x after H
end.
Tactic Notation "assert_eq" ident(x) constr(v) :=
let H := fresh in
assert (x = v) as H by reflexivity;
clear H.
Tactic Notation "Case_aux" ident(x) constr(name) :=
first [
set (x := name); move_to_top x
| assert_eq x name; move_to_top x
| fail 1 "because we are working on a different case" ].
Tactic Notation "Case" constr(name) := Case_aux Case name.
Tactic Notation "SCase" constr(name) := Case_aux SCase name.
Tactic Notation "SSCase" constr(name) := Case_aux SSCase name.
Tactic Notation "SSSCase" constr(name) := Case_aux SSSCase name.
Tactic Notation "SSSSCase" constr(name) := Case_aux SSSSCase name.
Tactic Notation "SSSSSCase" constr(name) := Case_aux SSSSSCase name.
Tactic Notation "SSSSSSCase" constr(name) := Case_aux SSSSSSCase name.
Tactic Notation "SSSSSSSCase" constr(name) := Case_aux SSSSSSSCase name.
match reverse goal with
| H : _ |- _ => try move x after H
end.
Tactic Notation "assert_eq" ident(x) constr(v) :=
let H := fresh in
assert (x = v) as H by reflexivity;
clear H.
Tactic Notation "Case_aux" ident(x) constr(name) :=
first [
set (x := name); move_to_top x
| assert_eq x name; move_to_top x
| fail 1 "because we are working on a different case" ].
Tactic Notation "Case" constr(name) := Case_aux Case name.
Tactic Notation "SCase" constr(name) := Case_aux SCase name.
Tactic Notation "SSCase" constr(name) := Case_aux SSCase name.
Tactic Notation "SSSCase" constr(name) := Case_aux SSSCase name.
Tactic Notation "SSSSCase" constr(name) := Case_aux SSSSCase name.
Tactic Notation "SSSSSCase" constr(name) := Case_aux SSSSSCase name.
Tactic Notation "SSSSSSCase" constr(name) := Case_aux SSSSSSCase name.
Tactic Notation "SSSSSSSCase" constr(name) := Case_aux SSSSSSSCase name.
For instance, we can now annotate our proofs as the following:
Example annotation_example:
forall A B, B /\ A -> (B /\ A).
Proof.
intros A B Hab.
split.
Case "Let's prove the left side of the conjunction: B".
apply Hab.
Case "Let's prove the right side of the conjunction: A".
apply Hab.
Qed.
forall A B, B /\ A -> (B /\ A).
Proof.
intros A B Hab.
split.
Case "Let's prove the left side of the conjunction: B".
apply Hab.
Case "Let's prove the right side of the conjunction: A".
apply Hab.
Qed.
The above example was trivial. Let's see now something more interesting.
Consider the following definition:
Inductive awesome_definition : Type :=
| Constructor1 : awesome_definition
| Constructor2 : awesome_definition.
We will now associate annotations to the goals generated by some
given tactic. It is also possible to specify whether we are in 'Case's, 'SCase's, etc
Tactic Notation "awesome_cases" tactic(first) ident(c) :=
first;
[ Case_aux c "1st Constructor"
| Case_aux c "2nd Constructor" ].
first;
[ Case_aux c "1st Constructor"
| Case_aux c "2nd Constructor" ].
Generally speaking, this will serve the purpose of automatically associating
the given strings foreach constructors of some given inductive definition.
With this tactical defined, we can use it as follows:
Example induction_example:
forall a, a = Constructor1 \/ a = Constructor2.
Proof.
intros.
awesome_cases (induction a) Case.
And just like that, we get our goals already annotated at the
top of our hypotheses. Moreover, the strings given on the
'(S)Case's must be consistent with a possible annotation already present.
Case "1st Constructor".
left; auto.
Case "2nd Constructor".
right; auto.
Qed.
left; auto.
Case "2nd Constructor".
right; auto.
Qed.
A Few Tactics
A Conclude-the-current-goal-or-Fail Tactic
Tactic Notation "thus" tactic(H) := solve [H; auto].
The above tactical fails if it doesn't solve the current goal. Here it is in
action.
Reflexivity-like Tactic
Tactic Notation "refl" := simpl; reflexivity.
Type-fast-Functional-induction Tactic
Ltac run f := functional induction f.
Ltac fromfalse := assert (False) as Hfalse; eauto; inversion Hfalse.
Some Functions and Facts on Lists
Require Export List.
Set Implicit Arguments.
Fixpoint fold_left2 (A:Type) (B:Type) (C:Type)
(f : A -> B -> C -> A)
(accu: A)
(l1: list B) (l2: list C) : option A :=
match l1, l2 with
nil, nil => Some accu
| cons a1 l1, cons a2 l2 => fold_left2 f (f accu a1 a2) l1 l2
| _, _ => None
end.
Set Implicit Arguments.
Fixpoint fold_left2 (A:Type) (B:Type) (C:Type)
(f : A -> B -> C -> A)
(accu: A)
(l1: list B) (l2: list C) : option A :=
match l1, l2 with
nil, nil => Some accu
| cons a1 l1, cons a2 l2 => fold_left2 f (f accu a1 a2) l1 l2
| _, _ => None
end.
For instance, we can use this definition for computations as follows:
Definition listAcc := (nil:list nat).
Definition listB := 1::2::3::nil.
Definition listC := 3::2::1::nil.
Eval compute in fold_left2 (fun a b c => (b+c)::a) listAcc listB listC.
Eval compute in fold_left2 (fun a b c => (b+c)::a) listAcc listB (0 :: listC).
A fact about list that may become handy. Got it from
a thread in the Coq mailing list.
Lemma in_app_cons_fact : forall A x (a : A) bs cs,
In x ((a :: bs) ++ cs) <-> In x (bs ++ (a :: cs)).
Proof.
intros A x a bs cs ; split ; intro H ; apply in_or_app ;
assert (H2 := in_app_or _ _ _ H) ; destruct H2.
destruct H0.
right ; constructor ; assumption.
left ; assumption.
do 2 right ; assumption.
left ; right ; assumption.
destruct H0.
left ; constructor ; assumption.
right ; assumption.
Qed.
Function beq_nat (x:nat) (y:nat) : bool :=
match x, y with
O, O => true
| S n, S m => beq_nat n m
| _, _ => false
end.
implementation of:
(n <= m) ? true : false
Function ble_nat n m : bool :=
match n, m with
O, _ => true
| S n', S m' => ble_nat n' m'
| _, _ => false
end.
match n, m with
O, _ => true
| S n', S m' => ble_nat n' m'
| _, _ => false
end.
Let's make it reflect into Prop!
Require Import Coq.Arith.Le.
Lemma ble_nat_reflection:
forall n m, ble_nat n m = true <-> n <= m.
Proof.
induction n; induction m; split; simpl; intros;
auto with arith;
try congruence.
Case "1/3".
inversion H.
Case "2/3".
destruct IHn with (m:=m); auto.
apply le_n_S; auto.
Case "3/3".
destruct IHn with (m:=m); auto.
apply H1.
apply le_S_n; auto.
Qed.
Hint Resolve ble_nat_reflection.
Require Import Coq.Strings.Ascii.
Require Import Coq.Strings.String.
Require Import NPeano.
Require Import Recdef.
Definition digit_of_nat n := ascii_of_nat (n + 48).
Function string_of_nat_aux n acc {measure (fun x => x) n} :=
match n with
| 0 => acc
| _ => string_of_nat_aux (n / 10) (String (digit_of_nat (n mod 10)) acc)
end.
Proof.
intros. apply Nat.div_lt; auto with arith.
Defined.
Definition string_of_nat n :=
match n with
| 0 => "0"%string
| _ => string_of_nat_aux n EmptyString
end.
Definition nat_to_string := string_of_nat.
Axiom nat_to_string_fact:
forall n m, n <> m -> nat_to_string n <> nat_to_string m.
Lemma string_append_fact:
forall s1 s2 s3: string, s2 = s3 -> append s1 s2 = append s1 s3.
Proof.
intros.
apply f_equal; auto.
Qed.
Lemma string_append_fact':
forall s1 s2 s3: string, s2 <> s3 -> append s1 s2 <> append s1 s3.
Proof.
induction s1; intros; simpl; auto.
assert ((s1 ++ s2)%string <> (s1 ++ s3)%string); try apply IHs1; auto.
intuition.
apply H0.
inversion H1. auto.
Qed.
Function beq_bool b b' : bool :=
match b, b' with
true , true => true
| false, false => true
| _ , _ => false
end.
Open Scope bool_scope.
Require Import Coq.Bool.Bool.
Function beq_ascii a a' : bool :=
match a, a' with
Ascii b1 b2 b3 b4 b5 b6 b7 b8,
Ascii b1' b2' b3' b4' b5' b6' b7' b8' =>
(beq_bool b1 b1') && (beq_bool b2 b2') && (beq_bool b3 b3') && (beq_bool b4 b4')
&& (beq_bool b5 b5') && (beq_bool b6 b6') && (beq_bool b7 b7') && (beq_bool b8 b8')
end.
Lemma beq_bool_refl:
forall b, beq_bool b b = true.
Proof.
destruct b; simpl; refl.
Qed.
Lemma ascii_eq_reflection:
forall a a', a=a' <-> beq_ascii a a'=true.
Proof.
destruct a; destruct a'; split; intro.
Case "Prop Entails Bool".
inversion H; subst; auto.
clear.
simpl.
repeat rewrite beq_bool_refl.
simpl; auto.
Case "Bool Entails Prop".
simpl in H.
repeat rewrite andb_true_iff in H.
do 7 (destruct H).
destruct b; destruct b7; simpl in H; try congruence; clear H;
destruct b0; destruct b8; simpl in H6; try congruence; clear H6;
destruct b1; destruct b9; simpl in H5; try congruence; clear H5;
destruct b2; destruct b10; simpl in H4; try congruence; clear H4;
destruct b3; destruct b11; simpl in H3; try congruence; clear H3;
destruct b4; destruct b12; simpl in H2; try congruence; clear H2;
destruct b5; destruct b13; simpl in H1; try congruence; clear H1;
destruct b6; destruct b14; simpl in H0; try congruence; clear H0.
Qed.
Function beq_string str str' : bool :=
match str, str' with
String a r, String a' r' => beq_ascii a a' && beq_string r r'
| EmptyString, EmptyString => true
| _ , _ => false
end.
Lemma beq_string_reflection_in_bool:
forall str str', beq_string str str'= true -> str=str'.
Proof.
induction str; induction str'; intros; simpl in *; try congruence.
rewrite andb_true_iff in H.
destruct H.
destruct ascii_eq_reflection with (a:=a) (a':=a0).
assert (a=a0); auto; subst.
apply f_equal.
apply IHstr; auto.
Qed.
Lemma beq_string_reflection_in_prop:
forall str str', str=str' -> beq_string str str'= true.
Proof.
induction str; induction str'; simpl; intros; try congruence.
inversion H; subst.
rewrite andb_true_iff.
split; auto.
destruct a0.
simpl.
repeat rewrite beq_bool_refl.
simpl; auto.
Qed.
Lemma beq_string_reflection:
forall str str', str=str' <-> beq_string str str'= true.
Proof.
split;
[ apply beq_string_reflection_in_prop
| apply beq_string_reflection_in_bool
]; auto.
Qed.
A notion of Identifier
For instance, we can define some identifiers in the following manner:
Module IdentDefinitionExample.
Definition FooVariable : ident := Ident "foo".
Definition BarVariable : ident := Ident "bar".
Definition XyzVariable : ident := Ident "foo".
End IdentDefinitionExample.
Definition FooVariable : ident := Ident "foo".
Definition BarVariable : ident := Ident "bar".
Definition XyzVariable : ident := Ident "foo".
End IdentDefinitionExample.
We will a function to determine whether to identifiers are equal.
Since our identifiers are simple constructions that take a string,
we simply need to check whether the given identifiers have identical strings.
Function beq_ident (id1 id2: ident) : bool :=
match id1, id2 with
| Ident str1, Ident str2 => match string_dec str1 str2 with
| left _ => true
| right _ => false
end
end.
match id1, id2 with
| Ident str1, Ident str2 => match string_dec str1 str2 with
| left _ => true
| right _ => false
end
end.
For the sake of readability, we define a notation to our comparison
over identifiers operator close to what one would see in a standard
programming language.
We shall use it seamlessly in the remaining of this development.
We can right now see our first proof. Let's start with something simple:
decidability on identifiers.
Lemma ident_dec : forall id1 id2 : ident, {id1 = id2} + {id1 <> id2}.
Proof.
repeat decide equality.
Qed.
Lemma str_symmetry:
forall str1 str2, (if string_dec str1 str2 then true else false) =
(if string_dec str2 str1 then true else false).
Proof.
intros.
destruct string_dec with (s1:=str1) (s2:=str2);
destruct string_dec with (s1:=str2) (s2:=str1); auto.
congruence.
Qed.
Proof.
repeat decide equality.
Qed.
Lemma str_symmetry:
forall str1 str2, (if string_dec str1 str2 then true else false) =
(if string_dec str2 str1 then true else false).
Proof.
intros.
destruct string_dec with (s1:=str1) (s2:=str2);
destruct string_dec with (s1:=str2) (s2:=str1); auto.
congruence.
Qed.
Moreover, we shall prove an obvious, yet very useful lemma about
our ident equality operator: it is symmetric.
Lemma beq_ident_symmetry:
forall i i' X, (i == i') = X <-> (i' == i) = X.
Proof.
Admitted.
Lemma bool_conjunction_fact:
forall id id' X,
(id == id') && X = true ->
(id == id') = true.
Proof.
intros.
functional induction beq_ident id id'; auto.
Qed.
Hint Resolve bool_conjunction_fact.
Lemma bool_conjunction_fact':
forall id id' X,
X && (id == id') = true ->
(id == id') = true.
Proof.
intros.
assert (X && (id == id') = (id == id') && X).
intuition.
rewrite H0 in H.
functional induction beq_ident id id'; auto.
Qed.
Hint Resolve bool_conjunction_fact'.
Lemma ident_equality_Prop2bool:
forall id id',
id = id' ->
(id == id') = true.
Proof.
intros.
functional induction beq_ident id id'; auto.
congruence.
Qed.
Lemma ident_equality_Prop2bool':
forall id id',
id <> id' ->
(id == id') = false.
Proof.
intros.
functional induction beq_ident id id'; auto.
congruence.
Qed.
Lemma ident_equality_bool2Prop:
forall id id',
(id == id') = false -> id <> id'.
Proof.
intros.
functional induction beq_ident id id'.
congruence.
intuition.
apply _x; auto.
inversion H0; auto.
Qed.
Lemma ident_equality_bool2Prop':
forall id id',
(id == id') = true -> id = id'.
Proof.
intros.
functional induction beq_ident id id'; auto.
congruence.
Qed.
Hint Resolve ident_equality_Prop2bool ident_equality_Prop2bool'
ident_equality_bool2Prop ident_equality_bool2Prop'.
Lemma ident_eq_reflection:
forall id1 id2, (id1==id2)=true <-> id1=id2.
Proof.
intros; split; auto.
Qed.
Lemma beq_ident_symmetry':
forall i i', (i == i') = false <-> (i' == i) = false.
Proof.
intros.
destruct ident_dec with (id1:=i) (id2:=i').
Case "Ids are equal".
assert ((i == i') = true).
eauto.
subst.
rewrite H.
split; congruence.
Case "Ids are not Equal".
assert ((i == i') = false).
eauto.
assert ((i' == i) = false).
eauto.
rewrite H; rewrite H0; intuition.
Qed.
Hint Resolve beq_ident_symmetry'.
forall i i' X, (i == i') = X <-> (i' == i) = X.
Proof.
Admitted.
Lemma bool_conjunction_fact:
forall id id' X,
(id == id') && X = true ->
(id == id') = true.
Proof.
intros.
functional induction beq_ident id id'; auto.
Qed.
Hint Resolve bool_conjunction_fact.
Lemma bool_conjunction_fact':
forall id id' X,
X && (id == id') = true ->
(id == id') = true.
Proof.
intros.
assert (X && (id == id') = (id == id') && X).
intuition.
rewrite H0 in H.
functional induction beq_ident id id'; auto.
Qed.
Hint Resolve bool_conjunction_fact'.
Lemma ident_equality_Prop2bool:
forall id id',
id = id' ->
(id == id') = true.
Proof.
intros.
functional induction beq_ident id id'; auto.
congruence.
Qed.
Lemma ident_equality_Prop2bool':
forall id id',
id <> id' ->
(id == id') = false.
Proof.
intros.
functional induction beq_ident id id'; auto.
congruence.
Qed.
Lemma ident_equality_bool2Prop:
forall id id',
(id == id') = false -> id <> id'.
Proof.
intros.
functional induction beq_ident id id'.
congruence.
intuition.
apply _x; auto.
inversion H0; auto.
Qed.
Lemma ident_equality_bool2Prop':
forall id id',
(id == id') = true -> id = id'.
Proof.
intros.
functional induction beq_ident id id'; auto.
congruence.
Qed.
Hint Resolve ident_equality_Prop2bool ident_equality_Prop2bool'
ident_equality_bool2Prop ident_equality_bool2Prop'.
Lemma ident_eq_reflection:
forall id1 id2, (id1==id2)=true <-> id1=id2.
Proof.
intros; split; auto.
Qed.
Lemma beq_ident_symmetry':
forall i i', (i == i') = false <-> (i' == i) = false.
Proof.
intros.
destruct ident_dec with (id1:=i) (id2:=i').
Case "Ids are equal".
assert ((i == i') = true).
eauto.
subst.
rewrite H.
split; congruence.
Case "Ids are not Equal".
assert ((i == i') = false).
eauto.
assert ((i' == i) = false).
eauto.
rewrite H; rewrite H0; intuition.
Qed.
Hint Resolve beq_ident_symmetry'.
Another useful definition is the not-equal operator. Simply defined as follows:
Definition bneq_ident id1 id2 : bool :=
match beq_ident id1 id2 with
| true => false
| false => true
end.
Notation "s1 '!=' s2" := (bneq_ident s1 s2) (at level 80).
Lemma bneq_false_entails_beq_true:
forall id id',
(id != id') = false ->
(id == id') = true.
Proof.
intros.
destruct ident_dec with (id1:=id) (id2:=id'); auto.
assert ((id == id') = false).
auto.
unfold bneq_ident in H.
rewrite H0 in H.
discriminate.
Qed.
Lemma bneq_true_entails_beq_false:
forall id id',
(id != id') = true ->
(id == id') = false.
Proof.
intros.
destruct ident_dec with (id1:=id) (id2:=id'); auto.
assert ((id == id') = true).
auto.
unfold bneq_ident in H.
rewrite H0 in H.
discriminate.
Qed.
Hint Resolve bneq_false_entails_beq_true bneq_true_entails_beq_false.
match beq_ident id1 id2 with
| true => false
| false => true
end.
Notation "s1 '!=' s2" := (bneq_ident s1 s2) (at level 80).
Lemma bneq_false_entails_beq_true:
forall id id',
(id != id') = false ->
(id == id') = true.
Proof.
intros.
destruct ident_dec with (id1:=id) (id2:=id'); auto.
assert ((id == id') = false).
auto.
unfold bneq_ident in H.
rewrite H0 in H.
discriminate.
Qed.
Lemma bneq_true_entails_beq_false:
forall id id',
(id != id') = true ->
(id == id') = false.
Proof.
intros.
destruct ident_dec with (id1:=id) (id2:=id'); auto.
assert ((id == id') = true).
auto.
unfold bneq_ident in H.
rewrite H0 in H.
discriminate.
Qed.
Hint Resolve bneq_false_entails_beq_true bneq_true_entails_beq_false.
As you can guess, comparison operators like beq_ident and bneq_ident
will be useful for other types of structures that we will define at a more
advanced stage. Of course, one would prefer to maintain the same notation
for these other comparison operators.
Unfortunately for us, Coq will not be always able to infer to which
actual definition the notation is referring to. As such, we will need to
define scopes. This is the purpose of the following two lines. Their
real significance will become clear with examples.
Bind Scope id_scope with ident.
Delimit Scope id_scope with id.
Delimit Scope id_scope with id.
Warming up: Some Simple Proof Examples
Module IdentProofExamples.
Definition x : ident := Ident "x".
Definition y : ident := Ident "y".
Example warm_up_proof:
x = y -> False.
Proof.
intro.
thus inversion H.
Qed.
Example warm_up_proof':
(x == y) = false.
Proof.
unfold beq_ident.
thus auto.
Qed.
End IdentProofExamples.
Require Export Arith.EqNat.
Hint Resolve ident_dec beq_ident_symmetry.
Generic comparison function over lists.
Function beq_list_X (X:Type) (l l':list X) (beq_X:X->X->bool) : bool :=
match l, l' with
nil, nil => true
| e::r, e'::r' => if beq_X e e' then beq_list_X r r' beq_X else false
| _ , _ => false
end.
Arguments beq_list_X [X] l l' beq_X.
match l, l' with
nil, nil => true
| e::r, e'::r' => if beq_X e e' then beq_list_X r r' beq_X else false
| _ , _ => false
end.
Arguments beq_list_X [X] l l' beq_X.
Function generate_list (X:Type) (size:nat) (val:X): list X :=
match size with
0 => nil
| S n => val :: generate_list n val
end.
Implicit Arguments generate_list [X].
Record ArrayList : Type := mk_arraylist {
element_type : Type;
arraylist_length : nat;
contents : list element_type
}.
Notation "'new' 'ArrayList<bool>' n" := (mk_arraylist n (generate_list n false)) (at level 80) .
Notation "'new' 'ArrayList<nat>' n" := (mk_arraylist n (generate_list n 0)) (at level 80) .
Function get_element_by_index X (l:list X) (i:nat) : option X :=
match i, l with
0 , e :: r => Some e
| S n, e :: r => get_element_by_index r n
| _, _ => None
end.
Implicit Arguments get_element_by_index [X].
Function getArrayListElement a i :=
get_element_by_index (@contents a) i.
Notation "a '[|' i '|]'" := (getArrayListElement a i) (at level 80).
Function set_element_by_index X (l:list X) i el : list X :=
match i, l with
0 , e :: r => el :: r
| S n, e :: r => e :: set_element_by_index r n el
| _, _ => nil
end.
Implicit Arguments set_element_by_index [X].
Function setArrayListElement a i el :=
let l := set_element_by_index (@contents a) i el in
mk_arraylist (@arraylist_length a) l.
Notation "a '[|' i '|]' '<-' el" := (setArrayListElement a i el) (at level 80).