l3lab/ntp-mathlib huggingface dataset:
/-- The first isomorphism theorem for sets: the quotient of α by the kernel of a function f
    bijects with f's image. -/
noncomputable def quotientKerEquivRange : Quotient (ker f) ≃ Set.range f :=
  Equiv.ofBijective
    ((@Quotient.lift _ (Set.range f) (ker f) fun x => ⟨f x, Set.mem_range_self x⟩) fun _ _ h =>
      Subtype.ext_val h)
    ⟨fun x y h => ker_lift_injective f <| by rcases x with ⟨⟩; rcases y with ⟨⟩; 

Proof state:
case mk.mk
α : Type u_1
β : Type u_2
r : Setoid α
f : α → β
x y : Quotient (ker f)
a✝¹ a✝ : α
h :
  Quotient.lift (fun x => { val := f x, property := (_ : f x ∈ Set.range f) })
      (_ :
        ∀ (x x_1 : α),
          x ≈ x_1 →
            { val := f x, property := (_ : f x ∈ Set.range f) } =
              { val := f x_1, property := (_ : f x_1 ∈ Set.range f) })
      (Quot.mk Setoid.r a✝¹) =
    Quotient.lift (fun x => { val := f x, property := (_ : f x ∈ Set.range f) })
      (_ :
        ∀ (x x_1 : α),
          x ≈ x_1 →
            { val := f x, property := (_ : f x ∈ Set.range f) } =
              { val := f x_1, property := (_ : f x_1 ∈ Set.range f) })
      (Quot.mk Setoid.r a✝)
⊢ Quotient.lift f (_ : ∀ (x x_1 : α), x ≈ x_1 → x ≈ x_1) (Quot.mk Setoid.r a✝¹) =
    Quotient.lift f (_ : ∀ (x x_1 : α), x ≈ x_1 → x ≈ x_1) (Quot.mk Setoid.r a✝)

Next tactic:
injections
/-- Given a surjective function f whose kernel is contained in an equivalence relation r, the
    equivalence relation on f's codomain defined by x ≈ y ↔ the elements of f⁻¹(x) are related to
    the elements of f⁻¹(y) by r. -/
def mapOfSurjective (r) (f : α → β) (h : ker f ≤ r) (hf : Surjective f) : Setoid β :=
  ⟨fun x y => ∃ a b, f a = x ∧ f b = y ∧ r.Rel a b,
    ⟨fun x =>
      let ⟨y, hy⟩ := hf x
      ⟨y, y, hy, hy, r.refl' y⟩,
      fun ⟨x, y, hx, hy, h⟩ => ⟨y, x, hy, hx, r.symm' h⟩,
      fun ⟨x, y, hx, hy, h₁⟩ ⟨y', z, hy', hz, h₂⟩ =>
      ⟨x, z, hx, hz, r.trans' h₁ <| r.trans' (h <| by 

Proof state:
α : Type u_1
β : Type u_2
r✝ : Setoid α
f✝ : α → β
r : Setoid α
f : α → β
h : ker f ≤ r
hf : Surjective f
x✝² y✝ z✝ : β
x✝¹ : ∃ a b, f a = x✝² ∧ f b = y✝ ∧ Rel r a b
x✝ : ∃ a b, f a = y✝ ∧ f b = z✝ ∧ Rel r a b
x y : α
hx : f x = x✝²
hy : f y = y✝
h₁ : Rel r x y
y' z : α
hy' : f y' = y✝
hz : f z = z✝
h₂ : Rel r y' z
⊢ Rel (ker f) y y'

Next tactic:
rwa [← hy'] at hy
/-- A special case of the equivalence closure of an equivalence relation r equalling r. -/
theorem mapOfSurjective_eq_map (h : ker f ≤ r) (hf : Surjective f) :
    map r f = mapOfSurjective r f h hf := by
  

Proof state:
α : Type u_1
β : Type u_2
r : Setoid α
f : α → β
h : ker f ≤ r
hf : Surjective f
⊢ map r f = mapOfSurjective r f h hf

Next tactic:
rw [← eqvGen_of_setoid (mapOfSurjective r f h hf)]
/-- A special case of the equivalence closure of an equivalence relation r equalling r. -/
theorem mapOfSurjective_eq_map (h : ker f ≤ r) (hf : Surjective f) :
    map r f = mapOfSurjective r f h hf := by
  rw [← eqvGen_of_setoid (mapOfSurjective r f h hf)]; 

Proof state:
α : Type u_1
β : Type u_2
r : Setoid α
f : α → β
h : ker f ≤ r
hf : Surjective f
⊢ map r f = EqvGen.Setoid Setoid.r

Next tactic:
rfl
/-- Given a map `f : N → M` and an equivalence relation `r` on `β`, the equivalence relation
    induced on `α` by `f` equals the kernel of `r`'s quotient map composed with `f`. -/
theorem comap_eq {f : α → β} {r : Setoid β} : comap f r = ker (@Quotient.mk'' _ r ∘ f) :=
  ext fun x y => show _ ↔ ⟦_⟧ = ⟦_⟧ by 

Proof state:
α : Type u_1
β : Type u_2
r✝ : Setoid α
f✝ f : α → β
r : Setoid β
x y : α
⊢ Setoid.r x y ↔ ⟦f x⟧ = ⟦f y⟧

Next tactic:
rw [Quotient.eq]
/-- Given a map `f : N → M` and an equivalence relation `r` on `β`, the equivalence relation
    induced on `α` by `f` equals the kernel of `r`'s quotient map composed with `f`. -/
theorem comap_eq {f : α → β} {r : Setoid β} : comap f r = ker (@Quotient.mk'' _ r ∘ f) :=
  ext fun x y => show _ ↔ ⟦_⟧ = ⟦_⟧ by rw [Quotient.eq]; 

Proof state:
α : Type u_1
β : Type u_2
r✝ : Setoid α
f✝ f : α → β
r : Setoid β
x y : α
⊢ Setoid.r x y ↔ f x ≈ f y

Next tactic:
rfl
/-- The third isomorphism theorem for sets. -/
def quotientQuotientEquivQuotient (s : Setoid α) (h : r ≤ s) :
    Quotient (ker (Quot.mapRight h)) ≃ Quotient s where
  toFun x :=
    (Quotient.liftOn' x fun w =>
        (Quotient.liftOn' w (@Quotient.mk'' _ s)) fun x y H => Quotient.sound <| h H)
      fun x y => Quotient.inductionOn₂' x y fun w z H => show @Quot.mk _ _ _ = @Quot.mk _ _ _ from H
  invFun x :=
    (Quotient.liftOn' x fun w => @Quotient.mk'' _ (ker <| Quot.mapRight h) <| @Quotient.mk'' _ r w)
      fun x y H => Quotient.sound' <| show @Quot.mk _ _ _ = @Quot.mk _ _ _ from Quotient.sound H
  left_inv x :=
    Quotient.inductionOn' x fun y => Quotient.inductionOn' y fun w => by 

Proof state:
α : Type u_1
β : Type u_2
r : Setoid α
f : α → β
s : Setoid α
h : r ≤ s
x : Quotient (ker (Quot.mapRight h))
y : Quot fun a₁ a₂ => Rel r a₁ a₂
w : α
⊢ (fun x =>
        Quotient.liftOn' x (fun w => Quotient.mk'' (Quotient.mk'' w))
          (_ : ∀ (x y : α), Setoid.r x y → Quotient.mk'' (Quotient.mk'' x) = Quotient.mk'' (Quotient.mk'' y)))
      ((fun x =>
          Quotient.liftOn' x (fun w => Quotient.liftOn' w Quotient.mk'' (_ : ∀ (x y : α), Setoid.r x y → ⟦x⟧ = ⟦y⟧))
            (_ :
              ∀ (x y : Quot fun a₁ a₂ => Rel r a₁ a₂),
                Setoid.r x y →
                  Quotient.liftOn' x Quotient.mk'' (_ : ∀ (x y : α), Setoid.r x y → ⟦x⟧ = ⟦y⟧) =
                    Quotient.liftOn' y Quotient.mk'' (_ : ∀ (x y : α), Setoid.r x y → ⟦x⟧ = ⟦y⟧)))
        (Quotient.mk'' (Quotient.mk'' w))) =
    Quotient.mk'' (Quotient.mk'' w)

Next tactic:
show ⟦_⟧ = _
/-- The third isomorphism theorem for sets. -/
def quotientQuotientEquivQuotient (s : Setoid α) (h : r ≤ s) :
    Quotient (ker (Quot.mapRight h)) ≃ Quotient s where
  toFun x :=
    (Quotient.liftOn' x fun w =>
        (Quotient.liftOn' w (@Quotient.mk'' _ s)) fun x y H => Quotient.sound <| h H)
      fun x y => Quotient.inductionOn₂' x y fun w z H => show @Quot.mk _ _ _ = @Quot.mk _ _ _ from H
  invFun x :=
    (Quotient.liftOn' x fun w => @Quotient.mk'' _ (ker <| Quot.mapRight h) <| @Quotient.mk'' _ r w)
      fun x y H => Quotient.sound' <| show @Quot.mk _ _ _ = @Quot.mk _ _ _ from Quotient.sound H
  left_inv x :=
    Quotient.inductionOn' x fun y => Quotient.inductionOn' y fun w => by show ⟦_⟧ = _; 

Proof state:
α : Type u_1
β : Type u_2
r : Setoid α
f : α → β
s : Setoid α
h : r ≤ s
x : Quotient (ker (Quot.mapRight h))
y : Quot fun a₁ a₂ => Rel r a₁ a₂
w : α
⊢ ⟦Quotient.mk'' w⟧ = Quotient.mk'' (Quotient.mk'' w)

Next tactic:
rfl
/-- The third isomorphism theorem for sets. -/
def quotientQuotientEquivQuotient (s : Setoid α) (h : r ≤ s) :
    Quotient (ker (Quot.mapRight h)) ≃ Quotient s where
  toFun x :=
    (Quotient.liftOn' x fun w =>
        (Quotient.liftOn' w (@Quotient.mk'' _ s)) fun x y H => Quotient.sound <| h H)
      fun x y => Quotient.inductionOn₂' x y fun w z H => show @Quot.mk _ _ _ = @Quot.mk _ _ _ from H
  invFun x :=
    (Quotient.liftOn' x fun w => @Quotient.mk'' _ (ker <| Quot.mapRight h) <| @Quotient.mk'' _ r w)
      fun x y H => Quotient.sound' <| show @Quot.mk _ _ _ = @Quot.mk _ _ _ from Quotient.sound H
  left_inv x :=
    Quotient.inductionOn' x fun y => Quotient.inductionOn' y fun w => by show ⟦_⟧ = _; rfl
  right_inv x := Quotient.inductionOn' x fun y => by 

Proof state:
α : Type u_1
β : Type u_2
r : Setoid α
f : α → β
s : Setoid α
h : r ≤ s
x : Quotient s
y : α
⊢ (fun x =>
        Quotient.liftOn' x (fun w => Quotient.liftOn' w Quotient.mk'' (_ : ∀ (x y : α), Setoid.r x y → ⟦x⟧ = ⟦y⟧))
          (_ :
            ∀ (x y : Quot fun a₁ a₂ => Rel r a₁ a₂),
              Setoid.r x y →
                Quotient.liftOn' x Quotient.mk'' (_ : ∀ (x y : α), Setoid.r x y → ⟦x⟧ = ⟦y⟧) =
                  Quotient.liftOn' y Quotient.mk'' (_ : ∀ (x y : α), Setoid.r x y → ⟦x⟧ = ⟦y⟧)))
      ((fun x =>
          Quotient.liftOn' x (fun w => Quotient.mk'' (Quotient.mk'' w))
            (_ : ∀ (x y : α), Setoid.r x y → Quotient.mk'' (Quotient.mk'' x) = Quotient.mk'' (Quotient.mk'' y)))
        (Quotient.mk'' y)) =
    Quotient.mk'' y

Next tactic:
show ⟦_⟧ = _
/-- The third isomorphism theorem for sets. -/
def quotientQuotientEquivQuotient (s : Setoid α) (h : r ≤ s) :
    Quotient (ker (Quot.mapRight h)) ≃ Quotient s where
  toFun x :=
    (Quotient.liftOn' x fun w =>
        (Quotient.liftOn' w (@Quotient.mk'' _ s)) fun x y H => Quotient.sound <| h H)
      fun x y => Quotient.inductionOn₂' x y fun w z H => show @Quot.mk _ _ _ = @Quot.mk _ _ _ from H
  invFun x :=
    (Quotient.liftOn' x fun w => @Quotient.mk'' _ (ker <| Quot.mapRight h) <| @Quotient.mk'' _ r w)
      fun x y H => Quotient.sound' <| show @Quot.mk _ _ _ = @Quot.mk _ _ _ from Quotient.sound H
  left_inv x :=
    Quotient.inductionOn' x fun y => Quotient.inductionOn' y fun w => by show ⟦_⟧ = _; rfl
  right_inv x := Quotient.inductionOn' x fun y => by show ⟦_⟧ = _; 

Proof state:
α : Type u_1
β : Type u_2
r : Setoid α
f : α → β
s : Setoid α
h : r ≤ s
x : Quotient s
y : α
⊢ ⟦y⟧ = Quotient.mk'' y

Next tactic:
rfl
/-- Given an equivalence relation `r` on `α`, the order-preserving bijection between the set of
equivalence relations containing `r` and the equivalence relations on the quotient of `α` by `r`. -/
def correspondence (r : Setoid α) : { s // r ≤ s } ≃o Setoid (Quotient r) where
  toFun s := ⟨Quotient.lift₂ s.1.1 fun _ _ _ _ h₁ h₂ ↦ Eq.propIntro
      (fun h ↦ s.1.trans' (s.1.trans' (s.1.symm' (s.2 h₁)) h) (s.2 h₂))
      (fun h ↦ s.1.trans' (s.1.trans' (s.2 h₁) h) (s.1.symm' (s.2 h₂))),
    ⟨Quotient.ind s.1.2.1, @fun x y ↦ Quotient.inductionOn₂ x y fun _ _ ↦ s.1.2.2,
      @fun x y z ↦ Quotient.inductionOn₃ x y z fun _ _ _ ↦ s.1.2.3⟩⟩
  invFun s := ⟨comap Quotient.mk' s, fun x y h => by 

Proof state:
α : Type u_1
β : Type u_2
r✝ : Setoid α
f : α → β
r : Setoid α
s : Setoid (Quotient r)
x y : α
h : Rel r x y
⊢ Rel (comap Quotient.mk' s) x y

Next tactic:
rw [comap_rel, eq_rel.2 h]
@[simp]
theorem Quotient.subsingleton_iff {s : Setoid α} : Subsingleton (Quotient s) ↔ s = ⊤ := by
  

Proof state:
α : Type u_1
β : Type u_2
s : Setoid α
⊢ Subsingleton (Quotient s) ↔ s = ⊤

Next tactic:
simp only [_root_.subsingleton_iff, eq_top_iff, Setoid.le_def, Setoid.top_def, Pi.top_apply,
    forall_const]
@[simp]
theorem Quotient.subsingleton_iff {s : Setoid α} : Subsingleton (Quotient s) ↔ s = ⊤ := by
  simp only [_root_.subsingleton_iff, eq_top_iff, Setoid.le_def, Setoid.top_def, Pi.top_apply,
    forall_const]
  

Proof state:
α : Type u_1
β : Type u_2
s : Setoid α
⊢ (∀ (x y : Quotient s), x = y) ↔ ∀ {x y : α}, ⊤ → Setoid.Rel s x y

Next tactic:
refine' (surjective_quotient_mk' _).forall.trans (forall_congr' fun a => _)
@[simp]
theorem Quotient.subsingleton_iff {s : Setoid α} : Subsingleton (Quotient s) ↔ s = ⊤ := by
  simp only [_root_.subsingleton_iff, eq_top_iff, Setoid.le_def, Setoid.top_def, Pi.top_apply,
    forall_const]
  refine' (surjective_quotient_mk' _).forall.trans (forall_congr' fun a => _)
  

Proof state:
α : Type u_1
β : Type u_2
s : Setoid α
a : α
⊢ (∀ (y : Quotient s), Quotient.mk' a = y) ↔ ∀ {y : α}, ⊤ → Setoid.Rel s a y

Next tactic:
refine' (surjective_quotient_mk' _).forall.trans (forall_congr' fun b => _)
@[simp]
theorem Quotient.subsingleton_iff {s : Setoid α} : Subsingleton (Quotient s) ↔ s = ⊤ := by
  simp only [_root_.subsingleton_iff, eq_top_iff, Setoid.le_def, Setoid.top_def, Pi.top_apply,
    forall_const]
  refine' (surjective_quotient_mk' _).forall.trans (forall_congr' fun a => _)
  refine' (surjective_quotient_mk' _).forall.trans (forall_congr' fun b => _)
  

Proof state:
α : Type u_1
β : Type u_2
s : Setoid α
a b : α
⊢ Quotient.mk' a = Quotient.mk' b ↔ ⊤ → Setoid.Rel s a b

Next tactic:
simp_rw [Prop.top_eq_true, true_implies, Quotient.eq']
@[simp]
theorem Quotient.subsingleton_iff {s : Setoid α} : Subsingleton (Quotient s) ↔ s = ⊤ := by
  simp only [_root_.subsingleton_iff, eq_top_iff, Setoid.le_def, Setoid.top_def, Pi.top_apply,
    forall_const]
  refine' (surjective_quotient_mk' _).forall.trans (forall_congr' fun a => _)
  refine' (surjective_quotient_mk' _).forall.trans (forall_congr' fun b => _)
  simp_rw [Prop.top_eq_true, true_implies, Quotient.eq']
  

Proof state:
α : Type u_1
β : Type u_2
s : Setoid α
a b : α
⊢ Setoid.r a b ↔ Setoid.Rel s a b

Next tactic:
rfl
theorem Quot.subsingleton_iff (r : α → α → Prop) : Subsingleton (Quot r) ↔ EqvGen r = ⊤ := by
  

Proof state:
α : Type u_1
β : Type u_2
r : α → α → Prop
⊢ Subsingleton (Quot r) ↔ EqvGen r = ⊤

Next tactic:
simp only [_root_.subsingleton_iff, _root_.eq_top_iff, Pi.le_def, Pi.top_apply, forall_const]
theorem Quot.subsingleton_iff (r : α → α → Prop) : Subsingleton (Quot r) ↔ EqvGen r = ⊤ := by
  simp only [_root_.subsingleton_iff, _root_.eq_top_iff, Pi.le_def, Pi.top_apply, forall_const]
  

Proof state:
α : Type u_1
β : Type u_2
r : α → α → Prop
⊢ (∀ (x y : Quot r), x = y) ↔ ∀ (i i_1 : α), ⊤ ≤ EqvGen r i i_1

Next tactic:
refine' (surjective_quot_mk _).forall.trans (forall_congr' fun a => _)
theorem Quot.subsingleton_iff (r : α → α → Prop) : Subsingleton (Quot r) ↔ EqvGen r = ⊤ := by
  simp only [_root_.subsingleton_iff, _root_.eq_top_iff, Pi.le_def, Pi.top_apply, forall_const]
  refine' (surjective_quot_mk _).forall.trans (forall_congr' fun a => _)
  

Proof state:
α : Type u_1
β : Type u_2
r : α → α → Prop
a : α
⊢ (∀ (y : Quot r), mk r a = y) ↔ ∀ (i : α), ⊤ ≤ EqvGen r a i

Next tactic:
refine' (surjective_quot_mk _).forall.trans (forall_congr' fun b => _)
theorem Quot.subsingleton_iff (r : α → α → Prop) : Subsingleton (Quot r) ↔ EqvGen r = ⊤ := by
  simp only [_root_.subsingleton_iff, _root_.eq_top_iff, Pi.le_def, Pi.top_apply, forall_const]
  refine' (surjective_quot_mk _).forall.trans (forall_congr' fun a => _)
  refine' (surjective_quot_mk _).forall.trans (forall_congr' fun b => _)
  

Proof state:
α : Type u_1
β : Type u_2
r : α → α → Prop
a b : α
⊢ mk r a = mk r b ↔ ⊤ ≤ EqvGen r a b

Next tactic:
rw [Quot.eq]
theorem Quot.subsingleton_iff (r : α → α → Prop) : Subsingleton (Quot r) ↔ EqvGen r = ⊤ := by
  simp only [_root_.subsingleton_iff, _root_.eq_top_iff, Pi.le_def, Pi.top_apply, forall_const]
  refine' (surjective_quot_mk _).forall.trans (forall_congr' fun a => _)
  refine' (surjective_quot_mk _).forall.trans (forall_congr' fun b => _)
  rw [Quot.eq]
  

Proof state:
α : Type u_1
β : Type u_2
r : α → α → Prop
a b : α
⊢ EqvGen r a b ↔ ⊤ ≤ EqvGen r a b

Next tactic:
simp only [forall_const, le_Prop_eq, OrderTop.toTop, Pi.orderTop, Pi.top_apply,
             Prop.top_eq_true, true_implies]
theorem gal_zero_isSolvable : IsSolvable (0 : F[X]).Gal := by 

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
⊢ IsSolvable (Gal 0)

Next tactic:
infer_instance
theorem gal_one_isSolvable : IsSolvable (1 : F[X]).Gal := by 

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
⊢ IsSolvable (Gal 1)

Next tactic:
infer_instance
theorem gal_C_isSolvable (x : F) : IsSolvable (C x).Gal := by 

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
x : F
⊢ IsSolvable (Gal (C x))

Next tactic:
infer_instance
theorem gal_X_isSolvable : IsSolvable (X : F[X]).Gal := by 

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
⊢ IsSolvable (Gal X)

Next tactic:
infer_instance
theorem gal_X_sub_C_isSolvable (x : F) : IsSolvable (X - C x).Gal := by 

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
x : F
⊢ IsSolvable (Gal (X - C x))

Next tactic:
infer_instance
theorem gal_X_pow_isSolvable (n : ℕ) : IsSolvable (X ^ n : F[X]).Gal := by 

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
n : ℕ
⊢ IsSolvable (Gal (X ^ n))

Next tactic:
infer_instance
theorem gal_prod_isSolvable {s : Multiset F[X]} (hs : ∀ p ∈ s, IsSolvable (Gal p)) :
    IsSolvable s.prod.Gal := by
  

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
s : Multiset F[X]
hs : ∀ p ∈ s, IsSolvable (Gal p)
⊢ IsSolvable (Gal (Multiset.prod s))

Next tactic:
apply Multiset.induction_on' s
theorem gal_prod_isSolvable {s : Multiset F[X]} (hs : ∀ p ∈ s, IsSolvable (Gal p)) :
    IsSolvable s.prod.Gal := by
  apply Multiset.induction_on' s
  · 

Proof state:
case h₁
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
s : Multiset F[X]
hs : ∀ p ∈ s, IsSolvable (Gal p)
⊢ IsSolvable (Gal (Multiset.prod 0))

Next tactic:
exact gal_one_isSolvable
theorem gal_prod_isSolvable {s : Multiset F[X]} (hs : ∀ p ∈ s, IsSolvable (Gal p)) :
    IsSolvable s.prod.Gal := by
  apply Multiset.induction_on' s
  · exact gal_one_isSolvable
  · 

Proof state:
case h₂
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
s : Multiset F[X]
hs : ∀ p ∈ s, IsSolvable (Gal p)
⊢ ∀ {a : F[X]} {s_1 : Multiset F[X]},
    a ∈ s → s_1 ⊆ s → IsSolvable (Gal (Multiset.prod s_1)) → IsSolvable (Gal (Multiset.prod (insert a s_1)))

Next tactic:
intro p t hps _ ht
theorem gal_prod_isSolvable {s : Multiset F[X]} (hs : ∀ p ∈ s, IsSolvable (Gal p)) :
    IsSolvable s.prod.Gal := by
  apply Multiset.induction_on' s
  · exact gal_one_isSolvable
  · intro p t hps _ ht
    

Proof state:
case h₂
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
s : Multiset F[X]
hs : ∀ p ∈ s, IsSolvable (Gal p)
p : F[X]
t : Multiset F[X]
hps : p ∈ s
a✝ : t ⊆ s
ht : IsSolvable (Gal (Multiset.prod t))
⊢ IsSolvable (Gal (Multiset.prod (insert p t)))

Next tactic:
rw [Multiset.insert_eq_cons, Multiset.prod_cons]
theorem gal_prod_isSolvable {s : Multiset F[X]} (hs : ∀ p ∈ s, IsSolvable (Gal p)) :
    IsSolvable s.prod.Gal := by
  apply Multiset.induction_on' s
  · exact gal_one_isSolvable
  · intro p t hps _ ht
    rw [Multiset.insert_eq_cons, Multiset.prod_cons]
    

Proof state:
case h₂
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
s : Multiset F[X]
hs : ∀ p ∈ s, IsSolvable (Gal p)
p : F[X]
t : Multiset F[X]
hps : p ∈ s
a✝ : t ⊆ s
ht : IsSolvable (Gal (Multiset.prod t))
⊢ IsSolvable (Gal (p * Multiset.prod t))

Next tactic:
exact gal_mul_isSolvable (hs p hps) ht
theorem gal_isSolvable_tower (p q : F[X]) (hpq : p.Splits (algebraMap F q.SplittingField))
    (hp : IsSolvable p.Gal) (hq : IsSolvable (q.map (algebraMap F p.SplittingField)).Gal) :
    IsSolvable q.Gal := by
  

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
p q : F[X]
hpq : Splits (algebraMap F (SplittingField q)) p
hp : IsSolvable (Gal p)
hq : IsSolvable (Gal (Polynomial.map (algebraMap F (SplittingField p)) q))
⊢ IsSolvable (Gal q)

Next tactic:
let K := p.SplittingField
theorem gal_isSolvable_tower (p q : F[X]) (hpq : p.Splits (algebraMap F q.SplittingField))
    (hp : IsSolvable p.Gal) (hq : IsSolvable (q.map (algebraMap F p.SplittingField)).Gal) :
    IsSolvable q.Gal := by
  let K := p.SplittingField
  

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
p q : F[X]
hpq : Splits (algebraMap F (SplittingField q)) p
hp : IsSolvable (Gal p)
hq : IsSolvable (Gal (Polynomial.map (algebraMap F (SplittingField p)) q))
K : Type u_1 := SplittingField p
⊢ IsSolvable (Gal q)

Next tactic:
let L := q.SplittingField
theorem gal_isSolvable_tower (p q : F[X]) (hpq : p.Splits (algebraMap F q.SplittingField))
    (hp : IsSolvable p.Gal) (hq : IsSolvable (q.map (algebraMap F p.SplittingField)).Gal) :
    IsSolvable q.Gal := by
  let K := p.SplittingField
  let L := q.SplittingField
  

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
p q : F[X]
hpq : Splits (algebraMap F (SplittingField q)) p
hp : IsSolvable (Gal p)
hq : IsSolvable (Gal (Polynomial.map (algebraMap F (SplittingField p)) q))
K : Type u_1 := SplittingField p
L : Type u_1 := SplittingField q
⊢ IsSolvable (Gal q)

Next tactic:
haveI : Fact (p.Splits (algebraMap F L)) := ⟨hpq⟩
theorem gal_isSolvable_tower (p q : F[X]) (hpq : p.Splits (algebraMap F q.SplittingField))
    (hp : IsSolvable p.Gal) (hq : IsSolvable (q.map (algebraMap F p.SplittingField)).Gal) :
    IsSolvable q.Gal := by
  let K := p.SplittingField
  let L := q.SplittingField
  haveI : Fact (p.Splits (algebraMap F L)) := ⟨hpq⟩
  

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
p q : F[X]
hpq : Splits (algebraMap F (SplittingField q)) p
hp : IsSolvable (Gal p)
hq : IsSolvable (Gal (Polynomial.map (algebraMap F (SplittingField p)) q))
K : Type u_1 := SplittingField p
L : Type u_1 := SplittingField q
this : Fact (Splits (algebraMap F L) p)
⊢ IsSolvable (Gal q)

Next tactic:
let ϕ : (L ≃ₐ[K] L) ≃* (q.map (algebraMap F K)).Gal :=
    (IsSplittingField.algEquiv L (q.map (algebraMap F K))).autCongr
theorem gal_isSolvable_tower (p q : F[X]) (hpq : p.Splits (algebraMap F q.SplittingField))
    (hp : IsSolvable p.Gal) (hq : IsSolvable (q.map (algebraMap F p.SplittingField)).Gal) :
    IsSolvable q.Gal := by
  let K := p.SplittingField
  let L := q.SplittingField
  haveI : Fact (p.Splits (algebraMap F L)) := ⟨hpq⟩
  let ϕ : (L ≃ₐ[K] L) ≃* (q.map (algebraMap F K)).Gal :=
    (IsSplittingField.algEquiv L (q.map (algebraMap F K))).autCongr
  

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
p q : F[X]
hpq : Splits (algebraMap F (SplittingField q)) p
hp : IsSolvable (Gal p)
hq : IsSolvable (Gal (Polynomial.map (algebraMap F (SplittingField p)) q))
K : Type u_1 := SplittingField p
L : Type u_1 := SplittingField q
this : Fact (Splits (algebraMap F L) p)
ϕ : (L ≃ₐ[K] L) ≃* Gal (Polynomial.map (algebraMap F K) q) :=
  AlgEquiv.autCongr (IsSplittingField.algEquiv L (Polynomial.map (algebraMap F K) q))
⊢ IsSolvable (Gal q)

Next tactic:
have ϕ_inj : Function.Injective ϕ.toMonoidHom := ϕ.injective
theorem gal_isSolvable_tower (p q : F[X]) (hpq : p.Splits (algebraMap F q.SplittingField))
    (hp : IsSolvable p.Gal) (hq : IsSolvable (q.map (algebraMap F p.SplittingField)).Gal) :
    IsSolvable q.Gal := by
  let K := p.SplittingField
  let L := q.SplittingField
  haveI : Fact (p.Splits (algebraMap F L)) := ⟨hpq⟩
  let ϕ : (L ≃ₐ[K] L) ≃* (q.map (algebraMap F K)).Gal :=
    (IsSplittingField.algEquiv L (q.map (algebraMap F K))).autCongr
  have ϕ_inj : Function.Injective ϕ.toMonoidHom := ϕ.injective
  

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
p q : F[X]
hpq : Splits (algebraMap F (SplittingField q)) p
hp : IsSolvable (Gal p)
hq : IsSolvable (Gal (Polynomial.map (algebraMap F (SplittingField p)) q))
K : Type u_1 := SplittingField p
L : Type u_1 := SplittingField q
this : Fact (Splits (algebraMap F L) p)
ϕ : (L ≃ₐ[K] L) ≃* Gal (Polynomial.map (algebraMap F K) q) :=
  AlgEquiv.autCongr (IsSplittingField.algEquiv L (Polynomial.map (algebraMap F K) q))
ϕ_inj : Function.Injective ⇑(MulEquiv.toMonoidHom ϕ)
⊢ IsSolvable (Gal q)

Next tactic:
haveI : IsSolvable (K ≃ₐ[F] K) := hp
theorem gal_isSolvable_tower (p q : F[X]) (hpq : p.Splits (algebraMap F q.SplittingField))
    (hp : IsSolvable p.Gal) (hq : IsSolvable (q.map (algebraMap F p.SplittingField)).Gal) :
    IsSolvable q.Gal := by
  let K := p.SplittingField
  let L := q.SplittingField
  haveI : Fact (p.Splits (algebraMap F L)) := ⟨hpq⟩
  let ϕ : (L ≃ₐ[K] L) ≃* (q.map (algebraMap F K)).Gal :=
    (IsSplittingField.algEquiv L (q.map (algebraMap F K))).autCongr
  have ϕ_inj : Function.Injective ϕ.toMonoidHom := ϕ.injective
  haveI : IsSolvable (K ≃ₐ[F] K) := hp
  

Proof state:
F : Type u_1
inst✝² : Field F
E : Type u_2
inst✝¹ : Field E
inst✝ : Algebra F E
p q : F[X]
hpq : Splits (algebraMap F (SplittingField q)) p
hp : IsSolvable (Gal p)
hq : IsSolvable (Gal (Polynomial.map (algebraMap F (SplittingField p)) q))
K : Type u_1 := SplittingField p
L : Type u_1 := SplittingField q
this✝ : Fact (Splits (algebraMap F L) p)
ϕ : (L ≃ₐ[K] L) ≃* Gal (Polynomial.map (algebraMap F K) q) :=
  AlgEquiv.autCongr (IsSplittingField.algEquiv L (Polynomial.map (algebraMap F K) q))
ϕ_inj : Function.Injective ⇑(MulEquiv.toMonoidHom ϕ)
this : IsSolvable (K ≃ₐ[F] K)
⊢ IsSolvable (Gal q)

Next tactic:
haveI : IsSolvable (L ≃ₐ[K] L) := solvable_of_solvable_injective ϕ_inj