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"):
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>
List.length in Init.Prelude
List.lengthTR in Init.Prelude
List.get in Init.Prelude
List.get!Internal in Init.GetElem
List.head in Init.Data.List.Basic
List.head! in Init.Data.List.BasicAux
List.tail in Init.Data.List.Basic
List.tail! in Init.Data.List.BasicAux
List.cons in Init.Prelude
List.consFixedLengthDigits in Mathlib.Data.Nat.Digits.Lemmas
List.append in Init.Prelude
List.appendTR in Init.Data.List.Basic
List.reverse in Init.Data.List.Basic
List.reverseAux in Init.Data.List.Basic
List.map in Init.Prelude
List.mapA in Init.Data.List.Control
List.filter in Init.Data.List.Basic
List.filterAuxM in Init.Data.List.Control
List.foldl in Init.Prelude
List.foldlIdx in Batteries.Data.List.Basic
Nat.succ in Init.Prelude
Nat.succPNat in Mathlib.Data.PNat.Defs
Nat.pred in Init.Prelude
Nat.add in Init.Prelude
Nat.sub in Init.Prelude
Nat.subDigitChar in Init.Data.Repr
Nat.mul in Init.Prelude
Nat.multichoose in Mathlib.Data.Nat.Choose.Basic
Nat.div in Init.Prelude
Nat.div.go in Init.Prelude
Nat.mod in Init.Prelude
Nat.mod.inductionOn in Init.Data.Nat.Div.Basic
Nat.min in Init.Data.Nat.Basic
Nat.minFac in Mathlib.Data.Nat.Prime.Defs
Nat.max in Init.Data.Nat.Basic
Nat.maxPowDiv in Mathlib.Data.Nat.MaxPowDiv
Nat.pow in Init.Prelude
Nat.lt in Init.Prelude
Nat.lt_iff_le_and_not_ge in Init.Data.Nat.Basic
Nat.le in Init.Prelude
Bool.and in Init.Prelude
Bool.and' in Init.Data.Bool
Bool.or in Init.Prelude
Bool.or' in Init.Data.Bool
Bool.not in Init.Prelude
Bool.not' in Init.Data.Bool
Bool.xor in Init.Data.Bool
Bool.xor3 in Mathlib.Data.Bool.Basic
Int.add in Init.Data.Int.Basic
Int.add_of_le in Init.Data.Int.Cooper
Int.sub in Init.Data.Int.Basic
Int.subNatNat in Init.Data.Int.Basic
Int.mul in Init.Data.Int.Basic
Int.multichoose in Mathlib.RingTheory.Binomial
Int.div2 in Mathlib.Data.Int.Bitwise
Int.divExact in Init.Data.Int.DivMod.Basic
Int.neg in Init.Data.Int.Basic
Int.negInduction in Mathlib.Data.Int.Init
Int.toNat in Init.Data.Int.Basic
Int.toNat? in Init.Data.Int.Basic
Int.ofNat in Init.Data.Int.Basic
Int.ofNatHom in Mathlib.Data.Int.Cast.Lemmas
Int.natAbs in Init.Data.Int.Basic
Int.natAbsHom in Mathlib.Data.Int.NatAbs
</used_lib_defs>

<lib_lemmas>
List.length_nil in Init.Data.List.Basic
List.length_cons in Init.Data.List.Basic
List.length_append in Init.Data.List.Basic
List.length_reverse in Init.Data.List.Lemmas
List.length_map in Init.Data.List.Lemmas
List.length_mapAccumr in Mathlib.Data.List.Defs
List.length_filterMap_eq_countP in Init.Data.List.Count
List.length_filterMap_le in Init.Data.List.Lemmas
List.length_take in Init.Data.List.Nat.TakeDrop
List.length_take_le in Init.Data.List.Nat.TakeDrop
List.length_drop in Init.Data.List.TakeDrop
List.length_dropLast in Init.Data.List.Lemmas
List.get_cons_succ in Init.Data.List.Lemmas
List.get_cons_succ' in Init.Data.List.Lemmas
List.get_cons_zero in Init.Data.List.Lemmas
List.mem_cons in Init.Data.List.Lemmas
List.mem_cons_of_mem in Init.Data.List.Lemmas
List.mem_cons_self in Init.Data.List.Lemmas
List.mem_append in Init.Data.List.Lemmas
List.mem_append_cons_self in Init.Data.List.Lemmas
List.mem_map in Init.Data.List.Lemmas
List.mem_mapFinIdx in Init.Data.List.MapIdx
List.mem_filter in Init.Data.List.Lemmas
List.mem_filterMap in Init.Data.List.Lemmas
List.filter_cons in Init.Data.List.Lemmas
List.filter_cons_of_neg in Init.Data.List.Lemmas
List.map_cons in Init.Data.List.Basic
List.map_const in Init.Data.List.Lemmas
List.reverse_cons in Init.Data.List.Basic
List.reverse_cons' in Mathlib.Data.List.Basic
Nat.add_comm in Init.Data.Nat.Basic
Nat.add_assoc in Init.Data.Nat.Basic
Nat.add_zero in Init.Core
Nat.zero_add in Init.Data.Nat.Basic
Nat.sub_self in Init.Data.Nat.Basic
Nat.sub_self_add in Init.Data.Nat.Basic
Nat.sub_zero in Init.Data.Nat.Basic
Nat.mul_comm in Init.Data.Nat.Basic
Nat.div_self in Init.Data.Nat.Div.Basic
Nat.mod_self in Init.Data.Nat.Div.Basic
Nat.lt_succ in Init.Data.Nat.Basic
Nat.lt_succ_floor in Mathlib.Algebra.Order.Floor.Semiring
Nat.lt_succ_iff in Init.Data.Nat.Basic
Nat.succ_lt_of_lt_pred in Init.Data.Nat.Basic
Nat.succ_lt_succ in Init.Data.Nat.Basic
Nat.le_succ in Init.Prelude
Nat.le_succ_iff in Init.Data.Nat.Lemmas
Nat.lt_of_lt_floor in Mathlib.Algebra.Order.Floor.Semiring
Nat.lt_of_lt_of_eq in Init.Data.Nat.Basic
Nat.lt_of_le_of_lt in Init.Prelude
Nat.lt_of_le_of_ne in Init.Prelude
Nat.le_of_lt in Init.Data.Nat.Basic
Nat.le_of_lt_add_of_dvd in Init.Data.Nat.Lemmas
Nat.le_trans in Init.Prelude
Nat.lt_trans in Init.Prelude
Nat.not_lt in Init.Data.Nat.Basic
Nat.not_lt_eq in Init.Data.Nat.Basic
Nat.lt_irrefl in Init.Prelude
Nat.le_refl in Init.Prelude
Bool.and_true in Init.SimpLemmas
Bool.and_false in Init.SimpLemmas
Bool.or_true in Init.SimpLemmas
Bool.or_false in Init.SimpLemmas
Bool.not_not in Init.SimpLemmas
Bool.not_not_eq in Init.SimpLemmas
Bool.decide_true in Mathlib.Data.Bool.Basic
Bool.decide_true_eq in Init.Data.Bool
Bool.decide_false in Mathlib.Data.Bool.Basic
Bool.decide_false_eq in Init.Data.Bool
Int.add_comm in Init.Data.Int.Lemmas
Int.add_assoc in Init.Data.Int.Lemmas
Int.sub_self in Init.Data.Int.Lemmas
Int.neg_neg in Init.Data.Int.Lemmas
Int.neg_negSucc in Init.Data.Int.Lemmas
Int.toNat_of_nonneg in Init.Data.Int.Order
Int.toNat_of_nonpos in Init.Data.Int.Lemmas
Int.ofNat_lt in Init.Data.Int.Order
Int.ofNat_lt_ofNat_of_lt in Mathlib.Data.Int.Order.Basic
Int.ofNat_le in Init.Data.Int.Order
Int.ofNat_le_ofNat_of_le in Mathlib.Data.Int.Order.Basic
Int.lt_of_add_lt_add_left in Init.Data.Int.Order
Int.lt_of_add_lt_add_right in Init.Data.Int.Order
Int.le_of_add_le_add_left in Init.Data.Int.Order
Int.le_of_add_le_add_right in Init.Data.Int.Order
decide_eq_true in Init.Prelude
decide_eq_false in Init.Prelude
of_decide_eq_true in Init.Prelude
of_decide_eq_false in Init.Prelude
eq_comm in Init.Core
ne_comm in Init.Core
Iff.intro in Init.Core
Iff.mp in Init.Core
Iff.mpr in Init.Core
And.intro in Init.Prelude
And.left in Init.Prelude
And.right in Init.Prelude
Or.inl in Init.Prelude
Or.inr in Init.Prelude
Classical.em in Init.Classical
</lib_lemmas>

<local_ctx>
import Mathlib

@[reducible, simp]
def minimumRightShifts_precond (nums : List Int) : Prop :=
  List.Nodup nums

def minimumRightShifts (nums : List Int) (h_precond : minimumRightShifts_precond (nums)) : Int :=
  let n := nums.length
  -- base cases: empty or single element list is already sorted
  if n <= 1 then 0 else

  -- local helper function to check if a list is sorted in ascending order
  let rec isSortedAux (l : List Int) : Bool :=
    match l with
    | [] => true       -- empty list is sorted
    | [_] => true      -- single element list is sorted
    | x :: y :: xs => if x <= y then isSortedAux (y :: xs) else false -- check pairwise

  -- check if the input list is already sorted
  if isSortedAux nums then 0 else

  -- local helper function to perform a single right shift
  -- assume the list `l` is non-empty based on the initial n > 1 check
  let rightShiftOnce (l : List Int) : List Int :=
     match l.reverse with
     | [] => [] -- should not happen for n > 1
     | last :: revInit => last :: revInit.reverse -- `last` is the original last element

  -- recursive function to check subsequent shifts
  -- `shifts_count` is the number of shifts already performed to get `current_list`
  -- we are checking if `current_list` is sorted
  let rec checkShifts (shifts_count : Nat) (current_list : List Int) : Int :=
    -- base case: stop recursion if we've checked n-1 shifts (count goes from 1 to n)
    -- the original list (0 shifts) has already been checked
    if shifts_count >= n then -1
    else
      -- check if the current state is sorted
      if isSortedAux current_list then
        (shifts_count : Int) -- found it after 'shifts_count' shifts
      else
        -- recursion: increment shift count, apply next shift
        checkShifts (shifts_count + 1) (rightShiftOnce current_list)
  -- specify the decreasing measure for the termination checker: n - shifts_count
  termination_by n - shifts_count

  -- start the checking process by performing the first shift and checking subsequent states
  -- the initial list (0 shifts) hass already been checked and is not sorted
  checkShifts 1 (rightShiftOnce nums) -- start checking from the state after 1 shift

@[reducible, simp]
def minimumRightShifts_postcond (nums : List Int) (result: Int) (h_precond : minimumRightShifts_precond (nums)) : Prop :=
  let n := nums.length

  let isSorted (l : List Int) := List.Pairwise (· ≤ ·) l
  let rightShift (k : Nat) (l : List Int) := l.rotateRight k

  -- specification logic based on the result value
  if n <= 1 then result = 0 else -- specification for base cases

  -- case 1: a non-negative result means a solution was found
  (result ≥ 0 ∧
   -- result corresponds to a valid shift count result < n
   result < n ∧
   -- applying result shifts results in a sorted list
   isSorted (rightShift result.toNat nums) ∧
   -- result is the minimum such non-negative shift count
   (List.range result.toNat |>.all (fun j => ¬ isSorted (rightShift j nums)))
  ) ∨

  -- case 2: result is -1 means no solution exists within n shifts
  (result = -1 ∧
   -- for all possible shift counts k from 0 to n-1, the resulting list is not sorted
   (List.range n |>.all (fun k => ¬ isSorted (rightShift k nums)))
  )
</local_ctx>

<target_theorem>
theorem minimumRightShifts_spec_satisfied (nums: List Int) (h_precond : minimumRightShifts_precond (nums)) :
    minimumRightShifts_postcond (nums) (minimumRightShifts (nums) h_precond) h_precond
</target_theorem>