-- Mathlib/.lake/packages/batteries/Batteries/Data/HashMap/WF.lean
theorem WF.filterMap {α β γ} {f : α → β → Option γ} [BEq α] [Hashable α]
    {m : Imp α β} (H : WF m) : WF (filterMap f m)

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β → Option γ
inst✝¹ : BEq α
inst✝ : Hashable α
m : Imp α β
H : m.WF
g₁ : AssocList α β → List (α × γ) :=
  fun l => List.filterMap (fun x => Option.map (fun x_1 => (x.fst, x_1)) (f x.fst x.snd)) l.toList
H1 :
  ∀ (l : AssocList α β) (n : ULift Nat) (acc : AssocList α γ),
    filterMap.go f acc l n = (((g₁ l).reverse ++ acc.toList).toAssocList, { down := n.down + (g₁ l).length })
g : AssocList α β → AssocList α γ := fun l => (g₁ l).reverse.toAssocList
M : Type (max u_3 u_1) → Type (max u_3 u_1) := StateT (ULift Nat) Id
H2 :
  ∀ (l : List (AssocList α β)) (n : ULift Nat),
    List.mapM (filterMap.go f AssocList.nil) l n =
      (List.map g l, { down := n.down + (List.map (fun x => x.toList.length) (List.map g l)).sum })
H3 :
  ∀ (l : List (α × β)),
    (List.map (fun a => a.fst)
          (List.filterMap
            (fun x =>
              match x with
              | (a, b) => Option.map (fun x => (a, x)) (f a b))
            l)).Sublist
      (List.map (fun x => x.fst) l)
⊢ (Imp.filterMap f m).WF

Tactic:
suffices ∀ bk sz (h : 0 < bk.length),
  m.buckets.val.mapM (m := M) (filterMap.go f .nil) ⟨0⟩ = (⟨bk⟩, ⟨sz⟩) →
  WF ⟨sz, ⟨bk⟩, h⟩ from this _ _ _ rfl

Result:
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β → Option γ
inst✝¹ : BEq α
inst✝ : Hashable α
m : Imp α β
H : m.WF
g₁ : AssocList α β → List (α × γ) :=
  fun l => List.filterMap (fun x => Option.map (fun x_1 => (x.fst, x_1)) (f x.fst x.snd)) l.toList
H1 :
  ∀ (l : AssocList α β) (n : ULift Nat) (acc : AssocList α γ),
    filterMap.go f acc l n = (((g₁ l).reverse ++ acc.toList).toAssocList, { down := n.down + (g₁ l).length })
g : AssocList α β → AssocList α γ := fun l => (g₁ l).reverse.toAssocList
M : Type (max u_3 u_1) → Type (max u_3 u_1) := StateT (ULift Nat) Id
H2 :
  ∀ (l : List (AssocList α β)) (n : ULift Nat),
    List.mapM (filterMap.go f AssocList.nil) l n =
      (List.map g l, { down := n.down + (List.map (fun x => x.toList.length) (List.map g l)).sum })
H3 :
  ∀ (l : List (α × β)),
    (List.map (fun a => a.fst)
          (List.filterMap
            (fun x =>
              match x with
              | (a, b) => Option.map (fun x => (a, x)) (f a b))
            l)).Sublist
      (List.map (fun x => x.fst) l)
⊢ ∀ (bk : List (AssocList α γ)) (sz : Nat) (h : 0 < bk.length),
    Array.mapM (filterMap.go f AssocList.nil) m.buckets.val { down := 0 } = ({ toList := bk }, { down := sz }) →
      { size := sz, buckets := ⟨{ toList := bk }, h⟩ }.WF

Proof state:
case cons
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β → Option γ
inst✝¹ : BEq α
inst✝ : Hashable α
m : Imp α β
H : m.WF
g₁ : AssocList α β → List (α × γ) :=
  fun l => List.filterMap (fun x => Option.map (fun x_1 => (x.fst, x_1)) (f x.fst x.snd)) l.toList
H1 :
  ∀ (l : AssocList α β) (n : ULift Nat) (acc : AssocList α γ),
    filterMap.go f acc l n = (((g₁ l).reverse ++ acc.toList).toAssocList, { down := n.down + (g₁ l).length })
g : AssocList α β → AssocList α γ := fun l => (g₁ l).reverse.toAssocList
M : Type (max u_3 u_1) → Type (max u_3 u_1) := StateT (ULift Nat) Id
l : AssocList α β
L : List (AssocList α β)
IH :
  ∀ (n : ULift Nat),
    List.mapM (filterMap.go f AssocList.nil) L n =
      (List.map g L, { down := n.down + (List.map (fun x => x.toList.length) (List.map g L)).sum })
n : ULift Nat
⊢ List.mapM (filterMap.go f AssocList.nil) (l :: L) n =
    (List.map g (l :: L), { down := n.down + (List.map (fun x => x.toList.length) (List.map g (l :: L))).sum })

Tactic:
simp [bind, StateT.bind, IH, H1, Nat.add_assoc, g]

Result:
case cons
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β → Option γ
inst✝¹ : BEq α
inst✝ : Hashable α
m : Imp α β
H : m.WF
g₁ : AssocList α β → List (α × γ) :=
  fun l => List.filterMap (fun x => Option.map (fun x_1 => (x.fst, x_1)) (f x.fst x.snd)) l.toList
H1 :
  ∀ (l : AssocList α β) (n : ULift Nat) (acc : AssocList α γ),
    filterMap.go f acc l n = (((g₁ l).reverse ++ acc.toList).toAssocList, { down := n.down + (g₁ l).length })
g : AssocList α β → AssocList α γ := fun l => (g₁ l).reverse.toAssocList
M : Type (max u_3 u_1) → Type (max u_3 u_1) := StateT (ULift Nat) Id
l : AssocList α β
L : List (AssocList α β)
IH :
  ∀ (n : ULift Nat),
    List.mapM (filterMap.go f AssocList.nil) L n =
      (List.map g L, { down := n.down + (List.map (fun x => x.toList.length) (List.map g L)).sum })
n : ULift Nat
⊢ pure ((g₁ l).reverse.toAssocList :: List.map (fun l => (g₁ l).reverse.toAssocList) L)
      {
        down :=
          n.down +
            ((g₁ l).length + (List.map ((fun x => x.toList.length) ∘ fun l => (g₁ l).reverse.toAssocList) L).sum) } =
    ((g₁ l).reverse.toAssocList :: List.map (fun l => (g₁ l).reverse.toAssocList) L,
      {
        down :=
          n.down +
            ((g₁ l).length + (List.map ((fun x => x.toList.length) ∘ fun l => (g₁ l).reverse.toAssocList) L).sum) })