l3lab/ntp-mathlib huggingface dataset:
/-- Lift a permutation-respecting function on 2 `AList`s to 2 `Finmap`s. -/
-- @[elab_as_elim] Porting note: we can't add `elab_as_elim` attr in this type
def liftOn₂ {γ} (s₁ s₂ : Finmap β) (f : AList β → AList β → γ)
    (H : ∀ a₁ b₁ a₂ b₂ : AList β,
      a₁.entries ~ a₂.entries → b₁.entries ~ b₂.entries → f a₁ b₁ = f a₂ b₂) : γ :=
  liftOn s₁ (fun l₁ => liftOn s₂ (f l₁) fun b₁ b₂ p => H _ _ _ _ (Perm.refl _) p) fun a₁ a₂ p => by
    have H' : f a₁ = f a₂ := funext fun _ => H _ _ _ _ p (Perm.refl _)
    

Proof state:
α : Type u
β : α → Type v
γ : Type ?u.20108
s₁ s₂ : Finmap β
f : AList β → AList β → γ
H : ∀ (a₁ b₁ a₂ b₂ : AList β), a₁.entries ~ a₂.entries → b₁.entries ~ b₂.entries → f a₁ b₁ = f a₂ b₂
a₁ a₂ : AList β
p : a₁.entries ~ a₂.entries
H' : f a₁ = f a₂
⊢ (fun l₁ => liftOn s₂ (f l₁) (_ : ∀ (b₁ b₂ : AList β), b₁.entries ~ b₂.entries → f l₁ b₁ = f l₁ b₂)) a₁ =
    (fun l₁ => liftOn s₂ (f l₁) (_ : ∀ (b₁ b₂ : AList β), b₁.entries ~ b₂.entries → f l₁ b₁ = f l₁ b₂)) a₂

Next tactic:
simp only [H']
@[simp]
theorem liftOn₂_toFinmap {γ} (s₁ s₂ : AList β) (f : AList β → AList β → γ) (H) :
    liftOn₂ ⟦s₁⟧ ⟦s₂⟧ f H = f s₁ s₂ :=
      by 

Proof state:
α : Type u
β : α → Type v
γ : Type u_1
s₁ s₂ : AList β
f : AList β → AList β → γ
H : ∀ (a₁ b₁ a₂ b₂ : AList β), a₁.entries ~ a₂.entries → b₁.entries ~ b₂.entries → f a₁ b₁ = f a₂ b₂
⊢ liftOn₂ ⟦s₁⟧ ⟦s₂⟧ f H = f s₁ s₂

Next tactic:
cases s₁
@[simp]
theorem liftOn₂_toFinmap {γ} (s₁ s₂ : AList β) (f : AList β → AList β → γ) (H) :
    liftOn₂ ⟦s₁⟧ ⟦s₂⟧ f H = f s₁ s₂ :=
      by cases s₁; 

Proof state:
case mk
α : Type u
β : α → Type v
γ : Type u_1
s₂ : AList β
f : AList β → AList β → γ
H : ∀ (a₁ b₁ a₂ b₂ : AList β), a₁.entries ~ a₂.entries → b₁.entries ~ b₂.entries → f a₁ b₁ = f a₂ b₂
entries✝ : List (Sigma β)
nodupKeys✝ : NodupKeys entries✝
⊢ liftOn₂ ⟦{ entries := entries✝, nodupKeys := nodupKeys✝ }⟧ ⟦s₂⟧ f H =
    f { entries := entries✝, nodupKeys := nodupKeys✝ } s₂

Next tactic:
cases s₂
@[simp]
theorem liftOn₂_toFinmap {γ} (s₁ s₂ : AList β) (f : AList β → AList β → γ) (H) :
    liftOn₂ ⟦s₁⟧ ⟦s₂⟧ f H = f s₁ s₂ :=
      by cases s₁; cases s₂; 

Proof state:
case mk.mk
α : Type u
β : α → Type v
γ : Type u_1
f : AList β → AList β → γ
H : ∀ (a₁ b₁ a₂ b₂ : AList β), a₁.entries ~ a₂.entries → b₁.entries ~ b₂.entries → f a₁ b₁ = f a₂ b₂
entries✝¹ : List (Sigma β)
nodupKeys✝¹ : NodupKeys entries✝¹
entries✝ : List (Sigma β)
nodupKeys✝ : NodupKeys entries✝
⊢ liftOn₂ ⟦{ entries := entries✝¹, nodupKeys := nodupKeys✝¹ }⟧ ⟦{ entries := entries✝, nodupKeys := nodupKeys✝ }⟧ f H =
    f { entries := entries✝¹, nodupKeys := nodupKeys✝¹ } { entries := entries✝, nodupKeys := nodupKeys✝ }

Next tactic:
rfl
@[elab_as_elim]
theorem induction_on {C : Finmap β → Prop} (s : Finmap β) (H : ∀ a : AList β, C ⟦a⟧) : C s := by
  

Proof state:
α : Type u
β : α → Type v
C : Finmap β → Prop
s : Finmap β
H : ∀ (a : AList β), C ⟦a⟧
⊢ C s

Next tactic:
rcases s with ⟨⟨a⟩, h⟩
@[elab_as_elim]
theorem induction_on {C : Finmap β → Prop} (s : Finmap β) (H : ∀ a : AList β, C ⟦a⟧) : C s := by
  rcases s with ⟨⟨a⟩, h⟩; 

Proof state:
case mk.mk
α : Type u
β : α → Type v
C : Finmap β → Prop
H : ∀ (a : AList β), C ⟦a⟧
entries✝ : Multiset (Sigma β)
a : List (Sigma β)
h : Multiset.NodupKeys (Quot.mk Setoid.r a)
⊢ C { entries := Quot.mk Setoid.r a, nodupKeys := h }

Next tactic:
exact H ⟨a, h⟩
@[ext]
theorem ext : ∀ {s t : Finmap β}, s.entries = t.entries → s = t
  | ⟨l₁, h₁⟩, ⟨l₂, _⟩, H => by 

Proof state:
α : Type u
β : α → Type v
l₁ : Multiset (Sigma β)
h₁ : Multiset.NodupKeys l₁
l₂ : Multiset (Sigma β)
nodupKeys✝ : Multiset.NodupKeys l₂
H : { entries := l₁, nodupKeys := h₁ }.entries = { entries := l₂, nodupKeys := nodupKeys✝ }.entries
⊢ { entries := l₁, nodupKeys := h₁ } = { entries := l₂, nodupKeys := nodupKeys✝ }

Next tactic:
congr
@[simp]
theorem keys_ext {s₁ s₂ : AList β} : keys ⟦s₁⟧ = keys ⟦s₂⟧ ↔ s₁.keys ~ s₂.keys := by
  

Proof state:
α : Type u
β : α → Type v
s₁ s₂ : AList β
⊢ keys ⟦s₁⟧ = keys ⟦s₂⟧ ↔ AList.keys s₁ ~ AList.keys s₂

Next tactic:
simp [keys, AList.keys]
@[simp]
theorem mem_singleton (x y : α) (b : β y) : x ∈ singleton y b ↔ x = y := by
  

Proof state:
α : Type u
β : α → Type v
x y : α
b : β y
⊢ x ∈ singleton y b ↔ x = y

Next tactic:
simp only [singleton]
@[simp]
theorem mem_singleton (x y : α) (b : β y) : x ∈ singleton y b ↔ x = y := by
  simp only [singleton]; 

Proof state:
α : Type u
β : α → Type v
x y : α
b : β y
⊢ x ∈ ⟦AList.singleton y b⟧ ↔ x = y

Next tactic:
erw [mem_cons, mem_nil_iff, or_false_iff]
@[simp]
theorem dlookup_list_toFinmap (a : α) (s : List (Sigma β)) : lookup a s.toFinmap = s.dlookup a := by
  

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
a : α
s : List (Sigma β)
⊢ lookup a (List.toFinmap s) = dlookup a s

Next tactic:
rw [List.toFinmap, lookup_toFinmap, lookup_to_alist]
lemma mem_lookup_iff {s : Finmap β} {a : α} {b : β a} :
    b ∈ s.lookup a ↔ Sigma.mk a b ∈ s.entries := by
  

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
s : Finmap β
a : α
b : β a
⊢ b ∈ lookup a s ↔ { fst := a, snd := b } ∈ s.entries

Next tactic:
rcases s with ⟨⟨l⟩, hl⟩
lemma mem_lookup_iff {s : Finmap β} {a : α} {b : β a} :
    b ∈ s.lookup a ↔ Sigma.mk a b ∈ s.entries := by
  rcases s with ⟨⟨l⟩, hl⟩; 

Proof state:
case mk.mk
α : Type u
β : α → Type v
inst✝ : DecidableEq α
a : α
b : β a
entries✝ : Multiset (Sigma β)
l : List (Sigma β)
hl : Multiset.NodupKeys (Quot.mk Setoid.r l)
⊢ b ∈ lookup a { entries := Quot.mk Setoid.r l, nodupKeys := hl } ↔
    { fst := a, snd := b } ∈ { entries := Quot.mk Setoid.r l, nodupKeys := hl }.entries

Next tactic:
exact List.mem_dlookup_iff hl
@[simp] lemma sigma_keys_lookup (s : Finmap β) :
    s.keys.sigma (fun i => (s.lookup i).toFinset) = ⟨s.entries, s.nodup_entries⟩ := by
  

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
s : Finmap β
⊢ (Finset.sigma (keys s) fun i => Option.toFinset (lookup i s)) =
    { val := s.entries, nodup := (_ : Multiset.Nodup s.entries) }

Next tactic:
ext x
@[simp] lemma sigma_keys_lookup (s : Finmap β) :
    s.keys.sigma (fun i => (s.lookup i).toFinset) = ⟨s.entries, s.nodup_entries⟩ := by
  ext x
  

Proof state:
case a
α : Type u
β : α → Type v
inst✝ : DecidableEq α
s : Finmap β
x : (i : α) × β i
⊢ (x ∈ Finset.sigma (keys s) fun i => Option.toFinset (lookup i s)) ↔
    x ∈ { val := s.entries, nodup := (_ : Multiset.Nodup s.entries) }

Next tactic:
have : x ∈ s.entries → x.1 ∈ s.keys := Multiset.mem_map_of_mem _
@[simp] lemma sigma_keys_lookup (s : Finmap β) :
    s.keys.sigma (fun i => (s.lookup i).toFinset) = ⟨s.entries, s.nodup_entries⟩ := by
  ext x
  have : x ∈ s.entries → x.1 ∈ s.keys := Multiset.mem_map_of_mem _
  

Proof state:
case a
α : Type u
β : α → Type v
inst✝ : DecidableEq α
s : Finmap β
x : (i : α) × β i
this : x ∈ s.entries → x.fst ∈ keys s
⊢ (x ∈ Finset.sigma (keys s) fun i => Option.toFinset (lookup i s)) ↔
    x ∈ { val := s.entries, nodup := (_ : Multiset.Nodup s.entries) }

Next tactic:
simpa [lookup_eq_some_iff]
@[simp]
theorem lookup_singleton_eq {a : α} {b : β a} : (singleton a b).lookup a = some b := by
  

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
a : α
b : β a
⊢ lookup a (singleton a b) = some b

Next tactic:
rw [singleton, lookup_toFinmap, AList.singleton, AList.lookup, dlookup_cons_eq]
theorem ext_lookup {s₁ s₂ : Finmap β} : (∀ x, s₁.lookup x = s₂.lookup x) → s₁ = s₂ :=
  induction_on₂ s₁ s₂ fun s₁ s₂ h => by
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
s₁✝ s₂✝ : Finmap β
s₁ s₂ : AList β
h : ∀ (x : α), lookup x ⟦s₁⟧ = lookup x ⟦s₂⟧
⊢ ⟦s₁⟧ = ⟦s₂⟧

Next tactic:
simp only [AList.lookup, lookup_toFinmap] at h
theorem ext_lookup {s₁ s₂ : Finmap β} : (∀ x, s₁.lookup x = s₂.lookup x) → s₁ = s₂ :=
  induction_on₂ s₁ s₂ fun s₁ s₂ h => by
    simp only [AList.lookup, lookup_toFinmap] at h
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
s₁✝ s₂✝ : Finmap β
s₁ s₂ : AList β
h : ∀ (x : α), dlookup x s₁.entries = dlookup x s₂.entries
⊢ ⟦s₁⟧ = ⟦s₂⟧

Next tactic:
rw [AList.toFinmap_eq]
theorem ext_lookup {s₁ s₂ : Finmap β} : (∀ x, s₁.lookup x = s₂.lookup x) → s₁ = s₂ :=
  induction_on₂ s₁ s₂ fun s₁ s₂ h => by
    simp only [AList.lookup, lookup_toFinmap] at h
    rw [AList.toFinmap_eq]
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
s₁✝ s₂✝ : Finmap β
s₁ s₂ : AList β
h : ∀ (x : α), dlookup x s₁.entries = dlookup x s₂.entries
⊢ s₁.entries ~ s₂.entries

Next tactic:
apply lookup_ext s₁.nodupKeys s₂.nodupKeys
theorem ext_lookup {s₁ s₂ : Finmap β} : (∀ x, s₁.lookup x = s₂.lookup x) → s₁ = s₂ :=
  induction_on₂ s₁ s₂ fun s₁ s₂ h => by
    simp only [AList.lookup, lookup_toFinmap] at h
    rw [AList.toFinmap_eq]
    apply lookup_ext s₁.nodupKeys s₂.nodupKeys
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
s₁✝ s₂✝ : Finmap β
s₁ s₂ : AList β
h : ∀ (x : α), dlookup x s₁.entries = dlookup x s₂.entries
⊢ ∀ (x : α) (y : β x), y ∈ dlookup x s₁.entries ↔ y ∈ dlookup x s₂.entries

Next tactic:
intro x y
theorem ext_lookup {s₁ s₂ : Finmap β} : (∀ x, s₁.lookup x = s₂.lookup x) → s₁ = s₂ :=
  induction_on₂ s₁ s₂ fun s₁ s₂ h => by
    simp only [AList.lookup, lookup_toFinmap] at h
    rw [AList.toFinmap_eq]
    apply lookup_ext s₁.nodupKeys s₂.nodupKeys
    intro x y
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
s₁✝ s₂✝ : Finmap β
s₁ s₂ : AList β
h : ∀ (x : α), dlookup x s₁.entries = dlookup x s₂.entries
x : α
y : β x
⊢ y ∈ dlookup x s₁.entries ↔ y ∈ dlookup x s₂.entries

Next tactic:
rw [h]
/-- An equivalence between `Finmap β` and pairs `(keys : Finset α, lookup : ∀ a, Option (β a))` such
that `(lookup a).isSome ↔ a ∈ keys`. -/
@[simps apply_coe_fst apply_coe_snd]
def keysLookupEquiv :
    Finmap β ≃ { f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1 } where
  toFun s := ⟨(s.keys, fun i => s.lookup i), fun _ => lookup_isSome⟩
  invFun f := mk (f.1.1.sigma <| fun i => (f.1.2 i).toFinset).val <| by
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
f : { f // ∀ (i : α), Option.isSome (f.2 i) = true ↔ i ∈ f.1 }
⊢ Multiset.NodupKeys (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val

Next tactic:
refine Multiset.nodup_keys.1 ((Finset.nodup _).map_on ?_)
/-- An equivalence between `Finmap β` and pairs `(keys : Finset α, lookup : ∀ a, Option (β a))` such
that `(lookup a).isSome ↔ a ∈ keys`. -/
@[simps apply_coe_fst apply_coe_snd]
def keysLookupEquiv :
    Finmap β ≃ { f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1 } where
  toFun s := ⟨(s.keys, fun i => s.lookup i), fun _ => lookup_isSome⟩
  invFun f := mk (f.1.1.sigma <| fun i => (f.1.2 i).toFinset).val <| by
    refine Multiset.nodup_keys.1 ((Finset.nodup _).map_on ?_)
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
f : { f // ∀ (i : α), Option.isSome (f.2 i) = true ↔ i ∈ f.1 }
⊢ ∀ x ∈ (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val,
    ∀ y ∈ (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val, x.fst = y.fst → x = y

Next tactic:
simp only [Finset.mem_val, Finset.mem_sigma, Option.mem_toFinset, Option.mem_def]
/-- An equivalence between `Finmap β` and pairs `(keys : Finset α, lookup : ∀ a, Option (β a))` such
that `(lookup a).isSome ↔ a ∈ keys`. -/
@[simps apply_coe_fst apply_coe_snd]
def keysLookupEquiv :
    Finmap β ≃ { f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1 } where
  toFun s := ⟨(s.keys, fun i => s.lookup i), fun _ => lookup_isSome⟩
  invFun f := mk (f.1.1.sigma <| fun i => (f.1.2 i).toFinset).val <| by
    refine Multiset.nodup_keys.1 ((Finset.nodup _).map_on ?_)
    simp only [Finset.mem_val, Finset.mem_sigma, Option.mem_toFinset, Option.mem_def]
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
f : { f // ∀ (i : α), Option.isSome (f.2 i) = true ↔ i ∈ f.1 }
⊢ ∀ (x : (a : α) × β a),
    x.fst ∈ (↑f).1 ∧ (↑f).2 x.fst = some x.snd →
      ∀ (y : (a : α) × β a), y.fst ∈ (↑f).1 ∧ (↑f).2 y.fst = some y.snd → x.fst = y.fst → x = y

Next tactic:
rintro ⟨i, x⟩ ⟨_, hx⟩ ⟨j, y⟩ ⟨_, hy⟩ (rfl : i = j)
/-- An equivalence between `Finmap β` and pairs `(keys : Finset α, lookup : ∀ a, Option (β a))` such
that `(lookup a).isSome ↔ a ∈ keys`. -/
@[simps apply_coe_fst apply_coe_snd]
def keysLookupEquiv :
    Finmap β ≃ { f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1 } where
  toFun s := ⟨(s.keys, fun i => s.lookup i), fun _ => lookup_isSome⟩
  invFun f := mk (f.1.1.sigma <| fun i => (f.1.2 i).toFinset).val <| by
    refine Multiset.nodup_keys.1 ((Finset.nodup _).map_on ?_)
    simp only [Finset.mem_val, Finset.mem_sigma, Option.mem_toFinset, Option.mem_def]
    rintro ⟨i, x⟩ ⟨_, hx⟩ ⟨j, y⟩ ⟨_, hy⟩ (rfl : i = j)
    

Proof state:
case mk.intro.mk.intro
α : Type u
β : α → Type v
inst✝ : DecidableEq α
f : { f // ∀ (i : α), Option.isSome (f.2 i) = true ↔ i ∈ f.1 }
i : α
x : β i
left✝¹ : { fst := i, snd := x }.fst ∈ (↑f).1
hx : (↑f).2 { fst := i, snd := x }.fst = some { fst := i, snd := x }.snd
y : β i
left✝ : { fst := i, snd := y }.fst ∈ (↑f).1
hy : (↑f).2 { fst := i, snd := y }.fst = some { fst := i, snd := y }.snd
⊢ { fst := i, snd := x } = { fst := i, snd := y }

Next tactic:
simpa using hx.symm.trans hy
/-- An equivalence between `Finmap β` and pairs `(keys : Finset α, lookup : ∀ a, Option (β a))` such
that `(lookup a).isSome ↔ a ∈ keys`. -/
@[simps apply_coe_fst apply_coe_snd]
def keysLookupEquiv :
    Finmap β ≃ { f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1 } where
  toFun s := ⟨(s.keys, fun i => s.lookup i), fun _ => lookup_isSome⟩
  invFun f := mk (f.1.1.sigma <| fun i => (f.1.2 i).toFinset).val <| by
    refine Multiset.nodup_keys.1 ((Finset.nodup _).map_on ?_)
    simp only [Finset.mem_val, Finset.mem_sigma, Option.mem_toFinset, Option.mem_def]
    rintro ⟨i, x⟩ ⟨_, hx⟩ ⟨j, y⟩ ⟨_, hy⟩ (rfl : i = j)
    simpa using hx.symm.trans hy
  left_inv f := ext <| by 

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
f : Finmap β
⊢ ((fun f =>
          { entries := (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val,
            nodupKeys := (_ : Multiset.NodupKeys (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val) })
        ((fun s =>
            { val := (keys s, fun i => lookup i s),
              property := (_ : ∀ (x : α), Option.isSome (lookup x s) = true ↔ x ∈ s) })
          f)).entries =
    f.entries

Next tactic:
simp
/-- An equivalence between `Finmap β` and pairs `(keys : Finset α, lookup : ∀ a, Option (β a))` such
that `(lookup a).isSome ↔ a ∈ keys`. -/
@[simps apply_coe_fst apply_coe_snd]
def keysLookupEquiv :
    Finmap β ≃ { f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1 } where
  toFun s := ⟨(s.keys, fun i => s.lookup i), fun _ => lookup_isSome⟩
  invFun f := mk (f.1.1.sigma <| fun i => (f.1.2 i).toFinset).val <| by
    refine Multiset.nodup_keys.1 ((Finset.nodup _).map_on ?_)
    simp only [Finset.mem_val, Finset.mem_sigma, Option.mem_toFinset, Option.mem_def]
    rintro ⟨i, x⟩ ⟨_, hx⟩ ⟨j, y⟩ ⟨_, hy⟩ (rfl : i = j)
    simpa using hx.symm.trans hy
  left_inv f := ext <| by simp
  right_inv := fun ⟨(s, f), hf⟩ => by
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
x✝ : { f // ∀ (i : α), Option.isSome (f.2 i) = true ↔ i ∈ f.1 }
s : Finset α
f : (a : α) → Option (β a)
hf : ∀ (i : α), Option.isSome ((s, f).2 i) = true ↔ i ∈ (s, f).1
⊢ (fun s =>
        { val := (keys s, fun i => lookup i s),
          property := (_ : ∀ (x : α), Option.isSome (lookup x s) = true ↔ x ∈ s) })
      ((fun f =>
          { entries := (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val,
            nodupKeys := (_ : Multiset.NodupKeys (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val) })
        { val := (s, f), property := hf }) =
    { val := (s, f), property := hf }

Next tactic:
dsimp only at hf
/-- An equivalence between `Finmap β` and pairs `(keys : Finset α, lookup : ∀ a, Option (β a))` such
that `(lookup a).isSome ↔ a ∈ keys`. -/
@[simps apply_coe_fst apply_coe_snd]
def keysLookupEquiv :
    Finmap β ≃ { f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1 } where
  toFun s := ⟨(s.keys, fun i => s.lookup i), fun _ => lookup_isSome⟩
  invFun f := mk (f.1.1.sigma <| fun i => (f.1.2 i).toFinset).val <| by
    refine Multiset.nodup_keys.1 ((Finset.nodup _).map_on ?_)
    simp only [Finset.mem_val, Finset.mem_sigma, Option.mem_toFinset, Option.mem_def]
    rintro ⟨i, x⟩ ⟨_, hx⟩ ⟨j, y⟩ ⟨_, hy⟩ (rfl : i = j)
    simpa using hx.symm.trans hy
  left_inv f := ext <| by simp
  right_inv := fun ⟨(s, f), hf⟩ => by
    dsimp only at hf
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
x✝ : { f // ∀ (i : α), Option.isSome (f.2 i) = true ↔ i ∈ f.1 }
s : Finset α
f : (a : α) → Option (β a)
hf : ∀ (i : α), Option.isSome (f i) = true ↔ i ∈ s
⊢ (fun s =>
        { val := (keys s, fun i => lookup i s),
          property := (_ : ∀ (x : α), Option.isSome (lookup x s) = true ↔ x ∈ s) })
      ((fun f =>
          { entries := (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val,
            nodupKeys := (_ : Multiset.NodupKeys (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val) })
        { val := (s, f), property := hf }) =
    { val := (s, f), property := hf }

Next tactic:
ext
/-- An equivalence between `Finmap β` and pairs `(keys : Finset α, lookup : ∀ a, Option (β a))` such
that `(lookup a).isSome ↔ a ∈ keys`. -/
@[simps apply_coe_fst apply_coe_snd]
def keysLookupEquiv :
    Finmap β ≃ { f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1 } where
  toFun s := ⟨(s.keys, fun i => s.lookup i), fun _ => lookup_isSome⟩
  invFun f := mk (f.1.1.sigma <| fun i => (f.1.2 i).toFinset).val <| by
    refine Multiset.nodup_keys.1 ((Finset.nodup _).map_on ?_)
    simp only [Finset.mem_val, Finset.mem_sigma, Option.mem_toFinset, Option.mem_def]
    rintro ⟨i, x⟩ ⟨_, hx⟩ ⟨j, y⟩ ⟨_, hy⟩ (rfl : i = j)
    simpa using hx.symm.trans hy
  left_inv f := ext <| by simp
  right_inv := fun ⟨(s, f), hf⟩ => by
    dsimp only at hf
    ext
    · 

Proof state:
case a.a.a
α : Type u
β : α → Type v
inst✝ : DecidableEq α
x✝ : { f // ∀ (i : α), Option.isSome (f.2 i) = true ↔ i ∈ f.1 }
s : Finset α
f : (a : α) → Option (β a)
hf : ∀ (i : α), Option.isSome (f i) = true ↔ i ∈ s
a✝ : α
⊢ a✝ ∈
      (↑((fun s =>
              { val := (keys s, fun i => lookup i s),
                property := (_ : ∀ (x : α), Option.isSome (lookup x s) = true ↔ x ∈ s) })
            ((fun f =>
                { entries := (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val,
                  nodupKeys := (_ : Multiset.NodupKeys (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val) })
              { val := (s, f), property := hf }))).1 ↔
    a✝ ∈ (↑{ val := (s, f), property := hf }).1

Next tactic:
simp [keys, Multiset.keys, ← hf, Option.isSome_iff_exists]
/-- An equivalence between `Finmap β` and pairs `(keys : Finset α, lookup : ∀ a, Option (β a))` such
that `(lookup a).isSome ↔ a ∈ keys`. -/
@[simps apply_coe_fst apply_coe_snd]
def keysLookupEquiv :
    Finmap β ≃ { f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1 } where
  toFun s := ⟨(s.keys, fun i => s.lookup i), fun _ => lookup_isSome⟩
  invFun f := mk (f.1.1.sigma <| fun i => (f.1.2 i).toFinset).val <| by
    refine Multiset.nodup_keys.1 ((Finset.nodup _).map_on ?_)
    simp only [Finset.mem_val, Finset.mem_sigma, Option.mem_toFinset, Option.mem_def]
    rintro ⟨i, x⟩ ⟨_, hx⟩ ⟨j, y⟩ ⟨_, hy⟩ (rfl : i = j)
    simpa using hx.symm.trans hy
  left_inv f := ext <| by simp
  right_inv := fun ⟨(s, f), hf⟩ => by
    dsimp only at hf
    ext
    · simp [keys, Multiset.keys, ← hf, Option.isSome_iff_exists]
    · 

Proof state:
case a.a.h.a
α : Type u
β : α → Type v
inst✝ : DecidableEq α
x✝¹ : { f // ∀ (i : α), Option.isSome (f.2 i) = true ↔ i ∈ f.1 }
s : Finset α
f : (a : α) → Option (β a)
hf : ∀ (i : α), Option.isSome (f i) = true ↔ i ∈ s
x✝ : α
a✝ : β x✝
⊢ a✝ ∈
      (↑((fun s =>
                { val := (keys s, fun i => lookup i s),
                  property := (_ : ∀ (x : α), Option.isSome (lookup x s) = true ↔ x ∈ s) })
              ((fun f =>
                  { entries := (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val,
                    nodupKeys :=
                      (_ : Multiset.NodupKeys (Finset.sigma (↑f).1 fun i => Option.toFinset ((↑f).2 i)).val) })
                { val := (s, f), property := hf }))).2
        x✝ ↔
    a✝ ∈ (↑{ val := (s, f), property := hf }).2 x✝

Next tactic:
simp (config := { contextual := true }) [lookup_eq_some_iff, ← hf]
@[simp] lemma keysLookupEquiv_symm_apply_keys :
    ∀ f : {f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1},
      (keysLookupEquiv.symm f).keys = f.1.1 :=
  keysLookupEquiv.surjective.forall.2 $ fun _ => by
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
x✝ : Finmap fun a => β a
⊢ keys (keysLookupEquiv.symm (keysLookupEquiv x✝)) = (↑(keysLookupEquiv x✝)).1

Next tactic:
simp only [Equiv.symm_apply_apply, keysLookupEquiv_apply_coe_fst]
@[simp] lemma keysLookupEquiv_symm_apply_lookup :
    ∀ (f : {f : Finset α × (∀ a, Option (β a)) // ∀ i, (f.2 i).isSome ↔ i ∈ f.1}) a,
      (keysLookupEquiv.symm f).lookup a = f.1.2 a :=
  keysLookupEquiv.surjective.forall.2 $ fun _ _ => by
    

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
x✝¹ : Finmap fun a => β a
x✝ : α
⊢ lookup x✝ (keysLookupEquiv.symm (keysLookupEquiv x✝¹)) = (↑(keysLookupEquiv x✝¹)).2 x✝

Next tactic:
simp only [Equiv.symm_apply_apply, keysLookupEquiv_apply_coe_snd]
@[simp]
theorem replace_toFinmap (a : α) (b : β a) (s : AList β) :
    replace a b ⟦s⟧ = (⟦s.replace a b⟧ : Finmap β) := by
  

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
a : α
b : β a
s : AList β
⊢ replace a b ⟦s⟧ = ⟦AList.replace a b s⟧

Next tactic:
simp [replace]
@[simp]
theorem keys_replace (a : α) (b : β a) (s : Finmap β) : (replace a b s).keys = s.keys :=
  induction_on s fun s => by 

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
a : α
b : β a
s✝ : Finmap β
s : AList β
⊢ keys (replace a b ⟦s⟧) = keys ⟦s⟧

Next tactic:
simp
@[simp]
theorem mem_replace {a a' : α} {b : β a} {s : Finmap β} : a' ∈ replace a b s ↔ a' ∈ s :=
  induction_on s fun s => by 

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
a a' : α
b : β a
s✝ : Finmap β
s : AList β
⊢ a' ∈ replace a b ⟦s⟧ ↔ a' ∈ ⟦s⟧

Next tactic:
simp
/-- `any f s` returns `true` iff there exists a value `v` in `s` such that `f v = true`. -/
def any (f : ∀ x, β x → Bool) (s : Finmap β) : Bool :=
  s.foldl (fun x y z => x || f y z)
    (fun _ _ _ _ => by 

Proof state:
α : Type u
β : α → Type v
f : (x : α) → β x → Bool
s : Finmap β
x✝³ : Bool
x✝² : α
x✝¹ : β x✝²
x✝ : α
⊢ ∀ (b₂ : β x✝),
    (fun x y z => x || f y z) ((fun x y z => x || f y z) x✝³ x✝² x✝¹) x✝ b₂ =
      (fun x y z => x || f y z) ((fun x y z => x || f y z) x✝³ x✝ b₂) x✝² x✝¹

Next tactic:
simp_rw [Bool.or_assoc, Bool.or_comm, imp_true_iff]
/-- `all f s` returns `true` iff `f v = true` for all values `v` in `s`. -/
def all (f : ∀ x, β x → Bool) (s : Finmap β) : Bool :=
  s.foldl (fun x y z => x && f y z)
    (fun _ _ _ _ => by 

Proof state:
α : Type u
β : α → Type v
f : (x : α) → β x → Bool
s : Finmap β
x✝³ : Bool
x✝² : α
x✝¹ : β x✝²
x✝ : α
⊢ ∀ (b₂ : β x✝),
    (fun x y z => x && f y z) ((fun x y z => x && f y z) x✝³ x✝² x✝¹) x✝ b₂ =
      (fun x y z => x && f y z) ((fun x y z => x && f y z) x✝³ x✝ b₂) x✝² x✝¹

Next tactic:
simp_rw [Bool.and_assoc, Bool.and_comm, imp_true_iff]
@[simp]
theorem erase_toFinmap (a : α) (s : AList β) : erase a ⟦s⟧ = AList.toFinmap (s.erase a) := by
  

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
a : α
s : AList β
⊢ erase a ⟦s⟧ = ⟦AList.erase a s⟧

Next tactic:
simp [erase]
@[simp]
theorem keys_erase_toFinset (a : α) (s : AList β) : keys ⟦s.erase a⟧ = (keys ⟦s⟧).erase a := by
  

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
a : α
s : AList β
⊢ keys ⟦AList.erase a s⟧ = Finset.erase (keys ⟦s⟧) a

Next tactic:
simp [Finset.erase, keys, AList.erase, keys_kerase]
@[simp]
theorem keys_erase (a : α) (s : Finmap β) : (erase a s).keys = s.keys.erase a :=
  induction_on s fun s => by 

Proof state:
α : Type u
β : α → Type v
inst✝ : DecidableEq α
a : α
s✝ : Finmap β
s : AList β
⊢ keys (erase a ⟦s⟧) = Finset.erase (keys ⟦s⟧) a

Next tactic:
simp