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:
* <all_available_defs>: Definitions available from imported modules (either from the current Lean repository or from Mathlib)
* <all_available_lemmas>: Lemmas/theorems available that may assist the proof.
* <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 available definitions and lemmas (all_available_defs, all_available_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:
<all_available_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)
</all_available_defs>

<all_available_lemmas>
theorem List.getElem_append_left{α : Type u_1} {l₁ l₂ : List α} {i : Nat} (hn : i < l₁.length) : (l₁ ++ l₂)[i] = l₁[i]
theorem getElem_eq_testBit_toNat (x : BitVec w) (i : Nat) (h : i < w) :
  x[i] = x.toNat.testBit i := rfl
@[simp, grind =] theorem toNat_cons (b : Bool) (x : BitVec w) :
    (cons b x).toNat = (b.toNat <<< w) ||| x.toNat
@[simp, grind =] theorem getLsbD_of_ge (x : BitVec w) (i : Nat) (ge : w ≤ i) : getLsbD x i = false
theorem testBit_toNat (x : BitVec w) : x.toNat.testBit i = x.getLsbD i
</all_available_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:
<all_available_defs>
BitVec in Init.Prelude
NFA in Mathlib.Computability.NFA
DecidableEq in Init.Prelude
Fintype in Mathlib.Data.Fintype.Defs
BitVec.ofNat in Init.Prelude
BitVec.cons in Init.Data.BitVec.Basic
List in Init.Prelude
Repr in Init.Data.Repr
NFA.accept in Mathlib.Computability.NFA
NFA.start in Mathlib.Computability.NFA
NFA.step in Mathlib.Computability.NFA
FinEnum in Mathlib.Data.FinEnum
BitVec.ofFin in Init.Prelude
cmp in Mathlib.Data.Ordering.Basic
NFA.stepSet in Mathlib.Computability.NFA
BitVec.ult in Init.Data.BitVec.Basic
Nat in Init.Prelude
BitVec.sle in Init.Data.BitVec.Basic
BitVec.slt in Init.Data.BitVec.Basic

inductive RelationOrdering
| lt | le | gt | ge
deriving Repr, Fintype

def bv2 : BitVec 4 := BitVec.ofNat 4 1

def bv1 : BitVec 4 := BitVec.ofNat 4 5

instance instFinEnumBV : FinEnum (BitVec w) where
  card := 2^w
  equiv := {
    toFun := fun x => x.toFin
    invFun := fun x => BitVec.ofFin x
    left_inv := by admit /- proof elided -/

def slt_swap_and_max_logical_after := [llvm|
{
^0(%arg374 : i8, %arg375 : i8):
  %0 = llvm.mlir.constant(false) : i1
  "llvm.return"(%0) : (i1) -> ()
}
]

def autOfTermBinop (op : TermBinop) (m₁ : CNFA (n + 1)) (m₂ : CNFA (m + 1)) : CNFA ((n ⊔ m) + 1 ) :=
  let mop : CNFA 3 := op.termGadget
  let f₁ := liftMaxSuccSucc1 n m
  let m1' := m₁.lift f₁
  let f₂ := liftMaxSuccSucc2 n m
  let m2' := m₂.lift f₂
  let mop := mop.lift $ liftLast3 (max (FinEnum.card (Fin n)) (FinEnum.card (Fin m)))
  let m := CNFA.inter m1' m2' |> CNFA.inter mop
  let mfinal := m.proj (liftOp _)
  mfinal.minimize

def p_constmask_after := [llvm|
{
^0(%arg49 : i32, %arg50 : i32):
  %0 = llvm.mlir.constant(65280 : i32) : i32
  %1 = llvm.mlir.constant(-65281 : i32) : i32
  %2 = llvm.and %arg49, %0 : i32
  %3 = llvm.and %arg50, %1 : i32
  %4 = llvm.or disjoint %2, %3 : i32
  "llvm.return"(%4) : (i32) -> ()
}
]

def main4c_before := [llvm|
{
^0(%arg124 : i32):
  %0 = llvm.mlir.constant(7 : i32) : i32
  %1 = llvm.mlir.constant(48 : i32) : i32
  %2 = llvm.mlir.constant(0 : i32) : i32
  %3 = llvm.mlir.constant(1 : i32) : i32
  %4 = llvm.and %arg124, %0 : i32
  %5 = llvm.icmp "ne" %4, %0 : i32
  %6 = llvm.and %arg124, %1 : i32
  %7 = llvm.icmp "ne" %6, %1 : i32
  %8 = llvm.or %5, %7 : i1
  %9 = "llvm.select"(%8, %2, %3) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i32, i32) -> i32
  "llvm.return"(%9) : (i32) -> ()
}
]

def p15_commutativity2_after := [llvm|
{
^0(%arg24 : i32, %arg25 : i32):
  %0 = llvm.mlir.constant(65535 : i32) : i32
  %1 = llvm.mlir.constant(42 : i32) : i32
  %2 = llvm.icmp "ugt" %arg24, %0 : i32
  %3 = "llvm.select"(%2, %0, %1) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i32, i32) -> i32
  "llvm.return"(%3) : (i32) -> ()
}
]

def test12commuted_after := [llvm|
{
^0(%arg87 : i32, %arg88 : i32):
  %0 = llvm.mlir.constant(-1 : i32) : i32
  %1 = llvm.and %arg87, %arg88 : i32
  %2 = llvm.xor %1, %0 : i32
  "llvm.return"(%2) : (i32) -> ()
}
]

def t4_after := [llvm|
{
^0(%arg210 : i8, %arg211 : i1):
  %0 = llvm.mlir.constant(42 : i8) : i8
  %1 = llvm.mlir.constant(-44 : i8) : i8
  %2 = "llvm.select"(%arg211, %0, %1) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i8, i8) -> i8
  %3 = llvm.add %2, %arg210 : i8
  "llvm.return"(%3) : (i8) -> ()
}
]

def icmp_sge_riscv_eq_icmp_sge_llvm_32 : LLVMPeepholeRewriteRefine 1 [Ty.llvm (.bitvec 32), Ty.llvm (.bitvec 32)] :=
  {lhs:= icmp_sge_llvm_32, rhs:= icmp_sge_riscv_32}

def PR60818_ne_before := [llvm|
{
^0(%arg5 : i32):
  %0 = llvm.mlir.constant(0 : i32) : i32
  %1 = llvm.sub %0, %arg5 : i32
  %2 = llvm.icmp "ne" %1, %arg5 : i32
  "llvm.return"(%2) : (i1) -> ()
}
]

Lean.mkApp3 in Lean.Expr

def shl_and_and_fail_before := [llvm|
{
^0(%arg170 : i8, %arg171 : i8):
  %0 = llvm.mlir.constant(4 : i8) : i8
  %1 = llvm.mlir.constant(5 : i8) : i8
  %2 = llvm.mlir.constant(88 : i8) : i8
  %3 = llvm.shl %arg170, %0 : i8
  %4 = llvm.shl %arg171, %1 : i8
  %5 = llvm.and %4, %2 : i8
  %6 = llvm.and %3, %5 : i8
  "llvm.return"(%6) : (i8) -> ()
}
]

instance : DecidableEq (NFA'.autMsbSet).σ :=

Lean.addAndCompile in Lean.AddDecl

def test9_after := [llvm|
{
^0(%arg111 : i32, %arg112 : i32):
  %0 = llvm.or %arg111, %arg112 : i32
  "llvm.return"(%0) : (i32) -> ()
}
]

def test6a_after := [llvm|
{
^0(%arg1 : i1):
  %0 = llvm.mlir.constant(true) : i1
  "llvm.return"(%0) : (i1) -> ()
}
]

BitVec.reduceOfNat in Lean.Meta.Tactic.Simp.BuiltinSimprocs.BitVec

def positive_biggerashr_shlnuwnsw_ashrexact_before := [llvm|
{
^0(%arg25 : i8):
  %0 = llvm.mlir.constant(6 : i8) : i8
  %1 = llvm.mlir.constant(3 : i8) : i8
  %2 = llvm.ashr exact %arg25, %0 : i8
  %3 = llvm.shl %2, %1 overflow<nsw,nuw> : i8
  "llvm.return"(%3) : (i8) -> ()
}
]

def sdiv? {w : Nat} (x y : BitVec w) : IntW w :=
  if y == 0 || (w != 1 && x == (BitVec.intMin w) && y == -1) then
    .poison
  else
    .value (x.sdiv y)

def ashr_ult_15_after := [llvm|
{
^0(%arg81 : i4):
  %0 = llvm.mlir.constant(-2 : i4) : i4
  %1 = llvm.icmp "ult" %arg81, %0 : i4
  "llvm.return"(%1) : (i1) -> ()
}
]

def liRiscv14 := [LV| {
  ^entry ():
    %0 = "li"() {imm = 14 : !i64} : (!i64) -> (!i64)
    %1 = "builtin.unrealized_conversion_cast"(%0) : (!i64) -> (i64)
    llvm.return %1 : i64
  }]

def icmp_eq_sext_eq_otherwise_after := [llvm|
{
^0(%arg15 : i32):
  %0 = llvm.mlir.constant(0 : i32) : i32
  %1 = llvm.icmp "eq" %arg15, %0 : i32
  "llvm.return"(%1) : (i1) -> ()
}
]

def test13_after := [llvm|
{
^0(%arg288 : i32, %arg289 : i32):
  %0 = llvm.mlir.constant(false) : i1
  "llvm.return"(%0) : (i1) -> ()
}
]

def test35_before := [llvm|
{
^0(%arg50 : i32):
  %0 = llvm.mlir.constant(0 : i32) : i32
  %1 = llvm.mlir.constant(60 : i32) : i32
  %2 = llvm.mlir.constant(100 : i32) : i32
  %3 = llvm.icmp "sge" %arg50, %0 : i32
  %4 = "llvm.select"(%3, %1, %2) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i32, i32) -> i32
  "llvm.return"(%4) : (i32) -> ()
}
]

def t7_twoshifts2_after := [llvm|
{
^0(%arg28 : i32, %arg29 : i32, %arg30 : i32, %arg31 : i32):
  %0 = llvm.mlir.constant(1 : i32) : i32
  %1 = llvm.mlir.constant(0 : i32) : i32
  %2 = llvm.shl %0, %arg29 overflow<nuw> : i32
  %3 = llvm.shl %arg30, %arg31 : i32
  %4 = llvm.and %3, %2 : i32
  %5 = llvm.icmp "eq" %4, %1 : i32
  "llvm.return"(%5) : (i1) -> ()
}
]

def PR42691_7_after := [llvm|
{
^0(%arg331 : i32):
  %0 = llvm.mlir.constant(-1 : i32) : i32
  %1 = llvm.mlir.constant(0 : i32) : i32
  %2 = llvm.add %arg331, %0 : i32
  %3 = llvm.icmp "slt" %2, %1 : i32
  "llvm.return"(%3) : (i1) -> ()
}
]

def eq_umin1_before := [llvm|
{
^0(%arg60 : i32, %arg61 : i32):
  %0 = llvm.icmp "ult" %arg60, %arg61 : i32
  %1 = "llvm.select"(%0, %arg60, %arg61) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i32, i32) -> i32
  %2 = llvm.icmp "eq" %1, %arg60 : i32
  "llvm.return"(%2) : (i1) -> ()
}
]

def ugt_swap_or_not_min_logical_after := [llvm|
{
^0(%arg4 : i8, %arg5 : i8):
  %0 = llvm.mlir.constant(0 : i8) : i8
  %1 = llvm.icmp "ne" %arg4, %0 : i8
  "llvm.return"(%1) : (i1) -> ()
}
]

def llvm_udiv_lower_riscv_flag_32: LLVMPeepholeRewriteRefine 32 [Ty.llvm (.bitvec 32), Ty.llvm (.bitvec 32)] :=
  {lhs := udiv_llvm_exact_32, rhs := udiv_riscv_32, correct := sorry }

def IntToString (i: Int): String := i.repr

def State.cseRegionArgList
  {Γstart Γ : Ctxt d.Ty}
  {lets : Lets d Γstart .pure Γ}
  (_ : State d lets)
  {ts : RegionSignature d.Ty}
  (rs : HVector ((fun t => Com d t.1 .impure t.2)) <| ts) :
  {rs' : HVector ((fun t => Com d t.1 .impure t.2)) <| ts //
    HVector.denote rs = HVector.denote rs' } :=
  let _ := HVector.map (fun _Γα com => Com.denote com) rs
  match ts, rs with
  | _, .nil => ⟨.nil, by admit /- proof elided -/
    ⟩
  | ⟨Γ, t⟩::ts, .cons region rs =>
    
    
    
    
    ⟨.cons region rs, by admit /- proof elided -/
    ⟩

def eq_smax3_after := [llvm|
{
^0(%arg59 : i32, %arg60 : i32):
  %0 = llvm.mlir.constant(3 : i32) : i32
  %1 = llvm.add %arg59, %0 : i32
  %2 = llvm.icmp "sge" %1, %arg60 : i32
  "llvm.return"(%2) : (i1) -> ()
}
]

def scalar_i16_shl_ult_const_before := [llvm|
{
^0(%arg16 : i16):
  %0 = llvm.mlir.constant(8 : i16) : i16
  %1 = llvm.mlir.constant(1024 : i16) : i16
  %2 = llvm.shl %arg16, %0 : i16
  %3 = llvm.icmp "ult" %2, %1 : i16
  "llvm.return"(%3) : (i1) -> ()
}
]

def trunc_lshr_zext_after := [llvm|
{
^0(%arg12 : i8):
  %0 = llvm.mlir.constant(6 : i8) : i8
  %1 = llvm.lshr %arg12, %0 : i8
  "llvm.return"(%1) : (i8) -> ()
}
]

Lean.Meta.check in Lean.Meta.Check

DFA in Mathlib.Computability.DFA

def mul_selectp2_x_before := [llvm|
{
^0(%arg22 : i8, %arg23 : i1):
  %0 = llvm.mlir.constant(2 : i8) : i8
  %1 = llvm.mlir.constant(4 : i8) : i8
  %2 = "llvm.select"(%arg23, %0, %1) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i8, i8) -> i8
  %3 = llvm.mul %2, %arg22 : i8
  "llvm.return"(%3) : (i8) -> ()
}
]

def sub_zext_trunc_lshr_after := [llvm|
{
^0(%arg15 : i64):
  %0 = llvm.mlir.constant(16 : i32) : i32
  %1 = llvm.mlir.constant(31 : i32) : i32
  %2 = llvm.mlir.constant(10 : i32) : i32
  %3 = llvm.trunc %arg15 : i64 to i32
  %4 = llvm.shl %3, %0 : i32
  %5 = llvm.ashr %4, %1 : i32
  %6 = llvm.add %5, %2 overflow<nsw> : i32
  "llvm.return"(%6) : (i32) -> ()
}
]

structure CliTest where
  name : Name
  mvars : Nat
  context : MContext mvars
  ty : MTy mvars
  eff : EffectKind
  code : MCom mvars context eff [ty]

def fsmWidthEq (a b : FSM α) : FSM α :=
  composeUnaryAux FSM.scanAnd (composeBinaryAux' FSM.nxor a b)

def icmp_eq_and_pow2_shl_pow2_negative2_before := [llvm|
{
^0(%arg60 : i32):
  %0 = llvm.mlir.constant(2 : i32) : i32
  %1 = llvm.mlir.constant(14 : i32) : i32
  %2 = llvm.mlir.constant(0 : i32) : i32
  %3 = llvm.shl %0, %arg60 : i32
  %4 = llvm.and %3, %1 : i32
  %5 = llvm.icmp "eq" %4, %2 : i32
  %6 = llvm.zext %5 : i1 to i32
  "llvm.return"(%6) : (i32) -> ()
}
]

def test86_after := [llvm|
{
^0(%arg59 : i16):
  %0 = llvm.mlir.constant(4 : i16) : i16
  %1 = llvm.ashr %arg59, %0 : i16
  "llvm.return"(%1) : (i16) -> ()
}
]

def ule_or_min_logical_before := [llvm|
{
^0(%arg140 : i8, %arg141 : i8):
  %0 = llvm.mlir.constant(0 : i8) : i8
  %1 = llvm.mlir.constant(true) : i1
  %2 = llvm.icmp "ule" %arg140, %arg141 : i8
  %3 = llvm.icmp "eq" %arg140, %0 : i8
  %4 = "llvm.select"(%2, %1, %3) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i1, i1) -> i1
  "llvm.return"(%4) : (i1) -> ()
}
]

def sgt_3_impliesT_sgt_2_after := [llvm|
{
^0(%arg14 : i8, %arg15 : i8):
  %0 = llvm.mlir.constant(4 : i8) : i8
  %1 = llvm.mlir.constant(false) : i1
  %2 = llvm.icmp "slt" %arg14, %0 : i8
  %3 = llvm.icmp "sgt" %arg15, %arg14 : i8
  %4 = "llvm.select"(%2, %3, %1) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i1, i1) -> i1
  "llvm.return"(%4) : (i1) -> ()
}
]

def test6a_after := [llvm|
{
^0(%arg47 : i55):
  %0 = llvm.mlir.constant(6 : i55) : i55
  %1 = llvm.mul %arg47, %0 : i55
  "llvm.return"(%1) : (i55) -> ()
}
]

@[simp_denote]
def or_llvm_i16:= [LV| {
    ^entry (%a: i16,%b: i16 ):
    %0 = llvm.or %a, %b : i16
    llvm.return %0 :i16
  }]

def shl_xor_lshr_after := [llvm|
{
^0(%arg100 : i32, %arg101 : i32, %arg102 : i32):
  %0 = llvm.lshr %arg102, %arg101 : i32
  %1 = llvm.xor %0, %arg100 : i32
  "llvm.return"(%1) : (i32) -> ()
}
]

@[simp_denote]
def sext_riscv_i8_to_i16 := [LV| {
  ^entry (%arg: i8):
    %0 = "builtin.unrealized_conversion_cast"(%arg) : (i8) -> (!i64)
    %1 = sext.b %0 : !i64
    %res = "builtin.unrealized_conversion_cast"(%1) : (!i64) -> (i16)
    llvm.return %res : i16
  }]

def Body.return : (body : Body Op Γ ty) → Var body.returnContext ty
  | ⟨⟨_, ret⟩, h⟩ => ⟨ret, by admit /- proof elided -/
  ⟩

def test7_before := [llvm|
{
^0(%arg7 : i47):
  %0 = llvm.mlir.constant(39 : i47) : i47
  %1 = llvm.mlir.constant(255 : i47) : i47
  %2 = llvm.ashr %arg7, %0 : i47
  %3 = llvm.and %2, %1 : i47
  "llvm.return"(%3) : (i47) -> ()
}
]

def max_of_min_before := [llvm|
{
^0(%arg22 : i32):
  %0 = llvm.mlir.constant(-1 : i32) : i32
  %1 = llvm.mlir.constant(0 : i32) : i32
  %2 = llvm.xor %arg22, %0 : i32
  %3 = llvm.icmp "sgt" %arg22, %1 : i32
  %4 = "llvm.select"(%3, %2, %0) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i32, i32) -> i32
  %5 = llvm.icmp "sgt" %4, %0 : i32
  %6 = "llvm.select"(%5, %4, %0) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i32, i32) -> i32
  "llvm.return"(%6) : (i32) -> ()
}
]

@[match_pattern] def add (w : Width φ)
  (NoWrapFlags: NoWrapFlags := {nsw := false , nuw := false}) : MOp φ
    := .binary w (.add NoWrapFlags )

def test2_before := [llvm|
{
^0(%arg59 : i32):
  %0 = llvm.mlir.constant(7 : i32) : i32
  %1 = llvm.mlir.constant(32 : i32) : i32
  %2 = llvm.and %arg59, %0 : i32
  %3 = llvm.and %arg59, %1 : i32
  %4 = llvm.add %2, %3 : i32
  "llvm.return"(%4) : (i32) -> ()
}
]

def a_or_b_after := [llvm|
{
^0(%arg15 : i32, %arg16 : i32):
  %0 = llvm.mlir.constant(0 : i32) : i32
  %1 = llvm.icmp "eq" %arg15, %0 : i32
  %2 = llvm.icmp "eq" %arg16, %0 : i32
  %3 = llvm.xor %1, %2 : i1
  "llvm.return"(%3) : (i1) -> ()
}
]

def shl_nsw_add_negative_before := [llvm|
{
^0(%arg45 : i32):
  %0 = llvm.mlir.constant(-1 : i32) : i32
  %1 = llvm.mlir.constant(2 : i32) : i32
  %2 = llvm.add %arg45, %0 : i32
  %3 = llvm.shl %1, %2 overflow<nsw> : i32
  "llvm.return"(%3) : (i32) -> ()
}
]

def f2_after := [llvm|
{
^0(%arg0 : i32, %arg1 : i32):
  %0 = llvm.mlir.constant(0 : i32) : i32
  %1 = llvm.mlir.constant(7 : i32) : i32
  %2 = llvm.mlir.constant(true) : i1
  %3 = llvm.icmp "slt" %arg0, %0 : i32
  %4 = llvm.lshr %1, %arg1 : i32
  %5 = llvm.icmp "sgt" %arg0, %4 : i32
  %6 = "llvm.select"(%3, %2, %5) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i1, i1) -> i1
  %7 = llvm.zext %6 : i1 to i32
  "llvm.return"(%7) : (i32) -> ()
}
]

instance : Inhabited (
  {lets : Lets d Γstart .pure Γ} →
  State d lets →
  (com: Com d Γ .pure α) →
  { com' : Com d Γ .pure α // ∀ (V: Ctxt.Valuation Γ), com.denote V = com'.denote V }) where
  default := fun _s com => ⟨com, by admit /- proof elided -/
  ⟩

Lean.Parser.Tactic.tacticSeq in Lean.Parser.Term.Basic

def ashrsgt_01_10_exact_after := [llvm|
{
^0(%arg91 : i4):
  %0 = llvm.mlir.constant(true) : i1
  "llvm.return"(%0) : (i1) -> ()
}
]

def scalar_i32_shl_and_signbit_eq_X_is_constant2_after := [llvm|
{
^0(%arg4 : i32):
  %0 = llvm.mlir.constant(31 : i32) : i32
  %1 = llvm.icmp "ne" %arg4, %0 : i32
  "llvm.return"(%1) : (i1) -> ()
}
]

def or_xor_tree_1000_before := [llvm|
{
^0(%arg21 : i32, %arg22 : i32, %arg23 : i32):
  %0 = llvm.mlir.constant(42 : i32) : i32
  %1 = llvm.mul %arg21, %0 : i32
  %2 = llvm.mul %arg22, %0 : i32
  %3 = llvm.mul %arg23, %0 : i32
  %4 = llvm.xor %1, %2 : i32
  %5 = llvm.xor %2, %3 : i32
  %6 = llvm.xor %5, %1 : i32
  %7 = llvm.or %6, %4 : i32
  "llvm.return"(%7) : (i32) -> ()
}
]

def and_or_not_not_commute1_after := [llvm|
{
^0(%arg372 : i32, %arg373 : i32, %arg374 : i32):
  %0 = llvm.mlir.constant(42 : i32) : i32
  %1 = llvm.mlir.constant(-1 : i32) : i32
  %2 = llvm.sdiv %0, %arg373 : i32
  %3 = llvm.or %arg374, %2 : i32
  %4 = llvm.and %3, %arg372 : i32
  %5 = llvm.xor %4, %1 : i32
  "llvm.return"(%5) : (i32) -> ()
}
]

def select_sub_icmp_after := [llvm|
{
^0(%arg214 : i32, %arg215 : i32, %arg216 : i32):
  %0 = llvm.mlir.constant(0 : i32) : i32
  %1 = llvm.icmp "eq" %arg214, %0 : i32
  %2 = "llvm.select"(%1, %arg216, %arg215) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i32, i32) -> i32
  "llvm.return"(%2) : (i32) -> ()
}
]

def trunc_equality_nuw_before := [llvm|
{
^0(%arg44 : i16, %arg45 : i16):
  %0 = llvm.trunc %arg44 overflow<nuw> : i16 to i8
  %1 = llvm.trunc %arg45 overflow<nuw> : i16 to i8
  %2 = llvm.icmp "eq" %0, %1 : i8
  "llvm.return"(%2) : (i1) -> ()
}
]

def sext_shl_mask_after := [llvm|
{
^0(%arg7 : i16, %arg8 : i32):
  %0 = llvm.mlir.constant(65535 : i32) : i32
  %1 = llvm.zext %arg7 : i16 to i32
  %2 = llvm.shl %1, %arg8 : i32
  %3 = llvm.and %2, %0 : i32
  "llvm.return"(%3) : (i32) -> ()
}
]

def processOneElem_inv {st : worklist.St A S} (s : State) (sa : S) (k : ℕ) :
    ∀ a sa', (f sa)[k]? = some (a, sa') →
    processOneElem_mot inits final f s sa k st →
    let st' := processOneElem A S final s st (a, sa')
    StInv A S st'.m st'.map :=

def irc_constants_AMinusC1MinusC2_neg1_neg1 : LLVMPeepholeRewriteRefine 64 [Ty.llvm (.bitvec 64)] where
  lhs := [LV| {
    ^entry (%a: i64):
      %c1 = llvm.mlir.constant (-1) : i64
      %c2 = llvm.mlir.constant (-1) : i64
      %0 = llvm.sub %a, %c1 : i64
      %1 = llvm.sub %0, %c2 : i64
      llvm.return %1 : i64
  }]
  rhs := [LV| {
    ^entry (%a: i64):
      %c1 = llvm.mlir.constant (-1) : i64
      %c2 = llvm.mlir.constant (-1) : i64
      %0 = llvm.add %c1, %c2 : i64
      %1 = llvm.sub %a, %0 : i64
      llvm.return %1 : i64
  }]

processBitVec (pbv : BVExpr.PackedBitVec) : ParseExprM (Option BVExprWrapper) := do
    let currState: ParsedInputState ← get
    let existingVal :=  currState.valToSymVar[pbv]?
    match existingVal with
    | none =>
      let numSymVars := currState.numSymVars
      let newId := 1001 + numSymVars
      let newExpr : GenBVExpr pbv.w := GenBVExpr.var newId

def merge {Γ : Ctxt _} (a b : Γ.Var (.tokenstream)) : Expr (DC) Γ .pure (.valuestream 1) :=
  Expr.mk
    (op := .merge)
    (ty_eq := rfl)
    (eff_le := by admit /- proof elided -/
    )
    (args := .cons a <| .cons b <| .nil)
    (regArgs := .nil)

BitVec.signExtend in Init.Data.BitVec.Basic

def fold_xor_zext_sandwich_before := [llvm|
{
^0(%arg101 : i1):
  %0 = llvm.mlir.constant(1 : i32) : i32
  %1 = llvm.zext %arg101 : i1 to i32
  %2 = llvm.xor %1, %0 : i32
  %3 = llvm.zext %2 : i32 to i64
  "llvm.return"(%3) : (i64) -> ()
}
]

def ugt_umax2_after := [llvm|
{
^0(%arg34 : i32, %arg35 : i32):
  %0 = llvm.icmp "ugt" %arg35, %arg34 : i32
  "llvm.return"(%0) : (i1) -> ()
}
]

def PR42691_7_before := [llvm|
{
^0(%arg331 : i32):
  %0 = llvm.mlir.constant(-2147483647 : i32) : i32
  %1 = llvm.mlir.constant(0 : i32) : i32
  %2 = llvm.icmp "uge" %arg331, %0 : i32
  %3 = llvm.icmp "eq" %arg331, %1 : i32
  %4 = llvm.or %2, %3 : i1
  "llvm.return"(%4) : (i1) -> ()
}
]

private def attrVal0Str : AttrVal := [mlir_attr_val| "foo"]

instance : DialectSignature ExOp ExTy where
  signature
  | .add    => ⟨[.nat, .nat], [], .nat, .pure⟩
  | .runK _ => ⟨[.nat], [([.nat], .nat)], .nat, .pure⟩

def add_nsw_poison_constant_fold_after := [llvm|
{
^0(%arg2 : i1, %arg3 : i8):
  %0 = llvm.mlir.constant(64 : i8) : i8
  %1 = llvm.mlir.constant(-127 : i8) : i8
  %2 = llvm.add %arg3, %0 overflow<nsw> : i8
  %3 = "llvm.select"(%arg2, %2, %1) <{"fastmathFlags" = #llvm.fastmath<none>}> : (i1, i8, i8) -> i8
  "llvm.return"(%3) : (i8) -> ()
}
]

def ashrslt_01_06_before := [llvm|
{
^0(%arg263 : i4):
  %0 = llvm.mlir.constant(1 : i4) : i4
  %1 = llvm.mlir.constant(6 : i4) : i4
  %2 = llvm.ashr %arg263, %0 : i4
  %3 = llvm.icmp "slt" %2, %1 : i4
  "llvm.return"(%3) : (i1) -> ()
}
]

def masked_icmps_mask_notallzeros_bmask_mixed_negated_7b_before := [llvm|
{
^0(%arg54 : i32):
  %0 = llvm.mlir.constant(6 : i32) : i32
  %1 = llvm.mlir.constant(0 : i32) : i32
  %2 = llvm.mlir.constant(15 : i32) : i32
  %3 = llvm.mlir.constant(8 : i32) : i32
  %4 = llvm.and %arg54, %0 : i32
  %5 = llvm.icmp "eq" %4, %1 : i32
  %6 = llvm.and %arg54, %2 : i32
  %7 = llvm.icmp "ne" %6, %3 : i32
  %8 = llvm.or %5, %7 : i1
  "llvm.return"(%8) : (i1) -> ()
}
]

def scalar_i32_lshr_and_negC_eq_X_is_constant1_after := [llvm|
{
^0(%arg9 : i32):
  %0 = llvm.mlir.constant(12345 : i32) : i32
  %1 = llvm.mlir.constant(8 : i32) : i32
  %2 = llvm.lshr %0, %arg9 : i32
  %3 = llvm.icmp "ult" %2, %1 : i32
  "llvm.return"(%3) : (i1) -> ()
}
]

def positive_biggerlshr_lshrexact_before := [llvm|
{
^0(%arg40 : i8):
  %0 = llvm.mlir.constant(6 : i8) : i8
  %1 = llvm.mlir.constant(3 : i8) : i8
  %2 = llvm.lshr exact %arg40, %0 : i8
  %3 = llvm.shl %2, %1 : i8
  "llvm.return"(%3) : (i8) -> ()
}
]

def test_negative_nsw_and_unsigned_pred_before := [llvm|
{
^0(%arg52 : i64):
  %0 = llvm.mlir.constant(10) : i64
  %1 = llvm.mlir.constant(3) : i64
  %2 = llvm.sub %0, %arg52 overflow<nsw> : i64
  %3 = llvm.icmp "ult" %2, %1 : i64
  "llvm.return"(%3) : (i1) -> ()
}
]

BitVec.toNat in Init.Prelude

def getSimpData (simpsetName : Name) : MetaM (SimpTheorems × Simprocs) := do
  let some ext ← (getSimpExtension? simpsetName)
    | throwError m!"'{simpsetName}' simp attribute not found!"
  let theorems ← ext.getTheorems
  let some ext ← (Simp.getSimprocExtension? simpsetName)
    | throwError m!"'{simpsetName}' simp attribute not found!"
  let simprocs ← ext.getSimprocs
  return (theorems, simprocs)

List.Vector.ofFn in Mathlib.Data.Vector.Defs

def shl_nsw_add_nsw_after := [llvm|
{
^0(%arg21 : i32):
  %0 = llvm.mlir.constant(-1 : i32) : i32
  %1 = llvm.shl %0, %arg21 overflow<nsw> : i32
  %2 = llvm.xor %1, %0 : i32
  "llvm.return"(%2) : (i32) -> ()
}
]

|

Union in Init.Core
</all_available_defs>

<all_available_lemmas>
BitVec.toNat_eq in Init.Data.BitVec.Lemmas
Nat.le_antisymm in Init.Prelude
BitVec.toInt_inj in Init.Data.BitVec.Lemmas
le_iff_lt_or_eq in Mathlib.Order.Defs.PartialOrder

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

theorem select_constants_and_icmp_ne0_zero_fval_proof : select_constants_and_icmp_ne0_zero_fval_before ⊑ select_constants_and_icmp_ne0_zero_fval_after

theorem masked_and_notA_slightly_optimized_logical_thm.extracted_1._2 : ∀ (x : BitVec 32),
  ¬ofBool (8#32 ≤ᵤ x) = 1#1 → 0#1 = ofBool (x &&& BitVec.ofInt 32 (-40) != 0#32)

lemma CNFA.autWidth_bv_language :
    (autWidth wp n).bv_recognizes { bv | wp.sat bv.w n }

theorem test1_proof : test1_before ⊑ test1_after

theorem scalar_i32_signbit_lshr_and_eq_X_is_constant1_proof : scalar_i32_signbit_lshr_and_eq_X_is_constant1_before ⊑ scalar_i32_signbit_lshr_and_eq_X_is_constant1_after

theorem same_signbit_wrong_type_proof : same_signbit_wrong_type_before ⊑ same_signbit_wrong_type_after

theorem xor_andn_commute3_proof : xor_andn_commute3_before ⊑ xor_andn_commute3_after

theorem t0_proof : t0_before ⊑ t0_after

theorem fold_add_udiv_urem_thm (e : IntW 32) :
  add (shl (LLVM.udiv e (const? 32 10)) (const? 32 4)) (urem e (const? 32 10)) ⊑
    add (mul (LLVM.udiv e (const? 32 10)) (const? 32 6) { «nsw» := false, «nuw» := true }) e

theorem test_logical_and_ne_a_b_thm.extracted_1._3 : ∀ (x x_1 : BitVec 8) (x_2 : BitVec 1),
  ¬x_2 = 1#1 → 0#1 = 1#1 → x_1 = x

theorem zext_true_val_must_be_one_thm.extracted_1._1 : ∀ (x : BitVec 1), x = 1#1 → zeroExtend 32 x = 1#32

theorem e_219 :
     - 5 * (x ^^^ y) + 2 *  ~~~x - 6 *  ~~~(x &&&  ~~~x) - 1 *  ~~~y + 3 *  ~~~(x ||| y) + 10 * (x &&&  ~~~y) + 6 * (x &&& y) =  - 2 *  ~~~(x &&& y) - 7 *  ~~~(x |||  ~~~y)

theorem logic_or_logic_and_3_thm.extracted_1._8 : ∀ (x x_1 x_2 : BitVec 1),
  ¬x_2 = 1#1 → ¬x = 1#1 → ¬x_1 = 1#1 → ¬0#1 = 1#1 → 0#1 = x_1

theorem neg_mask_thm.extracted_1._2 : ∀ (x : BitVec 16) (x_1 : BitVec 32),
  ¬(True ∧ x_1.ssubOverflow (signExtend 32 x) = true ∨
        15#16 ≥ ↑16 ∨ True ∧ (0#32).ssubOverflow (zeroExtend 32 (x >>> 15#16)) = true) →
    ofBool (x <ₛ 0#16) = 1#1 →
      ¬(True ∧ x_1.ssubOverflow (signExtend 32 x) = true) →
        x_1 - signExtend 32 x &&& 0#32 - zeroExtend 32 (x >>> 15#16) = x_1 - signExtend 32 x

theorem logical_and_bitwise_and_icmps_comm2_proof : logical_and_bitwise_and_icmps_comm2_before ⊑ logical_and_bitwise_and_icmps_comm2_after

theorem or_xor_common_op_commute7_thm.extracted_1._1 : ∀ (x x_1 x_2 : BitVec 8),
  x_2 ^^^ x_1 ||

theorem lshr_pow2_ult_proof : lshr_pow2_ult_before ⊑ lshr_pow2_ult_after

theorem ult_umin3_thm (e e_1 : IntW 32) :
  icmp IntPred.ugt (add e_1 (const? 32 3))
      (select (icmp IntPred.ult (add e_1 (const? 32 3)) e) (add e_1 (const? 32 3)) e) ⊑
    icmp IntPred.ult e (add e_1 (const? 32 3))

theorem t4_ugt_slt_65536_thm.extracted_1._9 : ∀ (x x_1 : BitVec 32),
  ¬ofBool (65535#32 <ᵤ x_1) = 1#1 → ¬ofBool (65535#32 <ₛ x_1) = 1#1 → ofBool (x_1 <ₛ 0#32) = 1#1 → False

theorem abs_must_be_positive_proof : abs_must_be_positive_before ⊑ abs_must_be_positive_after

theorem masked_icmps_mask_notallzeros_bmask_mixed_negated_1_logical_thm (e : IntW 32) :
  select (icmp IntPred.eq (LLVM.and e (const? 32 12)) (const? 32 0)) (const? 1 1)
      (icmp IntPred.ne (LLVM.and e (const? 32 7)) (const? 32 1)) ⊑
    icmp IntPred.ne (LLVM.and e (const? 32 15)) (const? 32 9)

theorem test_sext4_thm.extracted_1._5 : ∀ (x x_1 : BitVec 1),
  ¬x_1 = 1#1 → ¬x_1 ^^^ 1#1 = 1#1 → -1#32 = signExtend 32 x

theorem trunc_lshr_trunc_outofrange_proof : trunc_lshr_trunc_outofrange_before ⊑ trunc_lshr_trunc_outofrange_after

theorem zext_zext_sgt_thm (e e_1 : IntW 8) :
  icmp IntPred.sgt (zext 32 e_1) (zext 32 e) ⊑ icmp IntPred.ugt e_1 e

theorem test15e_thm.extracted_1._1 : ∀ (x : BitVec 32), ofBool (x &&& 128#32 != 0#32) = 1#1 → 1#32 ≥ ↑32 → False

theorem test1_proof : test1_before ⊑ test1_after

theorem foo4_proof : foo4_before ⊑ foo4_after

theorem main7a_logical_thm.extracted_1._6 : ∀ (x x_1 x_2 : BitVec 32),
  ofBool (x_2 &&& x_1 == x_2) = 1#1 →
    ofBool (x_2 &&& x_1 != x_2) = 1#1 → ¬ofBool (x &&& x_1 == x) = 1#1 → 1#32 = zeroExtend 32 1#1

theorem lshrult_01_11_exact_thm (e : IntW 4) :
  icmp IntPred.ult (lshr e (const? 4 1) { «exact» := true }) (const? 4 (-5)) ⊑ const? 1 1

theorem lshrult_03_15_proof : lshrult_03_15_before ⊑ lshrult_03_15_after

theorem ashr_can_be_lshr_thm.extracted_1._2 : ∀ (x : BitVec 32),
  ¬(True ∧ x >>> 16#32 <<< 16#32 ≠ x ∨
        16#32 ≥ ↑32 ∨ True ∧ signExtend 32 (truncate 16 (x.sshiftRight' 16#32)) ≠ x.sshiftRight' 16#32) →
    ¬(True ∧ x >>> 16#32 <<< 16#32 ≠ x ∨ 16#32 ≥ ↑32 ∨ True ∧ zeroExtend 32 (truncate 16 (x >>> 16#32)) ≠ x >>> 16#32) →
      truncate 16 (x.sshiftRight' 16#32) = truncate 16 (x >>> 16#32)

theorem ashr_and_thm.extracted_1._2 : ∀ (x : BitVec 8),
  ¬(x ≥ ↑8 ∨ x + 1#8 ≥ ↑8) →
    ¬x ≥ ↑8 →
      (BitVec.ofInt 8 (-64)).sshiftRight' x &&& (BitVec.ofInt 8 (-128)).sshiftRight' (x + 1#8) =
        (BitVec.ofInt 8 (-64)).sshiftRight' x

theorem trunc_sandwich_big_sum_shift2_thm.extracted_1._1 : ∀ (x : BitVec 32),
  ¬(31#32 ≥ ↑32 ∨ 1#12 ≥ ↑12) → truncate 12 (x >>> 31#32) >>> 1#12 = 0#12

theorem prove_exact_with_high_mask_thm (e : IntW 8) :
  LLVM.sdiv (LLVM.and e (const? 8 (-8))) (const? 8 4) ⊑ LLVM.and (ashr e (const? 8 2)) (const? 8 (-2))

theorem or_not_and_commute9_thm.extracted_1._1 : ∀ (x x_1 x_2 : BitVec 32),
  ¬((x_2 == 0 || 32 != 1 && 42#32 == intMin 32 && x_2 == -1) = true ∨
        (x_1 == 0 || 32 != 1 && 42#32 == intMin 32 && x_1 == -1) = true ∨
          (x == 0 || 32 != 1 && 42#32 == intMin 32 && x == -1) = true ∨
            (x_1 == 0 || 32 != 1 && 42#32 == intMin 32 && x_1 == -1) = true ∨
              (x_2 == 0 || 32 != 1 && 42#32 == intMin 32 && x_2 == -1) = true ∨
                (x == 0 || 32 != 1 && 42#32 == intMin 32 && x == -1) = true) →
    (x_1 == 0 || 32 != 1 && 42#32 == intMin 32 && x_1 == -1) = true ∨
        (x == 0 || 32 != 1 && 42#32 == intMin 32 && x == -1) = true ∨
          (x_2 == 0 || 32 != 1 && 42#32 == intMin 32 && x_2 == -1) = true →
      False

theorem not_false_not_thm.extracted_1._1 : ∀ (x : BitVec 1), x ^^^ 1#1 = 1#1 → x = 1#1 → False

theorem incr_add : a + (@ofBitVecSext w 1) ≈ʷ a.incr

theorem sel_false_val_is_a_masked_ashr_of_true_val1_proof : sel_false_val_is_a_masked_ashr_of_true_val1_before ⊑ sel_false_val_is_a_masked_ashr_of_true_val1_after

theorem src_x_and_nmask_uge_thm.extracted_1._6 : ∀ (x : BitVec 8) (x_1 : BitVec 1) (x_2 : BitVec 8),
  ¬x_1 = 1#1 → x_1 ^^^ 1#1 = 1#1 → ofBool (0#8 ≤ᵤ x_2 &&& 0#8) = 1#1

theorem test7_proof : test7_before ⊑ test7_after

@[simp]
theorem eval_latchDelayed_zero_eq (initVal : Bool)
    (x : Bool → BitStream) :
    (latchDelayed initVal).eval x 0 = initVal

theorem or_and_not_not_commute1_thm.extracted_1._2 : ∀ (x x_1 x_2 : BitVec 32),
  ¬((x_2 == 0 || 32 != 1 && 42#32 == intMin 32 && x_2 == -1) = true ∨
        (x_2 == 0 || 32 != 1 && 42#32 == intMin 32 && x_2 == -1) = true) →
    ¬(x_2 == 0 || 32 != 1 && 42#32 == intMin 32 && x_2 == -1) = true →
      (42#32).sdiv x_2 &&& ((x_1 ||| x) ^^^ -1#32) ||

theorem lshrult_02_05_exact_proof : lshrult_02_05_exact_before ⊑ lshrult_02_05_exact_after

theorem ult_umin3_proof : ult_umin3_before ⊑ ult_umin3_after

theorem orn_and_cmp_2_logical_thm (e e_1 : IntW 16) (e_2 : IntW 1) :
  select (select e_2 (icmp IntPred.sge e_1 e) (const? 1 0)) (const? 1 1) (icmp IntPred.slt e_1 e) ⊑
    select e_2 (const? 1 1) (icmp IntPred.slt e_1 e)

theorem nonexact_ashr_ne_noexactdiv_proof : nonexact_ashr_ne_noexactdiv_before ⊑ nonexact_ashr_ne_noexactdiv_after

theorem masked_icmps_mask_notallzeros_bmask_mixed_1_logical_proof : masked_icmps_mask_notallzeros_bmask_mixed_1_logical_before ⊑ masked_icmps_mask_notallzeros_bmask_mixed_1_logical_after

theorem lshrugt_01_12_proof : lshrugt_01_12_before ⊑ lshrugt_01_12_after

theorem test_nsw_and_signed_pred_thm (e : IntW 64) :
  icmp IntPred.sgt (sub (const? 64 3) e { «nsw» := true, «nuw» := false }) (const? 64 10) ⊑
    icmp IntPred.slt e (const? 64 (-7))

theorem shl_add_thm.extracted_1._2 : ∀ (x : BitVec 32),
  ¬x ≥ ↑32 →
    ¬(True ∧ ((-1#32) <<< x).sshiftRight' x ≠ -1#32 ∨ x ≥ ↑32) → 1#32 <<< x + -1#32 = (-1#32) <<< x ^^^ -1#32

theorem narrow_sext_and_thm.extracted_1._1 : ∀ (x : BitVec 32) (x_1 : BitVec 16),
  truncate 16 (signExtend 32 x_1 &&& x) = x_1 &&& truncate 16 x

theorem t0_thm.extracted_1._5 : ∀ (x x_1 x_2 : BitVec 8) (x_3 : BitVec 1),
  x_3 ^^^ 1#1 = 1#1 → x_3 = 1#1 → ofBool (x_2 == x_1) = 1#1 → False

theorem ashrslt_03_12_thm (e : IntW 4) : icmp IntPred.slt (ashr e (const? 4 3)) (const? 4 (-4)) ⊑ const? 1 0

theorem test86_thm (e : IntW 16) : trunc 16 (ashr (sext 32 e) (const? 32 4)) ⊑ ashr e (const? 16 4)

theorem sge_or_max_thm.extracted_1._1 : ∀ (x x_1 : BitVec 8),
  ofBool (x ≤ₛ x_1) ||

theorem ashrsgt_02_12_exact_thm (e : IntW 4) :
  icmp IntPred.sgt (ashr e (const? 4 2) { «exact» := true }) (const? 4 (-4)) ⊑ const? 1 1

theorem trunc_sandwich_max_sum_shift2_thm.extracted_1._1 : ∀ (x : BitVec 32),
  ¬(30#32 ≥ ↑32 ∨ 1#12 ≥ ↑12) →
    31#32 ≥ ↑32 ∨
        True ∧ signExtend 32 (truncate 12 (x >>> 31#32)) ≠ x >>> 31#32 ∨
          True ∧ zeroExtend 32 (truncate 12 (x >>> 31#32)) ≠ x >>> 31#32 →
      False

theorem ne_smax1_proof : ne_smax1_before ⊑ ne_smax1_after

theorem test2_thm (e : IntW 1) : LLVM.xor e e ⊑ const? 1 0

theorem neg_or_ashr_i32_commute_thm.extracted_1._1 : ∀ (x : BitVec 32),
  ¬((x == 0 || 32 != 1 && 42#32 == intMin 32 && x == -1) = true ∨
        (x == 0 || 32 != 1 && 42#32 == intMin 32 && x == -1) = true ∨ 31#32 ≥ ↑32) →
    (x == 0 || 32 != 1 && 42#32 == intMin 32 && x == -1) = true → False

theorem nonexact_lshr_eq_exactdiv_proof : nonexact_lshr_eq_exactdiv_before ⊑ nonexact_lshr_eq_exactdiv_after

theorem sext_sext_ne_thm.extracted_1._1 : ∀ (x x_1 : BitVec 8),
  ofBool (signExtend 32 x_1 != signExtend 32 x) = ofBool (x_1 != x)

theorem lshrult_02_14_exact_proof : lshrult_02_14_exact_before ⊑ lshrult_02_14_exact_after

theorem bools2_logical_commute1_and2_thm.extracted_1._5 : ∀ (x x_1 : BitVec 1), ¬x_1 = 1#1 → 0#1 = 1#1 → False

theorem eq_umax1_proof : eq_umax1_before ⊑ eq_umax1_after

theorem demorgan_nand_thm.extracted_1._1 : ∀ (x x_1 : BitVec 8), (x_1 ^^^ -1#8) &&& x ^^^ -1#8 = x_1 ||

theorem or2 {v w k l : Nat} (a : BitVec k) : v < w ∨ w < v ∨ 
  (a.setWidth v).setWidth l = (a.setWidth w).setWidth l

theorem com_Γ : com.1 = (Γn 1)

theorem slt_swap_or_not_max_commute_proof : slt_swap_or_not_max_commute_before ⊑ slt_swap_or_not_max_commute_after

theorem c_0_1_0_thm.extracted_1._1 : ∀ (x x_1 : BitVec 4),
  True ∧ (x &&& BitVec.ofInt 4 (-2) &&& (x_1 &&& 1#4) != 0) = true → False

theorem test23_proof : test23_before ⊑ test23_after

theorem select_of_symmetric_selects_commuted_thm.extracted_1._7 : ∀ (x x_1 : BitVec 32) (x_2 x_3 : BitVec 1),
  ¬x_3 = 1#1 → x_2 = 1#1 → ¬x_2 ^^^ x_3 = 1#1 → x = x_1

theorem ashrslt_02_05_exact_proof : ashrslt_02_05_exact_before ⊑ ashrslt_02_05_exact_after

theorem sub_ult_zext_thm.extracted_1._1 : ∀ (x : BitVec 1) (x_1 x_2 : BitVec 8),
  ofBool (x_2 - x_1 <ᵤ zeroExtend 8 x) = ofBool (x_2 == x_1) &&& x

theorem poison_ne_value (a : α) :
    @Ne (no_index _) poison (value a)

theorem land_bor_left1_proof : land_bor_left1_before ⊑ land_bor_left1_after

theorem or_eq_with_one_bit_diff_constants3_logical_thm.extracted_1._1 : ∀ (x : BitVec 8),
  ofBool (x == BitVec.ofInt 8 (-2)) = 1#1 → 1#1 = ofBool (x &&& 127#8 == 126#8)

theorem and_ne_with_one_bit_diff_constants1_thm.extracted_1._1 : ∀ (x : BitVec 32),
  ofBool (x != 51#32) &&& ofBool (x != 50#32) = ofBool (x + BitVec.ofInt 32 (-52) <ᵤ BitVec.ofInt 32 (-2))

theorem main3_proof : main3_before ⊑ main3_after

theorem e_2137 :
    11 * (e ^^^ ( ~~~d ||| ( ~~~e &&& f))) + 5 * ((d &&& e) ^^^ (e ||| f)) + 2 * (f ^^^  ~~~( ~~~d ||| (e &&& f))) - 2 * ( ~~~d ||| (e ||| f)) + 4 * (d &&& (e ^^^ f)) + 5 * (f ||| (d &&&  ~~~e)) - 5 * (f ^^^ (d ||| e)) + 7 * ((e &&& f) ^^^  ~~~(d &&& (e ^^^ f))) + 1 * ( ~~~d &&& ( ~~~e ||| f)) + 2 * ( ~~~f &&&  ~~~(d ^^^ e)) - 2 * ( ~~~(d ||| e) ||| (e ^^^ f)) + 5 * ( ~~~e &&& (d ^^^ f)) - 1 *  ~~~(d ^^^ (e ^^^ f)) + 11 * (d ||| (e &&& f)) - 1 *  ~~~(e &&& f) - 3 * (e ^^^ (d ||| f)) + 2 * (e ^^^  ~~~(d &&&  ~~~f)) - 5 * (f ^^^  ~~~(d &&& ( ~~~e &&& f))) + 4 * ((d |||  ~~~e) &&& (d ^^^ (e ^^^ f))) + 1 * ( ~~~(d ||| e) |||  ~~~(e ^^^ f)) - 11 * (f &&& (d |||  ~~~e)) - 1 *  ~~~( ~~~d ||| (e &&& f)) - 5 * (e ^^^ f) - 1 * ((d |||  ~~~e) &&&  ~~~(e ^^^ f)) + 1 *  ~~~(d ^^^ (e &&& f)) - 5 * (e ^^^ (d &&&  ~~~f)) - 1 * (d ||| (e ||| f)) + 4 * ( ~~~(d &&& e) &&& (d ^^^ (e ^^^ f))) - 11 * (e ^^^ (d &&& ( ~~~e ||| f))) + 2 * ((d &&& e) |||  ~~~(e ||| f)) - 6 * ( ~~~d ||| ( ~~~e &&& f)) - 1 * ((e &&&  ~~~f) ^^^ (d ||| (e ^^^ f))) - 7 * ( ~~~(d ^^^ e) &&& (d ^^^ f)) - 6 *  ~~~e - 11 *  ~~~(d &&& (e ^^^ f)) + 3 * (e ^^^  ~~~(d ||| (e ^^^ f))) + 1 * (f ^^^  ~~~( ~~~d &&& ( ~~~e ||| f))) + 1 * (e ^^^ (d |||  ~~~f)) - 11 * ( ~~~(d &&& e) &&&  ~~~(d ^^^ (e ^^^ f))) + 15 *  ~~~(d ||| (e ||| f)) + 8 *  ~~~( ~~~d ||| (e ||| f)) + 3 *  ~~~( ~~~d ||| ( ~~~e ||| f)) + 12 * ( ~~~d &&& ( ~~~e &&& f)) + 21 * ( ~~~d &&& (e &&& f)) + 28 * (d &&& ( ~~~e &&& f)) - 8 * (d &&& (e &&& f)) =  - 36 *  ~~~(d ||| ( ~~~e ||| f))

lemma reduce_stepSet {M : NFA α σ} {q : σ} (S1 : Set M.Reachable) (S2 : Set σ) :
    (∀ q, (∃ hq, S1 ⟨q, hq⟩) ↔ S2 q) →
    ((∃ hq,  ⟨q, hq⟩ ∈ M.reduce.stepSet S1 a) ↔ q ∈ M.stepSet S2 a)

theorem sext_xor_sub_2_thm (e : IntW 64) (e_1 : IntW 1) :
  sub (sext 64 e_1) (LLVM.xor e (sext 64 e_1)) ⊑ select e_1 e (sub (const? 64 0) e)

theorem e_332 :
     - 1 * x - 3 *  ~~~x + 4 *  ~~~(x ^^^ y) - 5 *  ~~~(x &&& y) + 4 * y + 9 *  ~~~(x ||| y) + 11 * (x &&&  ~~~y) - 2 * (x &&& y) = 5 *  ~~~(x &&&  ~~~x) - 9 *  ~~~(x |||  ~~~y)

theorem trunc_proof : trunc_before ⊑ trunc_after

theorem test19_proof : test19_before ⊑ test19_after

theorem slt_x_impliesT_ne_smin_todo_thm.extracted_1._3 : ∀ (x x_1 x_2 : BitVec 8),
  ¬ofBool (x_2 <ₛ x_1) = 1#1 → ofBool (x_2 != x) = ofBool (x != x_2)

theorem main3d_thm.extracted_1._1 : ∀ (x : BitVec 32),
  ofBool (x &&& 7#32 != 0#32) ||

@[simp] theorem zero_eq_last (h) : ⟨0, h⟩ = last Γ t

theorem mul_selectp2_x_thm.extracted_1._2 : ∀ (x : BitVec 8) (x_1 : BitVec 1),
  x_1 = 1#1 → ¬1#8 ≥ ↑8 → 2#8 * x = x <<< 1#8

theorem shl_add_add_no_const_thm.extracted_1._1 : ∀ (x x_1 x_2 x_3 : BitVec 8),
  ¬(x_2 ≥ ↑8 ∨ x_2 ≥ ↑8) → x_2 ≥ ↑8 → False

theorem splat_mul_known_lz_proof : splat_mul_known_lz_before ⊑ splat_mul_known_lz_after

theorem both_proof : both_before ⊑ both_after

theorem logic_or_logic_and_4_thm.extracted_1._6 : ∀ (x x_1 x_2 : BitVec 1),
  ¬x_2 = 1#1 → ¬x = 1#1 → x_1 = 1#1 → x_2 = x

theorem pr40493_neg3_proof : pr40493_neg3_before ⊑ pr40493_neg3_after

theorem positive_sameconst_lshrexact_thm (e : IntW 8) : shl (lshr e (const? 8 3) { «exact» := true }) (const? 8 3) ⊑ e

theorem udiv2_thm.extracted_1._2 : ∀ (x x_1 : BitVec 32),
  ¬(x ≥ ↑32 ∨ True ∧ x_1.umod (1#32 <<< x) ≠ 0 ∨ 1#32 <<< x = 0) →
    ¬(True ∧ x_1 >>> x <<< x ≠ x_1 ∨ x ≥ ↑32) → x_1 / 1#32 <<< x = x_1 >>> x

@[simp] theorem Com.returnVars_rets : returnVars (rets vs : Com d Γ eff t) = vs

theorem main4d_logical_thm.extracted_1._3 : ∀ (x : BitVec 32),
  ¬ofBool (x &&& 7#32 != 7#32) = 1#1 →
    ofBool (x &&& 16#32 == 0#32) = 1#1 → 0#32 = zeroExtend 32 (ofBool (x &&& 23#32 == 23#32))

theorem lshrugt_01_06_thm.extracted_1._1 : ∀ (x : BitVec 4),
  ¬1#4 ≥ ↑4 → ofBool (6#4 <ᵤ x >>> 1#4) = ofBool (BitVec.ofInt 4 (-3) <ᵤ x)

theorem p0_thm.extracted_1._1 : ∀ (x : BitVec 8),
  ¬(5#8 ≥ ↑8 ∨ True ∧ x <<< 5#8 >>> 5#8 <<< 5#8 ≠ x <<< 5#8 ∨ 5#8 ≥ ↑8) →
    ofBool ((x <<< 5#8).sshiftRight' 5#8 != x) = ofBool (x + BitVec.ofInt 8 (-4) <ᵤ BitVec.ofInt 8 (-8))

theorem trunc_shl_15_i16_i64_thm.extracted_1._1 : ∀ (x : BitVec 64), ¬15#64 ≥ ↑64 → 15#16 ≥ ↑16 → False

theorem udiv_i32_c_proof : udiv_i32_c_before ⊑ udiv_i32_c_after

theorem pr40493_neg2_thm.extracted_1._1 : ∀ (x : BitVec 32),
  ofBool (x * 12#32 &&& 15#32 == 0#32) = ofBool (x * 12#32 &&& 12#32 == 0#32)
</all_available_lemmas>

<local_ctx>
import Batteries.Data.Fin.Basic

import Batteries.Data.Fin.Lemmas

import Blase.SingleWidth.Defs

import Blase.AutoStructs.Constructions

import Blase.AutoStructs.Defs

import Blase.AutoStructs.FiniteStateMachine

import Mathlib.Tactic.Ring

import Mathlib.Data.Nat.Size -- TODO: remove and get rid of shiftLeft_eq_mul_pow use

import Mathlib.Data.BitVec

import Mathlib.Tactic.FinCases

open Fin.NatCast

open Mathlib

structure NFA.correct (M : NFA α σ) (ζ : M.sa) (L : Language α) where
  cond1 : ∀ w, (w ∈ L ↔ ∃ q ∈ M.accept, w ∈ ζ q)
  cond2 : ∀ w q, q ∈ M.eval w ↔ w ∈ ζ q

abbrev BVRel := ∀ ⦃w⦄, BitVec w → BitVec w → Prop

def NFA'.sa2 (M : NFA' 2) := M.σ → BVRel

structure NFA'.correct2 (M : NFA' 2) (ζ : M.sa2) (L : BVRel) where
  cond1 : ∀ (bv1 bv2 : BitVec w), (L bv1 bv2 ↔ ∃ q ∈ M.M.accept, ζ q bv1 bv2)
  cond2 q : q ∈ M.M.start ↔ ζ q .nil .nil
  cond3 q a w (bv1 bv2 : BitVec w) : q ∈ M.M.stepSet { q | ζ q bv1 bv2 } a ↔
              ζ q (BitVec.cons (a.getLsbD 0) bv1) (BitVec.cons (a.getLsbD 1) bv2)

section fsm

variable {arity : Type} [FinEnum arity]

open BitStream in

end fsm

section nfas_relations

inductive NFA.signedCmpState : Type where
| eq | gt | lt | ltfin | gtfin
deriving DecidableEq, Fintype

def NFA.signedCmpStep (q : NFA.signedCmpState) (a : BitVec 2) : List NFA.signedCmpState :=
  match q, a with
  | .eq, 0 => [ .eq ] | .eq, 3 => [.eq] | .eq, 1 => [.gt, .ltfin] | .eq, 2 => [ .lt, .gtfin ]
  | .gt, 0 => [ .gt, .gtfin ] | .gt, 1 => [ .gt, .ltfin ] | .gt, 3 => [ .gt, .gtfin ] | .gt, 2 => [ .lt, .gtfin ]
  | .lt, 0 => [ .lt, .ltfin ] | .lt, 1 => [ .gt, .ltfin ] | .lt, 2 => [ .lt, .gtfin ] | .lt, 3 => [ .lt, .ltfin ]
  | .gtfin, _ => ∅
  | .ltfin, _ => ∅

def NFA.autSignedCmp (cmp: RelationOrdering) : NFA (BitVec 2) signedCmpState where
  step s a := { s' | s' ∈ signedCmpStep s a }
  start := { s | s = signedCmpState.eq }
  accept := { s | s ∈ match cmp with | .lt => [NFA.signedCmpState.ltfin] | .le => [.ltfin, .eq] | .gt => [.gtfin] | .ge => [.gtfin, .eq] }

def NFA'.autSignedCmp (cmp: RelationOrdering) : NFA' 2 :=
  ⟨_, NFA.autSignedCmp cmp⟩

def RelationOrdering.srel (cmp : RelationOrdering) : BVRel :=
  match cmp with
  | .lt => fun _ bv1 bv2 => bv1.slt bv2
  | .le => fun _ bv1 bv2 => bv1.sle bv2
  | .gt => fun _ bv1 bv2 => bv2.slt bv1
  | .ge => fun _ bv1 bv2 => bv2.sle bv1

def NFA'.autSignedCmpSA (q : NFA.signedCmpState) : BVRel :=
  match q with
  | .eq => fun _ bv1 bv2 => bv1 = bv2
  | .lt => fun _ bv1 bv2 => bv1.ult bv2
  | .gt => fun _ bv1 bv2 => bv2.ult bv1
  | .ltfin => fun _ bv1 bv2 => bv1.slt bv2
  | .gtfin => fun _ bv1 bv2 => bv2.slt bv1
</local_ctx>

<target_theorem>
lemma NFA'.autSignedCmp_correct cmp : autSignedCmp cmp |>.correct2 autSignedCmpSA cmp.srel :=
</target_theorem>

