|
| 1 | +import MidenLean.Proofs.U128.Common |
| 2 | +import MidenLean.Proofs.Tactics |
| 3 | +import MidenLean.Generated.U128 |
| 4 | + |
| 5 | +namespace MidenLean.Proofs |
| 6 | + |
| 7 | +open MidenLean |
| 8 | +open MidenLean.StepLemmas |
| 9 | +open MidenLean.Tactics |
| 10 | + |
| 11 | +-- ============================================================================ |
| 12 | +-- Helper lemmas |
| 13 | +-- ============================================================================ |
| 14 | + |
| 15 | +private theorem felt32_val : (32 : Felt).val = 32 := |
| 16 | + felt_ofNat_val_lt 32 (by unfold GOLDILOCKS_PRIME; omega) |
| 17 | + |
| 18 | +private theorem felt32_isU32 : (32 : Felt).isU32 = true := by |
| 19 | + simp [Felt.isU32, felt32_val] |
| 20 | + |
| 21 | +private theorem shift_sub32_val |
| 22 | + (shift : Felt) (hshift : shift.val ≤ 31) : |
| 23 | + (Felt.ofNat (u32OverflowingSub 32 shift.val).2).val = 32 - shift.val := by |
| 24 | + unfold u32OverflowingSub |
| 25 | + simp [show shift.val ≤ 32 by omega] |
| 26 | + rw [felt_ofNat_val_lt _ (by unfold GOLDILOCKS_PRIME; omega)] |
| 27 | + |
| 28 | +private theorem pow32_sub_val |
| 29 | + (shift : Felt) (hshift_pos : 0 < shift.val) (hshift : shift.val ≤ 31) : |
| 30 | + (Felt.ofNat (2 ^ (32 - shift.val))).val = 2 ^ (32 - shift.val) := by |
| 31 | + apply felt_ofNat_val_lt |
| 32 | + have hpow_le : 2 ^ (32 - shift.val) ≤ 2 ^ 31 := by |
| 33 | + apply Nat.pow_le_pow_right |
| 34 | + omega |
| 35 | + omega |
| 36 | + unfold GOLDILOCKS_PRIME |
| 37 | + omega |
| 38 | + |
| 39 | +private theorem pow32_sub_isU32 |
| 40 | + (shift : Felt) (hshift_pos : 0 < shift.val) (hshift : shift.val ≤ 31) : |
| 41 | + (Felt.ofNat (2 ^ (32 - shift.val))).isU32 = true := by |
| 42 | + apply felt_ofNat_isU32_of_lt |
| 43 | + have hpow_le : 2 ^ (32 - shift.val) ≤ 2 ^ 31 := by |
| 44 | + apply Nat.pow_le_pow_right |
| 45 | + omega |
| 46 | + omega |
| 47 | + omega |
| 48 | + |
| 49 | +-- ============================================================================ |
| 50 | +-- Chunk definitions for the nonzero (else) branch |
| 51 | +-- ============================================================================ |
| 52 | + |
| 53 | +/-- Setup: push 32, compute 32−shift, pow2. -/ |
| 54 | +private def shr_k2_setup : List Op := [ |
| 55 | + .inst (.push 32), .inst (.dup 1), .inst (.u32WrappingSub), .inst (.pow2)] |
| 56 | + |
| 57 | +/-- Compute: shift a3 and a2, widen-multiply a3 by pow, OR with shifted a2. -/ |
| 58 | +private def shr_k2_compute : List Op := [ |
| 59 | + .inst (.dup 5), .inst (.dup 2), .inst (.u32Shr), |
| 60 | + .inst (.dup 5), .inst (.dup 3), .inst (.u32Shr), |
| 61 | + .inst (.dup 7), .inst (.dup 3), .inst (.u32WidenMul), |
| 62 | + .inst (.swap 1), .inst (.drop), .inst (.u32Or)] |
| 63 | + |
| 64 | +/-- Cleanup: move results down, drop temporaries. -/ |
| 65 | +private def shr_k2_cleanup : List Op := [ |
| 66 | + .inst (.movdn 7), .inst (.movdn 7), |
| 67 | + .inst (.drop), .inst (.drop), .inst (.drop), |
| 68 | + .inst (.drop), .inst (.drop), .inst (.drop)] |
| 69 | + |
| 70 | +-- ============================================================================ |
| 71 | +-- Chunk correctness lemmas |
| 72 | +-- ============================================================================ |
| 73 | + |
| 74 | +set_option maxHeartbeats 1600000 in |
| 75 | +private theorem shr_k2_setup_correct |
| 76 | + (shift a0 a1 a2 a3 : Felt) (rest : List Felt) |
| 77 | + (mem locs : Nat → Felt) (adv : List Felt) |
| 78 | + (hshift_u32 : shift.isU32 = true) |
| 79 | + (hshift : shift.val ≤ 31) (hshift_pos : 0 < shift.val) : |
| 80 | + exec 33 ⟨shift :: a0 :: a1 :: a2 :: a3 :: rest, mem, locs, adv⟩ shr_k2_setup = |
| 81 | + some ⟨Felt.ofNat (2 ^ (32 - shift.val)) :: shift :: a0 :: a1 :: a2 :: a3 :: rest, |
| 82 | + mem, locs, adv⟩ := by |
| 83 | + unfold exec shr_k2_setup execWithEnv |
| 84 | + simp only [List.foldlM] |
| 85 | + rw [stepPush] |
| 86 | + miden_bind |
| 87 | + miden_dup |
| 88 | + rw [stepU32WrappingSubLocal (ha := felt32_isU32) (hb := hshift_u32)] |
| 89 | + miden_bind |
| 90 | + simp only [felt32_val] |
| 91 | + have hpow_input_le63 : (Felt.ofNat (u32OverflowingSub 32 shift.val).2).val ≤ 63 := by |
| 92 | + rw [shift_sub32_val shift hshift]; omega |
| 93 | + rw [stepPow2 (ha := hpow_input_le63)] |
| 94 | + miden_bind |
| 95 | + have hpow_eq : |
| 96 | + Felt.ofNat (2 ^ (Felt.ofNat (u32OverflowingSub 32 shift.val).2).val) = |
| 97 | + Felt.ofNat (2 ^ (32 - shift.val)) := by |
| 98 | + congr 1; congr 1; exact shift_sub32_val shift hshift |
| 99 | + rw [hpow_eq]; rfl |
| 100 | + |
| 101 | +set_option maxHeartbeats 2000000 in |
| 102 | +private theorem shr_k2_compute_correct |
| 103 | + (shift a0 a1 a2 a3 : Felt) (rest : List Felt) |
| 104 | + (mem locs : Nat → Felt) (adv : List Felt) |
| 105 | + (hshift_u32 : shift.isU32 = true) |
| 106 | + (ha2_u32 : a2.isU32 = true) (ha3_u32 : a3.isU32 = true) |
| 107 | + (hshift : shift.val ≤ 31) (hshift_pos : 0 < shift.val) : |
| 108 | + let pow := Felt.ofNat (2 ^ (32 - shift.val)) |
| 109 | + exec 33 |
| 110 | + ⟨pow :: shift :: a0 :: a1 :: a2 :: a3 :: rest, mem, locs, adv⟩ |
| 111 | + shr_k2_compute = |
| 112 | + some ⟨Felt.ofNat ((a2.val / 2 ^ shift.val) ||| ((a3.val * 2 ^ (32 - shift.val)) % 2 ^ 32)) :: |
| 113 | + Felt.ofNat (a3.val / 2 ^ shift.val) :: |
| 114 | + pow :: shift :: a0 :: a1 :: a2 :: a3 :: rest, |
| 115 | + mem, locs, adv⟩ := by |
| 116 | + unfold exec shr_k2_compute execWithEnv |
| 117 | + simp only [List.foldlM] |
| 118 | + have hpow_u32 : (Felt.ofNat (2 ^ (32 - shift.val))).isU32 = true := |
| 119 | + pow32_sub_isU32 shift hshift_pos hshift |
| 120 | + have hpow_val : (Felt.ofNat (2 ^ (32 - shift.val))).val = 2 ^ (32 - shift.val) := |
| 121 | + pow32_sub_val shift hshift_pos hshift |
| 122 | + have ha3_shr_u32 : (Felt.ofNat (a3.val / 2 ^ shift.val)).isU32 = true := |
| 123 | + u32Shr_result_isU32 a3 shift ha3_u32 |
| 124 | + have ha2_shr_u32 : (Felt.ofNat (a2.val / 2 ^ shift.val)).isU32 = true := |
| 125 | + u32Shr_result_isU32 a2 shift ha2_u32 |
| 126 | + miden_dup |
| 127 | + miden_dup |
| 128 | + rw [stepU32ShrLocal (ha := ha3_u32) (hb := hshift_u32) (hshift := hshift)] |
| 129 | + miden_bind |
| 130 | + miden_dup |
| 131 | + miden_dup |
| 132 | + rw [stepU32ShrLocal (ha := ha2_u32) (hb := hshift_u32) (hshift := hshift)] |
| 133 | + miden_bind |
| 134 | + miden_dup |
| 135 | + miden_dup |
| 136 | + rw [stepU32WidenMul |
| 137 | + (a := a3) (b := Felt.ofNat (2 ^ (32 - shift.val))) |
| 138 | + (ha := ha3_u32) (hb := hpow_u32)] |
| 139 | + miden_bind |
| 140 | + rw [hpow_val] |
| 141 | + miden_swap |
| 142 | + rw [stepDrop] |
| 143 | + miden_bind |
| 144 | + rw [stepU32Or (ha := ha2_shr_u32) (hb := u32_mod_isU32 _)] |
| 145 | + miden_bind |
| 146 | + rw [felt_ofNat_val_lt _ (lt_of_le_of_lt (Nat.div_le_self _ _) (felt_val_lt_prime a2))] |
| 147 | + rw [felt_ofNat_val_lt _ (u32_mod_lt_prime _)] |
| 148 | + dsimp only [bind, Bind.bind, Option.bind, pure, Pure.pure] |
| 149 | + |
| 150 | +set_option maxHeartbeats 2000000 in |
| 151 | +private theorem shr_k2_cleanup_correct |
| 152 | + (r0 r1 pow shift a0 a1 a2 a3 : Felt) (rest : List Felt) |
| 153 | + (mem locs : Nat → Felt) (adv : List Felt) : |
| 154 | + exec 33 ⟨r0 :: r1 :: pow :: shift :: a0 :: a1 :: a2 :: a3 :: rest, mem, locs, adv⟩ |
| 155 | + shr_k2_cleanup = |
| 156 | + some ⟨r0 :: r1 :: rest, mem, locs, adv⟩ := by |
| 157 | + unfold exec shr_k2_cleanup execWithEnv |
| 158 | + simp only [List.foldlM] |
| 159 | + miden_movdn |
| 160 | + miden_movdn |
| 161 | + rw [stepDrop] |
| 162 | + miden_bind |
| 163 | + rw [stepDrop] |
| 164 | + miden_bind |
| 165 | + rw [stepDrop] |
| 166 | + miden_bind |
| 167 | + rw [stepDrop] |
| 168 | + miden_bind |
| 169 | + rw [stepDrop] |
| 170 | + miden_bind |
| 171 | + rw [stepDrop] |
| 172 | + dsimp only [bind, Bind.bind, Option.bind, pure, Pure.pure] |
| 173 | + |
| 174 | +-- ============================================================================ |
| 175 | +-- Nonzero path: compose chunks via exec_append |
| 176 | +-- ============================================================================ |
| 177 | + |
| 178 | +set_option maxHeartbeats 2000000 in |
| 179 | +private theorem shr_k2_nonzero |
| 180 | + (shift a0 a1 a2 a3 : Felt) (rest : List Felt) |
| 181 | + (mem locs : Nat → Felt) (adv : List Felt) |
| 182 | + (hshift_u32 : shift.isU32 = true) |
| 183 | + (ha2_u32 : a2.isU32 = true) (ha3_u32 : a3.isU32 = true) |
| 184 | + (hshift : shift.val ≤ 31) (hshift_pos : 0 < shift.val) : |
| 185 | + exec 33 ⟨shift :: a0 :: a1 :: a2 :: a3 :: rest, mem, locs, adv⟩ |
| 186 | + (shr_k2_setup ++ (shr_k2_compute ++ shr_k2_cleanup)) = |
| 187 | + some ⟨Felt.ofNat ((a2.val / 2 ^ shift.val) ||| ((a3.val * 2 ^ (32 - shift.val)) % 2 ^ 32)) :: |
| 188 | + Felt.ofNat (a3.val / 2 ^ shift.val) :: rest, mem, locs, adv⟩ := by |
| 189 | + rw [exec_append] |
| 190 | + rw [shr_k2_setup_correct shift a0 a1 a2 a3 rest mem locs adv hshift_u32 hshift hshift_pos] |
| 191 | + simp only [bind, Bind.bind, Option.bind] |
| 192 | + rw [exec_append] |
| 193 | + rw [shr_k2_compute_correct shift a0 a1 a2 a3 rest mem locs adv hshift_u32 ha2_u32 ha3_u32 |
| 194 | + hshift hshift_pos] |
| 195 | + simp only [bind, Bind.bind, Option.bind] |
| 196 | + exact shr_k2_cleanup_correct |
| 197 | + (Felt.ofNat ((a2.val / 2 ^ shift.val) ||| ((a3.val * 2 ^ (32 - shift.val)) % 2 ^ 32))) |
| 198 | + (Felt.ofNat (a3.val / 2 ^ shift.val)) |
| 199 | + (Felt.ofNat (2 ^ (32 - shift.val))) shift a0 a1 a2 a3 rest mem locs adv |
| 200 | + |
| 201 | +-- ============================================================================ |
| 202 | +-- Main theorem |
| 203 | +-- ============================================================================ |
| 204 | + |
| 205 | +set_option maxHeartbeats 4000000 in |
| 206 | +/-- `u128::shr_k2` right-shifts a 128-bit value by two whole limbs plus an |
| 207 | + additional bit count smaller than one limb. |
| 208 | + Input stack: [shift, a0, a1, a2, a3] ++ rest |
| 209 | + Output stack: [r0, r1] ++ rest |
| 210 | + where `r0` and `r1` are the low-to-high u32 limbs of `(a3:a2:a1:a0) >> (64 + shift)`. -/ |
| 211 | +theorem u128_shr_k2_correct |
| 212 | + (shift a0 a1 a2 a3 : Felt) (rest : List Felt) (s : MidenState) |
| 213 | + (hs : s.stack = shift :: a0 :: a1 :: a2 :: a3 :: rest) |
| 214 | + (hshift_u32 : shift.isU32 = true) |
| 215 | + (ha2_u32 : a2.isU32 = true) (ha3_u32 : a3.isU32 = true) |
| 216 | + (hshift : shift.val ≤ 31) : |
| 217 | + let pow := 2 ^ (32 - shift.val) |
| 218 | + exec 34 s Miden.Core.U128.shr_k2 = |
| 219 | + some (s.withStack ( |
| 220 | + if shift == (0 : Felt) then |
| 221 | + a2 :: a3 :: rest |
| 222 | + else |
| 223 | + Felt.ofNat ((a2.val / 2 ^ shift.val) ||| ((a3.val * pow) % 4294967296)) :: |
| 224 | + Felt.ofNat (a3.val / 2 ^ shift.val) :: rest)) := by |
| 225 | + obtain ⟨stk, mem, locs, adv⟩ := s |
| 226 | + simp only [MidenState.withStack] at hs ⊢ |
| 227 | + subst hs |
| 228 | + unfold exec Miden.Core.U128.shr_k2 execWithEnv |
| 229 | + simp only [List.foldlM] |
| 230 | + miden_dup |
| 231 | + rw [stepEqImm] |
| 232 | + miden_bind |
| 233 | + by_cases hzero : shift == (0 : Felt) |
| 234 | + · simp [hzero, MidenState.withStack] |
| 235 | + unfold execWithEnv |
| 236 | + simp only [List.foldlM] |
| 237 | + rw [stepDrop] |
| 238 | + miden_bind |
| 239 | + rw [stepDrop] |
| 240 | + miden_bind |
| 241 | + rw [stepDrop] |
| 242 | + · simp [hzero, MidenState.withStack] |
| 243 | + have hshift_ne_zero : shift.val ≠ 0 := by |
| 244 | + intro hval |
| 245 | + apply hzero |
| 246 | + apply beq_iff_eq.mpr |
| 247 | + exact (ZMod.val_eq_zero shift).mp hval |
| 248 | + have hshift_pos : 0 < shift.val := by omega |
| 249 | + have h := shr_k2_nonzero shift a0 a1 a2 a3 rest mem locs adv |
| 250 | + hshift_u32 ha2_u32 ha3_u32 hshift hshift_pos |
| 251 | + simp only [exec, shr_k2_setup, shr_k2_compute, shr_k2_cleanup, |
| 252 | + List.nil_append, List.cons_append] at h |
| 253 | + rw [h]; rfl |
| 254 | + |
| 255 | +end MidenLean.Proofs |
0 commit comments