You are an expert Lean4 and formal verification agent proving a Lean4 theorem. Your output **must** strictly adhere to the specified format.

### Input Information
You will receive the following components, relevant to the target theorem:
* <used_lib_defs>...</used_lib_defs>: Imported library definitions directly used by the theorem.
* <used_repo_defs>...</used_repo_defs>: Definitions from the local project (but outside the current file), directly or transitively used by the theorem.
* <lib_lemmas>...</lib_lemmas>: Imported library lemmas/theorems that may assist the proof.
* <repo_lemmas>...</repo_lemmas>: Lemmas/theorems defined in the local project outside this file.
* <local_ctx>...</local_ctx>: The local file context (imports, namespaces, definitions, comments, etc.) from the top of the file up to—but not including—the target theorem or any previously defined local lemmas/theorems.
* <target_theorem>...</target_theorem>: The target theorem statement, ending with `:=`.

**Notes**
1. Definitions include their full bodies, but for definitions whose bodies contain proofs, the proof parts are replaced by `admit` (i.e., admit /- proof elided -/), while the rest of the definition is kept unchanged; lemmas include only their statements.
2. You may freely reference any definition or lemma contained in these tags without redefining them.
3. The tags for used definitions and helpful lemmas (used_lib_defs, used_repo_defs, lib_lemmas, repo_lemmas) may be omitted if empty.

### Output Format & Requirements
Your output MUST contain a complete, syntactically correct Lean 4 proof for the target theorem.

**Strict Output Structure:**
<reasoning> 
...
</reasoning> 

<lean4_invented_lemmas>
-- each lemma is a full Lean 4 declaration with a complete proof
...
</lean4_invented_lemmas>

<lean4_proof>
-- Lean 4 proof body of the target theorem
...
</lean4_proof>

**Requirements:**
1. <reasoning> and <lean4_invented_lemmas> are OPTIONAL. Include them only if they aid proof construction.
2. <lean4_proof> MUST contain only the proof body (tactics/terms), starting by "by", NOT the theorem declaration.
3. <lean4_invented_lemmas> (if present) MUST contain syntactically correct lemma declarations that type checks AND complete proof bodies. Defining a lemma using `axiom` is NOT allowed.
4. <reasoning> contains high-level proof planning.
5. If you create a lemma under <lean4_invented_lemmas> whose name is namespaced (e.g., X.Y.Z), you MUST always refer to it using its fully qualified name. Do NOT rely on shortened names. For example, write rw [X.Y.Z], not rw [Z] for invented lemmas.
6. All Lean 4 code MUST be syntactically correct. No `sorry`, `admit`, or non-Lean text. Comments allowed using `--` or `/- ... -/`.
7. Follow the tags and structure EXACTLY. No extra text before, between, or after the sections.
8. Do NOT wrap invented lemmas in a `mutual` block unless they have genuine mutual recursion. Declare each lemma independently.

### Examples are provided here (example input are under "User Prompt", and example assistant response are under "Agent Response"):
Example 1 ------------------- 
User Prompt:
<used_lib_defs>
structure BitVec (w : Nat) where
  /-- Construct a `BitVec w` from a number less than `2^w`.
  O(1), because we use `Fin` as the internal representation of a bitvector. -/
  ofFin ::

  /-- Interpret a bitvector as a number less than `2^w`.
  O(1), because we use `Fin` as the internal representation of a bitvector. -/
  toFin : Fin (hPow 2 w)
</used_lib_defs>

<repo_lemmas>
theorem List.getElem_append_left{α : Type u_1} {l₁ l₂ : List α} {i : Nat} (hn : i < l₁.length) :\n(l₁ ++ l₂)[i] = l₁[i]
</repo_lemmas>

<local_ctx>
import Mathlib.Computability.NFA
import Mathlib.Data.FinEnum
import Mathlib.Data.Rel
import Mathlib.Data.Vector.Basic
import Blase.AutoStructs.ForLean

open Set
open Mathlib
open SetRel

-- this is better because card is defeq to n
instance (α : Type) : Inter (Language α) := ⟨Set.inter⟩
instance (α : Type) : Union (Language α) := ⟨Set.union⟩

/--
The set of `n`-tuples of bit vectors of an arbitrary width.
-/
structure BitVecs (n : Nat) where
  w : Nat
  bvs : List.Vector (BitVec w) n

def BitVecs.cast (bvs : BitVecs n) (h : n = n') : BitVecs n' :=
  { w := bvs.w, bvs := h ▸ bvs.bvs }

abbrev BitVecs.empty : BitVecs n := ⟨0, List.Vector.replicate n .nil⟩
abbrev BitVecs.singleton {w : Nat} (bv : BitVec w) : BitVecs 1 := ⟨w, bv ::ᵥ .nil⟩
abbrev BitVecs.pair {w : Nat} (bv1 bv2 : BitVec w) : BitVecs 2 := ⟨w, bv1 ::ᵥ bv2 ::ᵥ .nil⟩

/--
The set of `n`-tuples of bit vectors of an arbitrary width, encoded as a list of
bit vectors of width `n`. The width of the encoded bit vectors is the length of
the list.
-/
abbrev BitVecs' (n : Nat) := List (BitVec n)

@[simps]
def dec (bvs' : BitVecs' n) : BitVecs n where
  w := bvs'.length
  bvs := List.Vector.ofFn fun k => BitVec.ofFn fun i => bvs'[i].getLsbD k

@[simp]
</local_ctx>

<target_theorem>
lemma dec_snoc n (bvs' : BitVecs' n) (a : BitVec n) :
  dec (bvs' ++ [a]) =
    { w := bvs'.length + 1
      bvs := List.Vector.ofFn fun k =>
        BitVec.cons (a.getLsbD k) ((dec bvs').bvs.get k) } :=
</target_theorem>

Agent Response:
<reasoning> 
The goal is to understand what `dec` does after appending one more bitvector `a` to `bvs'`. By definition, `dec` builds a width `= length` and a vector of bitvectors whose `i`-th bit at position `k` is `bvs'[i].getLsbD k`. So for `bvs' ++ [a]`, we expect the width to increase by 1 and each resulting bitvector to be `BitVec.cons` of the new bit coming from `a`.

First, I use `ext` twice: once on the record to focus on the `bvs` field, and once on the resulting bitvector. This reduces everything to a pointwise statement:
for fixed `k` and `i` with `i < bvs'.length + 1`,
the bit `(bvs' ++ [a])[i].getLsbD k` should match the bit at index `i` of `BitVec.cons (a.getLsbD k) ((dec bvs').bvs.get k)`.

At this point I need a clean description of indexing into `BitVec.cons`: if we have `x : BitVec n`, then `(cons b x)[i]` should give `b` at the new “last” index `n` and `x[i]` otherwise. That motivates an auxiliary lemma `getElem_cons` expressing exactly this behavior, proved via the `testBit`/`toNat` characterization and a case split on `i < n`, `i = n`, `i > n`.

Using `getElem_cons`, the RHS becomes an `if` on whether `i = bvs'.length`. So I split into cases:
- If `i = bvs'.length`, then `(bvs' ++ [a])[i]` is the appended `a`, so both sides reduce to `a.getLsbD k`.
- If `i ≠ bvs'.length`, the bound `i < bvs'.length + 1` forces `i < bvs'.length`. Then `(bvs' ++ [a])[i] = bvs'[i]` by `List.getElem_append_left`, and the RHS takes the “else” branch and likewise gives the bit from `bvs'[i]`. In both subcases, `simp` (plus the definition of `dec`) finishes.

So the whole proof is: `ext` to bit level, unfold `dec` with `simp_all`, rewrite with `getElem_cons`, split on the boundary index `i = length bvs'`, and in the “< length” branch use `List.getElem_append_left` and bounds to line up both sides.
</reasoning> 

<lean4_invented_lemmas>
@[grind =]
theorem getElem_cons {b : Bool} {n} {x : BitVec n} {i : Nat} (h : i < n + 1) :
    (cons b x)[i] = if h : i = n then b else x[i] := by
  simp only [BitVec.getElem_eq_testBit_toNat, BitVec.toNat_cons, Nat.testBit_or]
  rw [Nat.testBit_shiftLeft]
  rcases Nat.lt_trichotomy i n with i_lt_n | i_eq_n | n_lt_i
  · have p1 : ¬(n ≤ i) := by omega
    have p2 : i ≠ n := by omega
    simp [p1, p2]
  · simp only [i_eq_n, ge_iff_le, Nat.le_refl, decide_true, Nat.sub_self, Nat.testBit_zero,
    Bool.true_and, BitVec.testBit_toNat, BitVec.getLsbD_of_ge, Bool.or_false]
    cases b <;> trivial
  · have p1 : i ≠ n := by omega
    have p2 : i - n ≠ 0 := by omega
    simp [p1, p2, Nat.testBit_bool_toNat]
</lean4_invented_lemmas>

<lean4_proof>
by
  ext k i hi <;> simp_all
  rw [getElem_cons]
  split
  next heq => simp_all
  next h =>
    have hlt : i < List.length bvs' := by simp at hi; omega
    rw [List.getElem_append_left hlt]
    simp
</lean4_proof>

### Your task:
<used_lib_defs>
Sierra.UInt128 in Aegis.Types
SierraBool.toBool in Aegis.Aux.Bool
Bool in Init.Prelude
Bool.toSierraBool in Aegis.Aux.Bool
Int in Init.Data.Int.Basic
List in Init.Prelude
Nat in Init.Prelude
Prod in Init.Prelude
Prod.mk in Init.Prelude
Sierra.U128_MOD in Aegis.Types
Sierra.U256_MOD in CorelibVerification.Aux.UInt256
Sierra.aegis_prove in Aegis.Commands
Sum in Init.Core
Sum.inl in Init.Core
Sum.inr in Init.Core
ZMod in Mathlib.Data.ZMod.Defs
ZMod.ndiv in Aegis.Aux.ZMod.DivMod
ZMod.val in Mathlib.Data.ZMod.Basic
sierra_simp' in CorelibVerification.Corelib.Integer
Unit in Init.Prelude
Bool.xor in Init.Data.Bool
</used_lib_defs>

<used_repo_defs>
def RAY_SCALE : ℕ := 1000000000000000000000000000

protected def add : Ray := r.toZMod + r'.toZMod

protected def toZMod : UInt128 := r

def Ray : Type := UInt128

protected def toRat : ℚ := w.toZMod.val / WAD_SCALE

def WAD_SCALE : ℕ := 1000000000000000000

protected def toRat : ℚ := r.toZMod.val / RAY_SCALE

def Wad.toRay (w : Wad) : Ray := w.toZMod * (Ray.DIFF : UInt128)

def DIFF : ℕ := 1000000000

protected def toZMod : UInt128 := w

def toRat : ℚ := if SierraBool.toBool w.2 then -(Ray.toRat w.1) else Ray.toRat w.1

protected def sub : Ray := r.toZMod - r'.toZMod

protected def ofZMod (a : UInt128) : Ray := a

def Wad.MAX_CONVERTIBLE_WAD : ℕ := 340282366920938463463374607431

protected def ofZMod (a : UInt128) : Wad := a

def Wad : Type := UInt128

def toWad : Wad := r.toZMod.ndiv DIFF

def toRat : ℚ := if w.2 then -(Wad.toRat w.1) else Wad.toRat w.1

def SignedWad := UInt128 × (Unit ⊕ Unit)

def SignedRay := UInt128 × (Unit ⊕ Unit)

protected def mul : Ray := (r.toZMod.val * r'.toZMod.val / RAY_SCALE  : UInt128)

protected def mul : Wad := (w.toZMod.val * w'.toZMod.val / WAD_SCALE : UInt128)

protected def div : Ray := (r.toZMod.val * RAY_SCALE / r'.toZMod.val : UInt128)

protected def div : Wad := (w.toZMod.val * WAD_SCALE / w'.toZMod.val : UInt128)
</used_repo_defs>

<lib_lemmas>
Aesop.BuiltinRules.not_intro in Aesop.BuiltinRules
Bool.false_eq_true in Init.Data.Bool
Int.cast_ofNat in Mathlib.Data.Int.Cast.Basic
Int.ofNat_eq_coe in Init.Data.Int.Basic
List.nil_append in Init.Data.List.Basic
Nat.cast_ofNat in Mathlib.Data.Nat.Cast.Defs
Nat.div_le_div_left in Init.Data.Nat.Div.Lemmas
Nat.div_le_div_right in Init.Data.Nat.Lemmas
Nat.eq_zero_of_mul_lt_right in CorelibVerification.Aux.ZMod
Nat.lt_of_add_lt in CorelibVerification.Aux.ZMod
Nat.mul_le_mul_right in Init.Data.Nat.Basic
Nat.pos_of_ne_zero in Init.Data.Nat.Basic
Sierra.U256_MOD_div in CorelibVerification.Aux.UInt256
Sum.isRight_inl in Init.Data.Sum.Basic
Sum.isRight_inr in Init.Data.Sum.Basic
ZMod.cast_rat_eq_zero_iff in CorelibVerification.Aux.ZMod
ZMod.intCast_zmod_eq_zero_iff_dvd in Mathlib.Data.ZMod.Basic
ZMod.natCast_zmod_val in Mathlib.Data.ZMod.Basic
ZMod.val_eq_zero in Mathlib.Data.ZMod.Basic
ZMod.val_injective in Mathlib.Data.ZMod.Basic
ZMod.val_mul_val_eq_hmul in CorelibVerification.Aux.ZMod
ZMod.val_zero in Mathlib.Data.ZMod.Basic
add_zero in Mathlib.Algebra.Group.Defs
and_false in Init.SimpLemmas
and_true in Init.SimpLemmas
exists_and_left in Init.PropLemmas
exists_and_right in Init.PropLemmas
exists_const in Init.PropLemmas
exists_eq_left in Init.PropLemmas
exists_eq_left' in Init.PropLemmas
exists_false in Init.PropLemmas
false_and in Init.SimpLemmas
false_or in Init.SimpLemmas
le_of_lt in Mathlib.Order.Defs.PartialOrder
le_trans in Mathlib.Order.Defs.PartialOrder
mul_comm in Mathlib.Algebra.Group.Defs
ne_eq in Init.SimpLemmas
not_true in Init.Core
or_false in Init.SimpLemmas
true_and in Init.SimpLemmas
zero_add in Mathlib.Algebra.Group.Defs
</lib_lemmas>

<repo_lemmas>
theorem toRat_lt_toRat_of_val_lt_val (h : @ZMod.val U128_MOD r < @ZMod.val U128_MOD r') :
    r.toRat < r'.toRat

theorem RAY_SCALE_rat_pos : 0 < (RAY_SCALE : ℚ)

theorem toRat_lt_toRat_of_val_lt_val (h : @ZMod.val U128_MOD w < @ZMod.val U128_MOD w') :
    w.toRat < w'.toRat

theorem WAD_SCALE_rat_pos : 0 < (WAD_SCALE : ℚ)

theorem toRat_le_toRat_of_val_le_val (h : @ZMod.val U128_MOD r ≤ @ZMod.val U128_MOD r') :
    r.toRat ≤ r'.toRat

theorem toRat_le_toRat_of_val_le_val (h : @ZMod.val U128_MOD w ≤ @ZMod.val U128_MOD w') :
    w.toRat ≤ w'.toRat

theorem zero_def : (0 : SignedWad) = (0, false.toSierraBool)

theorem zero_def : (0 : SignedRay) = (0, false.toSierraBool)

protected theorem mul_def :
    r * r' = (r.toZMod.val * r'.toZMod.val / RAY_SCALE  : UInt128)

theorem mul_def :
    w₁ * w₂ = ⟨Ray.mul w₁.1 w₂.1, Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

protected theorem mul_def :
    w * w' = (w.toZMod.val * w'.toZMod.val / WAD_SCALE  : UInt128)

protected theorem div_def :
    r / r' = (r.toZMod.val * RAY_SCALE / r'.toZMod.val : UInt128)

theorem mul_def :
    w₁ * w₂ = ⟨Wad.mul (w₁.1 : Wad) (w₂.1 : Wad), Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

protected theorem div_def :
    w / w' = (w.toZMod.val * WAD_SCALE / w'.toZMod.val : UInt128)

theorem div_def :
    w₁ / w₂ = ⟨Ray.div (w₁.1 : Ray) (w₂.1 : Ray), Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

theorem RAY_SCALE_pos : 0 < RAY_SCALE

theorem div_def :
    w₁ / w₂ = ⟨Wad.div (w₁.1 : Wad) (w₂.1 : Wad), Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩
</repo_lemmas>

<local_ctx>
import CorelibVerification

import WadrayVerification.Aux

import WadrayVerification.Load

open Sierra
</local_ctx>

<target_theorem>
theorem RAY_SCALE_val :
    (1000000000000000000000000000 : UInt128).val = 1000000000000000000000000000 :=
</target_theorem>

