rust_lean_playground/
lib.rs

1// From: https://github.com/AeneasVerif/aeneas/blob/3bf9aeecddb2600d788435bc83a6b84c6bdf054f/tests/src/tutorial/src/lib.rs#L186
2pub type Bignum = Vec<u32>;
3
4/// Add a bignum (`Vec<u32>`) in place, and return the carry.
5///
6/// We consider the bignum to be a little endian representation,
7/// i.e., the first element is the least significant.
8/// For adding we assume the bignums have the same length
9///
10/// ## Spec:
11///
12/// If we define:
13/// ```lean
14/// def toInt (l : List U32) : Int :=
15///  match l with
16///  | [] => 0
17///  | x :: l =>
18///    x + 2 ^ 32 * toInt l
19///```
20///
21/// Then the function satisfies:
22/// ```lean
23/// example (x : alloc.vec.Vec U32) (y : alloc.vec.Vec U32) (hLength : x.length = y.length) :
24///     ∃ x' c, add_with_carry x y = ok (c, x') ∧
25///     x'.length = x.length ∧
26///     c.val ≤ 1 ∧
27///     toInt x' + c.val * 2 ^ (32 * x'.length) = toInt x + toInt y :=
28///   add_with_carry_spec
29/// ```
30///
31/// I.e., if:
32/// - `x` has the same length as `y`
33///
34/// Then:
35/// - `add_with_carry x y` returns ok with result `c` and `x'`, updated value of `x`
36/// - `x'` has the same length as `x`
37/// - `c.val ≤ 1`
38/// - `toInt x' + c.val * 2 ^ (32 * x'.length) = toInt x + toInt y`.  
39///
40pub fn add_with_carry(x: &mut Bignum, y: &Bignum) -> u8 {
41    let mut c0 = 0u8;
42    let mut i = 0;
43    // Remark: we have (and need) the invariant that: c0 <= 1
44    while i < x.len() {
45        let (sum, c1) = x[i].overflowing_add(c0 as u32);
46        let (sum, c2) = sum.overflowing_add(y[i]);
47        // We have: c1 as u8 + c2 as u8 <= 1
48        c0 = c1 as u8 + c2 as u8;
49        x[i] = sum;
50        i += 1;
51    }
52    c0
53}
54
55// -----------------------------------------------------------------------//
56// Source:
57// [curve25519-dalek](https://docs.rs/curve25519-dalek/latest/src/curve25519_dalek/backend/serial/u64/field.rs.html#288)
58
59// pub const LOW_51_BIT_MASK: u64 = (1u64 << 51) - 1;
60pub const LOW_51_BIT_MASK: u64 = 2251799813685247u64; // 2^51  -1
61pub struct FieldElement51 {
62    pub limbs: [u64; 5],
63}
64
65// curve25519-dalek/src/backend/serial/u64/field.rs
66impl FieldElement51 {
67    /// Given 64-bit input limbs, reduce to enforce the bound 2^(51 + epsilon).
68    ///
69    /// ## Spec:
70    ///
71    /// If we define:
72    /// ```lean
73    /// def ArrayU645_to_Nat (limbs : Array U64 5#usize) : Nat :=
74    ///   ∑ i ∈ Finset.range 5, 2^(51 * i) * (limbs[i]!).val
75    ///
76    /// def p : Nat := 2^255 - 19
77    /// ```
78    ///
79    /// Then the function satisfies:
80    /// ```lean
81    /// example (limbs : Array U64 5#usize) :
82    ///     ∃ r, FieldElement51.reduce limbs = ok (r) ∧
83    ///     (∀ i, i < 5 → (r.limbs[i]!).val ≤ 2^51 + (2^13 - 1) * 19) ∧
84    ///     ArrayU645_to_Nat limbs ≡ ArrayU645_to_Nat r.limbs [MOD p] :=
85    ///   FieldElement51.reduce_spec limbs
86    /// ```
87    ///
88    /// I.e.,
89    /// - The function does not overflow and hence returns a result
90    /// - All the limbs of the result are small, ≤ 2^(51 + ε)
91    /// - The result is equal to the input mod p.
92    ///
93    #[inline(always)]
94    pub fn reduce(mut limbs: [u64; 5]) -> FieldElement51 {
95        // Since the input limbs are bounded by 2^64, the biggest
96        // carry-out is bounded by 2^13.
97        //
98        // The biggest carry-in is c4 * 19, resulting in
99        //
100        // 2^51 + 19*2^13 < 2^51.0000000001
101        //
102        // Because we don't need to canonicalize, only to reduce the
103        // limb sizes, it's OK to do a "weak reduction", where we
104        // compute the carry-outs in parallel.
105
106        let c0 = limbs[0] >> 51;
107        let c1 = limbs[1] >> 51;
108        let c2 = limbs[2] >> 51;
109        let c3 = limbs[3] >> 51;
110        let c4 = limbs[4] >> 51;
111
112        limbs[0] &= LOW_51_BIT_MASK;
113        limbs[1] &= LOW_51_BIT_MASK;
114        limbs[2] &= LOW_51_BIT_MASK;
115        limbs[3] &= LOW_51_BIT_MASK;
116        limbs[4] &= LOW_51_BIT_MASK;
117
118        limbs[0] += c4 * 19;
119        limbs[1] += c0;
120        limbs[2] += c1;
121        limbs[3] += c2;
122        limbs[4] += c3;
123
124        FieldElement51 { limbs }
125    }
126}