-- 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:
α : 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✝⟩
⊢ (let bucket := m.1.buckets[i✝];
    if AssocList.contains a bucket = true then
      let buckets := m.1.buckets.uset i✝ AssocList.nil h✝;
      let bucket := AssocList.modify a f bucket;
      ⟨{ size := m.1.size, buckets := buckets.uset i✝ bucket ⋯ }, ⋯⟩
    else ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩) =
    let bkt := m.1.buckets[i✝];
    if AssocList.contains a bkt = true then
      let buckets' := m.1.buckets.uset i✝ AssocList.nil h✝;
      let bkt' := AssocList.alter a (fun x => Option.map f x) bkt;
      let size' := if AssocList.contains a bkt' = true then m.1.size else m.1.size - 1;
      ⟨{ size := size', buckets := buckets'.uset i✝ bkt' ⋯ }, ⋯⟩
    else
      match Option.map f none with
      | none => ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩
      | some b =>
        let size' := m.1.size + 1;
        let buckets' := m.1.buckets.uset i✝ (AssocList.cons a b bkt) h✝;
        expandIfNecessary ⟨{ size := size', buckets := buckets' }, ⋯⟩

Tactic:
dsimp

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✝⟩
⊢ (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 }, ⋯⟩

Proof state:
α : Type u
β : α → Type v
inst✝² : BEq α
inst✝¹ : Hashable α
inst✝ : LawfulBEq α
m : Raw₀ α β
a : α
f : β a → β a
⊢ m.modify a f = m.alter a fun x => Option.map f x

Tactic:
rw [modify, alter]

Result:
α : Type u
β : α → Type v
inst✝² : BEq α
inst✝¹ : Hashable α
inst✝ : LawfulBEq α
m : Raw₀ α β
a : α
f : β a → β a
⊢ (match mkIdx m.1.buckets.size ⋯ (hash a) with
    | ⟨i, hi⟩ =>
      let bucket := m.1.buckets[i];
      if AssocList.contains a bucket = true then
        let buckets := m.1.buckets.uset i AssocList.nil hi;
        let bucket := AssocList.modify a f bucket;
        ⟨{ size := m.1.size, buckets := buckets.uset i bucket ⋯ }, ⋯⟩
      else ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩) =
    match mkIdx m.1.buckets.size ⋯ (hash a) with
    | ⟨i, h⟩ =>
      let bkt := m.1.buckets[i];
      if AssocList.contains a bkt = true then
        let buckets' := m.1.buckets.uset i AssocList.nil h;
        let bkt' := AssocList.alter a (fun x => Option.map f x) bkt;
        let size' := if AssocList.contains a bkt' = true then m.1.size else m.1.size - 1;
        ⟨{ size := size', buckets := buckets'.uset i bkt' ⋯ }, ⋯⟩
      else
        match Option.map f none with
        | none => ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩
        | some b =>
          let size' := m.1.size + 1;
          let buckets' := m.1.buckets.uset i (AssocList.cons a b bkt) h;
          expandIfNecessary ⟨{ size := size', buckets := buckets' }, ⋯⟩

Proof state:
α : Type u
β : α → Type v
inst✝² : BEq α
inst✝¹ : Hashable α
inst✝ : LawfulBEq α
m : Raw₀ α β
a : α
f : β a → β a
⊢ (match mkIdx m.1.buckets.size ⋯ (hash a) with
    | ⟨i, hi⟩ =>
      let bucket := m.1.buckets[i];
      if AssocList.contains a bucket = true then
        let buckets := m.1.buckets.uset i AssocList.nil hi;
        let bucket := AssocList.modify a f bucket;
        ⟨{ size := m.1.size, buckets := buckets.uset i bucket ⋯ }, ⋯⟩
      else ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩) =
    match mkIdx m.1.buckets.size ⋯ (hash a) with
    | ⟨i, h⟩ =>
      let bkt := m.1.buckets[i];
      if AssocList.contains a bkt = true then
        let buckets' := m.1.buckets.uset i AssocList.nil h;
        let bkt' := AssocList.alter a (fun x => Option.map f x) bkt;
        let size' := if AssocList.contains a bkt' = true then m.1.size else m.1.size - 1;
        ⟨{ size := size', buckets := buckets'.uset i bkt' ⋯ }, ⋯⟩
      else
        match Option.map f none with
        | none => ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩
        | some b =>
          let size' := m.1.size + 1;
          let buckets' := m.1.buckets.uset i (AssocList.cons a b bkt) h;
          expandIfNecessary ⟨{ size := size', buckets := buckets' }, ⋯⟩

Tactic:
split

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✝⟩
⊢ (let bucket := m.1.buckets[i✝];
    if AssocList.contains a bucket = true then
      let buckets := m.1.buckets.uset i✝ AssocList.nil h✝;
      let bucket := AssocList.modify a f bucket;
      ⟨{ size := m.1.size, buckets := buckets.uset i✝ bucket ⋯ }, ⋯⟩
    else ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩) =
    let bkt := m.1.buckets[i✝];
    if AssocList.contains a bkt = true then
      let buckets' := m.1.buckets.uset i✝ AssocList.nil h✝;
      let bkt' := AssocList.alter a (fun x => Option.map f x) bkt;
      let size' := if AssocList.contains a bkt' = true then m.1.size else m.1.size - 1;
      ⟨{ size := size', buckets := buckets'.uset i✝ bkt' ⋯ }, ⋯⟩
    else
      match Option.map f none with
      | none => ⟨{ size := m.1.size, buckets := m.1.buckets }, ⋯⟩
      | some b =>
        let size' := m.1.size + 1;
        let buckets' := m.1.buckets.uset i✝ (AssocList.cons a b bkt) h✝;
        expandIfNecessary ⟨{ size := size', buckets := buckets' }, ⋯⟩

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 : 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]) ⋯ },
      ⋯⟩

Tactic:
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