val reverse_bytes_nat32_quad32_seq (q: seq quad32) : seq quad32
let reverse_bytes_nat32_quad32_seq (q:seq quad32) : seq quad32 =
  seq_map reverse_bytes_nat32_quad32 q
val reverse_bytes_nat32_quad32_seq (q: seq quad32) : seq quad32
let reverse_bytes_nat32_quad32_seq (q: seq quad32) : seq quad32 =
seq_map reverse_bytes_nat32_quad32 q
Vale.Arch.Types.fsti.checked
Vale.Lib.Seqs_s.fst.checked Vale.Lib.Seqs.fsti.checked Vale.Def.Words_s.fsti.checked Vale.Def.Words.Two_s.fsti.checked Vale.Def.Words.Seq_s.fsti.checked Vale.Def.Words.Seq.fsti.checked Vale.Def.Words.Four_s.fsti.checked Vale.Def.Types_s.fst.checked Vale.Def.Opaque_s.fsti.checked prims.fst.checked FStar.Seq.fst.checked FStar.Pervasives.fsti.checked FStar.Mul.fst.checked
Vale.Arch.Types.fsti
FStar.Seq.Base.seq Vale.Def.Types_s.quad32 Vale.Lib.Seqs_s.seq_map Vale.Arch.Types.reverse_bytes_nat32_quad32
module Vale.Arch.Types

open FStar.Mul
open Vale.Def.Opaque_s
open Vale.Def.Types_s
open Vale.Lib.Seqs_s
open Vale.Lib.Seqs
open Vale.Def.Words_s
open Vale.Def.Words.Four_s
open Vale.Def.Words.Seq_s
open Vale.Def.Words.Seq
open FStar.Seq
open Vale.Def.Words.Two_s

unfold let ( *^ ) = nat32_xor
unfold let ( *^^ ) = quad32_xor

unfold let add_wrap32 (x:nat32) (y:nat32) : nat32 = add_wrap x y
unfold let add_wrap64 (x:nat64) (y:nat64) : nat64 = add_wrap x y
unfold let sub_wrap32 (x:nat32) (y:nat32) : nat32 = sub_wrap x y
unfold let sub_wrap64 (x:nat64) (y:nat64) : nat64 = sub_wrap x y

unfold let iand32 (a:nat32) (b:nat32) : nat32 = iand a b
unfold let ixor32 (a:nat32) (b:nat32) : nat32 = ixor a b
unfold let ior32 (a:nat32) (b:nat32) : nat32 = ior a b
unfold let inot32 (a:nat32) : nat32 = inot a
unfold let ishl32 (a:nat32) (s:int) : nat32 = ishl a s
unfold let ishr32 (a:nat32) (s:int) : nat32 = ishr a s

unfold let iand64 (a:nat64) (b:nat64) : nat64 = iand a b
unfold let ixor64 (a:nat64) (b:nat64) : nat64 = ixor a b
unfold let ior64 (a:nat64) (b:nat64) : nat64 = ior a b
unfold let inot64 (a:nat64) : nat64 = inot a
unfold let ishl64 (a:nat64) (s:int) : nat64 = ishl a s
unfold let ishr64 (a:nat64) (s:int) : nat64 = ishr a s

unfold let iand128 (a:nat128) (b:nat128) : nat128 = iand a b
unfold let ixor128 (a:nat128) (b:nat128) : nat128 = ixor a b
unfold let ior128 (a:nat128) (b:nat128) : nat128 = ior a b
unfold let inot128 (a:nat128) : nat128 = inot a
unfold let ishl128 (a:nat128) (s:int) : nat128 = ishl a s
unfold let ishr128 (a:nat128) (s:int) : nat128 = ishr a s

unfold let two_to_nat32 (x:two nat32) : nat64 = two_to_nat 32 x

val lemma_nat_to_two32 (_:unit) : Lemma
  (forall (x:nat64).{:pattern (nat_to_two 32 x)}
    nat_to_two 32 x == Mktwo (x % 0x100000000) (x / 0x100000000))

let quad32_shl32 (q:quad32) : quad32 =
  let Mkfour v0 v1 v2 v3 = q in
  Mkfour 0 v0 v1 v2

let add_wrap_quad32 (q0 q1:quad32) : quad32 =
  let open Vale.Def.Words_s in
  Mkfour (add_wrap q0.lo0 q1.lo0)
         (add_wrap q0.lo1 q1.lo1)
         (add_wrap q0.hi2 q1.hi2)
         (add_wrap q0.hi3 q1.hi3)

val lemma_BitwiseXorCommutative (x y:nat32) : Lemma (x *^ y == y *^ x)
val lemma_BitwiseXorWithZero (n:nat32) : Lemma (n *^ 0 == n)
val lemma_BitwiseXorCancel (n:nat32) : Lemma (n *^ n == 0)
val lemma_BitwiseXorCancel64 (n:nat64) : Lemma (ixor n n == 0)
val lemma_BitwiseXorAssociative (x y z:nat32) : Lemma (x *^ (y *^ z) == (x *^ y) *^ z)

val xor_lemmas (_:unit) : Lemma
  (ensures
    (forall (x y:nat32).{:pattern (x *^ y)} x *^ y == y *^ x) /\
    (forall (n:nat32).{:pattern (n *^ 0)} n *^ 0 == n) /\
    (forall (n:nat32).{:pattern (n *^ n)} n *^ n == 0) /\
    (forall (n:nat64).{:pattern (ixor n n)} ixor n n == 0) /\
    (forall (x y z:nat32).{:pattern (x *^ (y *^ z))} x *^ (y *^ z) == (x *^ y) *^ z)
  )

val lemma_quad32_xor (_:unit) : Lemma (forall q . {:pattern quad32_xor q q} quad32_xor q q == Mkfour 0 0 0 0)

let quad32_double_lo (q:quad32) : double32 = (four_to_two_two q).lo
let quad32_double_hi (q:quad32) : double32 = (four_to_two_two q).hi

val lemma_reverse_reverse_bytes_nat32 (n:nat32) :
  Lemma (reverse_bytes_nat32 (reverse_bytes_nat32 n) == n)
  [SMTPat (reverse_bytes_nat32 (reverse_bytes_nat32 n))]

val lemma_reverse_bytes_quad32 (q:quad32) :
  Lemma (reverse_bytes_quad32 (reverse_bytes_quad32 q) == q)
  [SMTPat (reverse_bytes_quad32 (reverse_bytes_quad32 q))]

val lemma_reverse_bytes_quad32_zero (_:unit) : Lemma
  (let z = Mkfour 0 0 0 0 in
   reverse_bytes_quad32 z == z)

val lemma_reverse_reverse_bytes_nat32_seq (s:seq nat32) :
  Lemma (reverse_bytes_nat32_seq (reverse_bytes_nat32_seq s) == s)
  [SMTPat (reverse_bytes_nat32_seq (reverse_bytes_nat32_seq s))]

unfold let quad32_to_seq (q:quad32) : seq nat32 = four_to_seq_LE q

val lemma_insert_nat64_properties (q:quad32) (n:nat64) :
  Lemma ( (let q' = insert_nat64 q n 0 in
            q'.hi2 == q.hi2 /\
            q'.hi3 == q.hi3) /\
           (let q' = insert_nat64 q n 1 in
            q'.lo0 == q.lo0 /\
            q'.lo1 == q.lo1))
  [SMTPat (insert_nat64 q n)]

val lemma_insert_nat64_nat32s (q:quad32) (n0 n1:nat32) :
  Lemma ( insert_nat64 q (two_to_nat32 (Mktwo n0 n1)) 0 ==
          Mkfour n0 n1 q.hi2 q.hi3 /\
          insert_nat64 q (two_to_nat32 (Mktwo n0 n1)) 1 ==
          Mkfour q.lo0 q.lo1 n0 n1 )

let lo64_def (q:quad32) : nat64 = two_to_nat 32 (two_select (four_to_two_two q) 0)
[@"opaque_to_smt"] let lo64 = opaque_make lo64_def
irreducible let lo64_reveal = opaque_revealer (`%lo64) lo64 lo64_def

let hi64_def (q:quad32) : nat64 = two_to_nat 32 (two_select (four_to_two_two q) 1)
[@"opaque_to_smt"] let hi64 = opaque_make hi64_def
irreducible let hi64_reveal = opaque_revealer (`%hi64) hi64 hi64_def

val lemma_lo64_properties (_:unit) : Lemma
  (forall (q0 q1:quad32).{:pattern lo64 q0; lo64 q1}
    (q0.lo0 == q1.lo0 /\ q0.lo1 == q1.lo1) <==> (lo64 q0 == lo64 q1))

val lemma_hi64_properties (_:unit) : Lemma
  (forall (q0 q1:quad32).{:pattern hi64 q0; hi64 q1}
    (q0.hi2 == q1.hi2 /\ q0.hi3 == q1.hi3) <==> (hi64 q0 == hi64 q1))

val lemma_reverse_bytes_quad32_64 (src orig final:quad32) : Lemma
  (requires final == insert_nat64 (insert_nat64 orig (reverse_bytes_nat64 (hi64 src)) 0) (reverse_bytes_nat64 (lo64 src)) 1)
  (ensures  final == reverse_bytes_quad32 src)

val lemma_equality_check_helper (q:quad32) :
  Lemma ((q.lo0 == 0 /\ q.lo1 == 0 ==> lo64 q == 0) /\
         ((not (q.lo0 = 0) \/ not (q.lo1 = 0)) ==> not (lo64 q = 0)) /\
         (q.hi2 == 0 /\ q.hi3 == 0 ==> hi64 q == 0) /\
         ((~(q.hi2 = 0) \/ ~(q.hi3 = 0)) ==> ~(hi64 q = 0)) /\
         (q.lo0 == 0xFFFFFFFF /\ q.lo1 == 0xFFFFFFFF <==> lo64 q == 0xFFFFFFFFFFFFFFFF) /\
         (q.hi2 == 0xFFFFFFFF /\ q.hi3 == 0xFFFFFFFF <==> hi64 q == 0xFFFFFFFFFFFFFFFF)
         )

let lemma_equality_check_helper_2 (q1 q2 cmp:quad32) (tmp1 result1 tmp2 tmp3 result2:nat64) : Lemma
  (requires cmp == Mkfour (if q1.lo0 = q2.lo0 then 0xFFFFFFFF else 0)
                          (if q1.lo1 = q2.lo1 then 0xFFFFFFFF else 0)
                          (if q1.hi2 = q2.hi2 then 0xFFFFFFFF else 0)
                          (if q1.hi3 = q2.hi3 then 0xFFFFFFFF else 0) /\
            tmp1 = lo64 cmp /\
            result1 = (if tmp1 = 0xFFFFFFFFFFFFFFFF then 0 else 1) /\
            tmp2 = hi64 cmp /\
            tmp3 = (if tmp2 = 0xFFFFFFFFFFFFFFFF then 0 else 1) /\
            result2 = tmp3 + result1)
  (ensures (if q1 = q2 then result2 = 0 else result2 > 0))
  =
  lemma_equality_check_helper cmp;
  ()

val push_pop_xmm (x y:quad32) : Lemma
  (let x' = insert_nat64 (insert_nat64 y (hi64 x) 1) (lo64 x) 0 in
   x == x')

val lemma_insrq_extrq_relations (x y:quad32) :
  Lemma (let z = insert_nat64 x (lo64 y) 0 in
         z == Mkfour y.lo0 y.lo1 x.hi2 x.hi3 /\
        (let z = insert_nat64 x (hi64 y) 1 in
         z == Mkfour x.lo0 x.lo1 y.hi2 y.hi3))

val le_bytes_to_nat64_to_bytes (s:nat64) :
  Lemma (le_bytes_to_nat64 (le_nat64_to_bytes s) == s)

val le_nat64_to_bytes_to_nat64 (s:seq nat8 { length s == 8 }) :
  Lemma (le_nat64_to_bytes (le_bytes_to_nat64 s) == s)

val le_bytes_to_seq_quad32_empty: unit ->
  Lemma (forall s . {:pattern (length (le_bytes_to_seq_quad32 s)) } length s == 0 ==> length (le_bytes_to_seq_quad32 s) == 0)

val be_bytes_to_seq_quad32_empty: unit ->
  Lemma (forall s . {:pattern (length (be_bytes_to_seq_quad32 s)) } length s == 0 ==> length (be_bytes_to_seq_quad32 s) == 0)

val le_bytes_to_seq_quad32_to_bytes_one_quad (b:quad32) :
  Lemma (le_bytes_to_seq_quad32 (le_quad32_to_bytes b) == create 1 b)

let be_quad32_to_bytes (q:quad32) : seq16 nat8 =
  seq_four_to_seq_BE (seq_map (nat_to_four 8) (four_to_seq_BE q))

val be_bytes_to_seq_quad32_to_bytes_one_quad (b:quad32) :
  Lemma (be_bytes_to_seq_quad32 (be_quad32_to_bytes b) == create 1 b)

val le_bytes_to_seq_quad32_to_bytes (s:seq quad32) :
  Lemma (le_bytes_to_seq_quad32 (le_seq_quad32_to_bytes s) == s)

val be_bytes_to_seq_quad32_to_bytes (s:seq quad32) :
  Lemma (be_bytes_to_seq_quad32 (seq_nat32_to_seq_nat8_BE (seq_four_to_seq_BE s)) == s)

val le_seq_quad32_to_bytes_to_seq_quad32 (s:seq nat8{length s % 16 = 0}) :
  Lemma (le_seq_quad32_to_bytes (le_bytes_to_seq_quad32 s) == s)

val le_quad32_to_bytes_to_quad32 (s:seq nat8 { length s == 16 }) :
  Lemma(le_quad32_to_bytes (le_bytes_to_quad32 s) == s)

val be_quad32_to_bytes_to_quad32 (s:seq nat8 { length s == 16 }) :
  Lemma(be_quad32_to_bytes (be_bytes_to_quad32 s) == s)

val le_seq_quad32_to_bytes_of_singleton (q:quad32) :
  Lemma (le_quad32_to_bytes q == le_seq_quad32_to_bytes (create 1 q))

val be_seq_quad32_to_bytes_of_singleton (q:quad32) :
  Lemma (be_quad32_to_bytes q == seq_nat32_to_seq_nat8_BE (seq_four_to_seq_BE (create 1 q)))

val le_quad32_to_bytes_injective: unit ->
  Lemma (forall b b' . le_quad32_to_bytes b == le_quad32_to_bytes b' ==> b == b')

val be_quad32_to_bytes_injective: unit ->
  Lemma (forall b b' . be_quad32_to_bytes b == be_quad32_to_bytes b' ==> b == b')

val le_quad32_to_bytes_injective_specific (b b':quad32) :
  Lemma (le_quad32_to_bytes b == le_quad32_to_bytes b' ==> b == b')

val be_quad32_to_bytes_injective_specific (b b':quad32) :
  Lemma (be_quad32_to_bytes b == be_quad32_to_bytes b' ==> b == b')

val le_seq_quad32_to_bytes_injective (b b':Seq.seq quad32) : Lemma
  (requires Seq.equal (le_seq_quad32_to_bytes b) (le_seq_quad32_to_bytes b'))
  (ensures b == b')

val seq_to_four_LE_is_seq_to_seq_four_LE (#a:Type) (s:seq4 a) : Lemma
  (create 1 (seq_to_four_LE s) == seq_to_seq_four_LE s)

val seq_to_four_BE_is_seq_to_seq_four_BE (#a:Type) (s:seq4 a) : Lemma
  (create 1 (seq_to_four_BE s) == seq_to_seq_four_BE s)

val le_bytes_to_seq_quad_of_singleton (q:quad32) (b:seq nat8 { length b == 16 }) : Lemma
  (requires q == le_bytes_to_quad32 b)
  (ensures create 1 q == le_bytes_to_seq_quad32 b)

val be_bytes_to_seq_quad_of_singleton (q:quad32) (b:seq nat8 { length b == 16 }) : Lemma
  (requires q == be_bytes_to_quad32 b)
  (ensures create 1 q == be_bytes_to_seq_quad32 b)

val le_bytes_to_quad32_to_bytes (q:quad32) :
  Lemma(le_bytes_to_quad32 (le_quad32_to_bytes q) == q)

val be_bytes_to_quad32_to_bytes (q:quad32) :
  Lemma (be_bytes_to_quad32 (be_quad32_to_bytes q) == q)
  [SMTPat (be_bytes_to_quad32 (be_quad32_to_bytes q))]

// Reverse each nat32 in the quad, but leave the nat32s in their original order
let reverse_bytes_nat32_quad32 (q:quad32) : quad32 =
  Vale.Def.Words.Four_s.four_map reverse_bytes_nat32 q

val lemma_reverse_reverse_bytes_nat32_quad32 (s:quad32) :
  Lemma (reverse_bytes_nat32_quad32 (reverse_bytes_nat32_quad32 s) == s)
  [SMTPat (reverse_bytes_nat32_quad32 (reverse_bytes_nat32_quad32 s))]
val reverse_bytes_nat32_quad32_seq (q: seq quad32) : seq quad32
q: FStar.Seq.Base.seq Vale.Def.Types_s.quad32 -> FStar.Seq.Base.seq Vale.Def.Types_s.quad32