Just to be sure I understood well the comments of Arthur Azevedo De Amorim, I tried to rewrite my Coq module trying to remove the syntactic sugar and the unnecessary concealement of the size.
First, just by looking at the available module in Coq, one can fin the Coq.Bool.Bvector
module that is quite close to what I want... But, a lot is missing (especially the modular arithmetic part) and, moreover, I disagree with a few rules such as encoding the sign or having true and false as a particular case of size = 1
).
So, my module is almost a copy of the Coq.Bool.Bvector
but with slight modifications to make me happy (I might be wrong, we will see it in the future).
Here is the rewritten module:
Require Import Arith Bool Vector.
(** Unsigned Bitvector type *)
Definition bitvector := Vector.t bool.
Definition nil := @Vector.nil bool.
Definition cons := @Vector.cons bool.
Definition bv_low := @Vector.hd bool.
Definition bv_high := @Vector.tl bool.
(** A bitvector is false if zero and true otherwise. *)
Definition bv_test n (v : bitvector n) := Vector.fold_left orb false v.
(** Bitwise operators *)
Definition bv_neg n (v : bitvector n) := Vector.map negb v.
Definition bv_and n (v w : bitvector n) := Vector.map2 andb v w.
Definition bv_or n (v w : bitvector n) := Vector.map2 orb v w.
Definition bv_xor n (v w : bitvector n) := Vector.map2 xorb v w.
(** Shift/Rotate operators *)
(* TODO *)
(** Arithmetic operators *)
(* TODO *)
Then, I tried to prove (again) the double negation thing with the help of the Guillaume Allais hints:
Lemma map_fusion :
forall {A B C} {g : B -> C} {f : A -> B} {n : nat} (v : Vector.t A n),
Vector.map g (Vector.map f v) = Vector.map (fun x => g (f x)) v.
Proof.
intros A B C g f n v ; induction v.
- reflexivity.
- simpl; f_equal; assumption.
Qed.
Lemma map_id :
forall {A} {n : nat} (v : Vector.t A n), Vector.map (@id A) v = v.
Proof.
intros A n v ; induction v.
- reflexivity.
- simpl; f_equal; assumption.
Qed.
Lemma map_extensional :
forall {A B} {f1 f2 : A -> B}
(feq : forall a, f1 a = f2 a) {n : nat} (v : Vector.t A n),
Vector.map f1 v = Vector.map f2 v.
Proof.
intros A B f1 f2 feq n v ; induction v.
- reflexivity.
- simpl; f_equal; [apply feq | assumption].
Qed.
Theorem double_neg :
forall (n : nat) (v : bitvector n), bv_neg n (bv_neg n v) = v.
Proof.
intros; unfold bv_neg.
rewrite map_fusion, <- map_id.
apply map_extensional.
- intros []; reflexivity.
Qed.
And, surprisingly (for me), it worked like a charm.
Thanks a lot to all, and if you have comments on this code, feel free to drop a comment. As I am very new to Coq, I really want to improve.
bitvector
, but not that you would want to use a type like that. Indeed,bitvector
as defined there is isomorphic tolist bool
, precisely because the length indexn
is allowed to vary freely; on the other hand, the innerVector
argument makes it harder to use, as your question shows. I would recommend droppingbitvector
and just usinglist bool
instead, which would simplify your development quite a bit. – Arthur Azevedo De Amorimlist bool
. But, on the other hand, building the whole bit-vector operators will require to add constraints on the size of the arguments (e.g.bv_and a b
require the size ofa
andb
to be the same). I guess you can do this with a list, but isn't it easier if the size is part of the type ? – perrorbitvector
, the way it is defined, is precisely that there aren't any useful constraints that you can put on the length. If you want to define e.g.bv_add
that requires both bit vectors to have the same length, you can use theVector
type from the Coq standard library directly.bv_add
would then have typeforall n, Vector.t bool n -> Vector.t bool n -> Vector.t bool n
. You can't do a similar definition with the original version ofbitvector
because the length argument is "hidden" by the constructor and doesn't appear in the type. – Arthur Azevedo De Amorim