val bn_mod_slow:
    #t:limb_t
  -> #len:BN.bn_len t
  -> nBits:size_nat
  -> n:lbignum t len
  -> a:lbignum t (len + len) ->
  Pure (lbignum t len)
  (requires
    1 < bn_v n /\ bn_v n % 2 = 1 /\
    nBits / bits t < len /\ pow2 nBits < bn_v n)
  (ensures fun res ->
    bn_v res == bn_v a % bn_v n)
let bn_mod_slow #t #len nBits n a =
  let r2, mu = BM.bn_mont_precomp nBits n in
  bn_mod_slow_precomp_lemma n mu r2 a;
  bn_mod_slow_precomp n mu r2 a
val bn_mod_slow:
    #t:limb_t
  -> #len:BN.bn_len t
  -> nBits:size_nat
  -> n:lbignum t len
  -> a:lbignum t (len + len) ->
  Pure (lbignum t len)
  (requires
    1 < bn_v n /\ bn_v n % 2 = 1 /\
    nBits / bits t < len /\ pow2 nBits < bn_v n)
  (ensures fun res ->
    bn_v res == bn_v a % bn_v n)
let bn_mod_slow #t #len nBits n a =
let r2, mu = BM.bn_mont_precomp nBits n in
bn_mod_slow_precomp_lemma n mu r2 a;
bn_mod_slow_precomp n mu r2 a
Hacl.Spec.Bignum.ModReduction.fst.checked
prims.fst.checked Lib.Sequence.fsti.checked Lib.IntTypes.fsti.checked Hacl.Spec.Montgomery.Lemmas.fst.checked Hacl.Spec.Bignum.Montgomery.fsti.checked Hacl.Spec.Bignum.Definitions.fst.checked Hacl.Spec.Bignum.AlmostMontgomery.fsti.checked Hacl.Spec.Bignum.fsti.checked Hacl.Spec.AlmostMontgomery.Lemmas.fst.checked FStar.Pervasives.fsti.checked FStar.Mul.fst.checked FStar.Math.Lemmas.fst.checked FStar.Calc.fsti.checked
Hacl.Spec.Bignum.ModReduction.fst
Hacl.Spec.Bignum.Definitions.limb_t Hacl.Spec.Bignum.bn_len Lib.IntTypes.size_nat Hacl.Spec.Bignum.Definitions.lbignum Prims.op_Addition Hacl.Spec.Bignum.Definitions.limb Hacl.Spec.Bignum.ModReduction.bn_mod_slow_precomp Prims.unit Hacl.Spec.Bignum.ModReduction.bn_mod_slow_precomp_lemma FStar.Pervasives.Native.tuple2 Hacl.Spec.Bignum.Montgomery.bn_mont_precomp
module Hacl.Spec.Bignum.ModReduction

open FStar.Mul

open Lib.IntTypes
open Lib.Sequence

open Hacl.Spec.Bignum.Definitions

module M = Hacl.Spec.Montgomery.Lemmas
module AM = Hacl.Spec.AlmostMontgomery.Lemmas

module BM = Hacl.Spec.Bignum.Montgomery
module BAM = Hacl.Spec.Bignum.AlmostMontgomery
module BN = Hacl.Spec.Bignum

#reset-options "--z3rlimit 50 --fuel 0 --ifuel 0"

///  Modular reduction based on Montgomery arithmetic

val bn_mod_slow_precomp:
    #t:limb_t
  -> #len:BN.bn_len t
  -> n:lbignum t len
  -> mu:limb t
  -> r2:lbignum t len
  -> a:lbignum t (len + len) ->
  lbignum t len

let bn_mod_slow_precomp #t #len n mu r2 a =
  let a_mod = BAM.bn_almost_mont_reduction n mu a in
  let res = BM.bn_to_mont n mu r2 a_mod in
  res


val bn_mod_slow_precomp_lemma:
    #t:limb_t
  -> #len:BN.bn_len t
  -> n:lbignum t len
  -> mu:limb t
  -> r2:lbignum t len
  -> a:lbignum t (len + len) -> Lemma
  (requires
    BM.bn_mont_pre n mu /\
    bn_v r2 == pow2 (2 * bits t * len) % bn_v n)
  (ensures bn_v (bn_mod_slow_precomp n mu r2 a) == bn_v a % bn_v n)

let bn_mod_slow_precomp_lemma #t #len n mu r2 a =
  let r = pow2 (bits t * len) in
  let d, _ = M.eea_pow2_odd (bits t * len) (bn_v n) in
  M.mont_preconditions_d (bits t) len (bn_v n);
  assert (M.mont_pre (bits t) len (bn_v n) (v mu));

  let a_mod = BAM.bn_almost_mont_reduction n mu a in
  let res = BM.bn_to_mont n mu r2 a_mod in
  BAM.bn_almost_mont_reduction_lemma n mu a;
  BM.bn_to_mont_lemma n mu r2 a_mod;

  bn_eval_bound a (len + len);
  assert (bn_v a < pow2 (bits t * (len + len)));
  Math.Lemmas.pow2_plus (bits t * len) (bits t * len);
  assert (bn_v a < r * r);

  AM.almost_mont_reduction_lemma (bits t) len (bn_v n) (v mu) (bn_v a);
  assert (bn_v a_mod % bn_v n == bn_v a * d % bn_v n);

  calc (==) {
    bn_v res;
    (==) { M.to_mont_lemma (bits t) len (bn_v n) (v mu) (bn_v a_mod) }
    bn_v a_mod * r % bn_v n;
    (==) { Math.Lemmas.lemma_mod_mul_distr_l (bn_v a_mod) r (bn_v n) }
    bn_v a_mod % bn_v n * r % bn_v n;
    (==) {  }
    (bn_v a * d % bn_v n) * r % bn_v n;
    (==) { M.lemma_mont_id1 (bn_v n) r d (bn_v a) }
    bn_v a % bn_v n;
    };

  assert (bn_v res == bn_v a % bn_v n)


val bn_mod_slow:
    #t:limb_t
  -> #len:BN.bn_len t
  -> nBits:size_nat
  -> n:lbignum t len
  -> a:lbignum t (len + len) ->
  Pure (lbignum t len)
  (requires
    1 < bn_v n /\ bn_v n % 2 = 1 /\
    nBits / bits t < len /\ pow2 nBits < bn_v n)
  (ensures fun res ->
    bn_v res == bn_v a % bn_v n)
val bn_mod_slow:
    #t:limb_t
  -> #len:BN.bn_len t
  -> nBits:size_nat
  -> n:lbignum t len
  -> a:lbignum t (len + len) ->
  Pure (lbignum t len)
  (requires
    1 < bn_v n /\ bn_v n % 2 = 1 /\
    nBits / bits t < len /\ pow2 nBits < bn_v n)
  (ensures fun res ->
    bn_v res == bn_v a % bn_v n)

    nBits: Lib.IntTypes.size_nat ->
    n: Hacl.Spec.Bignum.Definitions.lbignum t len ->
    a: Hacl.Spec.Bignum.Definitions.lbignum t (len + len)
  -> Prims.Pure (Hacl.Spec.Bignum.Definitions.lbignum t len)