l3lab/ntp-mathlib huggingface dataset:
theorem prod_antidiagonal_succ {n : ℕ} {f : ℕ × ℕ → M} :
    (∏ p in antidiagonal (n + 1), f p)
      = f (0, n + 1) * ∏ p in antidiagonal n, f (p.1 + 1, p.2) := by
  

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : CommMonoid M
inst✝ : AddCommMonoid N
n : ℕ
f : ℕ × ℕ → M
⊢ ∏ p in antidiagonal (n + 1), f p = f (0, n + 1) * ∏ p in antidiagonal n, f (p.1 + 1, p.2)

Next tactic:
rw [antidiagonal_succ, prod_cons, prod_map]
theorem prod_antidiagonal_succ {n : ℕ} {f : ℕ × ℕ → M} :
    (∏ p in antidiagonal (n + 1), f p)
      = f (0, n + 1) * ∏ p in antidiagonal n, f (p.1 + 1, p.2) := by
  rw [antidiagonal_succ, prod_cons, prod_map]; 

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : CommMonoid M
inst✝ : AddCommMonoid N
n : ℕ
f : ℕ × ℕ → M
⊢ f (0, n + 1) *
      ∏ x in antidiagonal n,
        f
          ((Function.Embedding.prodMap { toFun := Nat.succ, inj' := Nat.succ_injective } (Function.Embedding.refl ℕ))
            x) =
    f (0, n + 1) * ∏ p in antidiagonal n, f (p.1 + 1, p.2)

Next tactic:
rfl
@[to_additive]
theorem prod_antidiagonal_swap {n : ℕ} {f : ℕ × ℕ → M} :
    ∏ p in antidiagonal n, f p.swap = ∏ p in antidiagonal n, f p := by
  

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : CommMonoid M
inst✝ : AddCommMonoid N
n : ℕ
f : ℕ × ℕ → M
⊢ ∏ p in antidiagonal n, f (Prod.swap p) = ∏ p in antidiagonal n, f p

Next tactic:
conv_lhs => rw [← map_swap_antidiagonal, Finset.prod_map]
@[to_additive]
theorem prod_antidiagonal_swap {n : ℕ} {f : ℕ × ℕ → M} :
    ∏ p in antidiagonal n, f p.swap = ∏ p in antidiagonal n, f p := by
  conv_lhs => 

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : CommMonoid M
inst✝ : AddCommMonoid N
n : ℕ
f : ℕ × ℕ → M
| ∏ p in antidiagonal n, f (Prod.swap p)

Next tactic:
rw [← map_swap_antidiagonal, Finset.prod_map]
theorem prod_antidiagonal_succ' {n : ℕ} {f : ℕ × ℕ → M} : (∏ p in antidiagonal (n + 1), f p) =
    f (n + 1, 0) * ∏ p in antidiagonal n, f (p.1, p.2 + 1) := by
  

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : CommMonoid M
inst✝ : AddCommMonoid N
n : ℕ
f : ℕ × ℕ → M
⊢ ∏ p in antidiagonal (n + 1), f p = f (n + 1, 0) * ∏ p in antidiagonal n, f (p.1, p.2 + 1)

Next tactic:
rw [← prod_antidiagonal_swap, prod_antidiagonal_succ, ← prod_antidiagonal_swap]
theorem prod_antidiagonal_succ' {n : ℕ} {f : ℕ × ℕ → M} : (∏ p in antidiagonal (n + 1), f p) =
    f (n + 1, 0) * ∏ p in antidiagonal n, f (p.1, p.2 + 1) := by
  rw [← prod_antidiagonal_swap, prod_antidiagonal_succ, ← prod_antidiagonal_swap]
  

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : CommMonoid M
inst✝ : AddCommMonoid N
n : ℕ
f : ℕ × ℕ → M
⊢ f (Prod.swap (0, n + 1)) * ∏ p in antidiagonal n, f (Prod.swap ((Prod.swap p).1 + 1, (Prod.swap p).2)) =
    f (n + 1, 0) * ∏ p in antidiagonal n, f (p.1, p.2 + 1)

Next tactic:
rfl
@[to_additive]
theorem prod_antidiagonal_subst {n : ℕ} {f : ℕ × ℕ → ℕ → M} :
    ∏ p in antidiagonal n, f p n = ∏ p in antidiagonal n, f p (p.1 + p.2) :=
  prod_congr rfl fun p hp ↦ by 

Proof state:
M : Type u_1
N : Type u_2
inst✝¹ : CommMonoid M
inst✝ : AddCommMonoid N
n : ℕ
f : ℕ × ℕ → ℕ → M
p : ℕ × ℕ
hp : p ∈ antidiagonal n
⊢ f p n = f p (p.1 + p.2)

Next tactic:
rw [mem_antidiagonal.mp hp]
theorem not_eq_key {a} {l : List (Sigma β)} : a ∉ l.keys ↔ ∀ s : Sigma β, s ∈ l → a ≠ s.1 :=
  Iff.intro (fun h₁ s h₂ e => absurd (mem_keys_of_mem h₂) (by 

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
a : α
l : List (Sigma β)
h₁ : a ∉ keys l
s : Sigma β
h₂ : s ∈ l
e : a = s.fst
⊢ s.fst ∉ keys l

Next tactic:
rwa [e] at h₁
@[simp]
theorem nodupKeys_cons {s : Sigma β} {l : List (Sigma β)} :
    NodupKeys (s :: l) ↔ s.1 ∉ l.keys ∧ NodupKeys l := by 

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
s : Sigma β
l : List (Sigma β)
⊢ NodupKeys (s :: l) ↔ s.fst ∉ keys l ∧ NodupKeys l

Next tactic:
simp [keys, NodupKeys]
theorem NodupKeys.eq_of_mk_mem {a : α} {b b' : β a} {l : List (Sigma β)} (nd : NodupKeys l)
    (h : Sigma.mk a b ∈ l) (h' : Sigma.mk a b' ∈ l) : b = b' := by
  

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
a : α
b b' : β a
l : List (Sigma β)
nd : NodupKeys l
h : { fst := a, snd := b } ∈ l
h' : { fst := a, snd := b' } ∈ l
⊢ b = b'

Next tactic:
cases nd.eq_of_fst_eq h h' rfl
theorem NodupKeys.eq_of_mk_mem {a : α} {b b' : β a} {l : List (Sigma β)} (nd : NodupKeys l)
    (h : Sigma.mk a b ∈ l) (h' : Sigma.mk a b' ∈ l) : b = b' := by
  cases nd.eq_of_fst_eq h h' rfl; 

Proof state:
case refl
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
a : α
b : β a
l : List (Sigma β)
nd : NodupKeys l
h h' : { fst := a, snd := b } ∈ l
⊢ b = b

Next tactic:
rfl
theorem nodupKeys_join {L : List (List (Sigma β))} :
    NodupKeys (join L) ↔ (∀ l ∈ L, NodupKeys l) ∧ Pairwise Disjoint (L.map keys) := by
  

Proof state:
α : Type u
β : α → Type v
l l₁ l₂ : List (Sigma β)
L : List (List (Sigma β))
⊢ NodupKeys (join L) ↔ (∀ l ∈ L, NodupKeys l) ∧ Pairwise Disjoint (map keys L)

Next tactic:
rw [nodupKeys_iff_pairwise, pairwise_join, pairwise_map]
theorem nodupKeys_join {L : List (List (Sigma β))} :
    NodupKeys (join L) ↔ (∀ l ∈ L, NodupKeys l) ∧ Pairwise Disjoint (L.map keys) := by
  rw [nodupKeys_iff_pairwise, pairwise_join, pairwise_map]
  

Proof state:
α : Type u
β : α → Type v
l l₁ l₂ : List (Sigma β)
L : List (List (Sigma β))
⊢ (∀ l ∈ L, Pairwise (fun s s' => s.fst ≠ s'.fst) l) ∧ Pairwise (fun l₁ l₂ => ∀ x ∈ l₁, ∀ y ∈ l₂, x.fst ≠ y.fst) L ↔
    (∀ l ∈ L, NodupKeys l) ∧ Pairwise (fun a b => Disjoint (keys a) (keys b)) L

Next tactic:
refine' and_congr (ball_congr fun l _ => by simp [nodupKeys_iff_pairwise]) _
theorem nodupKeys_join {L : List (List (Sigma β))} :
    NodupKeys (join L) ↔ (∀ l ∈ L, NodupKeys l) ∧ Pairwise Disjoint (L.map keys) := by
  rw [nodupKeys_iff_pairwise, pairwise_join, pairwise_map]
  refine' and_congr (ball_congr fun l _ => by 

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
L : List (List (Sigma β))
l : List (Sigma β)
x✝ : l ∈ L
⊢ Pairwise (fun s s' => s.fst ≠ s'.fst) l ↔ NodupKeys l

Next tactic:
simp [nodupKeys_iff_pairwise]
theorem nodupKeys_join {L : List (List (Sigma β))} :
    NodupKeys (join L) ↔ (∀ l ∈ L, NodupKeys l) ∧ Pairwise Disjoint (L.map keys) := by
  rw [nodupKeys_iff_pairwise, pairwise_join, pairwise_map]
  refine' and_congr (ball_congr fun l _ => by simp [nodupKeys_iff_pairwise]) _
  

Proof state:
α : Type u
β : α → Type v
l l₁ l₂ : List (Sigma β)
L : List (List (Sigma β))
⊢ Pairwise (fun l₁ l₂ => ∀ x ∈ l₁, ∀ y ∈ l₂, x.fst ≠ y.fst) L ↔ Pairwise (fun a b => Disjoint (keys a) (keys b)) L

Next tactic:
apply iff_of_eq
theorem nodupKeys_join {L : List (List (Sigma β))} :
    NodupKeys (join L) ↔ (∀ l ∈ L, NodupKeys l) ∧ Pairwise Disjoint (L.map keys) := by
  rw [nodupKeys_iff_pairwise, pairwise_join, pairwise_map]
  refine' and_congr (ball_congr fun l _ => by simp [nodupKeys_iff_pairwise]) _
  apply iff_of_eq; 

Proof state:
case a
α : Type u
β : α → Type v
l l₁ l₂ : List (Sigma β)
L : List (List (Sigma β))
⊢ Pairwise (fun l₁ l₂ => ∀ x ∈ l₁, ∀ y ∈ l₂, x.fst ≠ y.fst) L = Pairwise (fun a b => Disjoint (keys a) (keys b)) L

Next tactic:
congr with (l₁ l₂)
theorem nodupKeys_join {L : List (List (Sigma β))} :
    NodupKeys (join L) ↔ (∀ l ∈ L, NodupKeys l) ∧ Pairwise Disjoint (L.map keys) := by
  rw [nodupKeys_iff_pairwise, pairwise_join, pairwise_map]
  refine' and_congr (ball_congr fun l _ => by simp [nodupKeys_iff_pairwise]) _
  apply iff_of_eq; congr with (l₁ l₂)
  

Proof state:
case a.e_R.h.h.a
α : Type u
β : α → Type v
l l₁✝ l₂✝ : List (Sigma β)
L : List (List (Sigma β))
l₁ l₂ : List (Sigma β)
⊢ (∀ x ∈ l₁, ∀ y ∈ l₂, x.fst ≠ y.fst) ↔ Disjoint (keys l₁) (keys l₂)

Next tactic:
simp [keys, disjoint_iff_ne]
theorem nodup_enum_map_fst (l : List α) : (l.enum.map Prod.fst).Nodup := by 

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
l : List α
⊢ Nodup (map Prod.fst (enum l))

Next tactic:
simp [List.nodup_range]
theorem dlookup_isSome {a : α} : ∀ {l : List (Sigma β)}, (dlookup a l).isSome ↔ a ∈ l.keys
  | [] => by 

Proof state:
α : Type u
β : α → Type v
l l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
⊢ Option.isSome (dlookup a []) = true ↔ a ∈ keys []

Next tactic:
simp
theorem dlookup_isSome {a : α} : ∀ {l : List (Sigma β)}, (dlookup a l).isSome ↔ a ∈ l.keys
  | [] => by simp
  | ⟨a', b⟩ :: l => by
    

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b : β a'
l : List (Sigma β)
⊢ Option.isSome (dlookup a ({ fst := a', snd := b } :: l)) = true ↔ a ∈ keys ({ fst := a', snd := b } :: l)

Next tactic:
by_cases h : a = a'
theorem dlookup_isSome {a : α} : ∀ {l : List (Sigma β)}, (dlookup a l).isSome ↔ a ∈ l.keys
  | [] => by simp
  | ⟨a', b⟩ :: l => by
    by_cases h : a = a'
    · 

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b : β a'
l : List (Sigma β)
h : a = a'
⊢ Option.isSome (dlookup a ({ fst := a', snd := b } :: l)) = true ↔ a ∈ keys ({ fst := a', snd := b } :: l)

Next tactic:
subst a'
theorem dlookup_isSome {a : α} : ∀ {l : List (Sigma β)}, (dlookup a l).isSome ↔ a ∈ l.keys
  | [] => by simp
  | ⟨a', b⟩ :: l => by
    by_cases h : a = a'
    · subst a'
      

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
l : List (Sigma β)
b : β a
⊢ Option.isSome (dlookup a ({ fst := a, snd := b } :: l)) = true ↔ a ∈ keys ({ fst := a, snd := b } :: l)

Next tactic:
simp
theorem dlookup_isSome {a : α} : ∀ {l : List (Sigma β)}, (dlookup a l).isSome ↔ a ∈ l.keys
  | [] => by simp
  | ⟨a', b⟩ :: l => by
    by_cases h : a = a'
    · subst a'
      simp
    · 

Proof state:
case neg
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b : β a'
l : List (Sigma β)
h : ¬a = a'
⊢ Option.isSome (dlookup a ({ fst := a', snd := b } :: l)) = true ↔ a ∈ keys ({ fst := a', snd := b } :: l)

Next tactic:
simp [h, dlookup_isSome]
theorem dlookup_eq_none {a : α} {l : List (Sigma β)} : dlookup a l = none ↔ a ∉ l.keys := by
  

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
l : List (Sigma β)
⊢ dlookup a l = none ↔ a ∉ keys l

Next tactic:
simp [← dlookup_isSome, Option.isNone_iff_eq_none]
theorem of_mem_dlookup {a : α} {b : β a} :
    ∀ {l : List (Sigma β)}, b ∈ dlookup a l → Sigma.mk a b ∈ l
  | ⟨a', b'⟩ :: l, H => by
    

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
b : β a
a' : α
b' : β a'
l : List (Sigma β)
H : b ∈ dlookup a ({ fst := a', snd := b' } :: l)
⊢ { fst := a, snd := b } ∈ { fst := a', snd := b' } :: l

Next tactic:
by_cases h : a = a'
theorem of_mem_dlookup {a : α} {b : β a} :
    ∀ {l : List (Sigma β)}, b ∈ dlookup a l → Sigma.mk a b ∈ l
  | ⟨a', b'⟩ :: l, H => by
    by_cases h : a = a'
    · 

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
b : β a
a' : α
b' : β a'
l : List (Sigma β)
H : b ∈ dlookup a ({ fst := a', snd := b' } :: l)
h : a = a'
⊢ { fst := a, snd := b } ∈ { fst := a', snd := b' } :: l

Next tactic:
subst a'
theorem of_mem_dlookup {a : α} {b : β a} :
    ∀ {l : List (Sigma β)}, b ∈ dlookup a l → Sigma.mk a b ∈ l
  | ⟨a', b'⟩ :: l, H => by
    by_cases h : a = a'
    · subst a'
      

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
b : β a
l : List (Sigma β)
b' : β a
H : b ∈ dlookup a ({ fst := a, snd := b' } :: l)
⊢ { fst := a, snd := b } ∈ { fst := a, snd := b' } :: l

Next tactic:
simp? at H says
        simp only [ne_eq, not_true_eq_false, dlookup_cons_eq, Option.mem_def,
          Option.some.injEq] at H
theorem of_mem_dlookup {a : α} {b : β a} :
    ∀ {l : List (Sigma β)}, b ∈ dlookup a l → Sigma.mk a b ∈ l
  | ⟨a', b'⟩ :: l, H => by
    by_cases h : a = a'
    · subst a'
      simp? at H says
        

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
b : β a
l : List (Sigma β)
b' : β a
H : b ∈ dlookup a ({ fst := a, snd := b' } :: l)
⊢ { fst := a, snd := b } ∈ { fst := a, snd := b' } :: l

Next tactic:
simp only [ne_eq, not_true_eq_false, dlookup_cons_eq, Option.mem_def,
          Option.some.injEq] at H
theorem of_mem_dlookup {a : α} {b : β a} :
    ∀ {l : List (Sigma β)}, b ∈ dlookup a l → Sigma.mk a b ∈ l
  | ⟨a', b'⟩ :: l, H => by
    by_cases h : a = a'
    · subst a'
      simp? at H says
        simp only [ne_eq, not_true_eq_false, dlookup_cons_eq, Option.mem_def,
          Option.some.injEq] at H
      

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
b : β a
l : List (Sigma β)
b' : β a
H : b' = b
⊢ { fst := a, snd := b } ∈ { fst := a, snd := b' } :: l

Next tactic:
simp [H]
theorem of_mem_dlookup {a : α} {b : β a} :
    ∀ {l : List (Sigma β)}, b ∈ dlookup a l → Sigma.mk a b ∈ l
  | ⟨a', b'⟩ :: l, H => by
    by_cases h : a = a'
    · subst a'
      simp? at H says
        simp only [ne_eq, not_true_eq_false, dlookup_cons_eq, Option.mem_def,
          Option.some.injEq] at H
      simp [H]
    · 

Proof state:
case neg
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
b : β a
a' : α
b' : β a'
l : List (Sigma β)
H : b ∈ dlookup a ({ fst := a', snd := b' } :: l)
h : ¬a = a'
⊢ { fst := a, snd := b } ∈ { fst := a', snd := b' } :: l

Next tactic:
simp only [ne_eq, h, not_false_iff, dlookup_cons_ne] at H
theorem of_mem_dlookup {a : α} {b : β a} :
    ∀ {l : List (Sigma β)}, b ∈ dlookup a l → Sigma.mk a b ∈ l
  | ⟨a', b'⟩ :: l, H => by
    by_cases h : a = a'
    · subst a'
      simp? at H says
        simp only [ne_eq, not_true_eq_false, dlookup_cons_eq, Option.mem_def,
          Option.some.injEq] at H
      simp [H]
    · simp only [ne_eq, h, not_false_iff, dlookup_cons_ne] at H
      

Proof state:
case neg
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
b : β a
a' : α
b' : β a'
l : List (Sigma β)
h : ¬a = a'
H : b ∈ dlookup a l
⊢ { fst := a, snd := b } ∈ { fst := a', snd := b' } :: l

Next tactic:
simp [of_mem_dlookup H]
theorem mem_dlookup {a} {b : β a} {l : List (Sigma β)} (nd : l.NodupKeys) (h : Sigma.mk a b ∈ l) :
    b ∈ dlookup a l := by
  

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
b : β a
l : List (Sigma β)
nd : NodupKeys l
h : { fst := a, snd := b } ∈ l
⊢ b ∈ dlookup a l

Next tactic:
cases' Option.isSome_iff_exists.mp (dlookup_isSome.mpr (mem_keys_of_mem h)) with b' h'
theorem mem_dlookup {a} {b : β a} {l : List (Sigma β)} (nd : l.NodupKeys) (h : Sigma.mk a b ∈ l) :
    b ∈ dlookup a l := by
  cases' Option.isSome_iff_exists.mp (dlookup_isSome.mpr (mem_keys_of_mem h)) with b' h'
  

Proof state:
case intro
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
b : β a
l : List (Sigma β)
nd : NodupKeys l
h : { fst := a, snd := b } ∈ l
b' : β { fst := a, snd := b }.fst
h' : dlookup { fst := a, snd := b }.fst l = some b'
⊢ b ∈ dlookup a l

Next tactic:
cases nd.eq_of_mk_mem h (of_mem_dlookup h')
theorem mem_dlookup {a} {b : β a} {l : List (Sigma β)} (nd : l.NodupKeys) (h : Sigma.mk a b ∈ l) :
    b ∈ dlookup a l := by
  cases' Option.isSome_iff_exists.mp (dlookup_isSome.mpr (mem_keys_of_mem h)) with b' h'
  cases nd.eq_of_mk_mem h (of_mem_dlookup h')
  

Proof state:
case intro.refl
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
b : β a
l : List (Sigma β)
nd : NodupKeys l
h : { fst := a, snd := b } ∈ l
h' : dlookup { fst := a, snd := b }.fst l = some b
⊢ b ∈ dlookup a l

Next tactic:
exact h'
theorem map_dlookup_eq_find (a : α) :
    ∀ l : List (Sigma β), (dlookup a l).map (Sigma.mk a) = find? (fun s => a = s.1) l
  | [] => rfl
  | ⟨a', b'⟩ :: l => by
    

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b' : β a'
l : List (Sigma β)
⊢ Option.map (Sigma.mk a) (dlookup a ({ fst := a', snd := b' } :: l)) =
    find? (fun s => decide (a = s.fst)) ({ fst := a', snd := b' } :: l)

Next tactic:
by_cases h : a = a'
theorem map_dlookup_eq_find (a : α) :
    ∀ l : List (Sigma β), (dlookup a l).map (Sigma.mk a) = find? (fun s => a = s.1) l
  | [] => rfl
  | ⟨a', b'⟩ :: l => by
    by_cases h : a = a'
    · 

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b' : β a'
l : List (Sigma β)
h : a = a'
⊢ Option.map (Sigma.mk a) (dlookup a ({ fst := a', snd := b' } :: l)) =
    find? (fun s => decide (a = s.fst)) ({ fst := a', snd := b' } :: l)

Next tactic:
subst a'
theorem map_dlookup_eq_find (a : α) :
    ∀ l : List (Sigma β), (dlookup a l).map (Sigma.mk a) = find? (fun s => a = s.1) l
  | [] => rfl
  | ⟨a', b'⟩ :: l => by
    by_cases h : a = a'
    · subst a'
      

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
l : List (Sigma β)
b' : β a
⊢ Option.map (Sigma.mk a) (dlookup a ({ fst := a, snd := b' } :: l)) =
    find? (fun s => decide (a = s.fst)) ({ fst := a, snd := b' } :: l)

Next tactic:
simp
theorem map_dlookup_eq_find (a : α) :
    ∀ l : List (Sigma β), (dlookup a l).map (Sigma.mk a) = find? (fun s => a = s.1) l
  | [] => rfl
  | ⟨a', b'⟩ :: l => by
    by_cases h : a = a'
    · subst a'
      simp
    · 

Proof state:
case neg
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b' : β a'
l : List (Sigma β)
h : ¬a = a'
⊢ Option.map (Sigma.mk a) (dlookup a ({ fst := a', snd := b' } :: l)) =
    find? (fun s => decide (a = s.fst)) ({ fst := a', snd := b' } :: l)

Next tactic:
simp [h]
theorem map_dlookup_eq_find (a : α) :
    ∀ l : List (Sigma β), (dlookup a l).map (Sigma.mk a) = find? (fun s => a = s.1) l
  | [] => rfl
  | ⟨a', b'⟩ :: l => by
    by_cases h : a = a'
    · subst a'
      simp
    · simp [h]
      

Proof state:
case neg
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b' : β a'
l : List (Sigma β)
h : ¬a = a'
⊢ Option.map (Sigma.mk a) (dlookup a l) = find? (fun s => decide (a = s.fst)) l

Next tactic:
exact map_dlookup_eq_find a l
theorem perm_dlookup (a : α) {l₁ l₂ : List (Sigma β)} (nd₁ : l₁.NodupKeys) (nd₂ : l₂.NodupKeys)
    (p : l₁ ~ l₂) : dlookup a l₁ = dlookup a l₂ := by
  

Proof state:
α : Type u
β : α → Type v
l l₁✝ l₂✝ : List (Sigma β)
inst✝ : DecidableEq α
a : α
l₁ l₂ : List (Sigma β)
nd₁ : NodupKeys l₁
nd₂ : NodupKeys l₂
p : l₁ ~ l₂
⊢ dlookup a l₁ = dlookup a l₂

Next tactic:
ext b
theorem perm_dlookup (a : α) {l₁ l₂ : List (Sigma β)} (nd₁ : l₁.NodupKeys) (nd₂ : l₂.NodupKeys)
    (p : l₁ ~ l₂) : dlookup a l₁ = dlookup a l₂ := by
  ext b; 

Proof state:
case a
α : Type u
β : α → Type v
l l₁✝ l₂✝ : List (Sigma β)
inst✝ : DecidableEq α
a : α
l₁ l₂ : List (Sigma β)
nd₁ : NodupKeys l₁
nd₂ : NodupKeys l₂
p : l₁ ~ l₂
b : β a
⊢ b ∈ dlookup a l₁ ↔ b ∈ dlookup a l₂

Next tactic:
simp only [mem_dlookup_iff nd₁, mem_dlookup_iff nd₂]
theorem perm_dlookup (a : α) {l₁ l₂ : List (Sigma β)} (nd₁ : l₁.NodupKeys) (nd₂ : l₂.NodupKeys)
    (p : l₁ ~ l₂) : dlookup a l₁ = dlookup a l₂ := by
  ext b; simp only [mem_dlookup_iff nd₁, mem_dlookup_iff nd₂]; 

Proof state:
case a
α : Type u
β : α → Type v
l l₁✝ l₂✝ : List (Sigma β)
inst✝ : DecidableEq α
a : α
l₁ l₂ : List (Sigma β)
nd₁ : NodupKeys l₁
nd₂ : NodupKeys l₂
p : l₁ ~ l₂
b : β a
⊢ { fst := a, snd := b } ∈ l₁ ↔ { fst := a, snd := b } ∈ l₂

Next tactic:
exact p.mem_iff
theorem lookup_ext {l₀ l₁ : List (Sigma β)} (nd₀ : l₀.NodupKeys) (nd₁ : l₁.NodupKeys)
    (h : ∀ x y, y ∈ l₀.dlookup x ↔ y ∈ l₁.dlookup x) : l₀ ~ l₁ :=
  mem_ext nd₀.nodup nd₁.nodup fun ⟨a, b⟩ => by
    

Proof state:
α : Type u
β : α → Type v
l l₁✝ l₂ : List (Sigma β)
inst✝ : DecidableEq α
l₀ l₁ : List (Sigma β)
nd₀ : NodupKeys l₀
nd₁ : NodupKeys l₁
h : ∀ (x : α) (y : β x), y ∈ dlookup x l₀ ↔ y ∈ dlookup x l₁
x✝ : Sigma β
a : α
b : β a
⊢ { fst := a, snd := b } ∈ l₀ ↔ { fst := a, snd := b } ∈ l₁

Next tactic:
rw [← mem_dlookup_iff, ← mem_dlookup_iff, h]
theorem lookup_ext {l₀ l₁ : List (Sigma β)} (nd₀ : l₀.NodupKeys) (nd₁ : l₁.NodupKeys)
    (h : ∀ x y, y ∈ l₀.dlookup x ↔ y ∈ l₁.dlookup x) : l₀ ~ l₁ :=
  mem_ext nd₀.nodup nd₁.nodup fun ⟨a, b⟩ => by
    rw [← mem_dlookup_iff, ← mem_dlookup_iff, h] <;> 

Proof state:
α : Type u
β : α → Type v
l l₁✝ l₂ : List (Sigma β)
inst✝ : DecidableEq α
l₀ l₁ : List (Sigma β)
nd₀ : NodupKeys l₀
nd₁ : NodupKeys l₁
h : ∀ (x : α) (y : β x), y ∈ dlookup x l₀ ↔ y ∈ dlookup x l₁
x✝ : Sigma β
a : α
b : β a
⊢ NodupKeys l₁

Next tactic:
assumption
theorem lookup_ext {l₀ l₁ : List (Sigma β)} (nd₀ : l₀.NodupKeys) (nd₁ : l₁.NodupKeys)
    (h : ∀ x y, y ∈ l₀.dlookup x ↔ y ∈ l₁.dlookup x) : l₀ ~ l₁ :=
  mem_ext nd₀.nodup nd₁.nodup fun ⟨a, b⟩ => by
    rw [← mem_dlookup_iff, ← mem_dlookup_iff, h] <;> 

Proof state:
α : Type u
β : α → Type v
l l₁✝ l₂ : List (Sigma β)
inst✝ : DecidableEq α
l₀ l₁ : List (Sigma β)
nd₀ : NodupKeys l₀
nd₁ : NodupKeys l₁
h : ∀ (x : α) (y : β x), y ∈ dlookup x l₀ ↔ y ∈ dlookup x l₁
x✝ : Sigma β
a : α
b : β a
⊢ NodupKeys l₀

Next tactic:
assumption
theorem lookupAll_eq_nil {a : α} :
    ∀ {l : List (Sigma β)}, lookupAll a l = [] ↔ ∀ b : β a, Sigma.mk a b ∉ l
  | [] => by 

Proof state:
α : Type u
β : α → Type v
l l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
⊢ lookupAll a [] = [] ↔ ∀ (b : β a), { fst := a, snd := b } ∉ []

Next tactic:
simp
theorem lookupAll_eq_nil {a : α} :
    ∀ {l : List (Sigma β)}, lookupAll a l = [] ↔ ∀ b : β a, Sigma.mk a b ∉ l
  | [] => by simp
  | ⟨a', b⟩ :: l => by
    

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b : β a'
l : List (Sigma β)
⊢ lookupAll a ({ fst := a', snd := b } :: l) = [] ↔
    ∀ (b_1 : β a), { fst := a, snd := b_1 } ∉ { fst := a', snd := b } :: l

Next tactic:
by_cases h : a = a'
theorem lookupAll_eq_nil {a : α} :
    ∀ {l : List (Sigma β)}, lookupAll a l = [] ↔ ∀ b : β a, Sigma.mk a b ∉ l
  | [] => by simp
  | ⟨a', b⟩ :: l => by
    by_cases h : a = a'
    · 

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b : β a'
l : List (Sigma β)
h : a = a'
⊢ lookupAll a ({ fst := a', snd := b } :: l) = [] ↔
    ∀ (b_1 : β a), { fst := a, snd := b_1 } ∉ { fst := a', snd := b } :: l

Next tactic:
subst a'
theorem lookupAll_eq_nil {a : α} :
    ∀ {l : List (Sigma β)}, lookupAll a l = [] ↔ ∀ b : β a, Sigma.mk a b ∉ l
  | [] => by simp
  | ⟨a', b⟩ :: l => by
    by_cases h : a = a'
    · subst a'
      

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
l : List (Sigma β)
b : β a
⊢ lookupAll a ({ fst := a, snd := b } :: l) = [] ↔ ∀ (b_1 : β a), { fst := a, snd := b_1 } ∉ { fst := a, snd := b } :: l

Next tactic:
simp
theorem lookupAll_eq_nil {a : α} :
    ∀ {l : List (Sigma β)}, lookupAll a l = [] ↔ ∀ b : β a, Sigma.mk a b ∉ l
  | [] => by simp
  | ⟨a', b⟩ :: l => by
    by_cases h : a = a'
    · subst a'
      simp
    · 

Proof state:
case neg
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b : β a'
l : List (Sigma β)
h : ¬a = a'
⊢ lookupAll a ({ fst := a', snd := b } :: l) = [] ↔
    ∀ (b_1 : β a), { fst := a, snd := b_1 } ∉ { fst := a', snd := b } :: l

Next tactic:
simp [h, lookupAll_eq_nil]
theorem head?_lookupAll (a : α) : ∀ l : List (Sigma β), head? (lookupAll a l) = dlookup a l
  | [] => by 

Proof state:
α : Type u
β : α → Type v
l l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a : α
⊢ head? (lookupAll a []) = dlookup a []

Next tactic:
simp
theorem head?_lookupAll (a : α) : ∀ l : List (Sigma β), head? (lookupAll a l) = dlookup a l
  | [] => by simp
  | ⟨a', b⟩ :: l => by
    

Proof state:
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b : β a'
l : List (Sigma β)
⊢ head? (lookupAll a ({ fst := a', snd := b } :: l)) = dlookup a ({ fst := a', snd := b } :: l)

Next tactic:
by_cases h : a = a'
theorem head?_lookupAll (a : α) : ∀ l : List (Sigma β), head? (lookupAll a l) = dlookup a l
  | [] => by simp
  | ⟨a', b⟩ :: l => by
    by_cases h : a = a'
    · 

Proof state:
case pos
α : Type u
β : α → Type v
l✝ l₁ l₂ : List (Sigma β)
inst✝ : DecidableEq α
a a' : α
b : β a'
l : List (Sigma β)
h : a = a'
⊢ head? (lookupAll a ({ fst := a', snd := b } :: l)) = dlookup a ({ fst := a', snd := b } :: l)

Next tactic:
subst h