l3lab/ntp-mathlib huggingface dataset:
open List Perm in
/-- We establish the bijection from `ℕ × smoothNumbers p` to `smoothNumbers (p+1)`
given by `(e, n) ↦ p^e * n` when `p` is a prime. See `Nat.smoothNumbers_succ` for
when `p` is not prime. -/
def equivProdNatSmoothNumbers {p : ℕ} (hp: p.Prime) :
    ℕ × smoothNumbers p ≃ smoothNumbers p.succ where
  toFun := fun ⟨e, n⟩ ↦ ⟨p ^ e * n, pow_mul_mem_smoothNumbers hp.ne_zero e n.2⟩
  invFun := fun ⟨m, _⟩  ↦ (m.factorization p,
                            ⟨(m.factors.filter (· < p)).prod, prod_mem_smoothNumbers ..⟩)
  left_inv := by
    rintro ⟨e, m, hm₀, hm⟩
    simp (config := { etaStruct := .all }) only
      [Set.coe_setOf, Set.mem_setOf_eq, Prod.mk.injEq, Subtype.mk.injEq]
    constructor
    · rw [factorization_mul (pos_iff_ne_zero.mp <| pos_pow_of_pos e hp.pos) hm₀]
      simp only [factorization_pow, Finsupp.coe_add, Finsupp.coe_smul, nsmul_eq_mul,
        Pi.coe_nat, cast_id, Pi.add_apply, Pi.mul_apply, hp.factorization_self,
        mul_one, add_right_eq_self]
      rw [← factors_count_eq, count_eq_zero]
      exact fun H ↦ (hm p H).false
    · nth_rw 2 [← prod_factors hm₀]
      refine prod_eq <| (filter _ <| perm_factors_mul (pow_ne_zero e hp.ne_zero) hm₀).trans ?_
      rw [filter_append, hp.factors_pow,
          filter_eq_nil.mpr <| fun q hq ↦ by rw [mem_replicate] at hq; simp [hq.2],
          nil_append, filter_eq_self.mpr <| fun q hq ↦ by simp [hm q hq]]
  right_inv := by
    rintro ⟨m, hm₀, hm⟩
    simp only [Set.coe_setOf, Set.mem_setOf_eq, Subtype.mk.injEq]
    rw [← factors_count_eq, ← prod_replicate, ← prod_append]
    nth_rw 3 [← prod_factors hm₀]
    have : m.factors.filter (· = p) = m.factors.filter (¬ · < p)
    · refine (filter_congr' <| fun q hq ↦ ?_).symm
      

Proof state:
case this
p : ℕ
hp : Prime p
m : ℕ
hm₀ : m ≠ 0
hm : ∀ p_1 ∈ factors m, p_1 < succ p
q : ℕ
hq : q ∈ factors m
⊢ (decide ¬q < p) = true ↔ decide (q = p) = true

Next tactic:
have H : ¬ p < q := fun hf ↦ Nat.lt_le_asymm hf <| lt_succ_iff.mp (hm q hq)
open List Perm in
/-- We establish the bijection from `ℕ × smoothNumbers p` to `smoothNumbers (p+1)`
given by `(e, n) ↦ p^e * n` when `p` is a prime. See `Nat.smoothNumbers_succ` for
when `p` is not prime. -/
def equivProdNatSmoothNumbers {p : ℕ} (hp: p.Prime) :
    ℕ × smoothNumbers p ≃ smoothNumbers p.succ where
  toFun := fun ⟨e, n⟩ ↦ ⟨p ^ e * n, pow_mul_mem_smoothNumbers hp.ne_zero e n.2⟩
  invFun := fun ⟨m, _⟩  ↦ (m.factorization p,
                            ⟨(m.factors.filter (· < p)).prod, prod_mem_smoothNumbers ..⟩)
  left_inv := by
    rintro ⟨e, m, hm₀, hm⟩
    simp (config := { etaStruct := .all }) only
      [Set.coe_setOf, Set.mem_setOf_eq, Prod.mk.injEq, Subtype.mk.injEq]
    constructor
    · rw [factorization_mul (pos_iff_ne_zero.mp <| pos_pow_of_pos e hp.pos) hm₀]
      simp only [factorization_pow, Finsupp.coe_add, Finsupp.coe_smul, nsmul_eq_mul,
        Pi.coe_nat, cast_id, Pi.add_apply, Pi.mul_apply, hp.factorization_self,
        mul_one, add_right_eq_self]
      rw [← factors_count_eq, count_eq_zero]
      exact fun H ↦ (hm p H).false
    · nth_rw 2 [← prod_factors hm₀]
      refine prod_eq <| (filter _ <| perm_factors_mul (pow_ne_zero e hp.ne_zero) hm₀).trans ?_
      rw [filter_append, hp.factors_pow,
          filter_eq_nil.mpr <| fun q hq ↦ by rw [mem_replicate] at hq; simp [hq.2],
          nil_append, filter_eq_self.mpr <| fun q hq ↦ by simp [hm q hq]]
  right_inv := by
    rintro ⟨m, hm₀, hm⟩
    simp only [Set.coe_setOf, Set.mem_setOf_eq, Subtype.mk.injEq]
    rw [← factors_count_eq, ← prod_replicate, ← prod_append]
    nth_rw 3 [← prod_factors hm₀]
    have : m.factors.filter (· = p) = m.factors.filter (¬ · < p)
    · refine (filter_congr' <| fun q hq ↦ ?_).symm
      have H : ¬ p < q := fun hf ↦ Nat.lt_le_asymm hf <| lt_succ_iff.mp (hm q hq)
      

Proof state:
case this
p : ℕ
hp : Prime p
m : ℕ
hm₀ : m ≠ 0
hm : ∀ p_1 ∈ factors m, p_1 < succ p
q : ℕ
hq : q ∈ factors m
H : ¬p < q
⊢ (decide ¬q < p) = true ↔ decide (q = p) = true

Next tactic:
simp only [not_lt, le_iff_eq_or_lt, H, or_false, eq_comm, true_eq_decide_iff]
open List Perm in
/-- We establish the bijection from `ℕ × smoothNumbers p` to `smoothNumbers (p+1)`
given by `(e, n) ↦ p^e * n` when `p` is a prime. See `Nat.smoothNumbers_succ` for
when `p` is not prime. -/
def equivProdNatSmoothNumbers {p : ℕ} (hp: p.Prime) :
    ℕ × smoothNumbers p ≃ smoothNumbers p.succ where
  toFun := fun ⟨e, n⟩ ↦ ⟨p ^ e * n, pow_mul_mem_smoothNumbers hp.ne_zero e n.2⟩
  invFun := fun ⟨m, _⟩  ↦ (m.factorization p,
                            ⟨(m.factors.filter (· < p)).prod, prod_mem_smoothNumbers ..⟩)
  left_inv := by
    rintro ⟨e, m, hm₀, hm⟩
    simp (config := { etaStruct := .all }) only
      [Set.coe_setOf, Set.mem_setOf_eq, Prod.mk.injEq, Subtype.mk.injEq]
    constructor
    · rw [factorization_mul (pos_iff_ne_zero.mp <| pos_pow_of_pos e hp.pos) hm₀]
      simp only [factorization_pow, Finsupp.coe_add, Finsupp.coe_smul, nsmul_eq_mul,
        Pi.coe_nat, cast_id, Pi.add_apply, Pi.mul_apply, hp.factorization_self,
        mul_one, add_right_eq_self]
      rw [← factors_count_eq, count_eq_zero]
      exact fun H ↦ (hm p H).false
    · nth_rw 2 [← prod_factors hm₀]
      refine prod_eq <| (filter _ <| perm_factors_mul (pow_ne_zero e hp.ne_zero) hm₀).trans ?_
      rw [filter_append, hp.factors_pow,
          filter_eq_nil.mpr <| fun q hq ↦ by rw [mem_replicate] at hq; simp [hq.2],
          nil_append, filter_eq_self.mpr <| fun q hq ↦ by simp [hm q hq]]
  right_inv := by
    rintro ⟨m, hm₀, hm⟩
    simp only [Set.coe_setOf, Set.mem_setOf_eq, Subtype.mk.injEq]
    rw [← factors_count_eq, ← prod_replicate, ← prod_append]
    nth_rw 3 [← prod_factors hm₀]
    have : m.factors.filter (· = p) = m.factors.filter (¬ · < p)
    · refine (filter_congr' <| fun q hq ↦ ?_).symm
      have H : ¬ p < q := fun hf ↦ Nat.lt_le_asymm hf <| lt_succ_iff.mp (hm q hq)
      simp only [not_lt, le_iff_eq_or_lt, H, or_false, eq_comm, true_eq_decide_iff]
    

Proof state:
case mk.intro
p : ℕ
hp : Prime p
m : ℕ
hm₀ : m ≠ 0
hm : ∀ p_1 ∈ factors m, p_1 < succ p
this : List.filter (fun x => decide (x = p)) (factors m) = List.filter (fun x => decide ¬x < p) (factors m)
⊢ prod (replicate (count p (factors m)) p ++ List.filter (fun x => decide (x < p)) (factors m)) = prod (factors m)

Next tactic:
refine prod_eq <| (filter_eq m.factors p).symm ▸ this ▸ perm_append_comm.trans ?_
open List Perm in
/-- We establish the bijection from `ℕ × smoothNumbers p` to `smoothNumbers (p+1)`
given by `(e, n) ↦ p^e * n` when `p` is a prime. See `Nat.smoothNumbers_succ` for
when `p` is not prime. -/
def equivProdNatSmoothNumbers {p : ℕ} (hp: p.Prime) :
    ℕ × smoothNumbers p ≃ smoothNumbers p.succ where
  toFun := fun ⟨e, n⟩ ↦ ⟨p ^ e * n, pow_mul_mem_smoothNumbers hp.ne_zero e n.2⟩
  invFun := fun ⟨m, _⟩  ↦ (m.factorization p,
                            ⟨(m.factors.filter (· < p)).prod, prod_mem_smoothNumbers ..⟩)
  left_inv := by
    rintro ⟨e, m, hm₀, hm⟩
    simp (config := { etaStruct := .all }) only
      [Set.coe_setOf, Set.mem_setOf_eq, Prod.mk.injEq, Subtype.mk.injEq]
    constructor
    · rw [factorization_mul (pos_iff_ne_zero.mp <| pos_pow_of_pos e hp.pos) hm₀]
      simp only [factorization_pow, Finsupp.coe_add, Finsupp.coe_smul, nsmul_eq_mul,
        Pi.coe_nat, cast_id, Pi.add_apply, Pi.mul_apply, hp.factorization_self,
        mul_one, add_right_eq_self]
      rw [← factors_count_eq, count_eq_zero]
      exact fun H ↦ (hm p H).false
    · nth_rw 2 [← prod_factors hm₀]
      refine prod_eq <| (filter _ <| perm_factors_mul (pow_ne_zero e hp.ne_zero) hm₀).trans ?_
      rw [filter_append, hp.factors_pow,
          filter_eq_nil.mpr <| fun q hq ↦ by rw [mem_replicate] at hq; simp [hq.2],
          nil_append, filter_eq_self.mpr <| fun q hq ↦ by simp [hm q hq]]
  right_inv := by
    rintro ⟨m, hm₀, hm⟩
    simp only [Set.coe_setOf, Set.mem_setOf_eq, Subtype.mk.injEq]
    rw [← factors_count_eq, ← prod_replicate, ← prod_append]
    nth_rw 3 [← prod_factors hm₀]
    have : m.factors.filter (· = p) = m.factors.filter (¬ · < p)
    · refine (filter_congr' <| fun q hq ↦ ?_).symm
      have H : ¬ p < q := fun hf ↦ Nat.lt_le_asymm hf <| lt_succ_iff.mp (hm q hq)
      simp only [not_lt, le_iff_eq_or_lt, H, or_false, eq_comm, true_eq_decide_iff]
    refine prod_eq <| (filter_eq m.factors p).symm ▸ this ▸ perm_append_comm.trans ?_
    

Proof state:
case mk.intro
p : ℕ
hp : Prime p
m : ℕ
hm₀ : m ≠ 0
hm : ∀ p_1 ∈ factors m, p_1 < succ p
this : List.filter (fun x => decide (x = p)) (factors m) = List.filter (fun x => decide ¬x < p) (factors m)
⊢ List.filter (fun x => decide (x < p)) (factors m) ++ List.filter (fun x => decide ¬x < p) (factors m) ~ factors m

Next tactic:
convert filter_append_perm ..
open List Perm in
/-- We establish the bijection from `ℕ × smoothNumbers p` to `smoothNumbers (p+1)`
given by `(e, n) ↦ p^e * n` when `p` is a prime. See `Nat.smoothNumbers_succ` for
when `p` is not prime. -/
def equivProdNatSmoothNumbers {p : ℕ} (hp: p.Prime) :
    ℕ × smoothNumbers p ≃ smoothNumbers p.succ where
  toFun := fun ⟨e, n⟩ ↦ ⟨p ^ e * n, pow_mul_mem_smoothNumbers hp.ne_zero e n.2⟩
  invFun := fun ⟨m, _⟩  ↦ (m.factorization p,
                            ⟨(m.factors.filter (· < p)).prod, prod_mem_smoothNumbers ..⟩)
  left_inv := by
    rintro ⟨e, m, hm₀, hm⟩
    simp (config := { etaStruct := .all }) only
      [Set.coe_setOf, Set.mem_setOf_eq, Prod.mk.injEq, Subtype.mk.injEq]
    constructor
    · rw [factorization_mul (pos_iff_ne_zero.mp <| pos_pow_of_pos e hp.pos) hm₀]
      simp only [factorization_pow, Finsupp.coe_add, Finsupp.coe_smul, nsmul_eq_mul,
        Pi.coe_nat, cast_id, Pi.add_apply, Pi.mul_apply, hp.factorization_self,
        mul_one, add_right_eq_self]
      rw [← factors_count_eq, count_eq_zero]
      exact fun H ↦ (hm p H).false
    · nth_rw 2 [← prod_factors hm₀]
      refine prod_eq <| (filter _ <| perm_factors_mul (pow_ne_zero e hp.ne_zero) hm₀).trans ?_
      rw [filter_append, hp.factors_pow,
          filter_eq_nil.mpr <| fun q hq ↦ by rw [mem_replicate] at hq; simp [hq.2],
          nil_append, filter_eq_self.mpr <| fun q hq ↦ by simp [hm q hq]]
  right_inv := by
    rintro ⟨m, hm₀, hm⟩
    simp only [Set.coe_setOf, Set.mem_setOf_eq, Subtype.mk.injEq]
    rw [← factors_count_eq, ← prod_replicate, ← prod_append]
    nth_rw 3 [← prod_factors hm₀]
    have : m.factors.filter (· = p) = m.factors.filter (¬ · < p)
    · refine (filter_congr' <| fun q hq ↦ ?_).symm
      have H : ¬ p < q := fun hf ↦ Nat.lt_le_asymm hf <| lt_succ_iff.mp (hm q hq)
      simp only [not_lt, le_iff_eq_or_lt, H, or_false, eq_comm, true_eq_decide_iff]
    refine prod_eq <| (filter_eq m.factors p).symm ▸ this ▸ perm_append_comm.trans ?_
    convert filter_append_perm ..
    

Proof state:
case h.e'_2.h.e'_6.h.e'_2.h
p : ℕ
hp : Prime p
m : ℕ
hm₀ : m ≠ 0
hm : ∀ p_1 ∈ factors m, p_1 < succ p
this : List.filter (fun x => decide (x = p)) (factors m) = List.filter (fun x => decide ¬x < p) (factors m)
x✝ : ℕ
⊢ (decide ¬x✝ < p) = !decide (x✝ < p)

Next tactic:
simp only [not_lt]
open List Perm in
/-- We establish the bijection from `ℕ × smoothNumbers p` to `smoothNumbers (p+1)`
given by `(e, n) ↦ p^e * n` when `p` is a prime. See `Nat.smoothNumbers_succ` for
when `p` is not prime. -/
def equivProdNatSmoothNumbers {p : ℕ} (hp: p.Prime) :
    ℕ × smoothNumbers p ≃ smoothNumbers p.succ where
  toFun := fun ⟨e, n⟩ ↦ ⟨p ^ e * n, pow_mul_mem_smoothNumbers hp.ne_zero e n.2⟩
  invFun := fun ⟨m, _⟩  ↦ (m.factorization p,
                            ⟨(m.factors.filter (· < p)).prod, prod_mem_smoothNumbers ..⟩)
  left_inv := by
    rintro ⟨e, m, hm₀, hm⟩
    simp (config := { etaStruct := .all }) only
      [Set.coe_setOf, Set.mem_setOf_eq, Prod.mk.injEq, Subtype.mk.injEq]
    constructor
    · rw [factorization_mul (pos_iff_ne_zero.mp <| pos_pow_of_pos e hp.pos) hm₀]
      simp only [factorization_pow, Finsupp.coe_add, Finsupp.coe_smul, nsmul_eq_mul,
        Pi.coe_nat, cast_id, Pi.add_apply, Pi.mul_apply, hp.factorization_self,
        mul_one, add_right_eq_self]
      rw [← factors_count_eq, count_eq_zero]
      exact fun H ↦ (hm p H).false
    · nth_rw 2 [← prod_factors hm₀]
      refine prod_eq <| (filter _ <| perm_factors_mul (pow_ne_zero e hp.ne_zero) hm₀).trans ?_
      rw [filter_append, hp.factors_pow,
          filter_eq_nil.mpr <| fun q hq ↦ by rw [mem_replicate] at hq; simp [hq.2],
          nil_append, filter_eq_self.mpr <| fun q hq ↦ by simp [hm q hq]]
  right_inv := by
    rintro ⟨m, hm₀, hm⟩
    simp only [Set.coe_setOf, Set.mem_setOf_eq, Subtype.mk.injEq]
    rw [← factors_count_eq, ← prod_replicate, ← prod_append]
    nth_rw 3 [← prod_factors hm₀]
    have : m.factors.filter (· = p) = m.factors.filter (¬ · < p)
    · refine (filter_congr' <| fun q hq ↦ ?_).symm
      have H : ¬ p < q := fun hf ↦ Nat.lt_le_asymm hf <| lt_succ_iff.mp (hm q hq)
      simp only [not_lt, le_iff_eq_or_lt, H, or_false, eq_comm, true_eq_decide_iff]
    refine prod_eq <| (filter_eq m.factors p).symm ▸ this ▸ perm_append_comm.trans ?_
    convert filter_append_perm ..
    simp only [not_lt]
    

Proof state:
case h.e'_2.h.e'_6.h.e'_2.h
p : ℕ
hp : Prime p
m : ℕ
hm₀ : m ≠ 0
hm : ∀ p_1 ∈ factors m, p_1 < succ p
this : List.filter (fun x => decide (x = p)) (factors m) = List.filter (fun x => decide ¬x < p) (factors m)
x✝ : ℕ
⊢ decide (p ≤ x✝) = !decide (x✝ < p)

Next tactic:
simp only [decide_not, Bool.not_not, lt_iff_not_ge]
theorem simply_connected_iff_unique_homotopic (X : Type*) [TopologicalSpace X] :
    SimplyConnectedSpace X ↔
      Nonempty X ∧ ∀ x y : X, Nonempty (Unique (Path.Homotopic.Quotient x y)) := by
  

Proof state:
X : Type u_1
inst✝ : TopologicalSpace X
⊢ SimplyConnectedSpace X ↔ Nonempty X ∧ ∀ (x y : X), Nonempty (Unique (Path.Homotopic.Quotient x y))

Next tactic:
simp only [simply_connected_def, equiv_punit_iff_unique,
    FundamentalGroupoid.nonempty_iff X, and_congr_right_iff, Nonempty.forall]
theorem simply_connected_iff_unique_homotopic (X : Type*) [TopologicalSpace X] :
    SimplyConnectedSpace X ↔
      Nonempty X ∧ ∀ x y : X, Nonempty (Unique (Path.Homotopic.Quotient x y)) := by
  simp only [simply_connected_def, equiv_punit_iff_unique,
    FundamentalGroupoid.nonempty_iff X, and_congr_right_iff, Nonempty.forall]
  

Proof state:
X : Type u_1
inst✝ : TopologicalSpace X
⊢ X →
    ((∀ (x y : FundamentalGroupoid X), Nonempty (Unique (x ⟶ y))) ↔
      ∀ (x y : X), Nonempty (Unique (Path.Homotopic.Quotient x y)))

Next tactic:
intros
theorem simply_connected_iff_unique_homotopic (X : Type*) [TopologicalSpace X] :
    SimplyConnectedSpace X ↔
      Nonempty X ∧ ∀ x y : X, Nonempty (Unique (Path.Homotopic.Quotient x y)) := by
  simp only [simply_connected_def, equiv_punit_iff_unique,
    FundamentalGroupoid.nonempty_iff X, and_congr_right_iff, Nonempty.forall]
  intros
  

Proof state:
X : Type u_1
inst✝ : TopologicalSpace X
a✝ : X
⊢ (∀ (x y : FundamentalGroupoid X), Nonempty (Unique (x ⟶ y))) ↔
    ∀ (x y : X), Nonempty (Unique (Path.Homotopic.Quotient x y))

Next tactic:
exact ⟨fun h _ _ => h _ _, fun h _ _ => h _ _⟩
instance (x y : X) : Subsingleton (Path.Homotopic.Quotient x y) :=
  @Unique.instSubsingleton _ (Nonempty.some (by
    

Proof state:
X : Type u_1
inst✝¹ : TopologicalSpace X
inst✝ : SimplyConnectedSpace X
x y : X
⊢ Nonempty (Unique (Path.Homotopic.Quotient x y))

Next tactic:
rw [simply_connected_iff_unique_homotopic] at *
instance (x y : X) : Subsingleton (Path.Homotopic.Quotient x y) :=
  @Unique.instSubsingleton _ (Nonempty.some (by
    rw [simply_connected_iff_unique_homotopic] at *; 

Proof state:
X : Type u_1
inst✝¹ : TopologicalSpace X
inst✝ : Nonempty X ∧ ∀ (x y : X), Nonempty (Unique (Path.Homotopic.Quotient x y))
x y : X
⊢ Nonempty (Unique (Path.Homotopic.Quotient x y))

Next tactic:
tauto
/-- A space is simply connected iff it is path connected, and there is at most one path
  up to homotopy between any two points. -/
theorem simply_connected_iff_paths_homotopic {Y : Type*} [TopologicalSpace Y] :
    SimplyConnectedSpace Y ↔
      PathConnectedSpace Y ∧ ∀ x y : Y, Subsingleton (Path.Homotopic.Quotient x y) :=
  ⟨by 

Proof state:
Y : Type u_1
inst✝ : TopologicalSpace Y
⊢ SimplyConnectedSpace Y → PathConnectedSpace Y ∧ ∀ (x y : Y), Subsingleton (Path.Homotopic.Quotient x y)

Next tactic:
intro
/-- A space is simply connected iff it is path connected, and there is at most one path
  up to homotopy between any two points. -/
theorem simply_connected_iff_paths_homotopic {Y : Type*} [TopologicalSpace Y] :
    SimplyConnectedSpace Y ↔
      PathConnectedSpace Y ∧ ∀ x y : Y, Subsingleton (Path.Homotopic.Quotient x y) :=
  ⟨by intro; 

Proof state:
Y : Type u_1
inst✝ : TopologicalSpace Y
a✝ : SimplyConnectedSpace Y
⊢ PathConnectedSpace Y ∧ ∀ (x y : Y), Subsingleton (Path.Homotopic.Quotient x y)

Next tactic:
constructor
/-- A space is simply connected iff it is path connected, and there is at most one path
  up to homotopy between any two points. -/
theorem simply_connected_iff_paths_homotopic {Y : Type*} [TopologicalSpace Y] :
    SimplyConnectedSpace Y ↔
      PathConnectedSpace Y ∧ ∀ x y : Y, Subsingleton (Path.Homotopic.Quotient x y) :=
  ⟨by intro; constructor <;> 

Proof state:
case left
Y : Type u_1
inst✝ : TopologicalSpace Y
a✝ : SimplyConnectedSpace Y
⊢ PathConnectedSpace Y

Next tactic:
infer_instance
/-- A space is simply connected iff it is path connected, and there is at most one path
  up to homotopy between any two points. -/
theorem simply_connected_iff_paths_homotopic {Y : Type*} [TopologicalSpace Y] :
    SimplyConnectedSpace Y ↔
      PathConnectedSpace Y ∧ ∀ x y : Y, Subsingleton (Path.Homotopic.Quotient x y) :=
  ⟨by intro; constructor <;> 

Proof state:
case right
Y : Type u_1
inst✝ : TopologicalSpace Y
a✝ : SimplyConnectedSpace Y
⊢ ∀ (x y : Y), Subsingleton (Path.Homotopic.Quotient x y)

Next tactic:
infer_instance
/-- A space is simply connected iff it is path connected, and there is at most one path
  up to homotopy between any two points. -/
theorem simply_connected_iff_paths_homotopic {Y : Type*} [TopologicalSpace Y] :
    SimplyConnectedSpace Y ↔
      PathConnectedSpace Y ∧ ∀ x y : Y, Subsingleton (Path.Homotopic.Quotient x y) :=
  ⟨by intro; constructor <;> infer_instance, fun h => by
    

Proof state:
Y : Type u_1
inst✝ : TopologicalSpace Y
h : PathConnectedSpace Y ∧ ∀ (x y : Y), Subsingleton (Path.Homotopic.Quotient x y)
⊢ SimplyConnectedSpace Y

Next tactic:
cases h
/-- A space is simply connected iff it is path connected, and there is at most one path
  up to homotopy between any two points. -/
theorem simply_connected_iff_paths_homotopic {Y : Type*} [TopologicalSpace Y] :
    SimplyConnectedSpace Y ↔
      PathConnectedSpace Y ∧ ∀ x y : Y, Subsingleton (Path.Homotopic.Quotient x y) :=
  ⟨by intro; constructor <;> infer_instance, fun h => by
    cases h; 

Proof state:
case intro
Y : Type u_1
inst✝ : TopologicalSpace Y
left✝ : PathConnectedSpace Y
right✝ : ∀ (x y : Y), Subsingleton (Path.Homotopic.Quotient x y)
⊢ SimplyConnectedSpace Y

Next tactic:
rw [simply_connected_iff_unique_homotopic]
/-- A space is simply connected iff it is path connected, and there is at most one path
  up to homotopy between any two points. -/
theorem simply_connected_iff_paths_homotopic {Y : Type*} [TopologicalSpace Y] :
    SimplyConnectedSpace Y ↔
      PathConnectedSpace Y ∧ ∀ x y : Y, Subsingleton (Path.Homotopic.Quotient x y) :=
  ⟨by intro; constructor <;> infer_instance, fun h => by
    cases h; rw [simply_connected_iff_unique_homotopic]
    

Proof state:
case intro
Y : Type u_1
inst✝ : TopologicalSpace Y
left✝ : PathConnectedSpace Y
right✝ : ∀ (x y : Y), Subsingleton (Path.Homotopic.Quotient x y)
⊢ Nonempty Y ∧ ∀ (x y : Y), Nonempty (Unique (Path.Homotopic.Quotient x y))

Next tactic:
exact ⟨inferInstance, fun x y => ⟨uniqueOfSubsingleton ⟦PathConnectedSpace.somePath x y⟧⟩⟩
/-- Another version of `simply_connected_iff_paths_homotopic` -/
theorem simply_connected_iff_paths_homotopic' {Y : Type*} [TopologicalSpace Y] :
    SimplyConnectedSpace Y ↔
      PathConnectedSpace Y ∧ ∀ {x y : Y} (p₁ p₂ : Path x y), Path.Homotopic p₁ p₂ := by
  

Proof state:
Y : Type u_1
inst✝ : TopologicalSpace Y
⊢ SimplyConnectedSpace Y ↔ PathConnectedSpace Y ∧ ∀ {x y : Y} (p₁ p₂ : Path x y), Path.Homotopic p₁ p₂

Next tactic:
convert simply_connected_iff_paths_homotopic (Y := Y)
/-- Another version of `simply_connected_iff_paths_homotopic` -/
theorem simply_connected_iff_paths_homotopic' {Y : Type*} [TopologicalSpace Y] :
    SimplyConnectedSpace Y ↔
      PathConnectedSpace Y ∧ ∀ {x y : Y} (p₁ p₂ : Path x y), Path.Homotopic p₁ p₂ := by
  convert simply_connected_iff_paths_homotopic (Y := Y)
  

Proof state:
case h.e'_2.h.e'_2.h.h.a
Y : Type u_1
inst✝ : TopologicalSpace Y
a✝¹ a✝ : Y
⊢ (∀ (p₁ p₂ : Path a✝¹ a✝), Path.Homotopic p₁ p₂) ↔ Subsingleton (Path.Homotopic.Quotient a✝¹ a✝)

Next tactic:
simp [Path.Homotopic.Quotient, Setoid.eq_top_iff]
/-- Another version of `simply_connected_iff_paths_homotopic` -/
theorem simply_connected_iff_paths_homotopic' {Y : Type*} [TopologicalSpace Y] :
    SimplyConnectedSpace Y ↔
      PathConnectedSpace Y ∧ ∀ {x y : Y} (p₁ p₂ : Path x y), Path.Homotopic p₁ p₂ := by
  convert simply_connected_iff_paths_homotopic (Y := Y)
  simp [Path.Homotopic.Quotient, Setoid.eq_top_iff]; 

Proof state:
case h.e'_2.h.e'_2.h.h.a
Y : Type u_1
inst✝ : TopologicalSpace Y
a✝¹ a✝ : Y
⊢ (∀ (p₁ p₂ : Path a✝¹ a✝), Path.Homotopic p₁ p₂) ↔ ∀ (x y : Path a✝¹ a✝), Setoid.Rel (Path.Homotopic.setoid a✝¹ a✝) x y

Next tactic:
rfl
@[to_additive]
theorem swap_mem_mulAntidiagonal [CommSemigroup α] {s t : Set α} {a : α} {x : α × α} :
    x.swap ∈ Set.mulAntidiagonal s t a ↔ x ∈ Set.mulAntidiagonal t s a := by
  

Proof state:
α : Type u_1
inst✝ : CommSemigroup α
s t : Set α
a : α
x : α × α
⊢ Prod.swap x ∈ mulAntidiagonal s t a ↔ x ∈ mulAntidiagonal t s a

Next tactic:
simp [mul_comm, and_left_comm]
@[to_additive (attr := simp)]
theorem swap_mem_mulAntidiagonal_aux [CommSemigroup α] {s t : Set α} {a : α} {x : α × α} :
    x.snd ∈ s ∧ x.fst ∈ t ∧ x.snd * x.fst = a
      ↔ x ∈ Set.mulAntidiagonal t s a := by
  

Proof state:
α : Type u_1
inst✝ : CommSemigroup α
s t : Set α
a : α
x : α × α
⊢ x.2 ∈ s ∧ x.1 ∈ t ∧ x.2 * x.1 = a ↔ x ∈ mulAntidiagonal t s a

Next tactic:
simp [mul_comm, and_left_comm]
@[to_additive Set.AddAntidiagonal.fst_eq_fst_iff_snd_eq_snd]
theorem fst_eq_fst_iff_snd_eq_snd : (x : α × α).1 = (y : α × α).1 ↔ (x : α × α).2 = (y : α × α).2 :=
  ⟨fun h =>
    mul_left_cancel
      (y.2.2.2.trans <| by
          

Proof state:
α : Type u_1
inst✝ : CancelCommMonoid α
s t : Set α
a : α
x y : ↑(mulAntidiagonal s t a)
h : (↑x).1 = (↑y).1
⊢ a = (↑y).1 * (↑x).2

Next tactic:
rw [← h]
@[to_additive Set.AddAntidiagonal.fst_eq_fst_iff_snd_eq_snd]
theorem fst_eq_fst_iff_snd_eq_snd : (x : α × α).1 = (y : α × α).1 ↔ (x : α × α).2 = (y : α × α).2 :=
  ⟨fun h =>
    mul_left_cancel
      (y.2.2.2.trans <| by
          rw [← h]
          

Proof state:
α : Type u_1
inst✝ : CancelCommMonoid α
s t : Set α
a : α
x y : ↑(mulAntidiagonal s t a)
h : (↑x).1 = (↑y).1
⊢ a = (↑x).1 * (↑x).2

Next tactic:
exact x.2.2.2.symm
@[to_additive Set.AddAntidiagonal.fst_eq_fst_iff_snd_eq_snd]
theorem fst_eq_fst_iff_snd_eq_snd : (x : α × α).1 = (y : α × α).1 ↔ (x : α × α).2 = (y : α × α).2 :=
  ⟨fun h =>
    mul_left_cancel
      (y.2.2.2.trans <| by
          rw [← h]
          exact x.2.2.2.symm).symm,
    fun h =>
    mul_right_cancel
      (y.2.2.2.trans <| by
          

Proof state:
α : Type u_1
inst✝ : CancelCommMonoid α
s t : Set α
a : α
x y : ↑(mulAntidiagonal s t a)
h : (↑x).2 = (↑y).2
⊢ a = (↑x).1 * (↑y).2

Next tactic:
rw [← h]
@[to_additive Set.AddAntidiagonal.fst_eq_fst_iff_snd_eq_snd]
theorem fst_eq_fst_iff_snd_eq_snd : (x : α × α).1 = (y : α × α).1 ↔ (x : α × α).2 = (y : α × α).2 :=
  ⟨fun h =>
    mul_left_cancel
      (y.2.2.2.trans <| by
          rw [← h]
          exact x.2.2.2.symm).symm,
    fun h =>
    mul_right_cancel
      (y.2.2.2.trans <| by
          rw [← h]
          

Proof state:
α : Type u_1
inst✝ : CancelCommMonoid α
s t : Set α
a : α
x y : ↑(mulAntidiagonal s t a)
h : (↑x).2 = (↑y).2
⊢ a = (↑x).1 * (↑x).2

Next tactic:
exact x.2.2.2.symm