l3lab/ntp-mathlib huggingface dataset:
/-- Given an open cover of `X`, and a morphism `𝒰.obj x ⟶ Y` for each open subscheme in the cover,
such that these morphisms are compatible in the intersection (pullback), we may glue the morphisms
together into a morphism `X ⟶ Y`.

Note:
If `X` is exactly (defeq to) the gluing of `U i`, then using `Multicoequalizer.desc` suffices.
-/
def glueMorphisms {Y : Scheme} (f : ∀ x, 𝒰.obj x ⟶ Y)
    (hf : ∀ x y, (pullback.fst : pullback (𝒰.map x) (𝒰.map y) ⟶ _) ≫ f x = pullback.snd ≫ f y) :
    X ⟶ Y := by
  refine' inv 𝒰.fromGlued ≫ _
  fapply Multicoequalizer.desc
  exact f
  

Proof state:
case h
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f : (x : 𝒰.J) → obj 𝒰 x ⟶ Y
hf : ∀ (x y : 𝒰.J), pullback.fst ≫ f x = pullback.snd ≫ f y
⊢ ∀ (a : (diagram (gluedCover 𝒰).toGlueData).L),
    MultispanIndex.fst (diagram (gluedCover 𝒰).toGlueData) a ≫
        f (MultispanIndex.fstFrom (diagram (gluedCover 𝒰).toGlueData) a) =
      MultispanIndex.snd (diagram (gluedCover 𝒰).toGlueData) a ≫
        f (MultispanIndex.sndFrom (diagram (gluedCover 𝒰).toGlueData) a)

Next tactic:
rintro ⟨i, j⟩
/-- Given an open cover of `X`, and a morphism `𝒰.obj x ⟶ Y` for each open subscheme in the cover,
such that these morphisms are compatible in the intersection (pullback), we may glue the morphisms
together into a morphism `X ⟶ Y`.

Note:
If `X` is exactly (defeq to) the gluing of `U i`, then using `Multicoequalizer.desc` suffices.
-/
def glueMorphisms {Y : Scheme} (f : ∀ x, 𝒰.obj x ⟶ Y)
    (hf : ∀ x y, (pullback.fst : pullback (𝒰.map x) (𝒰.map y) ⟶ _) ≫ f x = pullback.snd ≫ f y) :
    X ⟶ Y := by
  refine' inv 𝒰.fromGlued ≫ _
  fapply Multicoequalizer.desc
  exact f
  rintro ⟨i, j⟩
  

Proof state:
case h.mk
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f : (x : 𝒰.J) → obj 𝒰 x ⟶ Y
hf : ∀ (x y : 𝒰.J), pullback.fst ≫ f x = pullback.snd ≫ f y
i j : (gluedCover 𝒰).toGlueData.J
⊢ MultispanIndex.fst (diagram (gluedCover 𝒰).toGlueData) (i, j) ≫
      f (MultispanIndex.fstFrom (diagram (gluedCover 𝒰).toGlueData) (i, j)) =
    MultispanIndex.snd (diagram (gluedCover 𝒰).toGlueData) (i, j) ≫
      f (MultispanIndex.sndFrom (diagram (gluedCover 𝒰).toGlueData) (i, j))

Next tactic:
change pullback.fst ≫ f i = (_ ≫ _) ≫ f j
/-- Given an open cover of `X`, and a morphism `𝒰.obj x ⟶ Y` for each open subscheme in the cover,
such that these morphisms are compatible in the intersection (pullback), we may glue the morphisms
together into a morphism `X ⟶ Y`.

Note:
If `X` is exactly (defeq to) the gluing of `U i`, then using `Multicoequalizer.desc` suffices.
-/
def glueMorphisms {Y : Scheme} (f : ∀ x, 𝒰.obj x ⟶ Y)
    (hf : ∀ x y, (pullback.fst : pullback (𝒰.map x) (𝒰.map y) ⟶ _) ≫ f x = pullback.snd ≫ f y) :
    X ⟶ Y := by
  refine' inv 𝒰.fromGlued ≫ _
  fapply Multicoequalizer.desc
  exact f
  rintro ⟨i, j⟩
  change pullback.fst ≫ f i = (_ ≫ _) ≫ f j
  

Proof state:
case h.mk
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f : (x : 𝒰.J) → obj 𝒰 x ⟶ Y
hf : ∀ (x y : 𝒰.J), pullback.fst ≫ f x = pullback.snd ≫ f y
i j : (gluedCover 𝒰).toGlueData.J
⊢ pullback.fst ≫ f i = (t (gluedCover 𝒰).toGlueData i j ≫ GlueData.f (gluedCover 𝒰).toGlueData j i) ≫ f j

Next tactic:
erw [pullbackSymmetry_hom_comp_fst]
/-- Given an open cover of `X`, and a morphism `𝒰.obj x ⟶ Y` for each open subscheme in the cover,
such that these morphisms are compatible in the intersection (pullback), we may glue the morphisms
together into a morphism `X ⟶ Y`.

Note:
If `X` is exactly (defeq to) the gluing of `U i`, then using `Multicoequalizer.desc` suffices.
-/
def glueMorphisms {Y : Scheme} (f : ∀ x, 𝒰.obj x ⟶ Y)
    (hf : ∀ x y, (pullback.fst : pullback (𝒰.map x) (𝒰.map y) ⟶ _) ≫ f x = pullback.snd ≫ f y) :
    X ⟶ Y := by
  refine' inv 𝒰.fromGlued ≫ _
  fapply Multicoequalizer.desc
  exact f
  rintro ⟨i, j⟩
  change pullback.fst ≫ f i = (_ ≫ _) ≫ f j
  erw [pullbackSymmetry_hom_comp_fst]
  

Proof state:
case h.mk
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f : (x : 𝒰.J) → obj 𝒰 x ⟶ Y
hf : ∀ (x y : 𝒰.J), pullback.fst ≫ f x = pullback.snd ≫ f y
i j : (gluedCover 𝒰).toGlueData.J
⊢ pullback.fst ≫ f i = pullback.snd ≫ f j

Next tactic:
exact hf i j
@[simp, reassoc]
theorem ι_glueMorphisms {Y : Scheme} (f : ∀ x, 𝒰.obj x ⟶ Y)
    (hf : ∀ x y, (pullback.fst : pullback (𝒰.map x) (𝒰.map y) ⟶ _) ≫ f x = pullback.snd ≫ f y)
    (x : 𝒰.J) : 𝒰.map x ≫ 𝒰.glueMorphisms f hf = f x := by
  

Proof state:
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f : (x : 𝒰.J) → obj 𝒰 x ⟶ Y
hf : ∀ (x y : 𝒰.J), pullback.fst ≫ f x = pullback.snd ≫ f y
x : 𝒰.J
⊢ map 𝒰 x ≫ glueMorphisms 𝒰 f hf = f x

Next tactic:
rw [← ι_fromGlued, Category.assoc]
@[simp, reassoc]
theorem ι_glueMorphisms {Y : Scheme} (f : ∀ x, 𝒰.obj x ⟶ Y)
    (hf : ∀ x y, (pullback.fst : pullback (𝒰.map x) (𝒰.map y) ⟶ _) ≫ f x = pullback.snd ≫ f y)
    (x : 𝒰.J) : 𝒰.map x ≫ 𝒰.glueMorphisms f hf = f x := by
  rw [← ι_fromGlued, Category.assoc]
  

Proof state:
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f : (x : 𝒰.J) → obj 𝒰 x ⟶ Y
hf : ∀ (x y : 𝒰.J), pullback.fst ≫ f x = pullback.snd ≫ f y
x : 𝒰.J
⊢ GlueData.ι (gluedCover 𝒰) x ≫ fromGlued 𝒰 ≫ glueMorphisms 𝒰 f hf = f x

Next tactic:
erw [IsIso.hom_inv_id_assoc, Multicoequalizer.π_desc]
theorem hom_ext {Y : Scheme} (f₁ f₂ : X ⟶ Y) (h : ∀ x, 𝒰.map x ≫ f₁ = 𝒰.map x ≫ f₂) : f₁ = f₂ := by
  

Proof state:
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f₁ f₂ : X ⟶ Y
h : ∀ (x : 𝒰.J), map 𝒰 x ≫ f₁ = map 𝒰 x ≫ f₂
⊢ f₁ = f₂

Next tactic:
rw [← cancel_epi 𝒰.fromGlued]
theorem hom_ext {Y : Scheme} (f₁ f₂ : X ⟶ Y) (h : ∀ x, 𝒰.map x ≫ f₁ = 𝒰.map x ≫ f₂) : f₁ = f₂ := by
  rw [← cancel_epi 𝒰.fromGlued]
  

Proof state:
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f₁ f₂ : X ⟶ Y
h : ∀ (x : 𝒰.J), map 𝒰 x ≫ f₁ = map 𝒰 x ≫ f₂
⊢ fromGlued 𝒰 ≫ f₁ = fromGlued 𝒰 ≫ f₂

Next tactic:
apply Multicoequalizer.hom_ext
theorem hom_ext {Y : Scheme} (f₁ f₂ : X ⟶ Y) (h : ∀ x, 𝒰.map x ≫ f₁ = 𝒰.map x ≫ f₂) : f₁ = f₂ := by
  rw [← cancel_epi 𝒰.fromGlued]
  apply Multicoequalizer.hom_ext
  

Proof state:
case h
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f₁ f₂ : X ⟶ Y
h : ∀ (x : 𝒰.J), map 𝒰 x ≫ f₁ = map 𝒰 x ≫ f₂
⊢ ∀ (b : (diagram (gluedCover 𝒰).toGlueData).R),
    Multicoequalizer.π (diagram (gluedCover 𝒰).toGlueData) b ≫ fromGlued 𝒰 ≫ f₁ =
      Multicoequalizer.π (diagram (gluedCover 𝒰).toGlueData) b ≫ fromGlued 𝒰 ≫ f₂

Next tactic:
intro x
theorem hom_ext {Y : Scheme} (f₁ f₂ : X ⟶ Y) (h : ∀ x, 𝒰.map x ≫ f₁ = 𝒰.map x ≫ f₂) : f₁ = f₂ := by
  rw [← cancel_epi 𝒰.fromGlued]
  apply Multicoequalizer.hom_ext
  intro x
  

Proof state:
case h
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f₁ f₂ : X ⟶ Y
h : ∀ (x : 𝒰.J), map 𝒰 x ≫ f₁ = map 𝒰 x ≫ f₂
x : (diagram (gluedCover 𝒰).toGlueData).R
⊢ Multicoequalizer.π (diagram (gluedCover 𝒰).toGlueData) x ≫ fromGlued 𝒰 ≫ f₁ =
    Multicoequalizer.π (diagram (gluedCover 𝒰).toGlueData) x ≫ fromGlued 𝒰 ≫ f₂

Next tactic:
erw [Multicoequalizer.π_desc_assoc]
theorem hom_ext {Y : Scheme} (f₁ f₂ : X ⟶ Y) (h : ∀ x, 𝒰.map x ≫ f₁ = 𝒰.map x ≫ f₂) : f₁ = f₂ := by
  rw [← cancel_epi 𝒰.fromGlued]
  apply Multicoequalizer.hom_ext
  intro x
  erw [Multicoequalizer.π_desc_assoc]
  

Proof state:
case h
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f₁ f₂ : X ⟶ Y
h : ∀ (x : 𝒰.J), map 𝒰 x ≫ f₁ = map 𝒰 x ≫ f₂
x : (diagram (gluedCover 𝒰).toGlueData).R
⊢ map 𝒰 x ≫ f₁ = Multicoequalizer.π (diagram (gluedCover 𝒰).toGlueData) x ≫ fromGlued 𝒰 ≫ f₂

Next tactic:
erw [Multicoequalizer.π_desc_assoc]
theorem hom_ext {Y : Scheme} (f₁ f₂ : X ⟶ Y) (h : ∀ x, 𝒰.map x ≫ f₁ = 𝒰.map x ≫ f₂) : f₁ = f₂ := by
  rw [← cancel_epi 𝒰.fromGlued]
  apply Multicoequalizer.hom_ext
  intro x
  erw [Multicoequalizer.π_desc_assoc]
  erw [Multicoequalizer.π_desc_assoc]
  

Proof state:
case h
X : Scheme
𝒰 : OpenCover X
Y : Scheme
f₁ f₂ : X ⟶ Y
h : ∀ (x : 𝒰.J), map 𝒰 x ≫ f₁ = map 𝒰 x ≫ f₂
x : (diagram (gluedCover 𝒰).toGlueData).R
⊢ map 𝒰 x ≫ f₁ = map 𝒰 x ≫ f₂

Next tactic:
exact h x
/-- There is (computably) an equivalence between `α` and `Fin (card α)`.

Since it is not unique and depends on which permutation
of the universe list is used, the equivalence is wrapped in `Trunc` to
preserve computability.

See `Fintype.equivFin` for the noncomputable version,
and `Fintype.truncEquivFinOfCardEq` and `Fintype.equivFinOfCardEq`
for an equiv `α ≃ Fin n` given `Fintype.card α = n`.

See `Fintype.truncFinBijection` for a version without `[DecidableEq α]`.
-/
def truncEquivFin (α) [DecidableEq α] [Fintype α] : Trunc (α ≃ Fin (card α)) := by
  

Proof state:
α✝ : Type u_1
β : Type u_2
γ : Type u_3
α : Type ?u.83
inst✝¹ : DecidableEq α
inst✝ : Fintype α
⊢ Trunc (α ≃ Fin (card α))

Next tactic:
unfold card Finset.card
/-- There is (computably) an equivalence between `α` and `Fin (card α)`.

Since it is not unique and depends on which permutation
of the universe list is used, the equivalence is wrapped in `Trunc` to
preserve computability.

See `Fintype.equivFin` for the noncomputable version,
and `Fintype.truncEquivFinOfCardEq` and `Fintype.equivFinOfCardEq`
for an equiv `α ≃ Fin n` given `Fintype.card α = n`.

See `Fintype.truncFinBijection` for a version without `[DecidableEq α]`.
-/
def truncEquivFin (α) [DecidableEq α] [Fintype α] : Trunc (α ≃ Fin (card α)) := by
  unfold card Finset.card
  

Proof state:
α✝ : Type u_1
β : Type u_2
γ : Type u_3
α : Type ?u.83
inst✝¹ : DecidableEq α
inst✝ : Fintype α
⊢ Trunc (α ≃ Fin (Multiset.card univ.val))

Next tactic:
exact
    Quot.recOnSubsingleton'
      (motive := fun s : Multiset α =>
        (∀ x : α, x ∈ s) → s.Nodup → Trunc (α ≃ Fin (Multiset.card s)))
      univ.val
      (fun l (h : ∀ x : α, x ∈ l) (nd : l.Nodup) => Trunc.mk (nd.getEquivOfForallMemList _ h).symm)
      mem_univ_val univ.2
/-- There is (computably) a bijection between `Fin (card α)` and `α`.

Since it is not unique and depends on which permutation
of the universe list is used, the bijection is wrapped in `Trunc` to
preserve computability.

See `Fintype.truncEquivFin` for a version that gives an equivalence
given `[DecidableEq α]`.
-/
def truncFinBijection (α) [Fintype α] : Trunc { f : Fin (card α) → α // Bijective f } := by
  

Proof state:
α✝ : Type u_1
β : Type u_2
γ : Type u_3
α : Type ?u.1670
inst✝ : Fintype α
⊢ Trunc { f // Bijective f }

Next tactic:
unfold card Finset.card
/-- There is (computably) a bijection between `Fin (card α)` and `α`.

Since it is not unique and depends on which permutation
of the universe list is used, the bijection is wrapped in `Trunc` to
preserve computability.

See `Fintype.truncEquivFin` for a version that gives an equivalence
given `[DecidableEq α]`.
-/
def truncFinBijection (α) [Fintype α] : Trunc { f : Fin (card α) → α // Bijective f } := by
  unfold card Finset.card
  

Proof state:
α✝ : Type u_1
β : Type u_2
γ : Type u_3
α : Type ?u.1670
inst✝ : Fintype α
⊢ Trunc { f // Bijective f }

Next tactic:
refine
    Quot.recOnSubsingleton'
      (motive := fun s : Multiset α =>
        (∀ x : α, x ∈ s) → s.Nodup → Trunc {f : Fin (Multiset.card s) → α // Bijective f})
      univ.val
      (fun l (h : ∀ x : α, x ∈ l) (nd : l.Nodup) => Trunc.mk (nd.getBijectionOfForallMemList _ h))
      mem_univ_val univ.2
theorem card_of_subtype {p : α → Prop} (s : Finset α) (H : ∀ x : α, x ∈ s ↔ p x)
    [Fintype { x // p x }] : card { x // p x } = s.card := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
p : α → Prop
s : Finset α
H : ∀ (x : α), x ∈ s ↔ p x
inst✝ : Fintype { x // p x }
⊢ card { x // p x } = Finset.card s

Next tactic:
rw [← subtype_card s H]
theorem card_of_subtype {p : α → Prop} (s : Finset α) (H : ∀ x : α, x ∈ s ↔ p x)
    [Fintype { x // p x }] : card { x // p x } = s.card := by
  rw [← subtype_card s H]
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
p : α → Prop
s : Finset α
H : ∀ (x : α), x ∈ s ↔ p x
inst✝ : Fintype { x // p x }
⊢ card { x // p x } = card { x // p x }

Next tactic:
congr
theorem card_of_subtype {p : α → Prop} (s : Finset α) (H : ∀ x : α, x ∈ s ↔ p x)
    [Fintype { x // p x }] : card { x // p x } = s.card := by
  rw [← subtype_card s H]
  congr
  

Proof state:
case h.e_2.h
α : Type u_1
β : Type u_2
γ : Type u_3
p : α → Prop
s : Finset α
H : ∀ (x : α), x ∈ s ↔ p x
inst✝ : Fintype { x // p x }
⊢ inst✝ = Fintype.subtype s H

Next tactic:
apply Subsingleton.elim
theorem card_of_finset' {p : Set α} (s : Finset α) (H : ∀ x, x ∈ s ↔ x ∈ p) [Fintype p] :
    Fintype.card p = s.card := by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
p : Set α
s : Finset α
H : ∀ (x : α), x ∈ s ↔ x ∈ p
inst✝ : Fintype ↑p
⊢ card ↑p = Finset.card s

Next tactic:
rw [← card_ofFinset s H]
theorem card_of_finset' {p : Set α} (s : Finset α) (H : ∀ x, x ∈ s ↔ x ∈ p) [Fintype p] :
    Fintype.card p = s.card := by rw [← card_ofFinset s H]; 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
p : Set α
s : Finset α
H : ∀ (x : α), x ∈ s ↔ x ∈ p
inst✝ : Fintype ↑p
⊢ card ↑p = card ↑p

Next tactic:
congr
theorem card_of_finset' {p : Set α} (s : Finset α) (H : ∀ x, x ∈ s ↔ x ∈ p) [Fintype p] :
    Fintype.card p = s.card := by rw [← card_ofFinset s H]; congr; 

Proof state:
case h.e_2.h
α : Type u_1
β : Type u_2
γ : Type u_3
p : Set α
s : Finset α
H : ∀ (x : α), x ∈ s ↔ x ∈ p
inst✝ : Fintype ↑p
⊢ inst✝ = ofFinset s H

Next tactic:
apply Subsingleton.elim
theorem card_congr {α β} [Fintype α] [Fintype β] (f : α ≃ β) : card α = card β := by
  

Proof state:
α✝ : Type u_1
β✝ : Type u_2
γ : Type u_3
α : Type u_4
β : Type u_5
inst✝¹ : Fintype α
inst✝ : Fintype β
f : α ≃ β
⊢ card α = card β

Next tactic:
rw [← ofEquiv_card f]
theorem card_congr {α β} [Fintype α] [Fintype β] (f : α ≃ β) : card α = card β := by
  rw [← ofEquiv_card f]; 

Proof state:
α✝ : Type u_1
β✝ : Type u_2
γ : Type u_3
α : Type u_4
β : Type u_5
inst✝¹ : Fintype α
inst✝ : Fintype β
f : α ≃ β
⊢ card β = card β

Next tactic:
congr
theorem card_congr {α β} [Fintype α] [Fintype β] (f : α ≃ β) : card α = card β := by
  rw [← ofEquiv_card f]; congr; 

Proof state:
case h.e_2.h
α✝ : Type u_1
β✝ : Type u_2
γ : Type u_3
α : Type u_4
β : Type u_5
inst✝¹ : Fintype α
inst✝ : Fintype β
f : α ≃ β
⊢ ofEquiv α f = inst✝

Next tactic:
apply Subsingleton.elim
@[congr]
theorem card_congr' {α β} [Fintype α] [Fintype β] (h : α = β) : card α = card β :=
  card_congr (by 

Proof state:
α✝ : Type u_1
β✝ : Type u_2
γ : Type u_3
α β : Type u_4
inst✝¹ : Fintype α
inst✝ : Fintype β
h : α = β
⊢ α ≃ β

Next tactic:
rw [h]
/-- Two `Fintype`s with the same cardinality are (noncomputably) in bijection.

See `Fintype.truncEquivOfCardEq` for the computable version,
and `Fintype.truncEquivFinOfCardEq` and `Fintype.equivFinOfCardEq` for
the specialization to `Fin`.
-/
noncomputable def equivOfCardEq (h : card α = card β) : α ≃ β := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝¹ : Fintype α
inst✝ : Fintype β
h : card α = card β
⊢ α ≃ β

Next tactic:
letI := Classical.decEq α
/-- Two `Fintype`s with the same cardinality are (noncomputably) in bijection.

See `Fintype.truncEquivOfCardEq` for the computable version,
and `Fintype.truncEquivFinOfCardEq` and `Fintype.equivFinOfCardEq` for
the specialization to `Fin`.
-/
noncomputable def equivOfCardEq (h : card α = card β) : α ≃ β := by
  letI := Classical.decEq α
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝¹ : Fintype α
inst✝ : Fintype β
h : card α = card β
this : DecidableEq α := Classical.decEq α
⊢ α ≃ β

Next tactic:
letI := Classical.decEq β
/-- Two `Fintype`s with the same cardinality are (noncomputably) in bijection.

See `Fintype.truncEquivOfCardEq` for the computable version,
and `Fintype.truncEquivFinOfCardEq` and `Fintype.equivFinOfCardEq` for
the specialization to `Fin`.
-/
noncomputable def equivOfCardEq (h : card α = card β) : α ≃ β := by
  letI := Classical.decEq α
  letI := Classical.decEq β
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝¹ : Fintype α
inst✝ : Fintype β
h : card α = card β
this✝ : DecidableEq α := Classical.decEq α
this : DecidableEq β := Classical.decEq β
⊢ α ≃ β

Next tactic:
exact (truncEquivOfCardEq h).out
theorem Finset.eq_univ_of_card [Fintype α] (s : Finset α) (hs : s.card = Fintype.card α) :
    s = univ :=
  eq_of_subset_of_card_le (subset_univ _) <| by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝ : Fintype α
s : Finset α
hs : card s = Fintype.card α
⊢ card univ ≤ card s

Next tactic:
rw [hs, Finset.card_univ]
theorem Finset.card_eq_iff_eq_univ [Fintype α] (s : Finset α) :
    s.card = Fintype.card α ↔ s = Finset.univ :=
  ⟨s.eq_univ_of_card, by
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝ : Fintype α
s : Finset α
⊢ s = univ → card s = Fintype.card α

Next tactic:
rintro rfl
theorem Finset.card_eq_iff_eq_univ [Fintype α] (s : Finset α) :
    s.card = Fintype.card α ↔ s = Finset.univ :=
  ⟨s.eq_univ_of_card, by
    rintro rfl
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝ : Fintype α
⊢ card univ = Fintype.card α

Next tactic:
exact Finset.card_univ
@[simp]
theorem Finset.card_add_card_compl [DecidableEq α] [Fintype α] (s : Finset α) :
    s.card + sᶜ.card = Fintype.card α := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝¹ : DecidableEq α
inst✝ : Fintype α
s : Finset α
⊢ card s + card sᶜ = Fintype.card α

Next tactic:
rw [Finset.card_compl, ← Nat.add_sub_assoc (card_le_univ s), Nat.add_sub_cancel_left]
@[simp]
theorem Finset.card_compl_add_card [DecidableEq α] [Fintype α] (s : Finset α) :
    sᶜ.card + s.card = Fintype.card α := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝¹ : DecidableEq α
inst✝ : Fintype α
s : Finset α
⊢ card sᶜ + card s = Fintype.card α

Next tactic:
rw [add_comm, card_add_card_compl]
theorem Fintype.card_compl_set [Fintype α] (s : Set α) [Fintype s] [Fintype (↥sᶜ : Sort _)] :
    Fintype.card (↥sᶜ : Sort _) = Fintype.card α - Fintype.card s := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝² : Fintype α
s : Set α
inst✝¹ : Fintype ↑s
inst✝ : Fintype ↑sᶜ
⊢ card ↑sᶜ = card α - card ↑s

Next tactic:
classical rw [← Set.toFinset_card, ← Set.toFinset_card, ← Finset.card_compl, Set.toFinset_compl]
theorem Fintype.card_compl_set [Fintype α] (s : Set α) [Fintype s] [Fintype (↥sᶜ : Sort _)] :
    Fintype.card (↥sᶜ : Sort _) = Fintype.card α - Fintype.card s := by
  classical 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝² : Fintype α
s : Set α
inst✝¹ : Fintype ↑s
inst✝ : Fintype ↑sᶜ
⊢ card ↑sᶜ = card α - card ↑s

Next tactic:
rw [← Set.toFinset_card, ← Set.toFinset_card, ← Finset.card_compl, Set.toFinset_compl]
theorem Fintype.card_fin_lt_of_le {m n : ℕ} (h : m ≤ n) :
    Fintype.card {i : Fin n // i < m} = m := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
m n : ℕ
h : m ≤ n
⊢ card { i // ↑i < m } = m

Next tactic:
conv_rhs => rw [← Fintype.card_fin m]
theorem Fintype.card_fin_lt_of_le {m n : ℕ} (h : m ≤ n) :
    Fintype.card {i : Fin n // i < m} = m := by
  conv_rhs => 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
m n : ℕ
h : m ≤ n
| m

Next tactic:
rw [← Fintype.card_fin m]
theorem Fintype.card_fin_lt_of_le {m n : ℕ} (h : m ≤ n) :
    Fintype.card {i : Fin n // i < m} = m := by
  conv_rhs => rw [← Fintype.card_fin m]
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
m n : ℕ
h : m ≤ n
⊢ card { i // ↑i < m } = card (Fin m)

Next tactic:
apply Fintype.card_congr
theorem Fintype.card_fin_lt_of_le {m n : ℕ} (h : m ≤ n) :
    Fintype.card {i : Fin n // i < m} = m := by
  conv_rhs => rw [← Fintype.card_fin m]
  apply Fintype.card_congr
  

Proof state:
case f
α : Type u_1
β : Type u_2
γ : Type u_3
m n : ℕ
h : m ≤ n
⊢ { i // ↑i < m } ≃ Fin m

Next tactic:
exact { toFun := fun ⟨⟨i, _⟩, hi⟩ ↦ ⟨i, hi⟩
          invFun := fun ⟨i, hi⟩ ↦ ⟨⟨i, lt_of_lt_of_le hi h⟩, hi⟩
          left_inv := fun i ↦ rfl
          right_inv := fun i ↦ rfl }
@[simp]
theorem Finset.card_fin (n : ℕ) : Finset.card (Finset.univ : Finset (Fin n)) = n := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
n : ℕ
⊢ card univ = n

Next tactic:
rw [Finset.card_univ, Fintype.card_fin]
/-- A reversed version of `Fin.cast_eq_cast` that is easier to rewrite with. -/
theorem Fin.cast_eq_cast' {n m : ℕ} (h : Fin n = Fin m) :
    _root_.cast h = Fin.cast (fin_injective h) := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
n m : ℕ
h : Fin n = Fin m
⊢ _root_.cast h = cast (_ : n = m)

Next tactic:
cases fin_injective h
/-- A reversed version of `Fin.cast_eq_cast` that is easier to rewrite with. -/
theorem Fin.cast_eq_cast' {n m : ℕ} (h : Fin n = Fin m) :
    _root_.cast h = Fin.cast (fin_injective h) := by
  cases fin_injective h
  

Proof state:
case refl
α : Type u_1
β : Type u_2
γ : Type u_3
n : ℕ
h : Fin n = Fin n
⊢ _root_.cast h = cast (_ : n = n)

Next tactic:
rfl
theorem card_finset_fin_le {n : ℕ} (s : Finset (Fin n)) : s.card ≤ n := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
n : ℕ
s : Finset (Fin n)
⊢ card s ≤ n

Next tactic:
simpa only [Fintype.card_fin] using s.card_le_univ
theorem Fin.equiv_iff_eq {m n : ℕ} : Nonempty (Fin m ≃ Fin n) ↔ m = n :=
  ⟨fun ⟨h⟩ => by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
m n : ℕ
x✝ : Nonempty (Fin m ≃ Fin n)
h : Fin m ≃ Fin n
⊢ m = n

Next tactic:
simpa using Fintype.card_congr h
theorem Finite.of_injective {α β : Sort*} [Finite β] (f : α → β) (H : Injective f) : Finite α := by
  

Proof state:
α✝ : Type u_1
β✝ : Type u_2
γ : Type u_3
α : Sort u_4
β : Sort u_5
inst✝ : Finite β
f : α → β
H : Injective f
⊢ Finite α

Next tactic:
cases nonempty_fintype (PLift β)
theorem Finite.of_injective {α β : Sort*} [Finite β] (f : α → β) (H : Injective f) : Finite α := by
  cases nonempty_fintype (PLift β)
  

Proof state:
case intro
α✝ : Type u_1
β✝ : Type u_2
γ : Type u_3
α : Sort u_4
β : Sort u_5
inst✝ : Finite β
f : α → β
H : Injective f
val✝ : Fintype (PLift β)
⊢ Finite α

Next tactic:
rw [← Equiv.injective_comp Equiv.plift f, ← Equiv.comp_injective _ Equiv.plift.symm] at H
theorem Finite.of_injective {α β : Sort*} [Finite β] (f : α → β) (H : Injective f) : Finite α := by
  cases nonempty_fintype (PLift β)
  rw [← Equiv.injective_comp Equiv.plift f, ← Equiv.comp_injective _ Equiv.plift.symm] at H
  

Proof state:
case intro
α✝ : Type u_1
β✝ : Type u_2
γ : Type u_3
α : Sort u_4
β : Sort u_5
inst✝ : Finite β
f : α → β
H : Injective (⇑Equiv.plift.symm ∘ f ∘ ⇑Equiv.plift)
val✝ : Fintype (PLift β)
⊢ Finite α

Next tactic:
haveI := Fintype.ofInjective _ H
theorem Finite.of_injective {α β : Sort*} [Finite β] (f : α → β) (H : Injective f) : Finite α := by
  cases nonempty_fintype (PLift β)
  rw [← Equiv.injective_comp Equiv.plift f, ← Equiv.comp_injective _ Equiv.plift.symm] at H
  haveI := Fintype.ofInjective _ H
  

Proof state:
case intro
α✝ : Type u_1
β✝ : Type u_2
γ : Type u_3
α : Sort u_4
β : Sort u_5
inst✝ : Finite β
f : α → β
H : Injective (⇑Equiv.plift.symm ∘ f ∘ ⇑Equiv.plift)
val✝ : Fintype (PLift β)
this : Fintype (PLift α)
⊢ Finite α

Next tactic:
exact Finite.of_equiv _ Equiv.plift
theorem Finite.exists_univ_list (α) [Finite α] : ∃ l : List α, l.Nodup ∧ ∀ x : α, x ∈ l := by
  

Proof state:
α✝ : Type u_1
β : Type u_2
γ : Type u_3
α : Type u_4
inst✝ : Finite α
⊢ ∃ l, List.Nodup l ∧ ∀ (x : α), x ∈ l

Next tactic:
cases nonempty_fintype α
theorem Finite.exists_univ_list (α) [Finite α] : ∃ l : List α, l.Nodup ∧ ∀ x : α, x ∈ l := by
  cases nonempty_fintype α
  

Proof state:
case intro
α✝ : Type u_1
β : Type u_2
γ : Type u_3
α : Type u_4
inst✝ : Finite α
val✝ : Fintype α
⊢ ∃ l, List.Nodup l ∧ ∀ (x : α), x ∈ l

Next tactic:
obtain ⟨l, e⟩ := Quotient.exists_rep (@univ α _).1
theorem Finite.exists_univ_list (α) [Finite α] : ∃ l : List α, l.Nodup ∧ ∀ x : α, x ∈ l := by
  cases nonempty_fintype α
  obtain ⟨l, e⟩ := Quotient.exists_rep (@univ α _).1
  

Proof state:
case intro.intro
α✝ : Type u_1
β : Type u_2
γ : Type u_3
α : Type u_4
inst✝ : Finite α
val✝ : Fintype α
l : List α
e : ⟦l⟧ = univ.val
⊢ ∃ l, List.Nodup l ∧ ∀ (x : α), x ∈ l

Next tactic:
have := And.intro (@univ α _).2 (@mem_univ_val α _)
theorem Finite.exists_univ_list (α) [Finite α] : ∃ l : List α, l.Nodup ∧ ∀ x : α, x ∈ l := by
  cases nonempty_fintype α
  obtain ⟨l, e⟩ := Quotient.exists_rep (@univ α _).1
  have := And.intro (@univ α _).2 (@mem_univ_val α _)
  

Proof state:
case intro.intro
α✝ : Type u_1
β : Type u_2
γ : Type u_3
α : Type u_4
inst✝ : Finite α
val✝ : Fintype α
l : List α
e : ⟦l⟧ = univ.val
this : Multiset.Nodup univ.val ∧ ∀ (x : α), x ∈ univ.val
⊢ ∃ l, List.Nodup l ∧ ∀ (x : α), x ∈ l

Next tactic:
exact ⟨_, by rwa [← e] at this⟩