(* ----- 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).(* ----- 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).(* ----- 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.

(* Specifications: *)
Lemma byteToBits_length bt: length (byteToBits bt) = 8%nat.
Lemma bytes_bits_length : forall (bits : Blist) (bytes : list byte), bytes_bits_lists bits bytes -> length bits = (length bytes * 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.
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.
Theorem bytes_bits_def_eq : forall (bytes : list byte), bytes_bits_lists (bytesToBits bytes) bytes.
Theorem bytes_bits_comp_ind : forall (bits : Blist) (bytes : list byte), bits = bytesToBits bytes -> bytes_bits_lists bits bytes.
Theorem bytes_bits_ind_comp : forall (bits : Blist) (bytes : list byte), bytes_bits_lists bits bytes -> bytes = bitsToBytes bits.
Theorem bits_bytes_ind_comp : forall (bits : Blist) (bytes : list byte), bytes_bits_lists bits bytes -> bits = bytesToBits bytes.
Lemma bitsToBytes_app : forall (l m : Blist), InBlocks 8 l -> bitsToBytes (l ++ m) = bitsToBytes l ++ bitsToBytes m.
Lemma bitsToBytes_len_gen : forall (l : Blist) (n : nat), length l = (n * 8)%nat -> length (bitsToBytes l) = n.
Lemma bitsToBytes_len : forall (l : Blist), length l = 512%nat -> Zlength (bitsToBytes l) = 64%Z.
Lemma bits_bytes_bits_id : forall (l : Blist), InBlocks 8 l -> bytesToBits (bitsToBytes l) = l.
Lemma bytes_bits_lists_append: forall (l1 : Blist) (l2 : list byte) (m1 : Blist) (m2 : list byte), bytes_bits_lists l1 l2 -> bytes_bits_lists m1 m2 -> bytes_bits_lists (l1 ++ m1) (l2 ++ m2).
Lemma bytesToBits_nil_inv l: nil = bytesToBits l -> l = nil.
Lemma bytesToBits_cons b l: bytesToBits (b::l) = byteToBits b ++ bytesToBits l.
Lemma byteToBits_injective: forall a b, byteToBits a = byteToBits b -> a = b.
Lemma bytesToBits_injective: forall b1 b2, bytesToBits b1 = bytesToBits b2 -> b1=b2.
Lemma bitsToBytes_injective8 b1 b2 (B: bitsToBytes b1 = bitsToBytes b2) (L1: PeanoNat.Nat.divide 8 (length b1)) (L2: PeanoNat.Nat.divide 8 (length b2)): b1 = b2.
Lemma bitsToByte_cons: forall bits h t, (h::t) = bitsToBytes bits -> exists b0, exists b1, exists b2, exists b3, exists b4, exists b5, exists b6, exists b7, exists xs, bits = b0 :: b1 :: b2 :: b3 :: b4 :: b5 :: b6 :: b7 :: xs /\ h = bitsToByte [b0; b1; b2; b3; b4; b5; b6; b7] /\ t = bitsToBytes xs.