-- Mathlib/.lake/packages/lean4/src/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Lemmas/Operations/Mul.lean
theorem denote_blast (aig : AIG BVBit) (lhs rhs : BitVec w) (assign : Assignment)
      (input : BinaryRefVec aig w)
      (hleft : ∀ (idx : Nat) (hidx : idx < w), ⟦aig, input.lhs.get idx hidx, assign.toAIGAssignment⟧ = lhs.getLsbD idx)
      (hright : ∀ (idx : Nat) (hidx : idx < w), ⟦aig, input.rhs.get idx hidx, assign.toAIGAssignment⟧ = rhs.getLsbD idx) :
      ∀ (idx : Nat) (hidx : idx < w),
        ⟦(blast aig input).aig, (blast aig input).vec.get idx hidx, assign.toAIGAssignment⟧
          =
        (lhs * rhs).getLsbD idx

Proof state:
aig : AIG BVBit
assign : Assignment
idx✝ w : Nat
lhs rhs : BitVec w.succ
input : aig.BinaryRefVec w.succ
hleft :
  ∀ (idx : Nat) (hidx : idx < w.succ),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.lhs.get idx hidx }⟧ = lhs.getLsbD idx
hright :
  ∀ (idx : Nat) (hidx : idx < w.succ),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.rhs.get idx hidx }⟧ = rhs.getLsbD idx
hidx✝ : idx✝ < w.succ
res : RefVecEntry BVBit w.succ
hne : ¬w.succ = 0
hb :
  go
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).aig
      ((input.cast ⋯).lhs.cast ⋯) ((input.cast ⋯).rhs.cast ⋯) 1
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).vec =
    res
idx : Nat
hidx : idx < w.succ
heq✝ : rhs.getLsbD 0 = false
heq : ⟦assign.toAIGAssignment, { aig := aig, ref := input.rhs.get 0 ?hidx }⟧ = false
⊢ (⟦assign.toAIGAssignment, { aig := (blastConst aig 0#(w + 1)).aig, ref := (input.rhs.get 0 ⋯).cast ⋯ }⟧ &&
      ⟦assign.toAIGAssignment, { aig := (blastConst aig 0#(w + 1)).aig, ref := (input.lhs.get idx hidx).cast ⋯ }⟧) =
    (0#(w + 1)).getLsbD idx

Tactic:
rw [AIG.LawfulVecOperator.denote_mem_prefix (f := blastConst)]

Result:
aig : AIG BVBit
assign : Assignment
idx✝ w : Nat
lhs rhs : BitVec w.succ
input : aig.BinaryRefVec w.succ
hleft :
  ∀ (idx : Nat) (hidx : idx < w.succ),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.lhs.get idx hidx }⟧ = lhs.getLsbD idx
hright :
  ∀ (idx : Nat) (hidx : idx < w.succ),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.rhs.get idx hidx }⟧ = rhs.getLsbD idx
hidx✝ : idx✝ < w.succ
res : RefVecEntry BVBit w.succ
hne : ¬w.succ = 0
hb :
  go
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).aig
      ((input.cast ⋯).lhs.cast ⋯) ((input.cast ⋯).rhs.cast ⋯) 1
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).vec =
    res
idx : Nat
hidx : idx < w.succ
heq✝ : rhs.getLsbD 0 = false
heq : ⟦assign.toAIGAssignment, { aig := aig, ref := input.rhs.get 0 ?hidx }⟧ = false
⊢ (⟦assign.toAIGAssignment, { aig := aig, ref := { gate := ((input.rhs.get 0 ⋯).cast ⋯).gate, hgate := ?m.36386 } }⟧ &&
      ⟦assign.toAIGAssignment, { aig := (blastConst aig 0#(w + 1)).aig, ref := (input.lhs.get idx hidx).cast ⋯ }⟧) =
    (0#(w + 1)).getLsbD idx

aig : AIG BVBit
assign : Assignment
idx✝ w : Nat
lhs rhs : BitVec w.succ
input : aig.BinaryRefVec w.succ
hleft :
  ∀ (idx : Nat) (hidx : idx < w.succ),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.lhs.get idx hidx }⟧ = lhs.getLsbD idx
hright :
  ∀ (idx : Nat) (hidx : idx < w.succ),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.rhs.get idx hidx }⟧ = rhs.getLsbD idx
hidx✝ : idx✝ < w.succ
res : RefVecEntry BVBit w.succ
hne : ¬w.succ = 0
hb :
  go
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).aig
      ((input.cast ⋯).lhs.cast ⋯) ((input.cast ⋯).rhs.cast ⋯) 1
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).vec =
    res
idx : Nat
hidx : idx < w.succ
heq✝ : rhs.getLsbD 0 = false
heq : ⟦assign.toAIGAssignment, { aig := aig, ref := input.rhs.get 0 ?hidx }⟧ = false
⊢ ((input.rhs.get 0 ⋯).cast ⋯).gate < aig.decls.size

Proof state:
aig : AIG BVBit
assign : Assignment
idx✝ w : Nat
lhs rhs : BitVec w.succ
input : aig.BinaryRefVec w.succ
hleft :
  ∀ (idx : Nat) (hidx : idx < w.succ),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.lhs.get idx hidx }⟧ = lhs.getLsbD idx
hright :
  ∀ (idx : Nat) (hidx : idx < w.succ),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.rhs.get idx hidx }⟧ = rhs.getLsbD idx
hidx✝ : idx✝ < w.succ
res : RefVecEntry BVBit w.succ
hne : ¬w.succ = 0
hb :
  go
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).aig
      ((input.cast ⋯).lhs.cast ⋯) ((input.cast ⋯).rhs.cast ⋯) 1
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).vec =
    res
idx : Nat
hidx : idx < w.succ
heq✝ : rhs.getLsbD 0 = true
heq : ⟦assign.toAIGAssignment, { aig := aig, ref := input.rhs.get 0 ?hidx }⟧ = true
⊢ ((input.rhs.get 0 ⋯).cast ⋯).gate < aig.decls.size

Tactic:
simp [Ref.hgate]

Result:
no goals

Proof state:
w : Nat
aig : AIG BVBit
lhs rhs : BitVec w
assign : Assignment
input : aig.BinaryRefVec w
hleft :
  ∀ (idx : Nat) (hidx : idx < w),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.lhs.get idx hidx }⟧ = lhs.getLsbD idx
hright :
  ∀ (idx : Nat) (hidx : idx < w),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.rhs.get idx hidx }⟧ = rhs.getLsbD idx
idx : Nat
hidx : idx < w
res : RefVecEntry BVBit w
hne : ¬w = 0
hb :
  go
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).aig
      ((input.cast ⋯).lhs.cast ⋯) ((input.cast ⋯).rhs.cast ⋯) 1
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).vec =
    res
⊢ ⟦assign.toAIGAssignment, { aig := res.aig, ref := res.vec.get idx hidx }⟧ = (lhs.mulRec rhs w).getLsbD idx

Tactic:
have := Nat.exists_eq_succ_of_ne_zero hne

Result:
w : Nat
aig : AIG BVBit
lhs rhs : BitVec w
assign : Assignment
input : aig.BinaryRefVec w
hleft :
  ∀ (idx : Nat) (hidx : idx < w),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.lhs.get idx hidx }⟧ = lhs.getLsbD idx
hright :
  ∀ (idx : Nat) (hidx : idx < w),
    ⟦assign.toAIGAssignment, { aig := aig, ref := input.rhs.get idx hidx }⟧ = rhs.getLsbD idx
idx : Nat
hidx : idx < w
res : RefVecEntry BVBit w
hne : ¬w = 0
hb :
  go
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).aig
      ((input.cast ⋯).lhs.cast ⋯) ((input.cast ⋯).rhs.cast ⋯) 1
      (RefVec.ite (blastConst aig 0).aig
          { discr := (input.cast ⋯).rhs.get 0 ⋯, lhs := (input.cast ⋯).lhs, rhs := (blastConst aig 0).vec }).vec =
    res
this : ∃ k, w = k.succ
⊢ ⟦assign.toAIGAssignment, { aig := res.aig, ref := res.vec.get idx hidx }⟧ = (lhs.mulRec rhs w).getLsbD idx