val cswap2: cswap2_t M64 p
let cswap2 bit p1 p2 =
  let h0 = ST.get() in
  if EverCrypt.TargetConfig.hacl_can_compile_inline_asm then
    Vale.Inline.X64.Fswap_inline.cswap2 bit p1 p2
  else
    Vale.Wrapper.X64.Fswap.cswap2_e bit p1 p2;
  let h1 = ST.get() in
  // Seq.equal is swapped in the interop wrappers, so the SMTPat is not matching:
  // We have Seq.equal s1 s2 but are trying to prove s2 == s1
  let aux1 () : Lemma
    (requires v bit == 1)
    (ensures as_seq h1 p1 == as_seq h0 p2 /\ as_seq h1 p2 == as_seq h0 p1)
    = Seq.lemma_eq_elim (B.as_seq h0 p2) (B.as_seq h1 p1);
      Seq.lemma_eq_elim (B.as_seq h0 p1) (B.as_seq h1 p2)
  in let aux2 () : Lemma
    (requires v bit == 0)
    (ensures as_seq h1 p1 == as_seq h0 p1 /\ as_seq h1 p2 == as_seq h0 p2)
    = Seq.lemma_eq_elim (B.as_seq h0 p1) (B.as_seq h1 p1);
      Seq.lemma_eq_elim (B.as_seq h0 p2) (B.as_seq h1 p2)
  in
  Classical.move_requires aux1 ();
  Classical.move_requires aux2 ()
val cswap2: cswap2_t M64 p
let cswap2 bit p1 p2 =
let h0 = ST.get () in
if EverCrypt.TargetConfig.hacl_can_compile_inline_asm
then Vale.Inline.X64.Fswap_inline.cswap2 bit p1 p2
else Vale.Wrapper.X64.Fswap.cswap2_e bit p1 p2;
let h1 = ST.get () in
let aux1 ()
    : Lemma (requires v bit == 1)
      (ensures as_seq h1 p1 == as_seq h0 p2 /\ as_seq h1 p2 == as_seq h0 p1) =
  Seq.lemma_eq_elim (B.as_seq h0 p2) (B.as_seq h1 p1);
  Seq.lemma_eq_elim (B.as_seq h0 p1) (B.as_seq h1 p2)
in
let aux2 ()
    : Lemma (requires v bit == 0)
      (ensures as_seq h1 p1 == as_seq h0 p1 /\ as_seq h1 p2 == as_seq h0 p2) =
  Seq.lemma_eq_elim (B.as_seq h0 p1) (B.as_seq h1 p1);
  Seq.lemma_eq_elim (B.as_seq h0 p2) (B.as_seq h1 p2)
in
Classical.move_requires aux1 ();
Classical.move_requires aux2 ()
Hacl.Impl.Curve25519.Field64.Vale.fst.checked
Vale.Wrapper.X64.Fswap.fsti.checked Vale.Wrapper.X64.Fsub.fsti.checked Vale.Wrapper.X64.Fsqr.fsti.checked Vale.Wrapper.X64.Fmul.fsti.checked Vale.Wrapper.X64.Fadd.fsti.checked Vale.Inline.X64.Fswap_inline.fsti.checked Vale.Inline.X64.Fsqr_inline.fsti.checked Vale.Inline.X64.Fmul_inline.fsti.checked Vale.Inline.X64.Fadd_inline.fsti.checked Vale.Curve25519.Fast_defs.fst.checked Spec.Curve25519.fst.checked prims.fst.checked Lib.Sequence.fsti.checked Lib.IntTypes.fst.checked Lib.Buffer.fst.checked Hacl.Spec.Curve25519.Field64.Definition.fst.checked Hacl.Impl.Curve25519.Field64.fst.checked FStar.UInt32.fsti.checked FStar.Seq.fst.checked FStar.Pervasives.fsti.checked FStar.Mul.fst.checked FStar.Math.Lemmas.fst.checked FStar.HyperStack.ST.fsti.checked FStar.HyperStack.All.fst.checked FStar.HyperStack.fst.checked FStar.Classical.fsti.checked FStar.Calc.fsti.checked EverCrypt.TargetConfig.fsti.checked
Hacl.Impl.Curve25519.Field64.Vale.fst
Lib.IntTypes.uint64 Prims.b2t Prims.op_LessThanOrEqual Lib.IntTypes.v Lib.IntTypes.U64 Lib.IntTypes.SEC Hacl.Impl.Curve25519.Fields.Core.felem2 Hacl.Impl.Curve25519.Fields.Core.M64 FStar.Classical.move_requires Prims.unit Prims.eq2 Prims.int Prims.l_and Lib.Sequence.lseq Hacl.Impl.Curve25519.Fields.Core.limb Lib.IntTypes.U32 Lib.IntTypes.PUB Lib.IntTypes.op_Plus_Dot Hacl.Impl.Curve25519.Fields.Core.nlimb Lib.Buffer.as_seq Lib.Buffer.MUT Prims.squash Lib.IntTypes.int_t Lib.IntTypes.add_mod FStar.UInt32.uint_to_t FStar.UInt32.t Prims.Nil FStar.Pervasives.pattern FStar.Seq.Base.lemma_eq_elim FStar.Monotonic.HyperStack.mem FStar.HyperStack.ST.get EverCrypt.TargetConfig.hacl_can_compile_inline_asm Vale.Inline.X64.Fswap_inline.cswap2 Prims.bool Vale.Wrapper.X64.Fswap.cswap2_e
module Hacl.Impl.Curve25519.Field64.Vale

module HS = FStar.HyperStack
module ST = FStar.HyperStack.ST
open FStar.Calc

friend Lib.Buffer
friend Lib.IntTypes

open FStar.HyperStack
open FStar.HyperStack.All
open FStar.Mul

open Lib.Sequence
open Lib.IntTypes
open Lib.Buffer

module B = Lib.Buffer
module S = Hacl.Spec.Curve25519.Field64.Definition
module P = Spec.Curve25519

module FA = Vale.Wrapper.X64.Fadd

module F64 = Hacl.Impl.Curve25519.Field64

/// We are trying to connect HACL* abstractions with regular F* libraries, so in
/// addition to ``friend``'ing ``Lib.*``, we also write a couple lemmas that we
/// prove via normalization to facilitate the job of proving that calling the
/// Vale interop signatures faithfully implements the required HACL* signature.

#set-options "--max_fuel 0 --max_ifuel 0 --z3rlimit 300 --z3refresh"

let buffer_is_buffer a len: Lemma
  (ensures (lbuffer a len == b:B.buffer a{B.length b == UInt32.v len}))
  [ SMTPat (lbuffer a len) ]
=
  assert_norm (lbuffer a len == b:B.buffer a{B.length b == UInt32.v len})

let as_nat_is_as_nat (b:lbuffer uint64 4ul) (h:HS.mem): Lemma
  (ensures (FA.as_nat b h == F64.as_nat h b))
  [ SMTPat (as_nat h b) ]
=
  ()

let _: squash (Vale.Curve25519.Fast_defs.prime = Spec.Curve25519.prime) =
  assert_norm (Vale.Curve25519.Fast_defs.prime = Spec.Curve25519.prime)

// This one only goes through in a reasonable amount of rlimit thanks to
// ``as_nat_is_as_nat`` and ``buffer_is_buffer`` above.
[@ CInline]
let add_scalar out f1 f2 =
  if EverCrypt.TargetConfig.hacl_can_compile_inline_asm then
    Vale.Inline.X64.Fadd_inline.add_scalar out f1 f2
  else
    Vale.Wrapper.X64.Fadd.add_scalar_e out f1 f2

// Spec discrepancy. Need to call the right lemma from FStar.Math.Lemmas.
#push-options "--max_fuel 0 --max_ifuel 0 --z3rlimit 400"
[@ CInline]
let fadd out f1 f2 =
  let h0 = ST.get () in
  let aux () : Lemma (P.fadd (F64.fevalh h0 f1) (F64.fevalh h0 f2) == (FA.as_nat f1 h0 + FA.as_nat f2 h0) % Vale.Curve25519.Fast_defs.prime) =
    let a = P.fadd (F64.fevalh h0 f1) (F64.fevalh h0 f2) in
    let a1 = (as_nat h0 f1 % Vale.Curve25519.Fast_defs.prime + as_nat h0 f2 % Vale.Curve25519.Fast_defs.prime) % Vale.Curve25519.Fast_defs.prime in
    let a2 = (as_nat h0 f1 % Vale.Curve25519.Fast_defs.prime + as_nat h0 f2) % Vale.Curve25519.Fast_defs.prime in
    let a3 = (as_nat h0 f1 + as_nat h0 f2) % Vale.Curve25519.Fast_defs.prime in
    let b = (FA.as_nat f1 h0 + FA.as_nat f2 h0) % Vale.Curve25519.Fast_defs.prime in
    calc (==) {
      a;
      == {  FStar.Math.Lemmas.modulo_distributivity
    (as_nat h0 f1) (as_nat h0 f2) Vale.Curve25519.Fast_defs.prime }
      a1;
      == {  FStar.Math.Lemmas.lemma_mod_add_distr (as_nat h0 f1 % Vale.Curve25519.Fast_defs.prime)
      (as_nat h0 f2) Vale.Curve25519.Fast_defs.prime }
      a2;
      == { FStar.Math.Lemmas.lemma_mod_add_distr (as_nat h0 f2) (as_nat h0 f1) Vale.Curve25519.Fast_defs.prime }
      a3;
      == { }
      b;
    }
  in aux();
  if EverCrypt.TargetConfig.hacl_can_compile_inline_asm then
    Vale.Inline.X64.Fadd_inline.fadd out f1 f2
  else
    Vale.Wrapper.X64.Fadd.fadd_e out f1 f2
#pop-options

[@ CInline]
let fsub out f1 f2 =
  let h0 = ST.get() in
  let aux () : Lemma (P.fsub (F64.fevalh h0 f1) (F64.fevalh h0 f2) == (FA.as_nat f1 h0 - FA.as_nat f2 h0) % Vale.Curve25519.Fast_defs.prime) =
    let a = P.fsub (F64.fevalh h0 f1) (F64.fevalh h0 f2) in
    let a1 = (as_nat h0 f1 % Vale.Curve25519.Fast_defs.prime - as_nat h0 f2 % Vale.Curve25519.Fast_defs.prime) % Vale.Curve25519.Fast_defs.prime in
    let a2 = (as_nat h0 f1 % Vale.Curve25519.Fast_defs.prime - as_nat h0 f2) % Vale.Curve25519.Fast_defs.prime in
    let a3 = (as_nat h0 f1 - as_nat h0 f2) % Vale.Curve25519.Fast_defs.prime in
    let b = (FA.as_nat f1 h0 - FA.as_nat f2 h0) % Vale.Curve25519.Fast_defs.prime in
    calc (==) {
      a;
      == { }
      a1;
      == { FStar.Math.Lemmas.lemma_mod_sub_distr (as_nat h0 f1 % Vale.Curve25519.Fast_defs.prime) (as_nat h0 f2)
        Vale.Curve25519.Fast_defs.prime }
      a2;
      == { FStar.Math.Lemmas.lemma_mod_add_distr (- as_nat h0 f2) (as_nat h0 f1) Vale.Curve25519.Fast_defs.prime }
      a3;
      == { }
      b;
    }
  in aux();
  if EverCrypt.TargetConfig.hacl_can_compile_inline_asm then
    Vale.Inline.X64.Fadd_inline.fsub out f1 f2
  else
    Vale.Wrapper.X64.Fsub.fsub_e out f1 f2

#push-options "--z3rlimit 200"
let lemma_fmul_equiv (h0:HS.mem) (f1 f2:F64.u256) : Lemma
  (P.fmul (F64.fevalh h0 f1) (F64.fevalh h0 f2) == (FA.as_nat f1 h0 * FA.as_nat f2 h0) % Vale.Curve25519.Fast_defs.prime)
  = let a = P.fmul (F64.fevalh h0 f1) (F64.fevalh h0 f2) in
    let a1 = ((F64.as_nat h0 f1 % Vale.Curve25519.Fast_defs.prime) * (F64.as_nat h0 f2 % Vale.Curve25519.Fast_defs.prime)) % Vale.Curve25519.Fast_defs.prime in
    let a2 = ((F64.as_nat h0 f1 % Vale.Curve25519.Fast_defs.prime) * F64.as_nat h0 f2) % Vale.Curve25519.Fast_defs.prime in
    let a3 = (F64.as_nat h0 f1 * F64.as_nat h0 f2) % Vale.Curve25519.Fast_defs.prime in
    let b = (FA.as_nat f1 h0 * FA.as_nat f2 h0) % Vale.Curve25519.Fast_defs.prime in
    calc (==) {
      a;
      == { }
      a1;
      == { FStar.Math.Lemmas.lemma_mod_mul_distr_r (F64.as_nat h0 f1 % Vale.Curve25519.Fast_defs.prime) (F64.as_nat h0 f2) Vale.Curve25519.Fast_defs.prime }
      a2;
      == { FStar.Math.Lemmas.lemma_mod_mul_distr_l (F64.as_nat h0 f1) (F64.as_nat h0 f2) Vale.Curve25519.Fast_defs.prime }
      a3;
      == { }
      b;
    }
#pop-options

[@ CInline]
let fmul out f1 f2 tmp =
  let h0 = ST.get() in
  lemma_fmul_equiv h0 f1 f2;
  if EverCrypt.TargetConfig.hacl_can_compile_inline_asm then (
    assert (disjoint f1 (gsub tmp 0ul 8ul));
    assert (disjoint f2 (gsub tmp 0ul 8ul));
    Vale.Inline.X64.Fmul_inline.fmul out f1 f2 (sub tmp 0ul 8ul)
  ) else
    Vale.Wrapper.X64.Fmul.fmul_e (sub tmp 0ul 8ul) f1 out f2

#push-options "--z3rlimit 500"
[@ CInline]
let fmul2 out f1 f2 tmp =
  let h0 = ST.get() in
  lemma_fmul_equiv h0 (gsub f1 0ul 4ul) (gsub f2 0ul 4ul);
  lemma_fmul_equiv h0 (gsub f1 4ul 4ul) (gsub f2 4ul 4ul);
  if EverCrypt.TargetConfig.hacl_can_compile_inline_asm then
    Vale.Inline.X64.Fmul_inline.fmul2 out f1 f2 tmp
  else
    Vale.Wrapper.X64.Fmul.fmul2_e tmp f1 out f2
#pop-options

[@ CInline]
let fmul_scalar out f1 f2 =
  let h0 = ST.get() in
  let aux () : Lemma (P.fmul (F64.fevalh h0 f1) (v f2) == (FA.as_nat f1 h0 * v f2) % Vale.Curve25519.Fast_defs.prime) =
    let a = P.fmul (F64.fevalh h0 f1) (v f2) in
    let a1 =  ((F64.as_nat h0 f1 % Vale.Curve25519.Fast_defs.prime) * v f2) % Vale.Curve25519.Fast_defs.prime in
    let a2 = (F64.as_nat h0 f1 * v f2) % Vale.Curve25519.Fast_defs.prime in
    let b = (FA.as_nat f1 h0 * v f2) % Vale.Curve25519.Fast_defs.prime in
    calc (==) {
      a;
      == { }
      a1;
      == { FStar.Math.Lemmas.lemma_mod_mul_distr_l (F64.as_nat h0 f1) (v f2) Vale.Curve25519.Fast_defs.prime }
      a2;
      == { }
      b;
    }
  in aux();
  assert_norm (pow2 17 = 131072);
  if EverCrypt.TargetConfig.hacl_can_compile_inline_asm then
    Vale.Inline.X64.Fmul_inline.fmul_scalar out f1 f2
  else
    Vale.Wrapper.X64.Fmul.fmul_scalar_e out f1 f2

[@ CInline]
let fsqr out f1 tmp =
  let h0 = ST.get() in
  lemma_fmul_equiv h0 f1 f1;
  if EverCrypt.TargetConfig.hacl_can_compile_inline_asm then
    Vale.Inline.X64.Fsqr_inline.fsqr out f1 tmp
  else
    Vale.Wrapper.X64.Fsqr.fsqr_e tmp f1 out

[@ CInline]
let fsqr2 out f tmp =
  let h0 = ST.get() in
  lemma_fmul_equiv h0 (gsub f 0ul 4ul) (gsub f 0ul 4ul);
  lemma_fmul_equiv h0 (gsub f 4ul 4ul) (gsub f 4ul 4ul);
  if EverCrypt.TargetConfig.hacl_can_compile_inline_asm then
    Vale.Inline.X64.Fsqr_inline.fsqr2 out f tmp
  else
    Vale.Wrapper.X64.Fsqr.fsqr2_e tmp f out
val cswap2: cswap2_t M64 p
Hacl.Impl.Curve25519.Fields.Core.cswap2_t Hacl.Impl.Curve25519.Fields.Core.M64
  Hacl.Impl.Curve25519.Field64.Vale.p