-- Mathlib/.lake/packages/lean4/src/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Lemmas/Expr.lean
theorem go_denote_eq (aig : AIG BVBit) (expr : BVExpr w) (assign : Assignment) :
    ∀ (idx : Nat) (hidx : idx < w),
        ⟦(go aig expr).val.aig, (go aig expr).val.vec.get idx hidx, assign.toAIGAssignment⟧
          =
        (expr.eval assign).getLsbD idx

Proof state:
w✝ : Nat
assign : Assignment
w start len : Nat
inner : BVExpr w
ih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w),
    ⟦assign.toAIGAssignment, { aig := (go aig inner).val.aig, ref := (go aig inner).val.vec.get idx hidx }⟧ =
      (eval assign inner).getLsbD idx
aig : AIG BVBit
idx : Nat
hidx : idx < len
hsplit : start + idx < w
⊢ ⟦assign.toAIGAssignment, { aig := (go aig inner).1.aig, ref := (go aig inner).1.vec.get (start + idx) ⋯ }⟧ =
    (eval assign inner).getLsbD (start + idx)

Tactic:
rw [ih]

Result:
no goals

Proof state:
case append.isFalse
w : Nat
assign : Assignment
lw rw : Nat
lhs : BVExpr lw
rhs : BVExpr rw
lih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < lw),
    ⟦assign.toAIGAssignment, { aig := (go aig lhs).val.aig, ref := (go aig lhs).val.vec.get idx hidx }⟧ =
      (eval assign lhs).getLsbD idx
rih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < rw),
    ⟦assign.toAIGAssignment, { aig := (go aig rhs).val.aig, ref := (go aig rhs).val.vec.get idx hidx }⟧ =
      (eval assign rhs).getLsbD idx
aig : AIG BVBit
idx : Nat
hidx : idx < lw + rw
h✝ : ¬idx < rw
⊢ ⟦assign.toAIGAssignment,
      { aig := (go (go aig lhs).1.aig rhs).1.aig,
        ref := { gate := ((go aig lhs).1.vec.get (idx - rw) ⋯).gate, hgate := ⋯ } }⟧ =
    (eval assign lhs).getLsbD (idx - rw)

Tactic:
next hsplit => rw [go_denote_mem_prefix, lih]

Result:
no goals

Proof state:
case un.rotateRight
w : Nat
assign : Assignment
w✝ : Nat
expr : BVExpr w✝
ih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig expr).val.aig, ref := (go aig expr).val.vec.get idx hidx }⟧ =
      (eval assign expr).getLsbD idx
aig : AIG BVBit
idx : Nat
hidx : idx < w✝
n✝ : Nat
⊢ ⟦assign.toAIGAssignment,
      { aig := (go aig (un (BVUnOp.rotateRight n✝) expr)).val.aig,
        ref := (go aig (un (BVUnOp.rotateRight n✝) expr)).val.vec.get idx hidx }⟧ =
    (eval assign (un (BVUnOp.rotateRight n✝) expr)).getLsbD idx

Tactic:
simp [go, ih, hidx]

Result:
no goals

Proof state:
case un.arithShiftRightConst
w : Nat
assign : Assignment
w✝ : Nat
expr : BVExpr w✝
ih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig expr).val.aig, ref := (go aig expr).val.vec.get idx hidx }⟧ =
      (eval assign expr).getLsbD idx
aig : AIG BVBit
idx : Nat
hidx : idx < w✝
n : Nat
⊢ ⟦assign.toAIGAssignment,
      { aig := (go aig (un (BVUnOp.arithShiftRightConst n) expr)).val.aig,
        ref := (go aig (un (BVUnOp.arithShiftRightConst n) expr)).val.vec.get idx hidx }⟧ =
    (eval assign (un (BVUnOp.arithShiftRightConst n) expr)).getLsbD idx

Tactic:
rename_i w

Result:
case un.arithShiftRightConst
w✝ : Nat
assign : Assignment
w : Nat
expr : BVExpr w
ih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w),
    ⟦assign.toAIGAssignment, { aig := (go aig expr).val.aig, ref := (go aig expr).val.vec.get idx hidx }⟧ =
      (eval assign expr).getLsbD idx
aig : AIG BVBit
idx : Nat
hidx : idx < w
n : Nat
⊢ ⟦assign.toAIGAssignment,
      { aig := (go aig (un (BVUnOp.arithShiftRightConst n) expr)).val.aig,
        ref := (go aig (un (BVUnOp.arithShiftRightConst n) expr)).val.vec.get idx hidx }⟧ =
    (eval assign (un (BVUnOp.arithShiftRightConst n) expr)).getLsbD idx

Proof state:
case bin.xor
w : Nat
assign : Assignment
w✝ : Nat
lhs rhs : BVExpr w✝
lih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig lhs).val.aig, ref := (go aig lhs).val.vec.get idx hidx }⟧ =
      (eval assign lhs).getLsbD idx
rih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig rhs).val.aig, ref := (go aig rhs).val.vec.get idx hidx }⟧ =
      (eval assign rhs).getLsbD idx
aig : AIG BVBit
idx : Nat
hidx : idx < w✝
⊢ (⟦assign.toAIGAssignment,
        { aig := (go (go aig lhs).1.aig rhs).1.aig, ref := ((go aig lhs).1.vec.cast ⋯).get idx hidx }⟧ ^^
      (eval assign rhs).getLsbD idx) =
    ((eval assign lhs).getLsbD idx ^^ (eval assign rhs).getLsbD idx)

Tactic:
simp only [go_val_eq_bitblast, RefVec.get_cast]

Result:
case bin.xor
w : Nat
assign : Assignment
w✝ : Nat
lhs rhs : BVExpr w✝
lih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig lhs).val.aig, ref := (go aig lhs).val.vec.get idx hidx }⟧ =
      (eval assign lhs).getLsbD idx
rih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig rhs).val.aig, ref := (go aig rhs).val.vec.get idx hidx }⟧ =
      (eval assign rhs).getLsbD idx
aig : AIG BVBit
idx : Nat
hidx : idx < w✝
⊢ (⟦assign.toAIGAssignment,
        { aig := (bitblast (bitblast aig lhs).aig rhs).aig, ref := ((bitblast aig lhs).vec.get idx hidx).cast ⋯ }⟧ ^^
      (eval assign rhs).getLsbD idx) =
    ((eval assign lhs).getLsbD idx ^^ (eval assign rhs).getLsbD idx)

Proof state:
case bin.add.hleft
w : Nat
assign : Assignment
w✝ : Nat
lhs rhs : BVExpr w✝
lih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig lhs).val.aig, ref := (go aig lhs).val.vec.get idx hidx }⟧ =
      (eval assign lhs).getLsbD idx
rih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig rhs).val.aig, ref := (go aig rhs).val.vec.get idx hidx }⟧ =
      (eval assign rhs).getLsbD idx
aig : AIG BVBit
idx : Nat
hidx : idx < w✝
idx✝ : Nat
hidx✝ : idx✝ < w✝
⊢ ⟦assign.toAIGAssignment,
      { aig := (go (go aig lhs).1.aig rhs).1.aig, ref := ((go aig lhs).1.vec.cast ⋯).get idx✝ hidx✝ }⟧ =
    (eval assign lhs).getLsbD idx✝

Tactic:
rw [go_denote_mem_prefix]

Result:
case bin.add.hleft
w : Nat
assign : Assignment
w✝ : Nat
lhs rhs : BVExpr w✝
lih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig lhs).val.aig, ref := (go aig lhs).val.vec.get idx hidx }⟧ =
      (eval assign lhs).getLsbD idx
rih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig rhs).val.aig, ref := (go aig rhs).val.vec.get idx hidx }⟧ =
      (eval assign rhs).getLsbD idx
aig : AIG BVBit
idx : Nat
hidx : idx < w✝
idx✝ : Nat
hidx✝ : idx✝ < w✝
⊢ ⟦assign.toAIGAssignment,
      { aig := (go aig lhs).1.aig,
        ref := { gate := (((go aig lhs).1.vec.cast ⋯).get idx✝ hidx✝).gate, hgate := ?bin.add.hleft.hstart } }⟧ =
    (eval assign lhs).getLsbD idx✝

case bin.add.hleft.hstart
w : Nat
assign : Assignment
w✝ : Nat
lhs rhs : BVExpr w✝
lih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig lhs).val.aig, ref := (go aig lhs).val.vec.get idx hidx }⟧ =
      (eval assign lhs).getLsbD idx
rih :
  ∀ (aig : AIG BVBit) (idx : Nat) (hidx : idx < w✝),
    ⟦assign.toAIGAssignment, { aig := (go aig rhs).val.aig, ref := (go aig rhs).val.vec.get idx hidx }⟧ =
      (eval assign rhs).getLsbD idx
aig : AIG BVBit
idx : Nat
hidx : idx < w✝
idx✝ : Nat
hidx✝ : idx✝ < w✝
⊢ (((go aig lhs).1.vec.cast ⋯).get idx✝ hidx✝).gate < (go aig lhs).1.aig.decls.size