(* ----- Inductive *)
Inductive bytes_bits_lists : Blist -> list byte -> Prop :=
| eq_empty : bytes_bits_lists nil nil
| eq_cons : forall (bits : Blist) (bytes : list byte)
(b0 b1 b2 b3 b4 b5 b6 b7 : bool) (b : byte),
bytes_bits_lists bits bytes ->
convertByteBits [b0; b1; b2; b3; b4; b5; b6; b7] b ->
bytes_bits_lists (b0 :: b1 :: b2 :: b3 :: b4 :: b5 :: b6 :: b7 :: bits)
(b :: bytes).
(* ----- Computational *)
(* TODO: assumes Z is positive and in range, does not use Z.positive *)
Definition div_mod (num : Z) (denom : Z) : bool * Z :=
(Z.gtb (num / denom) 0, num mod denom).
Definition byteToBits (byte : byte) : Blist :=
let (b7, rem7) := div_mod (Byte.unsigned byte) 128 in
let (b6, rem6) := div_mod rem7 64 in
let (b5, rem5) := div_mod rem6 32 in
let (b4, rem4) := div_mod rem5 16 in
let (b3, rem3) := div_mod rem4 8 in
let (b2, rem2) := div_mod rem3 4 in
let (b1, rem1) := div_mod rem2 2 in
let (b0, rem0) := div_mod rem1 1 in
[b0; b1; b2; b3; b4; b5; b6; b7].
Fixpoint bytesToBits (bytes : list byte) : Blist :=
match bytes with
| [] => []
| byte :: xs => byteToBits byte ++ bytesToBits xs
end.
Definition bitsToByte (bits : Blist) : byte :=
Byte.repr
match bits with
| b0 :: b1 :: b2 :: b3 :: b4 :: b5 :: b6 :: b7 :: nil =>
(1 * (asZ b0) + 2 * (asZ b1) + 4 * (asZ b2) + 8 * (asZ b3)
+ 16 * (asZ b4) + 32 * (asZ b5) + 64 * (asZ b6) + 128 * (asZ b7))
| _ => -1                  (* should not happen *)
end.
Fixpoint bitsToBytes (bits : Blist) : list byte :=
match bits with
| b0 :: b1 :: b2 :: b3 :: b4 :: b5 :: b6 :: b7 :: xs =>
bitsToByte [b0; b1; b2; b3; b4; b5; b6; b7] :: bitsToBytes xs
| _ => []
end.
(* Definition isbyteZ i := (0 <= i < 256)%Z.  Lemma bitsToByte_isbyteZ b0 b1 b2 b3 b4 b5 b6 b7: isbyteZ (bitsToByte [b0; b1; b2; b3; b4; b5; b6; b7]). *)
(* -------------------- Various theorems and lemmas *)

(* Specifications: *)
Lemma byteToBits_length bt: length (byteToBits bt) = 8%nat.
Lemma bytesToBits_app : forall (l1 l2 : list byte),
bytesToBits (l1 ++ l2) = bytesToBits l1 ++ bytesToBits l2.
Lemma bytesToBits_len : forall (l : list byte),
length (bytesToBits l) = (length l * 8)%nat.
(* Prove by brute force (test all Z in range) *)
Theorem byte_bit_byte_id : forall (b : byte),
bitsToByte (byteToBits b) = b.
Theorem bits_byte_bits_id : forall (b0 b1 b2 b3 b4 b5 b6 b7 : bool),
[b0; b1; b2; b3; b4; b5; b6; b7] =
byteToBits (bitsToByte [b0; b1; b2; b3; b4; b5; b6; b7]).
Theorem bytes_bits_bytes_id : forall (bytes : list byte),
bitsToBytes (bytesToBits bytes) = bytes.