-- Mathlib/.lake/packages/lean4/src/lean/Std/Data/DHashMap/Internal/Model.lean
theorem modify_eq_alter [BEq α] [Hashable α] [LawfulBEq α] (m : Raw₀ α β) (a : α)
    (f : β a → β a) : m.modify a f = m.alter a (·.map f)

Proof state:
case isTrue
α : Type u
β : α → Type v
inst✝² : BEq α
inst✝¹ : Hashable α
inst✝ : LawfulBEq α
m : Raw₀ α β
a : α
f : β a → β a
x✝ : { u // u.toNat < m.1.buckets.size }
i✝ : USize
h✝¹ : i✝.toNat < m.1.buckets.size
heq✝ : mkIdx m.1.buckets.size ⋯ (hash a) = ⟨i✝, h✝¹⟩
h✝ : AssocList.contains a m.1.buckets[i✝.toNat] = true
⊢ ⟨{ size := m.1.size,
        buckets :=
          (m.1.buckets.set i✝.toNat AssocList.nil h✝¹).set i✝.toNat (AssocList.modify a f m.1.buckets[i✝.toNat]) ⋯ },
      ⋯⟩ =
    ⟨{
        size :=
          if AssocList.contains a (AssocList.alter a (fun x => Option.map f x) m.1.buckets[i✝.toNat]) = true then
            m.1.size
          else m.1.size - 1,
        buckets :=
          (m.1.buckets.set i✝.toNat AssocList.nil h✝¹).set i✝.toNat
            (AssocList.alter a (fun x => Option.map f x) m.1.buckets[i✝.toNat]) ⋯ },
      ⋯⟩

Tactic:
next h =>
simp only [AssocList.contains_eq] at h
simp only [AssocList.modify_eq_alter, Array.set_set, AssocList.contains_eq,
  containsKey_of_perm AssocList.toList_alter, ← modifyKey_eq_alterKey,
  containsKey_modifyKey, h, reduceIte]

Result:
no goals

Proof state:
case isFalse
α : Type u
β : α → Type v
inst✝² : BEq α
inst✝¹ : Hashable α
inst✝ : LawfulBEq α
m : Raw₀ α β
a : α
f : β a → β a
x✝ : { u // u.toNat < m.1.buckets.size }
i✝ : USize
h✝¹ : i✝.toNat < m.1.buckets.size
heq✝ : mkIdx m.1.buckets.size ⋯ (hash a) = ⟨i✝, h✝¹⟩
h✝ : ¬AssocList.contains a m.1.buckets[i✝.toNat] = true
⊢ ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩ = ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩

Tactic:
rfl

Result:
no goals

Proof state:
α : Type u
β : α → Type v
inst✝² : BEq α
inst✝¹ : Hashable α
inst✝ : LawfulBEq α
m : Raw₀ α β
a : α
f : β a → β a
x✝ : { u // u.toNat < m.1.buckets.size }
i✝ : USize
h✝ : i✝.toNat < m.1.buckets.size
heq✝ : mkIdx m.1.buckets.size ⋯ (hash a) = ⟨i✝, h✝⟩
h : AssocList.contains a m.1.buckets[i✝.toNat] = true
⊢ ⟨{ size := m.1.size,
        buckets :=
          (m.1.buckets.set i✝.toNat AssocList.nil h✝).set i✝.toNat (AssocList.modify a f m.1.buckets[i✝.toNat]) ⋯ },
      ⋯⟩ =
    ⟨{
        size :=
          if AssocList.contains a (AssocList.alter a (fun x => Option.map f x) m.1.buckets[i✝.toNat]) = true then
            m.1.size
          else m.1.size - 1,
        buckets :=
          (m.1.buckets.set i✝.toNat AssocList.nil h✝).set i✝.toNat
            (AssocList.alter a (fun x => Option.map f x) m.1.buckets[i✝.toNat]) ⋯ },
      ⋯⟩

Tactic:
simp only [AssocList.contains_eq] at h

Result:
α : Type u
β : α → Type v
inst✝² : BEq α
inst✝¹ : Hashable α
inst✝ : LawfulBEq α
m : Raw₀ α β
a : α
f : β a → β a
x✝ : { u // u.toNat < m.1.buckets.size }
i✝ : USize
h✝ : i✝.toNat < m.1.buckets.size
heq✝ : mkIdx m.1.buckets.size ⋯ (hash a) = ⟨i✝, h✝⟩
h : containsKey a m.1.buckets[i✝.toNat].toList = true
⊢ ⟨{ size := m.1.size,
        buckets :=
          (m.1.buckets.set i✝.toNat AssocList.nil h✝).set i✝.toNat (AssocList.modify a f m.1.buckets[i✝.toNat]) ⋯ },
      ⋯⟩ =
    ⟨{
        size :=
          if AssocList.contains a (AssocList.alter a (fun x => Option.map f x) m.1.buckets[i✝.toNat]) = true then
            m.1.size
          else m.1.size - 1,
        buckets :=
          (m.1.buckets.set i✝.toNat AssocList.nil h✝).set i✝.toNat
            (AssocList.alter a (fun x => Option.map f x) m.1.buckets[i✝.toNat]) ⋯ },
      ⋯⟩

Proof state:
α : Type u
β : α → Type v
inst✝² : BEq α
inst✝¹ : Hashable α
inst✝ : LawfulBEq α
m : Raw₀ α β
a : α
f : β a → β a
x✝ : { u // u.toNat < m.1.buckets.size }
i✝ : USize
h✝ : i✝.toNat < m.1.buckets.size
heq✝ : mkIdx m.1.buckets.size ⋯ (hash a) = ⟨i✝, h✝⟩
⊢ (if AssocList.contains a m.1.buckets[i✝.toNat] = true then
      ⟨{ size := m.1.size,
          buckets :=
            (m.1.buckets.set i✝.toNat AssocList.nil h✝).set i✝.toNat (AssocList.modify a f m.1.buckets[i✝.toNat]) ⋯ },
        ⋯⟩
    else ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩) =
    if AssocList.contains a m.1.buckets[i✝.toNat] = true then
      ⟨{
          size :=
            if AssocList.contains a (AssocList.alter a (fun x => Option.map f x) m.1.buckets[i✝.toNat]) = true then
              m.1.size
            else m.1.size - 1,
          buckets :=
            (m.1.buckets.set i✝.toNat AssocList.nil h✝).set i✝.toNat
              (AssocList.alter a (fun x => Option.map f x) m.1.buckets[i✝.toNat]) ⋯ },
        ⋯⟩
    else ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩

Tactic:
split

Result:
case isTrue
α : Type u
β : α → Type v
inst✝² : BEq α
inst✝¹ : Hashable α
inst✝ : LawfulBEq α
m : Raw₀ α β
a : α
f : β a → β a
x✝ : { u // u.toNat < m.1.buckets.size }
i✝ : USize
h✝¹ : i✝.toNat < m.1.buckets.size
heq✝ : mkIdx m.1.buckets.size ⋯ (hash a) = ⟨i✝, h✝¹⟩
h✝ : AssocList.contains a m.1.buckets[i✝.toNat] = true
⊢ ⟨{ size := m.1.size,
        buckets :=
          (m.1.buckets.set i✝.toNat AssocList.nil h✝¹).set i✝.toNat (AssocList.modify a f m.1.buckets[i✝.toNat]) ⋯ },
      ⋯⟩ =
    ⟨{
        size :=
          if AssocList.contains a (AssocList.alter a (fun x => Option.map f x) m.1.buckets[i✝.toNat]) = true then
            m.1.size
          else m.1.size - 1,
        buckets :=
          (m.1.buckets.set i✝.toNat AssocList.nil h✝¹).set i✝.toNat
            (AssocList.alter a (fun x => Option.map f x) m.1.buckets[i✝.toNat]) ⋯ },
      ⋯⟩

case isFalse
α : Type u
β : α → Type v
inst✝² : BEq α
inst✝¹ : Hashable α
inst✝ : LawfulBEq α
m : Raw₀ α β
a : α
f : β a → β a
x✝ : { u // u.toNat < m.1.buckets.size }
i✝ : USize
h✝¹ : i✝.toNat < m.1.buckets.size
heq✝ : mkIdx m.1.buckets.size ⋯ (hash a) = ⟨i✝, h✝¹⟩
h✝ : ¬AssocList.contains a m.1.buckets[i✝.toNat] = true
⊢ ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩ = ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩