Hacl.Poly1305.Field32xN.Lemmas1.fst
module Hacl.Poly1305.Field32xN.Lemmas1
open Lib.IntTypes
open Lib.IntVector
open Lib.Sequence
open FStar.Mul
open FStar.Calc
open Hacl.Spec.Poly1305.Vec
include Hacl.Spec.Poly1305.Field32xN
#set-options "--z3rlimit 50 --using_facts_from '* -FStar.Seq' --max_fuel 0 --max_ifuel 0"
val lemma_prime: unit -> Lemma (pow2 130 % prime = 5)
let lemma_prime () =
assert_norm (pow2 130 % prime = 5 % prime);
assert_norm (5 < prime);
FStar.Math.Lemmas.modulo_lemma 5 prime
noextract
val carry26_wide_zero: #w:lanes -> l:uint64xN w -> uint64xN w & uint64xN w
let carry26_wide_zero #w l = (vec_and l (mask26 w), vec_shift_right l 26ul)
val carry26_wide_zero_eq: #w:lanes -> f:uint64xN w -> Lemma
(carry26_wide_zero f == carry26_wide f (zero w))
let carry26_wide_zero_eq #w f =
let l1 = vec_add_mod f (zero w) in
assert (vec_v l1 == map2 ( +. ) (vec_v f) (vec_v (zero w)));
assert (forall (i:nat{i < w}). uint_v (vec_v l1).[i] == uint_v (vec_v f).[i]);
assert (forall (i:nat{i < w}). (vec_v l1).[i] == (vec_v f).[i]);
eq_intro (vec_v l1) (vec_v f);
assert (vec_v l1 == vec_v f);
vecv_extensionality l1 f
val vec_smul_mod_five_i: #w:lanes -> f:uint64xN w{uint64xN_fits f (4096 * max26)} -> i:nat{i < w} -> Lemma
(u64 5 *. (vec_v f).[i] == (vec_v f).[i] +. ((vec_v f).[i] <<. 2ul))
let vec_smul_mod_five_i #w f i =
let f = (vec_v f).[i] in
assert (v (f <<. 2ul) == (v f * pow2 2) % pow2 64);
Math.Lemmas.small_mod (v f * pow2 2) (pow2 64);
Math.Lemmas.small_mod (v f + v f * pow2 2) (pow2 64);
Math.Lemmas.small_mod (5 * v f) (pow2 64);
assert (5 * v f == v f + v f * 4);
v_injective (u64 5 *. f);
v_injective (f +. (f <<. 2ul))
val vec_smul_mod_five: #w:lanes -> f:uint64xN w{uint64xN_fits f (4096 * max26)} -> Lemma
(vec_smul_mod f (u64 5) == vec_add_mod f (vec_shift_left f 2ul))
let vec_smul_mod_five #w f =
let r1 = vec_smul_mod f (u64 5) in
let r2 = vec_add_mod f (vec_shift_left f 2ul) in
Classical.forall_intro (vec_smul_mod_five_i #w f);
eq_intro (vec_v r1) (vec_v r2);
vecv_extensionality r1 r2
noextract
val carry_wide_felem5_compact: #w:lanes -> inp:felem_wide5 w -> felem5 w
let carry_wide_felem5_compact #w (x0, x1, x2, x3, x4) =
// m_i <= 4096, x_i <= m_i * max26 * max26
// felem_wide_fits5 (x0, x1, x2, x3, x4) (m0, m1, m2, m3, m4)
let t0, c0 = carry26_wide_zero x0 in
// t0 <= max26 /\ c0 <= (m0 + 1) * max26
let t1, c1 = carry26_wide x1 c0 in
// t1 <= max26 /\ c1 <= (m1 + 1) * max26
let t2, c2 = carry26_wide x2 c1 in
// t2 <= max26 /\ c2 <= (m2 + 1) * max26
let t3, c3 = carry26_wide_zero x3 in
// t3 <= max26 /\ c3 <= (m3 + 1) * max26
let t3', c6 = carry26 t3 c2 in
// t3' <= max26 /\ c6 <= m2 + 2
let t4, c4 = carry26_wide x4 c3 in
// t4 <= max26 /\ c4 <= (m4 + 1) * max26
let t4' = vec_add_mod t4 c6 in
// t4' <= 2 * max26
let t0', c5 = carry26 t0 (vec_smul_mod c4 (u64 5)) in
// t0' <= max26 /\ c5 <= 5 * (m4 + 1) + 1
let t1' = vec_add_mod t1 c5 in
// t1' <= 2 * max26
(t0', t1', t2, t3', t4')
// felem_fits5 (t0', t1', t2, t3', t4') (1, 2, 1, 1, 2)
val carry26_wide_lemma_i:
#w:lanes
-> #m:scale64
-> l:uint64xN w{felem_wide_fits1 l m}
-> cin:uint64xN w{uint64xN_fits cin (4096 * max26)}
-> i:nat{i < w} ->
Lemma
(let (l0, l1) = carry26 #w l cin in
(uint64xN_v l0).[i] <= max26 /\ (uint64xN_v l1).[i] <= (m + 1) * max26 /\
(uint64xN_v l).[i] + (uint64xN_v cin).[i] == (uint64xN_v l1).[i] * pow2 26 + (uint64xN_v l0).[i])
let carry26_wide_lemma_i #w #m l cin i =
let l = (vec_v l).[i] in
let cin = (vec_v cin).[i] in
let mask26 = u64 0x3ffffff in
assert_norm (0x3ffffff = pow2 26 - 1);
FStar.Math.Lemmas.modulo_lemma (v l + v cin) (pow2 64);
let l' = l +! cin in
let l0 = l' &. mask26 in
let l1 = l' >>. 26ul in
mod_mask_lemma l' 26ul;
assert (v (mod_mask #U64 #SEC 26ul) == v mask26);
FStar.Math.Lemmas.pow2_modulo_modulo_lemma_1 (v l') 26 32;
FStar.Math.Lemmas.euclidean_division_definition (v l') (pow2 26)
val carry26_wide_fits_lemma:
#w:lanes
-> #m:scale64
-> l:uint64xN w{felem_wide_fits1 l m}
-> cin:uint64xN w{uint64xN_fits cin (4096 * max26)} ->
Lemma
(let (l0, l1) = carry26 #w l cin in
felem_fits1 l0 1 /\ uint64xN_fits l1 ((m + 1) * max26))
#push-options "--z3rlimit 100"
let carry26_wide_fits_lemma #w #m l cin =
match w with
| 1 ->
carry26_wide_lemma_i #w #m l cin 0
| 2 ->
carry26_wide_lemma_i #w #m l cin 0;
carry26_wide_lemma_i #w #m l cin 1
| 4 ->
carry26_wide_lemma_i #w #m l cin 0;
carry26_wide_lemma_i #w #m l cin 1;
carry26_wide_lemma_i #w #m l cin 2;
carry26_wide_lemma_i #w #m l cin 3
#pop-options
val carry26_wide_eval_lemma:
#w:lanes
-> #m:scale64
-> l:uint64xN w{felem_wide_fits1 l m}
-> cin:uint64xN w{uint64xN_fits cin (4096 * max26)} ->
Lemma
(let (l0, l1) = carry26 #w l cin in
//felem_fits1 l0 1 /\
uint64xN_fits l1 ((m + 1) * max26) /\
(forall (i:nat). i < w ==> (uint64xN_v l).[i] + (uint64xN_v cin).[i] ==
(uint64xN_v l1).[i] * pow2 26 + (uint64xN_v l0).[i]))
let carry26_wide_eval_lemma #w #m l cin =
carry26_wide_fits_lemma #w #m l cin;
match w with
| 1 ->
carry26_wide_lemma_i #w #m l cin 0
| 2 ->
carry26_wide_lemma_i #w #m l cin 0;
carry26_wide_lemma_i #w #m l cin 1
| 4 ->
carry26_wide_lemma_i #w #m l cin 0;
carry26_wide_lemma_i #w #m l cin 1;
carry26_wide_lemma_i #w #m l cin 2;
carry26_wide_lemma_i #w #m l cin 3
val carry26_lemma_i:
#w:lanes
-> m:scale64
-> ml:scale32
-> l:uint64xN w{felem_fits1 l ml}
-> cin:uint64xN w{uint64xN_fits cin (m * max26)}
-> i:nat{i < w} ->
Lemma
(let (l0, l1) = carry26 #w l cin in
(uint64xN_v l0).[i] <= max26 /\ (uint64xN_v l1).[i] < m + ml /\
(uint64xN_v l).[i] + (uint64xN_v cin).[i] == (uint64xN_v l1).[i] * pow2 26 + (uint64xN_v l0).[i])
let carry26_lemma_i #w m ml l cin i =
let l = (vec_v l).[i] in
let cin = (vec_v cin).[i] in
let mask26 = u64 0x3ffffff in
assert_norm (0x3ffffff = pow2 26 - 1);
FStar.Math.Lemmas.modulo_lemma (v l + v cin) (pow2 64);
let l' = l +! cin in
let l0 = l' &. mask26 in
let l1 = l' >>. 26ul in
mod_mask_lemma l' 26ul;
assert (v (mod_mask #U64 #SEC 26ul) == v mask26);
FStar.Math.Lemmas.pow2_modulo_modulo_lemma_1 (v l') 26 32;
FStar.Math.Lemmas.pow2_minus 32 26
val carry26_fits_lemma:
#w:lanes
-> m:scale64
-> ml:scale32
-> l:uint64xN w{felem_fits1 l ml}
-> cin:uint64xN w{uint64xN_fits cin (m * max26)} ->
Lemma
(let (l0, l1) = carry26 #w l cin in
felem_fits1 l0 1 /\ uint64xN_fits l1 (m + ml))
let carry26_fits_lemma #w m ml l cin =
match w with
| 1 ->
carry26_lemma_i #w m ml l cin 0
| 2 ->
carry26_lemma_i #w m ml l cin 0;
carry26_lemma_i #w m ml l cin 1
| 4 ->
carry26_lemma_i #w m ml l cin 0;
carry26_lemma_i #w m ml l cin 1;
carry26_lemma_i #w m ml l cin 2;
carry26_lemma_i #w m ml l cin 3
val carry26_eval_lemma:
#w:lanes
-> m:scale64
-> ml:scale32
-> l:uint64xN w{felem_fits1 l ml}
-> cin:uint64xN w{uint64xN_fits cin (m * max26)} ->
Lemma
(let (l0, l1) = carry26 #w l cin in
felem_fits1 l0 1 /\ uint64xN_fits l1 (m + ml) /\
(forall (i:nat). i < w ==> (uint64xN_v l).[i] + (uint64xN_v cin).[i] ==
(uint64xN_v l1).[i] * pow2 26 + (uint64xN_v l0).[i]))
let carry26_eval_lemma #w m ml l cin =
match w with
| 1 ->
carry26_lemma_i #w m ml l cin 0
| 2 ->
carry26_lemma_i #w m ml l cin 0;
carry26_lemma_i #w m ml l cin 1
| 4 ->
carry26_lemma_i #w m ml l cin 0;
carry26_lemma_i #w m ml l cin 1;
carry26_lemma_i #w m ml l cin 2;
carry26_lemma_i #w m ml l cin 3
val carry_wide_felem5_fits_lemma0:
#w:lanes
-> inp:felem_wide5 w{felem_wide_fits5 inp (126, 102, 78, 54, 30)} ->
Lemma (let (x0, x1, x2, x3, x4) = inp in
let t0, c0 = carry26_wide_zero x0 in
let t1, c1 = carry26_wide x1 c0 in
let t2, c2 = carry26_wide x2 c1 in
let t3, c3 = carry26_wide_zero x3 in
let t3', c6 = carry26 t3 c2 in
let t4, c4 = carry26_wide x4 c3 in
let t4' = vec_add_mod t4 c6 in
let tmp = (t0, t1, t2, t3', t4') in
felem_fits5 tmp (1, 1, 1, 1, 2) /\ felem_fits1 c4 31)
let carry_wide_felem5_fits_lemma0 #w inp =
let (x0, x1, x2, x3, x4) = inp in
let t0, c0 = carry26_wide_zero x0 in
carry26_wide_zero_eq x0;
carry26_wide_fits_lemma #w #126 x0 (zero w);
let t1, c1 = carry26_wide x1 c0 in
carry26_wide_fits_lemma #w #102 x1 c0;
let t2, c2 = carry26_wide x2 c1 in
carry26_wide_fits_lemma #w #78 x2 c1;
let t3, c3 = carry26_wide_zero x3 in
carry26_wide_zero_eq x3;
carry26_wide_fits_lemma #w #54 x3 (zero w);
let t3', c6 = carry26 t3 c2 in
carry26_fits_lemma 79 1 t3 c2;
let t4, c4 = carry26_wide x4 c3 in
carry26_wide_fits_lemma #w #30 x4 c3
val carry_wide_felem5_fits_lemma:
#w:lanes
-> inp:felem_wide5 w ->
Lemma
(requires felem_wide_fits5 inp (126, 102, 78, 54, 30))
(ensures felem_fits5 (carry_wide_felem5 inp) (1, 2, 1, 1, 2))
#push-options "--z3rlimit 200"
let carry_wide_felem5_fits_lemma #w inp =
let (x0, x1, x2, x3, x4) = inp in
let t0, c0 = carry26_wide_zero x0 in
let t1, c1 = carry26_wide x1 c0 in
let t2, c2 = carry26_wide x2 c1 in
let t3, c3 = carry26_wide_zero x3 in
let t3', c6 = carry26 t3 c2 in
let t4, c4 = carry26_wide x4 c3 in
let t4' = vec_add_mod t4 c6 in
carry_wide_felem5_fits_lemma0 #w inp;
vec_smul_mod_five c4;
let t0', c5 = carry26 t0 (vec_smul_mod c4 (u64 5)) in
carry26_fits_lemma 155 1 t0 (vec_smul_mod c4 (u64 5))
#pop-options
val carry_wide_felem5_eval_lemma_i0:
inp:tup64_5
-> tmp:tup64_5
-> vc0:nat -> vc1:nat -> vc2:nat -> vc3:nat -> vc4:nat -> vc6:nat ->
Lemma
(requires
(let (t0, t1, t2, t3, t4) = tmp in
let (xi0, xi1, xi2, xi3, xi4) = inp in
v xi0 == vc0 * pow2 26 + v t0 /\
v xi1 + vc0 == vc1 * pow2 26 + v t1 /\
v xi2 + vc1 == vc2 * pow2 26 + v t2 /\
v xi3 + vc2 == vc3 * pow2 26 + vc6 * pow2 26 + v t3 /\
v xi4 + vc3 == vc4 * pow2 26 + v t4 - vc6))
(ensures
(let (t0, t1, t2, t3, t4) = tmp in
let (ti0, ti1, ti2, ti3, ti4) = inp in
as_nat5 inp % prime ==
(v t0 + vc4 * 5 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104) % prime))
let carry_wide_felem5_eval_lemma_i0 inp tmp vc0 vc1 vc2 vc3 vc4 vc6 =
let (t0, t1, t2, t3, t4) = tmp in
let (xi0, xi1, xi2, xi3, xi4) = inp in
let tmp_n = v t0 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104 in
calc (==) {
as_nat5 inp % prime;
(==) { }
(v xi0 + v xi1 * pow26 + v xi2 * pow52 + v xi3 * pow78 + v xi4 * pow104) % prime;
(==) { }
(vc0 * pow2 26 + v t0 +
(vc1 * pow2 26 + v t1 - vc0) * pow26 +
(vc2 * pow2 26 + v t2 - vc1) * pow52 +
(vc3 * pow2 26 + vc6 * pow2 26 + v t3 - vc2) * pow78 +
(vc4 * pow2 26 + v t4 - vc6 - vc3) * pow104) % prime;
(==) {
assert_norm (pow2 26 * pow26 = pow52);
assert_norm (pow2 26 * pow52 = pow78);
assert_norm (pow2 26 * pow78 = pow104);
assert_norm (pow2 26 * pow104 = pow2 130)}
(v t0 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104 + vc4 * pow2 130) % prime;
(==) { FStar.Math.Lemmas.lemma_mod_plus_distr_r tmp_n (vc4 * pow2 130) prime }
(tmp_n + (vc4 * pow2 130 % prime)) % prime;
(==) { FStar.Math.Lemmas.lemma_mod_mul_distr_r (vc4) (pow2 130) prime }
(tmp_n + (vc4 * (pow2 130 % prime) % prime)) % prime;
(==) { lemma_prime () }
(tmp_n + (vc4 * 5 % prime)) % prime;
(==) { FStar.Math.Lemmas.lemma_mod_plus_distr_r tmp_n (vc4 * 5) prime }
(tmp_n + vc4 * 5) % prime;
};
assert (as_nat5 inp % prime == (tmp_n + vc4 * 5) % prime)
val carry_wide_felem5_eval_lemma_i1:
#w:lanes
-> inp:felem_wide5 w{felem_wide_fits5 inp (126, 102, 78, 54, 30)}
-> i:nat{i < w} ->
Lemma (let (x0, x1, x2, x3, x4) = inp in
let t0, c0 = carry26_wide_zero x0 in
let t1, c1 = carry26_wide x1 c0 in
let t2, c2 = carry26_wide x2 c1 in
let t3, c3 = carry26_wide_zero x3 in
let t3', c6 = carry26 t3 c2 in
let t4, c4 = carry26_wide x4 c3 in
let t4' = vec_add_mod t4 c6 in
let tmp = (t0, t1, t2, t3', t4') in
let (t0, t1, t2, t3, t4) = as_tup64_i tmp i in
let vc4 = (uint64xN_v c4).[i] in
(feval5 inp).[i] == (v t0 + vc4 * 5 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104) % prime)
let carry_wide_felem5_eval_lemma_i1 #w inp i =
let (x0, x1, x2, x3, x4) = inp in
let t0, c0 = carry26_wide_zero x0 in
let t1, c1 = carry26_wide x1 c0 in
let t2, c2 = carry26_wide x2 c1 in
let t3, c3 = carry26_wide_zero x3 in
carry26_wide_zero_eq x3;
carry26_wide_fits_lemma #w #54 x3 (zero w);
let t3', c6 = carry26 t3 c2 in
carry26_eval_lemma 79 1 t3 c2;
carry26_fits_lemma 79 1 t3 c2;
let t4, c4 = carry26_wide x4 c3 in
let t4' = vec_add_mod t4 c6 in
let tmp = (t0, t1, t2, t3, t4) in
let tmp' = (t0, t1, t2, t3', t4') in
let (t0, t1, t2, t3, t4) = as_tup64_i tmp i in
let (t0, t1, t2, t3', t4') = as_tup64_i tmp' i in
let (xi0, xi1, xi2, xi3, xi4) = as_tup64_i inp i in
let vc0 = (uint64xN_v c0).[i] in
let vc1 = (uint64xN_v c1).[i] in
let vc2 = (uint64xN_v c2).[i] in
let vc3 = (uint64xN_v c3).[i] in
let vc4 = (uint64xN_v c4).[i] in
let vc6 = (uint64xN_v c6).[i] in
carry26_wide_zero_eq x0;
carry26_wide_eval_lemma #w #126 x0 (zero w);
assert (v xi0 == vc0 * pow2 26 + v t0);
carry26_wide_eval_lemma #w #102 x1 c0;
assert (v xi1 + vc0 == vc1 * pow2 26 + v t1);
carry26_wide_eval_lemma #w #78 x2 c1;
assert (v xi2 + vc1 == vc2 * pow2 26 + v t2);
carry26_wide_zero_eq x3;
carry26_wide_eval_lemma #w #54 x3 (zero w);
assert (v xi3 == vc3 * pow2 26 + v t3);
assert (v t3 + vc2 == vc6 * pow2 26 + v t3');
carry26_wide_eval_lemma #w #30 x4 c3;
assert (v xi4 + vc3 == vc4 * pow2 26 + v t4);
carry26_wide_fits_lemma #w #30 x4 c3;
Math.Lemmas.small_mod (v t4 + vc6) (pow2 64);
assert (v t4' == v t4 + vc6);
carry_wide_felem5_eval_lemma_i0 (xi0, xi1, xi2, xi3, xi4) (t0, t1, t2, t3', t4') vc0 vc1 vc2 vc3 vc4 vc6;
assert ((feval5 inp).[i] == (v t0 + vc4 * 5 + v t1 * pow26 + v t2 * pow52 + v t3' * pow78 + v t4' * pow104) % prime)
val carry_wide_felem5_eval_lemma_i:
#w:lanes
-> inp:felem_wide5 w{felem_wide_fits5 inp (126, 102, 78, 54, 30)}
-> i:nat{i < w} ->
Lemma ((feval5 (carry_wide_felem5 #w inp)).[i] == (feval5 inp).[i])
#push-options "--z3rlimit 100"
let carry_wide_felem5_eval_lemma_i #w inp i =
let (x0, x1, x2, x3, x4) = inp in
let tmp0, c0 = carry26_wide_zero x0 in
let tmp1, c1 = carry26_wide x1 c0 in
let tmp2, c2 = carry26_wide x2 c1 in
let tmp3, c3 = carry26_wide_zero x3 in
let tmp3', c6 = carry26 tmp3 c2 in
let tmp4, c4 = carry26_wide x4 c3 in
let tmp4' = vec_add_mod tmp4 c6 in
carry_wide_felem5_fits_lemma0 #w inp;
Math.Lemmas.small_mod ((uint64xN_v c4).[i] * 5) (pow2 64);
let tmp0', c5 = carry26 tmp0 (vec_smul_mod c4 (u64 5)) in
carry26_eval_lemma 155 1 tmp0 (vec_smul_mod c4 (u64 5));
assert ((uint64xN_v tmp0).[i] + (uint64xN_v c4).[i] * 5 == (uint64xN_v c5).[i] * pow2 26 + (uint64xN_v tmp0').[i]);
let tmp1' = vec_add_mod tmp1 c5 in
Math.Lemmas.small_mod ((uint64xN_v tmp1).[i] + (uint64xN_v c5).[i]) (pow2 64);
assert ((uint64xN_v tmp1').[i] == (uint64xN_v tmp1).[i] + (uint64xN_v c5).[i]);
let out = (tmp0', tmp1', tmp2, tmp3', tmp4') in
let tmp = (tmp0, tmp1, tmp2, tmp3', tmp4') in
let (o0, o1, o2, o3, o4) = as_tup64_i out i in
let (t0, t1, t2, t3, t4) = as_tup64_i tmp i in
let vc4 = (uint64xN_v c4).[i] in
let vc5 = (uint64xN_v c5).[i] in
calc (==) {
(feval5 out).[i];
(==) { }
(v o0 + v o1 * pow26 + v o2 * pow52 + v o3 * pow78 + v o4 * pow104) % prime;
(==) { }
(v t0 + vc4 * 5 + (v t1 + vc5) * pow26 - vc5 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104) % prime;
};
Math.Lemmas.distributivity_add_left (v t1) vc5 pow26;
assert ((feval5 out).[i] == (v t0 + vc4 * 5 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104) % prime);
carry_wide_felem5_eval_lemma_i1 #w inp i;
assert ((feval5 inp).[i] == (v t0 + vc4 * 5 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104) % prime);
assert ((feval5 out).[i] == (feval5 inp).[i]);
vec_smul_mod_five c4
#pop-options
val carry_wide_felem5_eval_lemma:
#w:lanes
-> inp:felem_wide5 w
-> Lemma
(requires felem_wide_fits5 inp (126, 102, 78, 54, 30))
(ensures feval5 (carry_wide_felem5 #w inp) == feval5 inp)
let carry_wide_felem5_eval_lemma #w inp =
let o = carry_wide_felem5 #w inp in
FStar.Classical.forall_intro (carry_wide_felem5_eval_lemma_i #w inp);
eq_intro (feval5 o) (feval5 inp)
val lemma_subtract_p5_0:
f:tup64_5{tup64_fits5 f (1, 1, 1, 1, 1)}
-> f':tup64_5 ->
Lemma
(requires
(let (f0, f1, f2, f3, f4) = f in
let (f0', f1', f2', f3', f4') = f' in
(v f4 <> 0x3ffffff || v f3 <> 0x3ffffff || v f2 <> 0x3ffffff || v f1 <> 0x3ffffff || v f0 < 0x3fffffb) /\
(v f0' = v f0 && v f1' = v f1 && v f2' = v f2 && v f3' = v f3 && v f4' = v f4)))
(ensures as_nat5 f' == as_nat5 f % prime)
let lemma_subtract_p5_0 f f' =
let (f0, f1, f2, f3, f4) = f in
let (f0', f1', f2', f3', f4') = f' in
assert_norm (max26 = pow2 26 - 1);
assert_norm (0x3ffffff = max26);
assert_norm (0x3fffffb = max26 - 4);
assert (as_nat5 f == v f0 + v f1 * pow26 + v f2 * pow52 + v f3 * pow78 + v f4 * pow104);
assert (as_nat5 f <= pow26 - 5 + (pow2 26 - 1) * pow26 + (pow2 26 - 1) * pow52 + (pow2 26 - 1) * pow78 + (pow2 26 - 1) * pow104);
assert_norm (pow2 26 * pow104 = pow2 130);
assert (as_nat5 f < pow2 130 - 5);
assert (as_nat5 f == as_nat5 f');
FStar.Math.Lemmas.modulo_lemma (as_nat5 f') prime
val lemma_subtract_p5_1:
f:tup64_5{tup64_fits5 f (1, 1, 1, 1, 1)}
-> f':tup64_5 ->
Lemma
(requires
(let (f0, f1, f2, f3, f4) = f in
let (f0', f1', f2', f3', f4') = f' in
(v f4 = 0x3ffffff && v f3 = 0x3ffffff && v f2 = 0x3ffffff && v f1 = 0x3ffffff && v f0 >= 0x3fffffb) /\
(v f0' = v f0 - 0x3fffffb && v f1' = v f1 - 0x3ffffff && v f2' = v f2 - 0x3ffffff && v f3' = v f3 - 0x3ffffff && v f4' = v f4 - 0x3ffffff)))
(ensures as_nat5 f' == as_nat5 f % prime)
let lemma_subtract_p5_1 f f' =
let (f0, f1, f2, f3, f4) = f in
let (f0', f1', f2', f3', f4') = f' in
//assert_norm (max26 = pow2 26 - 1);
assert_norm (0x3ffffff = pow2 26 - 1);
assert_norm (0x3fffffb = pow2 26 - 5);
assert (as_nat5 f' < prime);
calc (==) {
as_nat5 f' % prime;
(==) { }
(v f0' + v f1' * pow26 + v f2' * pow52 + v f3' * pow78 + v f4' * pow104) % prime;
(==) { }
(v f0 - (pow2 26 - 5) + (v f1 - (pow2 26 - 1)) * pow26 + (v f2 - (pow2 26 - 1)) * pow52 +
(v f3 - (pow2 26 - 1)) * pow78 + (v f4 - (pow2 26 - 1)) * pow104) % prime;
(==) {
assert_norm (pow2 26 * pow26 = pow52);
assert_norm (pow2 26 * pow52 = pow78);
assert_norm (pow2 26 * pow78 = pow104);
assert_norm (pow2 26 * pow104 = pow2 130) }
(v f0 + v f1 * pow26 + v f2 * pow52 + v f3 * pow78 + v f4 * pow104 - prime) % prime;
(==) { FStar.Math.Lemmas.lemma_mod_sub (v f0 + v f1 * pow26 + v f2 * pow52 + v f3 * pow78 + v f4 * pow104) prime 1 }
(v f0 + v f1 * pow26 + v f2 * pow52 + v f3 * pow78 + v f4 * pow104) % prime;
(==) { }
as_nat5 f % prime;
};
assert (as_nat5 f' % prime == as_nat5 f % prime);
FStar.Math.Lemmas.modulo_lemma (as_nat5 f') prime
val lemma_subtract_p5:
f:tup64_5{tup64_fits5 f (1, 1, 1, 1, 1)}
-> f':tup64_5 ->
Lemma
(requires
(let (f0, f1, f2, f3, f4) = f in
let (f0', f1', f2', f3', f4') = f' in
((v f4 = 0x3ffffff && v f3 = 0x3ffffff && v f2 = 0x3ffffff && v f1 = 0x3ffffff && v f0 >= 0x3fffffb) /\
(v f0' = v f0 - 0x3fffffb && v f1' = v f1 - 0x3ffffff && v f2' = v f2 - 0x3ffffff && v f3' = v f3 - 0x3ffffff && v f4' = v f4 - 0x3ffffff)) \/
((v f4 <> 0x3ffffff || v f3 <> 0x3ffffff || v f2 <> 0x3ffffff || v f1 <> 0x3ffffff || v f0 < 0x3fffffb) /\
(v f0' = v f0 && v f1' = v f1 && v f2' = v f2 && v f3' = v f3 && v f4' = v f4))))
(ensures as_nat5 f' == as_nat5 f % prime)
let lemma_subtract_p5 f f' =
let (f0, f1, f2, f3, f4) = f in
let (f0', f1', f2', f3', f4') = f' in
assert_norm (max26 = pow2 26 - 1);
if ((v f4 <> 0x3ffffff || v f3 <> 0x3ffffff || v f2 <> 0x3ffffff || v f1 <> 0x3ffffff || v f0 < 0x3fffffb) &&
(v f0' = v f0 && v f1' = v f1 && v f2' = v f2 && v f3' = v f3 && v f4' = v f4))
then lemma_subtract_p5_0 f f'
else lemma_subtract_p5_1 f f'
noextract
val subtract_p5_s:
#w:lanes
-> f:felem5 w{felem_fits5 f (1, 1, 1, 1, 1)}
-> i:nat{i < w} ->
Pure tup64_5
(requires True)
(ensures fun out ->
tup64_fits5 out (1, 1, 1, 1, 1) /\
as_nat5 out == as_nat5 (as_tup64_i f i) % prime)
#push-options "--z3rlimit 100"
let subtract_p5_s #w f i =
let (f0, f1, f2, f3, f4) = as_tup64_i f i in
let mask0 = eq_mask f4 (u64 0x3ffffff) in
let mask1 = mask0 &. eq_mask f3 (u64 0x3ffffff) in
let mask2 = mask1 &. eq_mask f2 (u64 0x3ffffff) in
let mask3 = mask2 &. eq_mask f1 (u64 0x3ffffff) in
let mask4 = mask3 &. gte_mask f0 (u64 0x3fffffb) in
let p0 = mask4 &. u64 0x3fffffb in
logand_lemma mask4 (u64 0x3fffffb);
let p1 = mask4 &. u64 0x3ffffff in
logand_lemma mask4 (u64 0x3ffffff);
let p2 = mask4 &. u64 0x3ffffff in
let p3 = mask4 &. u64 0x3ffffff in
let p4 = mask4 &. u64 0x3ffffff in
let f0' = f0 -. p0 in
let f1' = f1 -. p1 in
let f2' = f2 -. p2 in
let f3' = f3 -. p3 in
let f4' = f4 -. p4 in
lemma_subtract_p5 (f0, f1, f2, f3, f4) (f0', f1', f2', f3', f4');
(f0', f1', f2', f3', f4')
#pop-options
#push-options "--max_ifuel 1"
val subtract_p5_felem5_lemma_i:
#w:lanes
-> f:felem5 w{felem_fits5 f (1, 1, 1, 1, 1)}
-> i:nat{i < w} ->
Lemma
(tup64_fits5 (as_tup64_i (subtract_p5 #w f) i) (1, 1, 1, 1, 1) /\
as_nat5 (as_tup64_i (subtract_p5 #w f) i) == as_nat5 (as_tup64_i f i) % prime)
let subtract_p5_felem5_lemma_i #w f i =
assert (subtract_p5_s #w f i == as_tup64_i (subtract_p5 #w f) i)
#pop-options
val subtract_p5_felem5_lemma:
#w:lanes
-> f:felem5 w{felem_fits5 f (1, 1, 1, 1, 1)} ->
Lemma
(felem_fits5 (subtract_p5 f) (1, 1, 1, 1, 1) /\
(fas_nat5 (subtract_p5 f)).[0] == (feval5 f).[0])
let subtract_p5_felem5_lemma #w f =
match w with
| 1 ->
subtract_p5_felem5_lemma_i #w f 0
| 2 ->
subtract_p5_felem5_lemma_i #w f 0;
subtract_p5_felem5_lemma_i #w f 1
| 4 ->
subtract_p5_felem5_lemma_i #w f 0;
subtract_p5_felem5_lemma_i #w f 1;
subtract_p5_felem5_lemma_i #w f 2;
subtract_p5_felem5_lemma_i #w f 3
noextract
let acc_inv_t (#w:lanes) (acc:felem5 w) : Type0 =
let (o0, o1, o2, o3, o4) = acc in
forall (i:nat). i < w ==>
(if uint_v (vec_v o0).[i] >= pow2 26 then
tup64_fits5 (as_tup64_i acc i) (2, 1, 1, 1, 1) /\
uint_v (vec_v o0).[i] % pow2 26 < 47
else tup64_fits5 (as_tup64_i acc i) (1, 1, 1, 1, 1))
val acc_inv_lemma_i:
#w:lanes
-> acc:felem5 w{felem_fits5 acc (1, 1, 1, 1, 1)}
-> cin:uint64xN w{uint64xN_fits cin 45}
-> i:nat{i < w} ->
Lemma
(let (i0, i1, i2, i3, i4) = acc in
let i0' = vec_add_mod i0 cin in
let acc1 = (i0', i1, i2, i3, i4) in
(if (uint64xN_v i0').[i] >= pow2 26 then
tup64_fits5 (as_tup64_i acc1 i) (2, 1, 1, 1, 1) /\
(uint64xN_v i0').[i] % pow2 26 < 47
else tup64_fits5 (as_tup64_i acc1 i) (1, 1, 1, 1, 1)))
let acc_inv_lemma_i #w acc cin i =
let (i0, i1, i2, i3, i4) = acc in
let i0' = vec_add_mod i0 cin in
assert ((vec_v i0').[i] == (vec_v i0).[i] +. (vec_v cin).[i]);
assert ((uint64xN_v i0).[i] + (uint64xN_v cin).[i] <= max26 + 46);
assert_norm (max26 = pow2 26 - 1);
FStar.Math.Lemmas.euclidean_division_definition ((uint64xN_v i0).[i] + (uint64xN_v cin).[i]) (pow2 26)
val acc_inv_lemma:
#w:lanes
-> acc:felem5 w{felem_fits5 acc (1, 1, 1, 1, 1)}
-> cin:uint64xN w{uint64xN_fits cin 45} ->
Lemma
(let (i0, i1, i2, i3, i4) = acc in
let i0' = vec_add_mod i0 cin in
acc_inv_t (i0', i1, i2, i3, i4))
let acc_inv_lemma #w acc cin =
match w with
| 1 ->
acc_inv_lemma_i #w acc cin 0
| 2 ->
acc_inv_lemma_i #w acc cin 0;
acc_inv_lemma_i #w acc cin 1
| 4 ->
acc_inv_lemma_i #w acc cin 0;
acc_inv_lemma_i #w acc cin 1;
acc_inv_lemma_i #w acc cin 2;
acc_inv_lemma_i #w acc cin 3
val carry_full_felem5_fits_lemma0: #w:lanes -> f:felem5 w{felem_fits5 f (8, 8, 8, 8, 8)} ->
Lemma (let (f0, f1, f2, f3, f4) = f in
let tmp0,c0 = carry26 f0 (zero w) in
let tmp1,c1 = carry26 f1 c0 in
let tmp2,c2 = carry26 f2 c1 in
let tmp3,c3 = carry26 f3 c2 in
let tmp4,c4 = carry26 f4 c3 in
felem_fits5 (tmp0, tmp1, tmp2, tmp3, tmp4) (1, 1, 1, 1, 1) /\ uint64xN_fits c4 9)
let carry_full_felem5_fits_lemma0 #w (f0, f1, f2, f3, f4) =
let tmp0,c0 = carry26 f0 (zero w) in
carry26_fits_lemma 1 8 f0 (zero w);
let tmp1,c1 = carry26 f1 c0 in
carry26_fits_lemma 1 8 f1 c0;
let tmp2,c2 = carry26 f2 c1 in
carry26_fits_lemma 1 8 f2 c1;
let tmp3,c3 = carry26 f3 c2 in
carry26_fits_lemma 1 8 f3 c2;
let tmp4,c4 = carry26 f4 c3 in
carry26_fits_lemma 1 8 f4 c3;
assert (felem_fits5 (tmp0, tmp1, tmp2, tmp3, tmp4) (1, 1, 1, 1, 1));
assert (uint64xN_fits c4 9)
val carry_full_felem5_fits_lemma: #w:lanes -> f:felem5 w{felem_fits5 f (8, 8, 8, 8, 8)} ->
Lemma (acc_inv_t (carry_full_felem5 f))
let carry_full_felem5_fits_lemma #w f =
let (f0, f1, f2, f3, f4) = f in
let tmp0,c0 = carry26 f0 (zero w) in
let tmp1,c1 = carry26 f1 c0 in
let tmp2,c2 = carry26 f2 c1 in
let tmp3,c3 = carry26 f3 c2 in
let tmp4,c4 = carry26 f4 c3 in
carry_full_felem5_fits_lemma0 #w f;
assert (felem_fits1 tmp0 1 /\ uint64xN_fits c4 9);
let tmp0' = vec_add_mod tmp0 (vec_smul_mod c4 (u64 5)) in
acc_inv_lemma (tmp0, tmp1, tmp2, tmp3, tmp4) (vec_smul_mod c4 (u64 5))
val carry_full_felem5_eval_lemma_i0:
inp:tup64_5
-> tmp:tup64_5
-> vc0:nat -> vc1:nat -> vc2:nat -> vc3:nat -> vc4:nat ->
Lemma
(requires
(let (t0, t1, t2, t3, t4) = tmp in
let (ti0, ti1, ti2, ti3, ti4) = inp in
v ti0 == vc0 * pow2 26 + v t0 /\
v ti1 + vc0 == vc1 * pow2 26 + v t1 /\
v ti2 + vc1 == vc2 * pow2 26 + v t2 /\
v ti3 + vc2 == vc3 * pow2 26 + v t3 /\
v ti4 + vc3 == vc4 * pow2 26 + v t4))
(ensures
(let (t0, t1, t2, t3, t4) = tmp in
let (ti0, ti1, ti2, ti3, ti4) = inp in
as_nat5 inp % prime ==
(v t0 + vc4 * 5 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104) % prime))
let carry_full_felem5_eval_lemma_i0 inp tmp vc0 vc1 vc2 vc3 vc4 =
let (t0, t1, t2, t3, t4) = tmp in
let (ti0, ti1, ti2, ti3, ti4) = inp in
let tmp_n = v t0 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104 in
calc (==) {
as_nat5 inp % prime;
(==) { }
(v ti0 + v ti1 * pow26 + v ti2 * pow52 + v ti3 * pow78 + v ti4 * pow104) % prime;
(==) { }
(vc0 * pow2 26 + v t0 +
(vc1 * pow2 26 + v t1 - vc0) * pow26 +
(vc2 * pow2 26 + v t2 - vc1) * pow52 +
(vc3 * pow2 26 + v t3 - vc2) * pow78 +
(vc4 * pow2 26 + v t4 - vc3) * pow104) % prime;
(==) {
assert_norm (pow2 26 * pow26 = pow52);
assert_norm (pow2 26 * pow52 = pow78);
assert_norm (pow2 26 * pow78 = pow104);
assert_norm (pow2 26 * pow104 = pow2 130)}
(v t0 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104 + vc4 * pow2 130) % prime;
(==) { FStar.Math.Lemmas.lemma_mod_plus_distr_r tmp_n (vc4 * pow2 130) prime }
(tmp_n + (vc4 * pow2 130 % prime)) % prime;
(==) { FStar.Math.Lemmas.lemma_mod_mul_distr_r (vc4) (pow2 130) prime }
(tmp_n + (vc4 * (pow2 130 % prime) % prime)) % prime;
(==) { lemma_prime () }
(tmp_n + (vc4 * 5 % prime)) % prime;
(==) { FStar.Math.Lemmas.lemma_mod_plus_distr_r tmp_n (vc4 * 5) prime }
(tmp_n + vc4 * 5) % prime;
};
assert (as_nat5 inp % prime == (tmp_n + vc4 * 5) % prime)
val carry_full_felem5_eval_lemma_i1:
#w:lanes
-> inp:felem_wide5 w{felem_fits5 inp (8, 8, 8, 8, 8)}
-> i:nat{i < w} ->
Lemma
(let (i0, i1, i2, i3, i4) = inp in
let tmp0,c0 = carry26 i0 (zero w) in
let tmp1,c1 = carry26 i1 c0 in
let tmp2,c2 = carry26 i2 c1 in
let tmp3,c3 = carry26 i3 c2 in
let tmp4,c4 = carry26 i4 c3 in
let tmp = (tmp0, tmp1, tmp2, tmp3, tmp4) in
let (t0, t1, t2, t3, t4) = as_tup64_i tmp i in
let vc4 = (uint64xN_v c4).[i] in
(feval5 inp).[i] == (v t0 + vc4 * 5 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104) % prime)
let carry_full_felem5_eval_lemma_i1 #w inp i =
let (i0, i1, i2, i3, i4) = inp in
let tmp0,c0 = carry26 i0 (zero w) in
let tmp1,c1 = carry26 i1 c0 in
let tmp2,c2 = carry26 i2 c1 in
let tmp3,c3 = carry26 i3 c2 in
let tmp4,c4 = carry26 i4 c3 in
let tmp = (tmp0, tmp1, tmp2, tmp3, tmp4) in
let (t0, t1, t2, t3, t4) = as_tup64_i tmp i in
let (ti0, ti1, ti2, ti3, ti4) = as_tup64_i inp i in
let vc0 = (uint64xN_v c0).[i] in
let vc1 = (uint64xN_v c1).[i] in
let vc2 = (uint64xN_v c2).[i] in
let vc3 = (uint64xN_v c3).[i] in
let vc4 = (uint64xN_v c4).[i] in
carry26_eval_lemma 1 8 i0 (zero w);
assert (v ti0 == vc0 * pow2 26 + v t0);
carry26_eval_lemma 1 8 i1 c0;
assert (v ti1 + vc0 == vc1 * pow2 26 + v t1);
carry26_eval_lemma 1 8 i2 c1;
assert (v ti2 + vc1 == vc2 * pow2 26 + v t2);
carry26_eval_lemma 1 8 i3 c2;
assert (v ti3 + vc2 == vc3 * pow2 26 + v t3);
carry26_eval_lemma 1 8 i4 c3;
assert (v ti4 + vc3 == vc4 * pow2 26 + v t4);
carry_full_felem5_eval_lemma_i0 (ti0, ti1, ti2, ti3, ti4) (t0, t1, t2, t3, t4) vc0 vc1 vc2 vc3 vc4;
assert ((feval5 inp).[i] ==
(v t0 + vc4 * 5 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104) % prime)
val carry_full_felem5_eval_lemma_i:
#w:lanes
-> inp:felem_wide5 w{felem_fits5 inp (8, 8, 8, 8, 8)}
-> i:nat{i < w} ->
Lemma ((feval5 (carry_full_felem5 #w inp)).[i] == (feval5 inp).[i])
let carry_full_felem5_eval_lemma_i #w inp i =
let (i0, i1, i2, i3, i4) = inp in
let tmp0,c0 = carry26 i0 (zero w) in
let tmp1,c1 = carry26 i1 c0 in
let tmp2,c2 = carry26 i2 c1 in
let tmp3,c3 = carry26 i3 c2 in
let tmp4,c4 = carry26 i4 c3 in
let tmp = (tmp0, tmp1, tmp2, tmp3, tmp4) in
let (t0, t1, t2, t3, t4) = as_tup64_i tmp i in
let (ti0, ti1, ti2, ti3, ti4) = as_tup64_i inp i in
let vc4 = (uint64xN_v c4).[i] in
carry_full_felem5_fits_lemma0 #w inp;
let cin = vec_smul_mod c4 (u64 5) in
assert ((uint64xN_v cin).[i] == vc4 * 5);
let tmp0' = vec_add_mod tmp0 cin in
Math.Lemmas.small_mod ((uint64xN_v tmp0).[i] + vc4 * 5) (pow2 64);
assert ((uint64xN_v tmp0').[i] == (uint64xN_v tmp0).[i] + vc4 * 5);
let out = (tmp0', tmp1, tmp2, tmp3, tmp4) in
let (o0, o1, o2, o3, o4) = as_tup64_i out i in
assert ((feval5 out).[i] == (v t0 + vc4 * 5 + v t1 * pow26 + v t2 * pow52 + v t3 * pow78 + v t4 * pow104) % prime);
carry_full_felem5_eval_lemma_i1 #w inp i;
assert ((feval5 out).[i] == (feval5 inp).[i])
val carry_full_felem5_eval_lemma:
#w:lanes
-> inp:felem_wide5 w
-> Lemma
(requires felem_fits5 inp (8, 8, 8, 8, 8))
(ensures feval5 (carry_full_felem5 #w inp) == feval5 inp)
let carry_full_felem5_eval_lemma #w inp =
let o = carry_full_felem5 #w inp in
FStar.Classical.forall_intro (carry_full_felem5_eval_lemma_i #w inp);
eq_intro (feval5 o) (feval5 inp)
val carry_full_felem5_lemma:
#w:lanes
-> f:felem5 w{felem_fits5 f (8, 8, 8, 8, 8)} ->
Lemma
(felem_fits5 (carry_full_felem5 f) (2, 1, 1, 1, 1) /\
feval5 (carry_full_felem5 f) == feval5 f)
let carry_full_felem5_lemma #w f =
carry_full_felem5_eval_lemma f;
carry_full_felem5_fits_lemma f
val carry_reduce_lemma_i:
#w:lanes
-> l:uint64xN w
-> cin:uint64xN w
-> i:nat{i < w} ->
Lemma
(requires
(uint64xN_v l).[i] <= 2 * max26 /\
(uint64xN_v cin).[i] <= 62 * max26)
(ensures
(let (l0, l1) = carry26 #w l cin in
(uint64xN_v l0).[i] <= max26 /\ (uint64xN_v l1).[i] <= 63 /\
(uint64xN_v l).[i] + (uint64xN_v cin).[i] ==
(uint64xN_v l1).[i] * pow2 26 + (uint64xN_v l0).[i]))
let carry_reduce_lemma_i #w l cin i =
let li = (vec_v l).[i] in
let cini = (vec_v cin).[i] in
let mask26 = u64 0x3ffffff in
assert_norm (0x3ffffff = pow2 26 - 1);
FStar.Math.Lemmas.modulo_lemma (v li + v cini) (pow2 64);
let li' = li +! cini in
let li0 = li' &. mask26 in
let li1 = li' >>. 26ul in
mod_mask_lemma li' 26ul;
assert (v (mod_mask #U64 #SEC 26ul) == v mask26);
FStar.Math.Lemmas.pow2_modulo_modulo_lemma_1 (v li') 26 32;
FStar.Math.Lemmas.pow2_minus 32 26
#push-options "--z3rlimit 600"
val carry_reduce_felem5_fits_lemma_i0:
#w:lanes
-> f:felem5 w{acc_inv_t f}
-> i:nat{i < w} ->
Lemma
(let (f0, f1, f2, f3, f4) = f in
let tmp0,c0 = carry26 f0 (zero w) in
let tmp1,c1 = carry26 f1 c0 in
let tmp2,c2 = carry26 f2 c1 in
let tmp3,c3 = carry26 f3 c2 in
let tmp4,c4 = carry26 f4 c3 in
let res = (tmp0, tmp1, tmp2, tmp3, tmp4) in
(if (uint64xN_v f0).[i] < pow2 26 then (uint64xN_v tmp0).[i] < pow2 26 else (uint64xN_v tmp0).[i] <= 46) /\
(if (uint64xN_v f0).[i] < pow2 26 then (uint64xN_v c4).[i] = 0 else (uint64xN_v c4).[i] <= 63))
let carry_reduce_felem5_fits_lemma_i0 #w f i =
let (f0, f1, f2, f3, f4) = f in
let tmp0,c0 = carry26 f0 (zero w) in
carry_reduce_lemma_i f0 (zero w) i;
assert (if (uint64xN_v f0).[i] < pow2 26 then (uint64xN_v tmp0).[i] < pow2 26 else (uint64xN_v tmp0).[i] <= 46);
assert (if (uint64xN_v f0).[i] < pow2 26 then (uint64xN_v c0).[i] = 0 else (uint64xN_v c0).[i] <= 63);
let tmp1,c1 = carry26 f1 c0 in
carry_reduce_lemma_i f1 c0 i;
assert (if (uint64xN_v c0).[i] = 0 then (uint64xN_v c1).[i] = 0 else (uint64xN_v c1).[i] <= 63);
let tmp2,c2 = carry26 f2 c1 in
carry_reduce_lemma_i f2 c1 i;
assert (if (uint64xN_v c0).[i] = 0 then (uint64xN_v c2).[i] = 0 else (uint64xN_v c2).[i] <= 63);
let tmp3,c3 = carry26 f3 c2 in
carry_reduce_lemma_i f3 c2 i;
assert (if (uint64xN_v c0).[i] = 0 then (uint64xN_v c3).[i] = 0 else (uint64xN_v c3).[i] <= 63);
let tmp4,c4 = carry26 f4 c3 in
carry_reduce_lemma_i f4 c3 i;
assert (if (uint64xN_v c0).[i] = 0 then (uint64xN_v c4).[i] = 0 else (uint64xN_v c4).[i] <= 63);
assert (if (uint64xN_v f0).[i] < pow2 26 then (uint64xN_v c0).[i] = 0 /\ (uint64xN_v c4).[i] = 0 else (uint64xN_v c4).[i] <= 63)
val carry_reduce_felem5_fits_lemma_i1:
#w:lanes
-> f:felem5 w{acc_inv_t f}
-> i:nat{i < w} ->
Lemma
(let (f0, f1, f2, f3, f4) = f in
let tmp0,c0 = carry26 f0 (zero w) in
let tmp1,c1 = carry26 f1 c0 in
let tmp2,c2 = carry26 f2 c1 in
let tmp3,c3 = carry26 f3 c2 in
let tmp4,c4 = carry26 f4 c3 in
let res = (tmp0, tmp1, tmp2, tmp3, tmp4) in
(uint64xN_v c4).[i] <= 63 /\
tup64_fits5 (as_tup64_i res i) (1, 1, 1, 1, 1))
let carry_reduce_felem5_fits_lemma_i1 #w f i =
let (f0, f1, f2, f3, f4) = f in
let tmp0,c0 = carry26 f0 (zero w) in
carry_reduce_lemma_i f0 (zero w) i;
let tmp1,c1 = carry26 f1 c0 in
carry_reduce_lemma_i f1 c0 i;
let tmp2,c2 = carry26 f2 c1 in
carry_reduce_lemma_i f2 c1 i;
let tmp3,c3 = carry26 f3 c2 in
carry_reduce_lemma_i f3 c2 i;
let tmp4,c4 = carry26 f4 c3 in
carry_reduce_lemma_i f4 c3 i;
let res = (tmp0, tmp1, tmp2, tmp3, tmp4) in
assert (tup64_fits5 (as_tup64_i res i) (1, 1, 1, 1, 1))
val carry_reduce_felem5_fits_lemma_i:
#w:lanes
-> f:felem5 w{acc_inv_t f}
-> i:nat{i < w} ->
Lemma (tup64_fits5 (as_tup64_i (carry_full_felem5 f) i) (1, 1, 1, 1, 1))
let carry_reduce_felem5_fits_lemma_i #w f i =
assert_norm (max26 == pow2 26 - 1);
let (f0, f1, f2, f3, f4) = f in
let tmp0,c0 = carry26 f0 (zero w) in
let tmp1,c1 = carry26 f1 c0 in
let tmp2,c2 = carry26 f2 c1 in
let tmp3,c3 = carry26 f3 c2 in
let tmp4,c4 = carry26 f4 c3 in
carry_reduce_felem5_fits_lemma_i1 #w f i;
FStar.Math.Lemmas.modulo_lemma ((uint64xN_v c4).[i] * 5) (pow2 64);
assert ((uint64xN_v (vec_smul_mod c4 (u64 5))).[i] == (uint64xN_v c4).[i] * 5);
let tmp0' = vec_add_mod tmp0 (vec_smul_mod c4 (u64 5)) in
carry_reduce_felem5_fits_lemma_i0 #w f i;
let res = (tmp0', tmp1, tmp2, tmp3, tmp4) in
assert (tup64_fits5 (as_tup64_i res i) (1, 1, 1, 1, 1))
#pop-options
#push-options "--z3rlimit 100"
val carry_reduce_felem5_fits_lemma:
#w:lanes
-> f:felem5 w{acc_inv_t f} ->
Lemma (felem_fits5 (carry_full_felem5 f) (1, 1, 1, 1, 1))
let carry_reduce_felem5_fits_lemma #w f =
match w with
| 1 ->
carry_reduce_felem5_fits_lemma_i #w f 0
| 2 ->
carry_reduce_felem5_fits_lemma_i #w f 0;
carry_reduce_felem5_fits_lemma_i #w f 1
| 4 ->
carry_reduce_felem5_fits_lemma_i #w f 0;
carry_reduce_felem5_fits_lemma_i #w f 1;
carry_reduce_felem5_fits_lemma_i #w f 2;
carry_reduce_felem5_fits_lemma_i #w f 3
val carry_reduce_felem5_lemma:
#w:lanes
-> f:felem5 w{acc_inv_t f} ->
Lemma
(felem_fits5 (carry_full_felem5 f) (1, 1, 1, 1, 1) /\
feval5 (carry_full_felem5 f) == feval5 f)
let carry_reduce_felem5_lemma #w f =
carry_reduce_felem5_fits_lemma #w f;
carry_full_felem5_eval_lemma f
#pop-options