/-
Copyright (c) 2021 Anatole Dedecker. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Anatole Dedecker, Bhavik Mehta
-/
import measure_theory.integral.interval_integral
import order.filter.at_top_bot
import measure_theory.function.jacobian

/-!
# Links between an integral and its "improper" version

In its current state, mathlib only knows how to talk about definite ("proper") integrals,
in the sense that it treats integrals over `[x, +∞)` the same as it treats integrals over
`[y, z]`. For example, the integral over `[1, +∞)` is **not** defined to be the limit of
the integral over `[1, x]` as `x` tends to `+∞`, which is known as an **improper integral**.

Indeed, the "proper" definition is stronger than the "improper" one. The usual counterexample
is `x ↦ sin(x)/x`, which has an improper integral over `[1, +∞)` but no definite integral.

Although definite integrals have better properties, they are hardly usable when it comes to
computing integrals on unbounded sets, which is much easier using limits. Thus, in this file,
we prove various ways of studying the proper integral by studying the improper one.

## Definitions

The main definition of this file is `measure_theory.ae_cover`. It is a rather technical
definition whose sole purpose is generalizing and factoring proofs. Given an index type `ι`, a
countably generated filter `l` over `ι`, and an `ι`-indexed family `φ` of subsets of a measurable
space `α` equipped with a measure `μ`, one should think of a hypothesis `hφ : ae_cover μ l φ` as
a sufficient condition for being able to interpret `∫ x, f x ∂μ` (if it exists) as the limit
of `∫ x in φ i, f x ∂μ` as `i` tends to `l`.

When using this definition with a measure restricted to a set `s`, which happens fairly often,
one should not try too hard to use a `ae_cover` of subsets of `s`, as it often makes proofs
more complicated than necessary. See for example the proof of
`measure_theory.integrable_on_Iic_of_interval_integral_norm_tendsto` where we use `(λ x, Ioi x)`
as an `ae_cover` w.r.t. `μ.restrict (Iic b)`, instead of using `(λ x, Ioc x b)`.

## Main statements

- `measure_theory.ae_cover.lintegral_tendsto_of_countably_generated` : if `φ` is a `ae_cover μ l`,
  where `l` is a countably generated filter, and if `f` is a measurable `ennreal`-valued function,
  then `∫⁻ x in φ n, f x ∂μ` tends to `∫⁻ x, f x ∂μ` as `n` tends to `l`
- `measure_theory.ae_cover.integrable_of_integral_norm_tendsto` : if `φ` is a `ae_cover μ l`,
  where `l` is a countably generated filter, if `f` is measurable and integrable on each `φ n`,
  and if `∫ x in φ n, ‖f x‖ ∂μ` tends to some `I : ℝ` as n tends to `l`, then `f` is integrable
- `measure_theory.ae_cover.integral_tendsto_of_countably_generated` : if `φ` is a `ae_cover μ l`,
  where `l` is a countably generated filter, and if `f` is measurable and integrable (globally),
  then `∫ x in φ n, f x ∂μ` tends to `∫ x, f x ∂μ` as `n` tends to `+∞`.

We then specialize these lemmas to various use cases involving intervals, which are frequent
in analysis.
-/

open measure_theory filter set topological_space
open_locale ennreal nnreal topology

namespace measure_theory

section ae_cover

variables {α ι : Type*} [measurable_space α] (μ : measure α) (l : filter ι)

/-- A sequence `φ` of subsets of `α` is a `ae_cover` w.r.t. a measure `μ` and a filter `l`
    if almost every point (w.r.t. `μ`) of `α` eventually belongs to `φ n` (w.r.t. `l`), and if
    each `φ n` is measurable.
    This definition is a technical way to avoid duplicating a lot of proofs.
    It should be thought of as a sufficient condition for being able to interpret
    `∫ x, f x ∂μ` (if it exists) as the limit of `∫ x in φ n, f x ∂μ` as `n` tends to `l`.

    See for example `measure_theory.ae_cover.lintegral_tendsto_of_countably_generated`,
    `measure_theory.ae_cover.integrable_of_integral_norm_tendsto` and
    `measure_theory.ae_cover.integral_tendsto_of_countably_generated`. -/
structure ae_cover (φ : ι → set α) : Prop :=
(ae_eventually_mem : ∀ᵐ x ∂μ, ∀ᶠ i in l, x ∈ φ i)
(measurable : ∀ i, measurable_set $ φ i)

variables {μ} {l}

section preorder_α

variables [preorder α] [topological_space α] [order_closed_topology α]
  [opens_measurable_space α] {a b : ι → α}
  (ha : tendsto a l at_bot) (hb : tendsto b l at_top)

lemma ae_cover_Icc :
  ae_cover μ l (λ i, Icc (a i) (b i)) :=
{ ae_eventually_mem := ae_of_all μ (λ x,
    (ha.eventually $ eventually_le_at_bot x).mp $
    (hb.eventually $ eventually_ge_at_top x).mono $
    λ i hbi hai, ⟨hai, hbi⟩ ),
  measurable := λ i, measurable_set_Icc }

lemma ae_cover_Ici :
  ae_cover μ l (λ i, Ici $ a i) :=
{ ae_eventually_mem := ae_of_all μ (λ x,
    (ha.eventually $ eventually_le_at_bot x).mono $
    λ i hai, hai ),
  measurable := λ i, measurable_set_Ici }

lemma ae_cover_Iic :
  ae_cover μ l (λ i, Iic $ b i) :=
{ ae_eventually_mem := ae_of_all μ (λ x,
    (hb.eventually $ eventually_ge_at_top x).mono $
    λ i hbi, hbi ),
  measurable := λ i, measurable_set_Iic }

end preorder_α

section linear_order_α

variables [linear_order α] [topological_space α] [order_closed_topology α]
  [opens_measurable_space α] {a b : ι → α}
  (ha : tendsto a l at_bot) (hb : tendsto b l at_top)

lemma ae_cover_Ioo [no_min_order α] [no_max_order α] :
  ae_cover μ l (λ i, Ioo (a i) (b i)) :=
{ ae_eventually_mem := ae_of_all μ (λ x,
    (ha.eventually $ eventually_lt_at_bot x).mp $
    (hb.eventually $ eventually_gt_at_top x).mono $
    λ i hbi hai, ⟨hai, hbi⟩ ),
  measurable := λ i, measurable_set_Ioo }

lemma ae_cover_Ioc [no_min_order α] : ae_cover μ l (λ i, Ioc (a i) (b i)) :=
{ ae_eventually_mem := ae_of_all μ (λ x,
    (ha.eventually $ eventually_lt_at_bot x).mp $
    (hb.eventually $ eventually_ge_at_top x).mono $
    λ i hbi hai, ⟨hai, hbi⟩ ),
  measurable := λ i, measurable_set_Ioc }

lemma ae_cover_Ico [no_max_order α] : ae_cover μ l (λ i, Ico (a i) (b i)) :=
{ ae_eventually_mem := ae_of_all μ (λ x,
    (ha.eventually $ eventually_le_at_bot x).mp $
    (hb.eventually $ eventually_gt_at_top x).mono $
    λ i hbi hai, ⟨hai, hbi⟩ ),
  measurable := λ i, measurable_set_Ico }

lemma ae_cover_Ioi [no_min_order α] :
  ae_cover μ l (λ i, Ioi $ a i) :=
{ ae_eventually_mem := ae_of_all μ (λ x,
    (ha.eventually $ eventually_lt_at_bot x).mono $
    λ i hai, hai ),
  measurable := λ i, measurable_set_Ioi }

lemma ae_cover_Iio [no_max_order α] :
  ae_cover μ l (λ i, Iio $ b i) :=
{ ae_eventually_mem := ae_of_all μ (λ x,
    (hb.eventually $ eventually_gt_at_top x).mono $
    λ i hbi, hbi ),
  measurable := λ i, measurable_set_Iio }

end linear_order_α

section finite_intervals

variables [linear_order α] [topological_space α] [order_closed_topology α]
  [opens_measurable_space α] {a b : ι → α} {A B : α}
  (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B))

lemma ae_cover_Ioo_of_Icc :
  ae_cover (μ.restrict $ Ioo A B) l (λ i, Icc (a i) (b i)) :=
{ ae_eventually_mem := (ae_restrict_iff' measurable_set_Ioo).mpr (
    ae_of_all μ (λ x hx,
    (ha.eventually $ eventually_le_nhds hx.left).mp $
    (hb.eventually $ eventually_ge_nhds hx.right).mono $
    λ i hbi hai, ⟨hai, hbi⟩)),
  measurable := λ i, measurable_set_Icc, }

lemma ae_cover_Ioo_of_Ico :
  ae_cover (μ.restrict $ Ioo A B) l (λ i, Ico (a i) (b i)) :=
{ ae_eventually_mem := (ae_restrict_iff' measurable_set_Ioo).mpr (
    ae_of_all μ (λ x hx,
    (ha.eventually $ eventually_le_nhds hx.left).mp $
    (hb.eventually $ eventually_gt_nhds hx.right).mono $
    λ i hbi hai, ⟨hai, hbi⟩)),
  measurable := λ i, measurable_set_Ico, }

lemma ae_cover_Ioo_of_Ioc :
  ae_cover (μ.restrict $ Ioo A B) l (λ i, Ioc (a i) (b i)) :=
{ ae_eventually_mem := (ae_restrict_iff' measurable_set_Ioo).mpr (
    ae_of_all μ (λ x hx,
    (ha.eventually $ eventually_lt_nhds hx.left).mp $
    (hb.eventually $ eventually_ge_nhds hx.right).mono $
    λ i hbi hai, ⟨hai, hbi⟩)),
  measurable := λ i, measurable_set_Ioc, }

lemma ae_cover_Ioo_of_Ioo :
  ae_cover (μ.restrict $ Ioo A B) l (λ i, Ioo (a i) (b i)) :=
{ ae_eventually_mem := (ae_restrict_iff' measurable_set_Ioo).mpr (
    ae_of_all μ (λ x hx,
    (ha.eventually $ eventually_lt_nhds hx.left).mp $
    (hb.eventually $ eventually_gt_nhds hx.right).mono $
    λ i hbi hai, ⟨hai, hbi⟩)),
  measurable := λ i, measurable_set_Ioo, }

variables [has_no_atoms μ]

lemma ae_cover_Ioc_of_Icc (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Ioc A B) l (λ i, Icc (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Ioc.symm, ae_cover_Ioo_of_Icc ha hb]

lemma ae_cover_Ioc_of_Ico (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Ioc A B) l (λ i, Ico (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Ioc.symm, ae_cover_Ioo_of_Ico ha hb]

lemma ae_cover_Ioc_of_Ioc (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Ioc A B) l (λ i, Ioc (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Ioc.symm, ae_cover_Ioo_of_Ioc ha hb]

lemma ae_cover_Ioc_of_Ioo (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Ioc A B) l (λ i, Ioo (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Ioc.symm, ae_cover_Ioo_of_Ioo ha hb]

lemma ae_cover_Ico_of_Icc (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Ico A B) l (λ i, Icc (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Ico.symm, ae_cover_Ioo_of_Icc ha hb]

lemma ae_cover_Ico_of_Ico (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Ico A B) l (λ i, Ico (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Ico.symm, ae_cover_Ioo_of_Ico ha hb]

lemma ae_cover_Ico_of_Ioc (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Ico A B) l (λ i, Ioc (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Ico.symm, ae_cover_Ioo_of_Ioc ha hb]

lemma ae_cover_Ico_of_Ioo (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Ico A B) l (λ i, Ioo (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Ico.symm, ae_cover_Ioo_of_Ioo ha hb]

lemma ae_cover_Icc_of_Icc (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Icc A B) l (λ i, Icc (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Icc.symm, ae_cover_Ioo_of_Icc ha hb]

lemma ae_cover_Icc_of_Ico (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Icc A B) l (λ i, Ico (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Icc.symm, ae_cover_Ioo_of_Ico ha hb]

lemma ae_cover_Icc_of_Ioc (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Icc A B) l (λ i, Ioc (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Icc.symm, ae_cover_Ioo_of_Ioc ha hb]

lemma ae_cover_Icc_of_Ioo (ha : tendsto a l (𝓝 A)) (hb : tendsto b l (𝓝 B)) :
  ae_cover (μ.restrict $ Icc A B) l (λ i, Ioo (a i) (b i)) :=
by simp [measure.restrict_congr_set Ioo_ae_eq_Icc.symm, ae_cover_Ioo_of_Ioo ha hb]

end finite_intervals

lemma ae_cover.restrict {φ : ι → set α} (hφ : ae_cover μ l φ) {s : set α} :
  ae_cover (μ.restrict s) l φ :=
{ ae_eventually_mem := ae_restrict_of_ae hφ.ae_eventually_mem,
  measurable := hφ.measurable }

lemma ae_cover_restrict_of_ae_imp {s : set α} {φ : ι → set α}
  (hs : measurable_set s) (ae_eventually_mem : ∀ᵐ x ∂μ, x ∈ s → ∀ᶠ n in l, x ∈ φ n)
  (measurable : ∀ n, measurable_set $ φ n) :
  ae_cover (μ.restrict s) l φ :=
{ ae_eventually_mem := by rwa ae_restrict_iff' hs,
  measurable := measurable }

lemma ae_cover.inter_restrict {φ : ι → set α} (hφ : ae_cover μ l φ)
  {s : set α} (hs : measurable_set s) :
  ae_cover (μ.restrict s) l (λ i, φ i ∩ s) :=
ae_cover_restrict_of_ae_imp hs
  (hφ.ae_eventually_mem.mono (λ x hx hxs, hx.mono $ λ i hi, ⟨hi, hxs⟩))
  (λ i, (hφ.measurable i).inter hs)

lemma ae_cover.ae_tendsto_indicator {β : Type*} [has_zero β] [topological_space β]
  (f : α → β) {φ : ι → set α} (hφ : ae_cover μ l φ) :
  ∀ᵐ x ∂μ, tendsto (λ i, (φ i).indicator f x) l (𝓝 $ f x) :=
hφ.ae_eventually_mem.mono (λ x hx, tendsto_const_nhds.congr' $
  hx.mono $ λ n hn, (indicator_of_mem hn _).symm)

lemma ae_cover.ae_measurable {β : Type*} [measurable_space β] [l.is_countably_generated] [l.ne_bot]
  {f : α → β} {φ : ι → set α} (hφ : ae_cover μ l φ)
  (hfm : ∀ i, ae_measurable f (μ.restrict $ φ i)) : ae_measurable f μ :=
begin
  obtain ⟨u, hu⟩ := l.exists_seq_tendsto,
  have := ae_measurable_Union_iff.mpr (λ (n : ℕ), hfm (u n)),
  rwa measure.restrict_eq_self_of_ae_mem at this,
  filter_upwards [hφ.ae_eventually_mem] with x hx using
    let ⟨i, hi⟩ := (hu.eventually hx).exists in mem_Union.mpr ⟨i, hi⟩
end

lemma ae_cover.ae_strongly_measurable {β : Type*} [topological_space β] [pseudo_metrizable_space β]
  [l.is_countably_generated] [l.ne_bot]
  {f : α → β} {φ : ι → set α} (hφ : ae_cover μ l φ)
  (hfm : ∀ i, ae_strongly_measurable f (μ.restrict $ φ i)) : ae_strongly_measurable f μ :=
begin
  obtain ⟨u, hu⟩ := l.exists_seq_tendsto,
  have := ae_strongly_measurable_Union_iff.mpr (λ (n : ℕ), hfm (u n)),
  rwa measure.restrict_eq_self_of_ae_mem at this,
  filter_upwards [hφ.ae_eventually_mem] with x hx using
    let ⟨i, hi⟩ := (hu.eventually hx).exists in mem_Union.mpr ⟨i, hi⟩
end

end ae_cover

lemma ae_cover.comp_tendsto {α ι ι' : Type*} [measurable_space α] {μ : measure α} {l : filter ι}
  {l' : filter ι'} {φ : ι → set α} (hφ : ae_cover μ l φ)
  {u : ι' → ι} (hu : tendsto u l' l) :
  ae_cover μ l' (φ ∘ u) :=
{ ae_eventually_mem := hφ.ae_eventually_mem.mono (λ x hx, hu.eventually hx),
  measurable := λ i, hφ.measurable (u i) }

section ae_cover_Union_Inter_countable

variables {α ι : Type*} [countable ι]
  [measurable_space α] {μ : measure α}

lemma ae_cover.bInter_Ici_ae_cover [semilattice_sup ι] [nonempty ι] {φ : ι → set α}
  (hφ : ae_cover μ at_top φ) : ae_cover μ at_top (λ (n : ι), ⋂ k (h : k ∈ Ici n), φ k) :=
{ ae_eventually_mem := hφ.ae_eventually_mem.mono
    begin
      intros x h,
      rw eventually_at_top at *,
      rcases h with ⟨i, hi⟩,
      use i,
      intros j hj,
      exact mem_bInter (λ k hk, hi k (le_trans hj hk)),
    end,
  measurable := λ i, measurable_set.bInter (to_countable _) (λ n _, hφ.measurable n) }

end ae_cover_Union_Inter_countable

section lintegral

variables {α ι : Type*} [measurable_space α] {μ : measure α} {l : filter ι}

private lemma lintegral_tendsto_of_monotone_of_nat {φ : ℕ → set α}
  (hφ : ae_cover μ at_top φ) (hmono : monotone φ) {f : α → ℝ≥0∞} (hfm : ae_measurable f μ) :
  tendsto (λ i, ∫⁻ x in φ i, f x ∂μ) at_top (𝓝 $ ∫⁻ x, f x ∂μ) :=
let F := λ n, (φ n).indicator f in
have key₁ : ∀ n, ae_measurable (F n) μ, from λ n, hfm.indicator (hφ.measurable n),
have key₂ : ∀ᵐ (x : α) ∂μ, monotone (λ n, F n x), from ae_of_all _
  (λ x i j hij, indicator_le_indicator_of_subset (hmono hij) (λ x, zero_le $ f x) x),
have key₃ : ∀ᵐ (x : α) ∂μ, tendsto (λ n, F n x) at_top (𝓝 (f x)), from hφ.ae_tendsto_indicator f,
(lintegral_tendsto_of_tendsto_of_monotone key₁ key₂ key₃).congr
  (λ n, lintegral_indicator f (hφ.measurable n))

lemma ae_cover.lintegral_tendsto_of_nat {φ : ℕ → set α} (hφ : ae_cover μ at_top φ)
  {f : α → ℝ≥0∞} (hfm : ae_measurable f μ) :
  tendsto (λ i, ∫⁻ x in φ i, f x ∂μ) at_top (𝓝 $ ∫⁻ x, f x ∂μ) :=
begin
  have lim₁ := lintegral_tendsto_of_monotone_of_nat (hφ.bInter_Ici_ae_cover)
    (λ i j hij, bInter_subset_bInter_left (Ici_subset_Ici.mpr hij)) hfm,
  have lim₂ := lintegral_tendsto_of_monotone_of_nat (hφ.bUnion_Iic_ae_cover)
    (λ i j hij, bUnion_subset_bUnion_left (Iic_subset_Iic.mpr hij)) hfm,
  have le₁ := λ n, lintegral_mono_set (bInter_subset_of_mem left_mem_Ici),
  have le₂ := λ n, lintegral_mono_set (subset_bUnion_of_mem right_mem_Iic),
  exact tendsto_of_tendsto_of_tendsto_of_le_of_le lim₁ lim₂ le₁ le₂
end

lemma ae_cover.lintegral_tendsto_of_countably_generated [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → ℝ≥0∞}
  (hfm : ae_measurable f μ) : tendsto (λ i, ∫⁻ x in φ i, f x ∂μ) l (𝓝 $ ∫⁻ x, f x ∂μ) :=
tendsto_of_seq_tendsto (λ u hu, (hφ.comp_tendsto hu).lintegral_tendsto_of_nat hfm)

lemma ae_cover.lintegral_eq_of_tendsto [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → ℝ≥0∞} (I : ℝ≥0∞)
  (hfm : ae_measurable f μ) (htendsto : tendsto (λ i, ∫⁻ x in φ i, f x ∂μ) l (𝓝 I)) :
  ∫⁻ x, f x ∂μ = I :=
tendsto_nhds_unique (hφ.lintegral_tendsto_of_countably_generated hfm) htendsto

lemma ae_cover.supr_lintegral_eq_of_countably_generated [nonempty ι] [l.ne_bot]
  [l.is_countably_generated] {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → ℝ≥0∞}
  (hfm : ae_measurable f μ) : (⨆ (i : ι), ∫⁻ x in φ i, f x ∂μ) = ∫⁻ x, f x ∂μ :=
begin
  have := hφ.lintegral_tendsto_of_countably_generated hfm,
  refine csupr_eq_of_forall_le_of_forall_lt_exists_gt
    (λ i, lintegral_mono' measure.restrict_le_self le_rfl) (λ w hw, _),
  rcases exists_between hw with ⟨m, hm₁, hm₂⟩,
  rcases (eventually_ge_of_tendsto_gt hm₂ this).exists with ⟨i, hi⟩,
  exact ⟨i, lt_of_lt_of_le hm₁ hi⟩,
end

end lintegral

section integrable

variables {α ι E : Type*} [measurable_space α] {μ : measure α} {l : filter ι}
  [normed_add_comm_group E]

lemma ae_cover.integrable_of_lintegral_nnnorm_bounded [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → E} (I : ℝ) (hfm : ae_strongly_measurable f μ)
  (hbounded : ∀ᶠ i in l, ∫⁻ x in φ i, ‖f x‖₊ ∂μ ≤ ennreal.of_real I) :
  integrable f μ :=
begin
  refine ⟨hfm, (le_of_tendsto _ hbounded).trans_lt ennreal.of_real_lt_top⟩,
  exact hφ.lintegral_tendsto_of_countably_generated hfm.ennnorm
end

lemma ae_cover.integrable_of_lintegral_nnnorm_tendsto [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → E} (I : ℝ)
  (hfm : ae_strongly_measurable f μ)
  (htendsto : tendsto (λ i, ∫⁻ x in φ i, ‖f x‖₊ ∂μ) l (𝓝 $ ennreal.of_real I)) :
  integrable f μ :=
begin
  refine hφ.integrable_of_lintegral_nnnorm_bounded (max 1 (I + 1)) hfm _,
  refine htendsto.eventually (ge_mem_nhds _),
  refine (ennreal.of_real_lt_of_real_iff (lt_max_of_lt_left zero_lt_one)).2 _,
  exact lt_max_of_lt_right (lt_add_one I),
end

lemma ae_cover.integrable_of_lintegral_nnnorm_bounded' [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → E} (I : ℝ≥0) (hfm : ae_strongly_measurable f μ)
  (hbounded : ∀ᶠ i in l, ∫⁻ x in φ i, ‖f x‖₊ ∂μ ≤ I) :
  integrable f μ :=
hφ.integrable_of_lintegral_nnnorm_bounded I hfm
  (by simpa only [ennreal.of_real_coe_nnreal] using hbounded)

lemma ae_cover.integrable_of_lintegral_nnnorm_tendsto' [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → E} (I : ℝ≥0)
  (hfm : ae_strongly_measurable f μ)
  (htendsto : tendsto (λ i, ∫⁻ x in φ i, ‖f x‖₊ ∂μ) l (𝓝 I)) :
  integrable f μ :=
hφ.integrable_of_lintegral_nnnorm_tendsto I hfm
  (by simpa only [ennreal.of_real_coe_nnreal] using htendsto)

lemma ae_cover.integrable_of_integral_norm_bounded [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → E}
  (I : ℝ) (hfi : ∀ i, integrable_on f (φ i) μ)
  (hbounded : ∀ᶠ i in l, ∫ x in φ i, ‖f x‖ ∂μ ≤ I) :
  integrable f μ :=
begin
  have hfm : ae_strongly_measurable f μ :=
    hφ.ae_strongly_measurable (λ i, (hfi i).ae_strongly_measurable),
  refine hφ.integrable_of_lintegral_nnnorm_bounded I hfm _,
  conv at hbounded in (integral _ _)
  { rw integral_eq_lintegral_of_nonneg_ae (ae_of_all _ (λ x, @norm_nonneg E _ (f x)))
    hfm.norm.restrict },
  conv at hbounded in (ennreal.of_real _) { dsimp, rw ← coe_nnnorm, rw ennreal.of_real_coe_nnreal },
  refine hbounded.mono (λ i hi, _),
  rw ←ennreal.of_real_to_real (ne_top_of_lt (hfi i).2),
  apply ennreal.of_real_le_of_real hi,
end

lemma ae_cover.integrable_of_integral_norm_tendsto [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → E}
  (I : ℝ) (hfi : ∀ i, integrable_on f (φ i) μ)
  (htendsto : tendsto (λ i, ∫ x in φ i, ‖f x‖ ∂μ) l (𝓝 I)) :
  integrable f μ :=
let ⟨I', hI'⟩ := htendsto.is_bounded_under_le in hφ.integrable_of_integral_norm_bounded I' hfi hI'

lemma ae_cover.integrable_of_integral_bounded_of_nonneg_ae [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → ℝ} (I : ℝ)
  (hfi : ∀ i, integrable_on f (φ i) μ) (hnng : ∀ᵐ x ∂μ, 0 ≤ f x)
  (hbounded : ∀ᶠ i in l, ∫ x in φ i, f x ∂μ ≤ I) :
  integrable f μ :=
hφ.integrable_of_integral_norm_bounded I hfi $ hbounded.mono $ λ i hi,
  (integral_congr_ae $ ae_restrict_of_ae $ hnng.mono $ λ x, real.norm_of_nonneg).le.trans hi

lemma ae_cover.integrable_of_integral_tendsto_of_nonneg_ae [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → ℝ} (I : ℝ)
  (hfi : ∀ i, integrable_on f (φ i) μ) (hnng : ∀ᵐ x ∂μ, 0 ≤ f x)
  (htendsto : tendsto (λ i, ∫ x in φ i, f x ∂μ) l (𝓝 I)) :
  integrable f μ :=
let ⟨I', hI'⟩ := htendsto.is_bounded_under_le in
  hφ.integrable_of_integral_bounded_of_nonneg_ae I' hfi hnng hI'

end integrable

section integral

variables {α ι E : Type*} [measurable_space α] {μ : measure α} {l : filter ι}
  [normed_add_comm_group E] [normed_space ℝ E] [complete_space E]

lemma ae_cover.integral_tendsto_of_countably_generated [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → E} (hfi : integrable f μ) :
  tendsto (λ i, ∫ x in φ i, f x ∂μ) l (𝓝 $ ∫ x, f x ∂μ) :=
suffices h : tendsto (λ i, ∫ (x : α), (φ i).indicator f x ∂μ) l (𝓝 (∫ (x : α), f x ∂μ)),
by { convert h, ext n, rw integral_indicator (hφ.measurable n) },
tendsto_integral_filter_of_dominated_convergence (λ x, ‖f x‖)
  (eventually_of_forall $ λ i, hfi.ae_strongly_measurable.indicator $ hφ.measurable i)
  (eventually_of_forall $ λ i, ae_of_all _ $ λ x, norm_indicator_le_norm_self _ _)
  hfi.norm (hφ.ae_tendsto_indicator f)

/-- Slight reformulation of
    `measure_theory.ae_cover.integral_tendsto_of_countably_generated`. -/
lemma ae_cover.integral_eq_of_tendsto [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → E}
  (I : E) (hfi : integrable f μ)
  (h : tendsto (λ n, ∫ x in φ n, f x ∂μ) l (𝓝 I)) :
  ∫ x, f x ∂μ = I :=
tendsto_nhds_unique (hφ.integral_tendsto_of_countably_generated hfi) h

lemma ae_cover.integral_eq_of_tendsto_of_nonneg_ae [l.ne_bot] [l.is_countably_generated]
  {φ : ι → set α} (hφ : ae_cover μ l φ) {f : α → ℝ} (I : ℝ)
  (hnng : 0 ≤ᵐ[μ] f) (hfi : ∀ n, integrable_on f (φ n) μ)
  (htendsto : tendsto (λ n, ∫ x in φ n, f x ∂μ) l (𝓝 I)) :
  ∫ x, f x ∂μ = I :=
have hfi' : integrable f μ,
  from hφ.integrable_of_integral_tendsto_of_nonneg_ae I hfi hnng htendsto,
hφ.integral_eq_of_tendsto I hfi' htendsto

end integral

section integrable_of_interval_integral

variables {ι E : Type*} {μ : measure ℝ}
          {l : filter ι} [filter.ne_bot l] [is_countably_generated l]
          [normed_add_comm_group E]
          {a b : ι → ℝ} {f : ℝ → E}

lemma integrable_of_interval_integral_norm_bounded
  (I : ℝ) (hfi : ∀ i, integrable_on f (Ioc (a i) (b i)) μ)
  (ha : tendsto a l at_bot) (hb : tendsto b l at_top)
  (h : ∀ᶠ i in l, ∫ x in a i .. b i, ‖f x‖ ∂μ ≤ I) :
  integrable f μ :=
begin
  have hφ : ae_cover μ l _ := ae_cover_Ioc ha hb,
  refine hφ.integrable_of_integral_norm_bounded I hfi (h.mp _),
  filter_upwards [ha.eventually (eventually_le_at_bot 0), hb.eventually (eventually_ge_at_top 0)]
    with i hai hbi ht,
  rwa ←interval_integral.integral_of_le (hai.trans hbi)
end

/-- If `f` is integrable on intervals `Ioc (a i) (b i)`,
where `a i` tends to -∞ and `b i` tends to ∞, and
`∫ x in a i .. b i, ‖f x‖ ∂μ` converges to `I : ℝ` along a filter `l`,
then `f` is integrable on the interval (-∞, ∞) -/
lemma integrable_of_interval_integral_norm_tendsto
  (I : ℝ) (hfi : ∀ i, integrable_on f (Ioc (a i) (b i)) μ)
  (ha : tendsto a l at_bot) (hb : tendsto b l at_top)
  (h : tendsto (λ i, ∫ x in a i .. b i, ‖f x‖ ∂μ) l (𝓝 I)) :
  integrable f μ :=
let ⟨I', hI'⟩ := h.is_bounded_under_le in
  integrable_of_interval_integral_norm_bounded I' hfi ha hb hI'

lemma integrable_on_Iic_of_interval_integral_norm_bounded (I b : ℝ)
  (hfi : ∀ i, integrable_on f (Ioc (a i) b) μ) (ha : tendsto a l at_bot)
  (h : ∀ᶠ i in l, (∫ x in a i .. b, ‖f x‖ ∂μ) ≤ I) :
  integrable_on f (Iic b) μ :=
begin
  have hφ : ae_cover (μ.restrict $ Iic b) l _ := ae_cover_Ioi ha,
  have hfi : ∀ i, integrable_on f (Ioi (a i)) (μ.restrict $ Iic b),
  { intro i,
    rw [integrable_on, measure.restrict_restrict (hφ.measurable i)],
    exact hfi i },
  refine hφ.integrable_of_integral_norm_bounded I hfi (h.mp _),
  filter_upwards [ha.eventually (eventually_le_at_bot b)] with i hai,
  rw [interval_integral.integral_of_le hai, measure.restrict_restrict (hφ.measurable i)],
  exact id
end

/-- If `f` is integrable on intervals `Ioc (a i) b`,
where `a i` tends to -∞, and
`∫ x in a i .. b, ‖f x‖ ∂μ` converges to `I : ℝ` along a filter `l`,
then `f` is integrable on the interval (-∞, b) -/
lemma integrable_on_Iic_of_interval_integral_norm_tendsto (I b : ℝ)
  (hfi : ∀ i, integrable_on f (Ioc (a i) b) μ) (ha : tendsto a l at_bot)
  (h : tendsto (λ i, ∫ x in a i .. b, ‖f x‖ ∂μ) l (𝓝 I)) :
  integrable_on f (Iic b) μ :=
let ⟨I', hI'⟩ := h.is_bounded_under_le in
  integrable_on_Iic_of_interval_integral_norm_bounded I' b hfi ha hI'

lemma integrable_on_Ioi_of_interval_integral_norm_bounded (I a : ℝ)
  (hfi : ∀ i, integrable_on f (Ioc a (b i)) μ) (hb : tendsto b l at_top)
  (h : ∀ᶠ i in l, (∫ x in a .. b i, ‖f x‖ ∂μ) ≤ I) :
  integrable_on f (Ioi a) μ :=
begin
  have hφ : ae_cover (μ.restrict $ Ioi a) l _ := ae_cover_Iic hb,
  have hfi : ∀ i, integrable_on f (Iic (b i)) (μ.restrict $ Ioi a),
  { intro i,
    rw [integrable_on, measure.restrict_restrict (hφ.measurable i), inter_comm],
    exact hfi i },
  refine hφ.integrable_of_integral_norm_bounded I hfi (h.mp _),
  filter_upwards [hb.eventually (eventually_ge_at_top a)] with i hbi,
  rw [interval_integral.integral_of_le hbi, measure.restrict_restrict (hφ.measurable i),
      inter_comm],
  exact id
end

/-- If `f` is integrable on intervals `Ioc a (b i)`,
where `b i` tends to ∞, and
`∫ x in a .. b i, ‖f x‖ ∂μ` converges to `I : ℝ` along a filter `l`,
then `f` is integrable on the interval (a, ∞) -/
lemma integrable_on_Ioi_of_interval_integral_norm_tendsto (I a : ℝ)
  (hfi : ∀ i, integrable_on f (Ioc a (b i)) μ) (hb : tendsto b l at_top)
  (h : tendsto (λ i, ∫ x in a .. b i, ‖f x‖ ∂μ) l (𝓝 $ I)) :
  integrable_on f (Ioi a) μ :=
let ⟨I', hI'⟩ := h.is_bounded_under_le in
  integrable_on_Ioi_of_interval_integral_norm_bounded I' a hfi hb hI'

lemma integrable_on_Ioc_of_interval_integral_norm_bounded {I a₀ b₀ : ℝ}
  (hfi : ∀ i, integrable_on f $ Ioc (a i) (b i))
  (ha : tendsto a l $ 𝓝 a₀) (hb : tendsto b l $ 𝓝 b₀)
  (h : ∀ᶠ i in l, (∫ x in Ioc (a i) (b i), ‖f x‖) ≤ I) : integrable_on f (Ioc a₀ b₀) :=
begin
  refine (ae_cover_Ioc_of_Ioc ha hb).integrable_of_integral_norm_bounded I
    (λ i, (hfi i).restrict measurable_set_Ioc) (eventually.mono h _),
  intros i hi, simp only [measure.restrict_restrict measurable_set_Ioc],
  refine le_trans (set_integral_mono_set (hfi i).norm _ _) hi,
  { apply ae_of_all, simp only [pi.zero_apply, norm_nonneg, forall_const] },
  { apply ae_of_all, intros c hc, exact hc.1 },
end

lemma integrable_on_Ioc_of_interval_integral_norm_bounded_left {I a₀ b : ℝ}
  (hfi : ∀ i, integrable_on f $ Ioc (a i) b) (ha : tendsto a l $ 𝓝 a₀)
  (h : ∀ᶠ i in l, (∫ x in Ioc (a i) b, ‖f x‖ ) ≤ I) : integrable_on f (Ioc a₀ b) :=
integrable_on_Ioc_of_interval_integral_norm_bounded hfi ha tendsto_const_nhds h

lemma integrable_on_Ioc_of_interval_integral_norm_bounded_right {I a b₀ : ℝ}
  (hfi : ∀ i, integrable_on f $ Ioc a (b i)) (hb : tendsto b l $ 𝓝 b₀)
  (h : ∀ᶠ i in l, (∫ x in Ioc a (b i), ‖f x‖ ) ≤ I) : integrable_on f (Ioc a b₀) :=
integrable_on_Ioc_of_interval_integral_norm_bounded hfi tendsto_const_nhds hb h

end integrable_of_interval_integral

section integral_of_interval_integral

variables {ι E : Type*} {μ : measure ℝ}
          {l : filter ι} [is_countably_generated l]
          [normed_add_comm_group E] [normed_space ℝ E] [complete_space E]
          {a b : ι → ℝ} {f : ℝ → E}

lemma interval_integral_tendsto_integral
  (hfi : integrable f μ) (ha : tendsto a l at_bot) (hb : tendsto b l at_top) :
  tendsto (λ i, ∫ x in a i .. b i, f x ∂μ) l (𝓝 $ ∫ x, f x ∂μ) :=
begin
  let φ := λ i, Ioc (a i) (b i),
  have hφ : ae_cover μ l φ := ae_cover_Ioc ha hb,
  refine (hφ.integral_tendsto_of_countably_generated hfi).congr' _,
  filter_upwards [ha.eventually (eventually_le_at_bot 0), hb.eventually (eventually_ge_at_top 0)]
    with i hai hbi,
  exact (interval_integral.integral_of_le (hai.trans hbi)).symm
end

lemma interval_integral_tendsto_integral_Iic (b : ℝ)
  (hfi : integrable_on f (Iic b) μ) (ha : tendsto a l at_bot) :
  tendsto (λ i, ∫ x in a i .. b, f x ∂μ) l (𝓝 $ ∫ x in Iic b, f x ∂μ) :=
begin
  let φ := λ i, Ioi (a i),
  have hφ : ae_cover (μ.restrict $ Iic b) l φ := ae_cover_Ioi ha,
  refine (hφ.integral_tendsto_of_countably_generated hfi).congr' _,
  filter_upwards [ha.eventually (eventually_le_at_bot $ b)] with i hai,
  rw [interval_integral.integral_of_le hai, measure.restrict_restrict (hφ.measurable i)],
  refl,
end

lemma interval_integral_tendsto_integral_Ioi (a : ℝ)
  (hfi : integrable_on f (Ioi a) μ) (hb : tendsto b l at_top) :
  tendsto (λ i, ∫ x in a .. b i, f x ∂μ) l (𝓝 $ ∫ x in Ioi a, f x ∂μ) :=
begin
  let φ := λ i, Iic (b i),
  have hφ : ae_cover (μ.restrict $ Ioi a) l φ := ae_cover_Iic hb,
  refine (hφ.integral_tendsto_of_countably_generated hfi).congr' _,
  filter_upwards [hb.eventually (eventually_ge_at_top $ a)] with i hbi,
  rw [interval_integral.integral_of_le hbi, measure.restrict_restrict (hφ.measurable i),
      inter_comm],
  refl,
end

end integral_of_interval_integral

section Ioi_change_variables

open real
open_locale interval

variables {E : Type*} {μ : measure ℝ} {f : ℝ → E}
  [normed_add_comm_group E] [normed_space ℝ E] [complete_space E]

/-- Change-of-variables formula for `Ioi` integrals of vector-valued functions, proved by taking
limits from the result for finite intervals. -/
lemma integral_comp_smul_deriv_Ioi {f f' : ℝ → ℝ} {g : ℝ → E} {a : ℝ}
  (hf : continuous_on f $ Ici a)
  (hft : tendsto f at_top at_top)
  (hff' : ∀ x ∈ Ioi a, has_deriv_within_at f (f' x) (Ioi x) x)
  (hg_cont : continuous_on g $ f '' Ioi a)
  (hg1 : integrable_on g $ f '' Ici a)
  (hg2 : integrable_on (λ x, f' x • (g ∘ f) x) (Ici a)) :
  ∫ x in Ioi a, f' x • (g ∘ f) x = ∫ u in Ioi (f a), g u :=
begin
  have eq : ∀ b : ℝ, a < b → ∫ x in a..b, f' x • (g ∘ f) x = ∫ u in f a .. f b, g u,
  { intros b hb,
    have i1 : Ioo (min a b) (max a b) ⊆ Ioi a,
    { rw min_eq_left hb.le, exact Ioo_subset_Ioi_self },
    have i2 : [a, b] ⊆ Ici a,
    { rw uIcc_of_le hb.le, exact Icc_subset_Ici_self },
    refine interval_integral.integral_comp_smul_deriv''' (hf.mono i2)
      (λ x hx, hff' x $ mem_of_mem_of_subset hx i1) (hg_cont.mono $ image_subset _ _)
      (hg1.mono_set $ image_subset _ _) (hg2.mono_set i2),
    { rw min_eq_left hb.le, exact Ioo_subset_Ioi_self },
    { rw uIcc_of_le hb.le, exact Icc_subset_Ici_self } },
  rw integrable_on_Ici_iff_integrable_on_Ioi at hg2,
  have t2 := interval_integral_tendsto_integral_Ioi _ hg2 tendsto_id,
  have : Ioi (f a) ⊆ f '' Ici a := (Ioi_subset_Ici_self.trans $
    is_preconnected.intermediate_value_Ici is_preconnected_Ici left_mem_Ici
    (le_principal_iff.mpr $ Ici_mem_at_top _) hf hft),
  have t1 := (interval_integral_tendsto_integral_Ioi _ (hg1.mono_set this) tendsto_id).comp hft,
  exact tendsto_nhds_unique (tendsto.congr' (eventually_eq_of_mem (Ioi_mem_at_top a) eq) t2) t1,
end

/-- Change-of-variables formula for `Ioi` integrals of scalar-valued functions -/
lemma integral_comp_mul_deriv_Ioi {f f' : ℝ → ℝ} {g : ℝ → ℝ} {a : ℝ}
  (hf : continuous_on f $ Ici a)
  (hft : tendsto f at_top at_top)
  (hff' : ∀ x ∈ Ioi a, has_deriv_within_at f (f' x) (Ioi x) x)
  (hg_cont : continuous_on g $ f '' Ioi a)
  (hg1 : integrable_on g $ f '' Ici a)
  (hg2 : integrable_on (λ x, (g ∘ f) x * f' x) (Ici a)) :
  ∫ x in Ioi a, (g ∘ f) x * f' x = ∫ u in Ioi (f a), g u :=
begin
  have hg2' : integrable_on (λ x, f' x • (g ∘ f) x) (Ici a) := by simpa [mul_comm] using hg2,
  simpa [mul_comm] using integral_comp_smul_deriv_Ioi hf hft hff' hg_cont hg1 hg2',
end

/-- Substitution `y = x ^ p` in integrals over `Ioi 0` -/
lemma integral_comp_rpow_Ioi (g : ℝ → E) {p : ℝ} (hp : p ≠ 0) :
  ∫ x in Ioi 0, (|p| * x ^ (p - 1)) • g (x ^ p) = ∫ y in Ioi 0, g y :=
begin
  let S := Ioi (0 : ℝ),
  have a1 : ∀ x:ℝ, x ∈ S → has_deriv_within_at (λ (t:ℝ), t ^ p) (p * x ^ (p - 1)) S x :=
    λ x hx, (has_deriv_at_rpow_const (or.inl (mem_Ioi.mp hx).ne')).has_deriv_within_at,
  have a2 : inj_on (λ x:ℝ, x ^ p) S,
  { rcases lt_or_gt_of_ne hp,
    { apply strict_anti_on.inj_on,
      intros x hx y hy hxy,
      rw [←inv_lt_inv (rpow_pos_of_pos hx p) (rpow_pos_of_pos hy p),
      ←rpow_neg (le_of_lt hx), ←rpow_neg (le_of_lt hy)],
      exact rpow_lt_rpow (le_of_lt hx) hxy (neg_pos.mpr h), },
    exact strict_mono_on.inj_on (λ x hx y hy hxy, rpow_lt_rpow (mem_Ioi.mp hx).le hxy h),},
  have a3 : (λ (t : ℝ), t ^ p) '' S = S,
  { ext1, rw mem_image, split,
    { rintro ⟨y, hy, rfl⟩, exact rpow_pos_of_pos hy p },
    { intro hx, refine ⟨x ^ (1 / p), rpow_pos_of_pos hx _, _⟩,
      rw [←rpow_mul (le_of_lt hx), one_div_mul_cancel hp, rpow_one], } },
  have := integral_image_eq_integral_abs_deriv_smul measurable_set_Ioi a1 a2 g,
  rw a3 at this, rw this,
  refine set_integral_congr measurable_set_Ioi _,
  intros x hx, dsimp only,
  rw [abs_mul, abs_of_nonneg (rpow_nonneg_of_nonneg (le_of_lt hx) _)],
end

lemma integral_comp_rpow_Ioi_of_pos {g : ℝ → E} {p : ℝ} (hp : 0 < p) :
  ∫ x in Ioi 0, (p * x ^ (p - 1)) • g (x ^ p) = ∫ y in Ioi 0, g y :=
begin
  convert integral_comp_rpow_Ioi g hp.ne',
  funext, congr, rw abs_of_nonneg hp.le,
end

end Ioi_change_variables

end measure_theory


/-
Copyright (c) 2021 Jordan Brown, Thomas Browning, Patrick Lutz. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jordan Brown, Thomas Browning, Patrick Lutz

! This file was ported from Lean 3 source module group_theory.solvable
! leanprover-community/mathlib commit f47581155c818e6361af4e4fda60d27d020c226b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Fin.VecNotation
import Mathbin.GroupTheory.Abelianization
import Mathbin.GroupTheory.Perm.ViaEmbedding
import Mathbin.GroupTheory.Subgroup.Simple
import Mathbin.SetTheory.Cardinal.Basic

/-!
# Solvable Groups

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

In this file we introduce the notion of a solvable group. We define a solvable group as one whose
derived series is eventually trivial. This requires defining the commutator of two subgroups and
the derived series of a group.

## Main definitions

* `derived_series G n` : the `n`th term in the derived series of `G`, defined by iterating
    `general_commutator` starting with the top subgroup
* `is_solvable G` : the group `G` is solvable
-/

open Subgroup

variable {G G' : Type _} [Group G] [Group G'] {f : G →* G'}

section derivedSeries

variable (G)

#print derivedSeries /-
/-- The derived series of the group `G`, obtained by starting from the subgroup `⊤` and repeatedly
  taking the commutator of the previous subgroup with itself for `n` times. -/
def derivedSeries : ℕ → Subgroup G
  | 0 => ⊤
  | n + 1 => ⁅derivedSeries n, derivedSeries n⁆
#align derived_series derivedSeries
-/

/- warning: derived_series_zero -> derivedSeries_zero is a dubious translation:
lean 3 declaration is
  forall (G : Type.{u1}) [_inst_1 : Group.{u1} G], Eq.{succ u1} (Subgroup.{u1} G _inst_1) (derivedSeries.{u1} G _inst_1 (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Top.top.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasTop.{u1} G _inst_1))
but is expected to have type
  forall (G : Type.{u1}) [_inst_1 : Group.{u1} G], Eq.{succ u1} (Subgroup.{u1} G _inst_1) (derivedSeries.{u1} G _inst_1 (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Top.top.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instTopSubgroup.{u1} G _inst_1))
Case conversion may be inaccurate. Consider using '#align derived_series_zero derivedSeries_zeroₓ'. -/
@[simp]
theorem derivedSeries_zero : derivedSeries G 0 = ⊤ :=
  rfl
#align derived_series_zero derivedSeries_zero

#print derivedSeries_succ /-
@[simp]
theorem derivedSeries_succ (n : ℕ) :
    derivedSeries G (n + 1) = ⁅derivedSeries G n, derivedSeries G n⁆ :=
  rfl
#align derived_series_succ derivedSeries_succ
-/

#print derivedSeries_normal /-
theorem derivedSeries_normal (n : ℕ) : (derivedSeries G n).Normal :=
  by
  induction' n with n ih
  · exact (⊤ : Subgroup G).normal_of_characteristic
  · exact Subgroup.commutator_normal (derivedSeries G n) (derivedSeries G n)
#align derived_series_normal derivedSeries_normal
-/

#print derivedSeries_one /-
@[simp]
theorem derivedSeries_one : derivedSeries G 1 = commutator G :=
  rfl
#align derived_series_one derivedSeries_one
-/

end derivedSeries

section CommutatorMap

section DerivedSeriesMap

variable (f)

/- warning: map_derived_series_le_derived_series -> map_derivedSeries_le_derivedSeries is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {G' : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Group.{u2} G'] (f : MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) (n : Nat), LE.le.{u2} (Subgroup.{u2} G' _inst_2) (Preorder.toLE.{u2} (Subgroup.{u2} G' _inst_2) (PartialOrder.toPreorder.{u2} (Subgroup.{u2} G' _inst_2) (SetLike.partialOrder.{u2, u2} (Subgroup.{u2} G' _inst_2) G' (Subgroup.setLike.{u2} G' _inst_2)))) (Subgroup.map.{u1, u2} G _inst_1 G' _inst_2 f (derivedSeries.{u1} G _inst_1 n)) (derivedSeries.{u2} G' _inst_2 n)
but is expected to have type
  forall {G : Type.{u1}} {G' : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Group.{u2} G'] (f : MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) (n : Nat), LE.le.{u2} (Subgroup.{u2} G' _inst_2) (Preorder.toLE.{u2} (Subgroup.{u2} G' _inst_2) (PartialOrder.toPreorder.{u2} (Subgroup.{u2} G' _inst_2) (CompleteSemilatticeInf.toPartialOrder.{u2} (Subgroup.{u2} G' _inst_2) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Subgroup.{u2} G' _inst_2) (Subgroup.instCompleteLatticeSubgroup.{u2} G' _inst_2))))) (Subgroup.map.{u1, u2} G _inst_1 G' _inst_2 f (derivedSeries.{u1} G _inst_1 n)) (derivedSeries.{u2} G' _inst_2 n)
Case conversion may be inaccurate. Consider using '#align map_derived_series_le_derived_series map_derivedSeries_le_derivedSeriesₓ'. -/
theorem map_derivedSeries_le_derivedSeries (n : ℕ) :
    (derivedSeries G n).map f ≤ derivedSeries G' n :=
  by
  induction' n with n ih
  · exact le_top
  · simp only [derivedSeries_succ, map_commutator, commutator_mono, ih]
#align map_derived_series_le_derived_series map_derivedSeries_le_derivedSeries

variable {f}

/- warning: derived_series_le_map_derived_series -> derivedSeries_le_map_derivedSeries is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {G' : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Group.{u2} G'] {f : MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))}, (Function.Surjective.{succ u1, succ u2} G G' (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) (fun (_x : MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) => G -> G') (MonoidHom.hasCoeToFun.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) f)) -> (forall (n : Nat), LE.le.{u2} (Subgroup.{u2} G' _inst_2) (Preorder.toLE.{u2} (Subgroup.{u2} G' _inst_2) (PartialOrder.toPreorder.{u2} (Subgroup.{u2} G' _inst_2) (SetLike.partialOrder.{u2, u2} (Subgroup.{u2} G' _inst_2) G' (Subgroup.setLike.{u2} G' _inst_2)))) (derivedSeries.{u2} G' _inst_2 n) (Subgroup.map.{u1, u2} G _inst_1 G' _inst_2 f (derivedSeries.{u1} G _inst_1 n)))
but is expected to have type
  forall {G : Type.{u2}} {G' : Type.{u1}} [_inst_1 : Group.{u2} G] [_inst_2 : Group.{u1} G'] {f : MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))}, (Function.Surjective.{succ u2, succ u1} G G' (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => G') _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G G' (MulOneClass.toMul.{u2} G (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)))) (MulOneClass.toMul.{u1} G' (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2))) (MonoidHom.monoidHomClass.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))))) f)) -> (forall (n : Nat), LE.le.{u1} (Subgroup.{u1} G' _inst_2) (Preorder.toLE.{u1} (Subgroup.{u1} G' _inst_2) (PartialOrder.toPreorder.{u1} (Subgroup.{u1} G' _inst_2) (CompleteSemilatticeInf.toPartialOrder.{u1} (Subgroup.{u1} G' _inst_2) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Subgroup.{u1} G' _inst_2) (Subgroup.instCompleteLatticeSubgroup.{u1} G' _inst_2))))) (derivedSeries.{u1} G' _inst_2 n) (Subgroup.map.{u2, u1} G _inst_1 G' _inst_2 f (derivedSeries.{u2} G _inst_1 n)))
Case conversion may be inaccurate. Consider using '#align derived_series_le_map_derived_series derivedSeries_le_map_derivedSeriesₓ'. -/
theorem derivedSeries_le_map_derivedSeries (hf : Function.Surjective f) (n : ℕ) :
    derivedSeries G' n ≤ (derivedSeries G n).map f :=
  by
  induction' n with n ih
  · exact (map_top_of_surjective f hf).ge
  · exact commutator_le_map_commutator ih ih
#align derived_series_le_map_derived_series derivedSeries_le_map_derivedSeries

/- warning: map_derived_series_eq -> map_derivedSeries_eq is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {G' : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Group.{u2} G'] {f : MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))}, (Function.Surjective.{succ u1, succ u2} G G' (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) (fun (_x : MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) => G -> G') (MonoidHom.hasCoeToFun.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) f)) -> (forall (n : Nat), Eq.{succ u2} (Subgroup.{u2} G' _inst_2) (Subgroup.map.{u1, u2} G _inst_1 G' _inst_2 f (derivedSeries.{u1} G _inst_1 n)) (derivedSeries.{u2} G' _inst_2 n))
but is expected to have type
  forall {G : Type.{u2}} {G' : Type.{u1}} [_inst_1 : Group.{u2} G] [_inst_2 : Group.{u1} G'] {f : MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))}, (Function.Surjective.{succ u2, succ u1} G G' (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => G') _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G G' (MulOneClass.toMul.{u2} G (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)))) (MulOneClass.toMul.{u1} G' (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2))) (MonoidHom.monoidHomClass.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))))) f)) -> (forall (n : Nat), Eq.{succ u1} (Subgroup.{u1} G' _inst_2) (Subgroup.map.{u2, u1} G _inst_1 G' _inst_2 f (derivedSeries.{u2} G _inst_1 n)) (derivedSeries.{u1} G' _inst_2 n))
Case conversion may be inaccurate. Consider using '#align map_derived_series_eq map_derivedSeries_eqₓ'. -/
theorem map_derivedSeries_eq (hf : Function.Surjective f) (n : ℕ) :
    (derivedSeries G n).map f = derivedSeries G' n :=
  le_antisymm (map_derivedSeries_le_derivedSeries f n) (derivedSeries_le_map_derivedSeries hf n)
#align map_derived_series_eq map_derivedSeries_eq

end DerivedSeriesMap

end CommutatorMap

section Solvable

variable (G)

#print IsSolvable /-
/-- A group `G` is solvable if its derived series is eventually trivial. We use this definition
  because it's the most convenient one to work with. -/
class IsSolvable : Prop where
  solvable : ∃ n : ℕ, derivedSeries G n = ⊥
#align is_solvable IsSolvable
-/

/- warning: is_solvable_def -> isSolvable_def is a dubious translation:
lean 3 declaration is
  forall (G : Type.{u1}) [_inst_1 : Group.{u1} G], Iff (IsSolvable.{u1} G _inst_1) (Exists.{1} Nat (fun (n : Nat) => Eq.{succ u1} (Subgroup.{u1} G _inst_1) (derivedSeries.{u1} G _inst_1 n) (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasBot.{u1} G _inst_1))))
but is expected to have type
  forall (G : Type.{u1}) [_inst_1 : Group.{u1} G], Iff (IsSolvable.{u1} G _inst_1) (Exists.{1} Nat (fun (n : Nat) => Eq.{succ u1} (Subgroup.{u1} G _inst_1) (derivedSeries.{u1} G _inst_1 n) (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instBotSubgroup.{u1} G _inst_1))))
Case conversion may be inaccurate. Consider using '#align is_solvable_def isSolvable_defₓ'. -/
theorem isSolvable_def : IsSolvable G ↔ ∃ n : ℕ, derivedSeries G n = ⊥ :=
  ⟨fun h => h.solvable, fun h => ⟨h⟩⟩
#align is_solvable_def isSolvable_def

#print CommGroup.isSolvable /-
instance (priority := 100) CommGroup.isSolvable {G : Type _} [CommGroup G] : IsSolvable G :=
  ⟨⟨1, le_bot_iff.mp (Abelianization.commutator_subset_ker (MonoidHom.id G))⟩⟩
#align comm_group.is_solvable CommGroup.isSolvable
-/

/- warning: is_solvable_of_comm -> isSolvable_of_comm is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [hG : Group.{u1} G], (forall (a : G) (b : G), Eq.{succ u1} G (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G hG))))) a b) (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G hG))))) b a)) -> (IsSolvable.{u1} G hG)
but is expected to have type
  forall {G : Type.{u1}} [hG : Group.{u1} G], (forall (a : G) (b : G), Eq.{succ u1} G (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G hG))))) a b) (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G hG))))) b a)) -> (IsSolvable.{u1} G hG)
Case conversion may be inaccurate. Consider using '#align is_solvable_of_comm isSolvable_of_commₓ'. -/
theorem isSolvable_of_comm {G : Type _} [hG : Group G] (h : ∀ a b : G, a * b = b * a) :
    IsSolvable G := by
  letI hG' : CommGroup G := { hG with mul_comm := h }
  cases hG
  exact CommGroup.isSolvable
#align is_solvable_of_comm isSolvable_of_comm

/- warning: is_solvable_of_top_eq_bot -> isSolvable_of_top_eq_bot is a dubious translation:
lean 3 declaration is
  forall (G : Type.{u1}) [_inst_1 : Group.{u1} G], (Eq.{succ u1} (Subgroup.{u1} G _inst_1) (Top.top.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasTop.{u1} G _inst_1)) (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasBot.{u1} G _inst_1))) -> (IsSolvable.{u1} G _inst_1)
but is expected to have type
  forall (G : Type.{u1}) [_inst_1 : Group.{u1} G], (Eq.{succ u1} (Subgroup.{u1} G _inst_1) (Top.top.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instTopSubgroup.{u1} G _inst_1)) (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instBotSubgroup.{u1} G _inst_1))) -> (IsSolvable.{u1} G _inst_1)
Case conversion may be inaccurate. Consider using '#align is_solvable_of_top_eq_bot isSolvable_of_top_eq_botₓ'. -/
theorem isSolvable_of_top_eq_bot (h : (⊤ : Subgroup G) = ⊥) : IsSolvable G :=
  ⟨⟨0, h⟩⟩
#align is_solvable_of_top_eq_bot isSolvable_of_top_eq_bot

#print isSolvable_of_subsingleton /-
instance (priority := 100) isSolvable_of_subsingleton [Subsingleton G] : IsSolvable G :=
  isSolvable_of_top_eq_bot G (by ext <;> simp at *)
#align is_solvable_of_subsingleton isSolvable_of_subsingleton
-/

variable {G}

/- warning: solvable_of_ker_le_range -> solvable_of_ker_le_range is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {G' : Type.{u2}} {G'' : Type.{u3}} [_inst_3 : Group.{u2} G'] [_inst_4 : Group.{u3} G''] (f : MonoidHom.{u2, u1} G' G (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_3))) (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (g : MonoidHom.{u1, u3} G G'' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u3} G'' (DivInvMonoid.toMonoid.{u3} G'' (Group.toDivInvMonoid.{u3} G'' _inst_4)))), (LE.le.{u1} (Subgroup.{u1} G _inst_1) (Preorder.toLE.{u1} (Subgroup.{u1} G _inst_1) (PartialOrder.toPreorder.{u1} (Subgroup.{u1} G _inst_1) (SetLike.partialOrder.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)))) (MonoidHom.ker.{u1, u3} G _inst_1 G'' (Monoid.toMulOneClass.{u3} G'' (DivInvMonoid.toMonoid.{u3} G'' (Group.toDivInvMonoid.{u3} G'' _inst_4))) g) (MonoidHom.range.{u2, u1} G' _inst_3 G _inst_1 f)) -> (forall [hG' : IsSolvable.{u2} G' _inst_3] [hG'' : IsSolvable.{u3} G'' _inst_4], IsSolvable.{u1} G _inst_1)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {G' : Type.{u3}} {G'' : Type.{u2}} [_inst_3 : Group.{u3} G'] [_inst_4 : Group.{u2} G''] (f : MonoidHom.{u3, u1} G' G (Monoid.toMulOneClass.{u3} G' (DivInvMonoid.toMonoid.{u3} G' (Group.toDivInvMonoid.{u3} G' _inst_3))) (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (g : MonoidHom.{u1, u2} G G'' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G'' (DivInvMonoid.toMonoid.{u2} G'' (Group.toDivInvMonoid.{u2} G'' _inst_4)))), (LE.le.{u1} (Subgroup.{u1} G _inst_1) (Preorder.toLE.{u1} (Subgroup.{u1} G _inst_1) (PartialOrder.toPreorder.{u1} (Subgroup.{u1} G _inst_1) (CompleteSemilatticeInf.toPartialOrder.{u1} (Subgroup.{u1} G _inst_1) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instCompleteLatticeSubgroup.{u1} G _inst_1))))) (MonoidHom.ker.{u1, u2} G _inst_1 G'' (Monoid.toMulOneClass.{u2} G'' (DivInvMonoid.toMonoid.{u2} G'' (Group.toDivInvMonoid.{u2} G'' _inst_4))) g) (MonoidHom.range.{u3, u1} G' _inst_3 G _inst_1 f)) -> (forall [hG' : IsSolvable.{u3} G' _inst_3] [hG'' : IsSolvable.{u2} G'' _inst_4], IsSolvable.{u1} G _inst_1)
Case conversion may be inaccurate. Consider using '#align solvable_of_ker_le_range solvable_of_ker_le_rangeₓ'. -/
theorem solvable_of_ker_le_range {G' G'' : Type _} [Group G'] [Group G''] (f : G' →* G)
    (g : G →* G'') (hfg : g.ker ≤ f.range) [hG' : IsSolvable G'] [hG'' : IsSolvable G''] :
    IsSolvable G := by
  obtain ⟨n, hn⟩ := id hG''
  obtain ⟨m, hm⟩ := id hG'
  refine' ⟨⟨n + m, le_bot_iff.mp (map_bot f ▸ hm ▸ _)⟩⟩
  clear hm
  induction' m with m hm
  ·
    exact
      f.range_eq_map ▸
        ((derivedSeries G n).map_eq_bot_iff.mp
              (le_bot_iff.mp ((map_derivedSeries_le_derivedSeries g n).trans hn.le))).trans
          hfg
  · exact commutator_le_map_commutator hm hm
#align solvable_of_ker_le_range solvable_of_ker_le_range

/- warning: solvable_of_solvable_injective -> solvable_of_solvable_injective is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {G' : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Group.{u2} G'] {f : MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))}, (Function.Injective.{succ u1, succ u2} G G' (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) (fun (_x : MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) => G -> G') (MonoidHom.hasCoeToFun.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) f)) -> (forall [h : IsSolvable.{u2} G' _inst_2], IsSolvable.{u1} G _inst_1)
but is expected to have type
  forall {G : Type.{u2}} {G' : Type.{u1}} [_inst_1 : Group.{u2} G] [_inst_2 : Group.{u1} G'] {f : MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))}, (Function.Injective.{succ u2, succ u1} G G' (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => G') _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G G' (MulOneClass.toMul.{u2} G (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)))) (MulOneClass.toMul.{u1} G' (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2))) (MonoidHom.monoidHomClass.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))))) f)) -> (forall [h : IsSolvable.{u1} G' _inst_2], IsSolvable.{u2} G _inst_1)
Case conversion may be inaccurate. Consider using '#align solvable_of_solvable_injective solvable_of_solvable_injectiveₓ'. -/
theorem solvable_of_solvable_injective (hf : Function.Injective f) [h : IsSolvable G'] :
    IsSolvable G :=
  solvable_of_ker_le_range (1 : G' →* G) f ((f.ker_eq_bot_iff.mpr hf).symm ▸ bot_le)
#align solvable_of_solvable_injective solvable_of_solvable_injective

/- warning: subgroup_solvable_of_solvable -> subgroup_solvable_of_solvable is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [h : IsSolvable.{u1} G _inst_1], IsSolvable.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) (Subgroup.toGroup.{u1} G _inst_1 H)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [h : IsSolvable.{u1} G _inst_1], IsSolvable.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H)) (Subgroup.toGroup.{u1} G _inst_1 H)
Case conversion may be inaccurate. Consider using '#align subgroup_solvable_of_solvable subgroup_solvable_of_solvableₓ'. -/
instance subgroup_solvable_of_solvable (H : Subgroup G) [h : IsSolvable G] : IsSolvable H :=
  solvable_of_solvable_injective H.subtype_injective
#align subgroup_solvable_of_solvable subgroup_solvable_of_solvable

/- warning: solvable_of_surjective -> solvable_of_surjective is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {G' : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Group.{u2} G'] {f : MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))}, (Function.Surjective.{succ u1, succ u2} G G' (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) (fun (_x : MonoidHom.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) => G -> G') (MonoidHom.hasCoeToFun.{u1, u2} G G' (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} G' (DivInvMonoid.toMonoid.{u2} G' (Group.toDivInvMonoid.{u2} G' _inst_2)))) f)) -> (forall [h : IsSolvable.{u1} G _inst_1], IsSolvable.{u2} G' _inst_2)
but is expected to have type
  forall {G : Type.{u2}} {G' : Type.{u1}} [_inst_1 : Group.{u2} G] [_inst_2 : Group.{u1} G'] {f : MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))}, (Function.Surjective.{succ u2, succ u1} G G' (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => G') _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G G' (MulOneClass.toMul.{u2} G (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)))) (MulOneClass.toMul.{u1} G' (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))) G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2))) (MonoidHom.monoidHomClass.{u2, u1} G G' (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} G' (DivInvMonoid.toMonoid.{u1} G' (Group.toDivInvMonoid.{u1} G' _inst_2)))))) f)) -> (forall [h : IsSolvable.{u2} G _inst_1], IsSolvable.{u1} G' _inst_2)
Case conversion may be inaccurate. Consider using '#align solvable_of_surjective solvable_of_surjectiveₓ'. -/
theorem solvable_of_surjective (hf : Function.Surjective f) [h : IsSolvable G] : IsSolvable G' :=
  solvable_of_ker_le_range f (1 : G' →* G) ((f.range_top_of_surjective hf).symm ▸ le_top)
#align solvable_of_surjective solvable_of_surjective

#print solvable_quotient_of_solvable /-
instance solvable_quotient_of_solvable (H : Subgroup G) [H.Normal] [h : IsSolvable G] :
    IsSolvable (G ⧸ H) :=
  solvable_of_surjective (QuotientGroup.mk'_surjective H)
#align solvable_quotient_of_solvable solvable_quotient_of_solvable
-/

/- warning: solvable_prod -> solvable_prod is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {G' : Type.{u2}} [_inst_3 : Group.{u2} G'] [h : IsSolvable.{u1} G _inst_1] [h' : IsSolvable.{u2} G' _inst_3], IsSolvable.{max u1 u2} (Prod.{u1, u2} G G') (Prod.group.{u1, u2} G G' _inst_1 _inst_3)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {G' : Type.{u2}} [_inst_3 : Group.{u2} G'] [h : IsSolvable.{u1} G _inst_1] [h' : IsSolvable.{u2} G' _inst_3], IsSolvable.{max u2 u1} (Prod.{u1, u2} G G') (Prod.instGroupProd.{u1, u2} G G' _inst_1 _inst_3)
Case conversion may be inaccurate. Consider using '#align solvable_prod solvable_prodₓ'. -/
instance solvable_prod {G' : Type _} [Group G'] [h : IsSolvable G] [h' : IsSolvable G'] :
    IsSolvable (G × G') :=
  solvable_of_ker_le_range (MonoidHom.inl G G') (MonoidHom.snd G G') fun x hx =>
    ⟨x.1, Prod.ext rfl hx.symm⟩
#align solvable_prod solvable_prod

end Solvable

section IsSimpleGroup

variable [IsSimpleGroup G]

#print IsSimpleGroup.derivedSeries_succ /-
theorem IsSimpleGroup.derivedSeries_succ {n : ℕ} : derivedSeries G n.succ = commutator G :=
  by
  induction' n with n ih
  · exact derivedSeries_one G
  rw [derivedSeries_succ, ih]
  cases' (commutator.normal G).eq_bot_or_eq_top with h h
  · rw [h, commutator_bot_left]
  · rwa [h]
#align is_simple_group.derived_series_succ IsSimpleGroup.derivedSeries_succ
-/

/- warning: is_simple_group.comm_iff_is_solvable -> IsSimpleGroup.comm_iff_isSolvable is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] [_inst_3 : IsSimpleGroup.{u1} G _inst_1], Iff (forall (a : G) (b : G), Eq.{succ u1} G (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) a b) (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) b a)) (IsSolvable.{u1} G _inst_1)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] [_inst_3 : IsSimpleGroup.{u1} G _inst_1], Iff (forall (a : G) (b : G), Eq.{succ u1} G (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) a b) (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) b a)) (IsSolvable.{u1} G _inst_1)
Case conversion may be inaccurate. Consider using '#align is_simple_group.comm_iff_is_solvable IsSimpleGroup.comm_iff_isSolvableₓ'. -/
theorem IsSimpleGroup.comm_iff_isSolvable : (∀ a b : G, a * b = b * a) ↔ IsSolvable G :=
  ⟨isSolvable_of_comm, fun ⟨⟨n, hn⟩⟩ => by
    cases n
    · intro a b
      refine' (mem_bot.1 _).trans (mem_bot.1 _).symm <;>
        · rw [← hn]
          exact mem_top _
    · rw [IsSimpleGroup.derivedSeries_succ] at hn
      intro a b
      rw [← mul_inv_eq_one, mul_inv_rev, ← mul_assoc, ← mem_bot, ← hn, commutator_eq_closure]
      exact subset_closure ⟨a, b, rfl⟩⟩
#align is_simple_group.comm_iff_is_solvable IsSimpleGroup.comm_iff_isSolvable

end IsSimpleGroup

section PermNotSolvable

/- warning: not_solvable_of_mem_derived_series -> not_solvable_of_mem_derivedSeries is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {g : G}, (Ne.{succ u1} G g (OfNat.ofNat.{u1} G 1 (OfNat.mk.{u1} G 1 (One.one.{u1} G (MulOneClass.toHasOne.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))))))) -> (forall (n : Nat), Membership.Mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) g (derivedSeries.{u1} G _inst_1 n)) -> (Not (IsSolvable.{u1} G _inst_1))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {g : G}, (Ne.{succ u1} G g (OfNat.ofNat.{u1} G 1 (One.toOfNat1.{u1} G (InvOneClass.toOne.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1))))))) -> (forall (n : Nat), Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) g (derivedSeries.{u1} G _inst_1 n)) -> (Not (IsSolvable.{u1} G _inst_1))
Case conversion may be inaccurate. Consider using '#align not_solvable_of_mem_derived_series not_solvable_of_mem_derivedSeriesₓ'. -/
theorem not_solvable_of_mem_derivedSeries {g : G} (h1 : g ≠ 1)
    (h2 : ∀ n : ℕ, g ∈ derivedSeries G n) : ¬IsSolvable G :=
  mt (isSolvable_def _).mp
    (not_exists_of_forall_not fun n h =>
      h1 (Subgroup.mem_bot.mp ((congr_arg (Membership.Mem g) h).mp (h2 n))))
#align not_solvable_of_mem_derived_series not_solvable_of_mem_derivedSeries

#print Equiv.Perm.fin_5_not_solvable /-
theorem Equiv.Perm.fin_5_not_solvable : ¬IsSolvable (Equiv.Perm (Fin 5)) :=
  by
  let x : Equiv.Perm (Fin 5) := ⟨![1, 2, 0, 3, 4], ![2, 0, 1, 3, 4], by decide, by decide⟩
  let y : Equiv.Perm (Fin 5) := ⟨![3, 4, 2, 0, 1], ![3, 4, 2, 0, 1], by decide, by decide⟩
  let z : Equiv.Perm (Fin 5) := ⟨![0, 3, 2, 1, 4], ![0, 3, 2, 1, 4], by decide, by decide⟩
  have key : x = z * ⁅x, y * x * y⁻¹⁆ * z⁻¹ := by decide
  refine' not_solvable_of_mem_derivedSeries (show x ≠ 1 by decide) fun n => _
  induction' n with n ih
  · exact mem_top x
  · rw [key, (derivedSeries_normal _ _).mem_comm_iff, inv_mul_cancel_left]
    exact commutator_mem_commutator ih ((derivedSeries_normal _ _).conj_mem _ ih _)
#align equiv.perm.fin_5_not_solvable Equiv.Perm.fin_5_not_solvable
-/

/- warning: equiv.perm.not_solvable -> Equiv.Perm.not_solvable is a dubious translation:
lean 3 declaration is
  forall (X : Type.{u1}), (LE.le.{succ u1} Cardinal.{u1} Cardinal.hasLe.{u1} (OfNat.ofNat.{succ u1} Cardinal.{u1} 5 (OfNat.mk.{succ u1} Cardinal.{u1} 5 (bit1.{succ u1} Cardinal.{u1} Cardinal.hasOne.{u1} Cardinal.hasAdd.{u1} (bit0.{succ u1} Cardinal.{u1} Cardinal.hasAdd.{u1} (One.one.{succ u1} Cardinal.{u1} Cardinal.hasOne.{u1}))))) (Cardinal.mk.{u1} X)) -> (Not (IsSolvable.{u1} (Equiv.Perm.{succ u1} X) (Equiv.Perm.permGroup.{u1} X)))
but is expected to have type
  forall (X : Type.{u1}), (LE.le.{succ u1} Cardinal.{u1} Cardinal.instLECardinal.{u1} (OfNat.ofNat.{succ u1} Cardinal.{u1} 5 (instOfNat.{succ u1} Cardinal.{u1} 5 Cardinal.instNatCastCardinal.{u1} (instAtLeastTwoHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 3 (instOfNatNat 3))))) (Cardinal.mk.{u1} X)) -> (Not (IsSolvable.{u1} (Equiv.Perm.{succ u1} X) (Equiv.Perm.permGroup.{u1} X)))
Case conversion may be inaccurate. Consider using '#align equiv.perm.not_solvable Equiv.Perm.not_solvableₓ'. -/
theorem Equiv.Perm.not_solvable (X : Type _) (hX : 5 ≤ Cardinal.mk X) :
    ¬IsSolvable (Equiv.Perm X) := by
  intro h
  have key : Nonempty (Fin 5 ↪ X) := by
    rwa [← Cardinal.lift_mk_le, Cardinal.mk_fin, Cardinal.lift_natCast, Nat.cast_bit1,
      Nat.cast_bit0, Nat.cast_one, Cardinal.lift_id]
  exact
    Equiv.Perm.fin_5_not_solvable
      (solvable_of_solvable_injective (Equiv.Perm.viaEmbeddingHom_injective (Nonempty.some key)))
#align equiv.perm.not_solvable Equiv.Perm.not_solvable

end PermNotSolvable


-- Author(s): Andrés Goens
-- See Copyright Notice in LICENSE

import Lean
import Std
open Std.HashMap
namespace Util

theorem n_minus_one_le_n {n : Nat} : n > 0 → n - 1 < n := by
  cases n with
  | zero => simp []
  | succ n =>
    intros
    rw [Nat.succ_eq_add_one, Nat.add_sub_cancel]
    apply Nat.le.refl

def filterNones {α : Type} : List (Option α) → List α
  | none::rest => filterNones rest
  | (some val):: rest => val::(filterNones rest)
  | [] => []

def Array.sum : Array Nat → Nat
  | arr => arr.foldl (init := 0) (· + ·)

def filterNonesArr {α : Type} : Array (Option α) → Array α
  | arr => arr.toList |> filterNones |>.toArray

def blesort : List Nat → List Nat
  | as => Array.toList $ Array.qsort as.toArray (λ x y => Nat.ble x y)

-- TODO: probably horribly slow!
def alphabetic : String → String → Bool
  | ⟨a::as⟩, ⟨b::bs⟩ => a < b || (a == b) && alphabetic ⟨as⟩ ⟨bs⟩
  | ⟨[]⟩, _ => true
  | ⟨_::_⟩, _ => false

def lexBLt : Nat × Nat → Nat × Nat → Bool
  | (n₁,n₂), (m₁,m₂) => Nat.blt n₁ m₁ || ((n₁ == m₁) && Nat.blt n₂ m₂)

def lexBLe : Nat × Nat → Nat × Nat → Bool
| n, m => lexBLt n m || n == m

def _root_.List.maximum : List Nat → Nat
  | a::[] => a
  | a::rest => max a (rest.maximum)
  | [] => 0

partial def removeDuplicates [BEq α] : List α → List α
  | [] => []
  | (x :: xs) => x :: removeDuplicates (xs.filter (λ y => y != x))

def _root_.List.sublist [BEq α] : List α → List α → Bool
  | l₁, l₂ => l₁.all (λ e => l₂.elem e)

def _root_.List.setEq [BEq α] : List α → List α → Bool
  | l₁, l₂ => l₁.sublist l₂ && l₂.sublist l₁

private def countOcurrencesAux [BEq α] : List α → List (α × Nat)
  | [] => []
  | x::xs =>
    let rest := countOcurrencesAux xs
    match rest.lookup x with
    | some num => (x, num + 1)::rest
    | none => (x,1)::rest

def _root_.List.countOcurrences [BEq α] : List α → List (α × Nat)
  | ls => countOcurrencesAux ls.reverse |>.reverse

def setJoinPair [BEq α] (l₁ l₂ : List α) : List α :=
  match l₁, l₂ with
  | l₁, [] => l₁
  | [], l₂ => l₂
  | a::as, b::bs => match as.contains b, bs.contains a with
    | true, true => setJoinPair as bs
    | false, true => b :: setJoinPair as bs
    | true, false => a :: setJoinPair as bs
    | false, false => a :: b :: setJoinPair as bs

def setJoin [BEq α] (ls : List (List α)) : List α :=
  ls.foldl (init := []) setJoinPair

def _root_.List.intersection [BEq α] (ls ls' : List α) : List α :=
  match ls with
    | [] => []
    | l::rest => if ls'.contains l
      then l::(intersection rest ls')
      else intersection rest ls'

inductive Color
  | red : Color
  | green : Color
  | blue : Color
  | yellow : Color
  | black : Color
  | magenta : Color
  | cyan : Color
  | white : Color

def Color.toNat : Color → Nat
 | .black   => 0
 | .red     => 1
 | .green   => 2
 | .yellow  => 3
 | .blue    => 4
 | .magenta => 5
 | .cyan    => 6
 | .white   => 7

def Color.isBright : Color → Bool
 | .black => false
 | .red     => false
 | .green   => true
 | .yellow  => true
 | .blue    => false
 | .magenta => true --?
 | .cyan    => true --?
 | .white   => true

instance : Inhabited Color where default := Color.black

def colorString : Color → String → String :=
  λ color str =>
    let textColor := if color.isBright then Color.black else Color.white
    s!"\x1b[3{textColor.toNat};4{color.toNat}m" ++ str ++ "\u001b[0m"

-- Removed sublist condition from type as it made programming
-- with this basically impossible...
inductive ListTree (α : Type) [BEq α] : Type
  | leaf : List α → ListTree α
  | parentNil : List α → ListTree α
  | parentCons : ListTree α → ListTree α → ListTree α
  deriving Repr

open Lean in
private def quoteListTree [Quote α] [BEq α] : ListTree α → Term
  | .leaf a => Syntax.mkCApp ``ListTree.leaf #[quote a]
  | .parentNil a => Syntax.mkCApp ``ListTree.parentNil #[quote a]
  | .parentCons child sibs => Syntax.mkCApp ``ListTree.parentCons
    #[quoteListTree child, quoteListTree sibs]

instance {α : Type} [BEq α] [Lean.Quote α] : Lean.Quote (ListTree α) where quote := quoteListTree

def ListTree.beq {α : Type} [BEq α] : ListTree α → ListTree α → Bool
  | .leaf a, .leaf b => a.setEq b
  | .parentNil a, .parentNil b => a.setEq b
  | .parentCons child₁ sibs₁, .parentCons child₂ sibs₂ => (beq child₁ child₂) && (beq sibs₁ sibs₂)
  | _, _ => false

instance {α : Type} [BEq α]  : BEq (ListTree α) where beq := ListTree.beq

-- TODO: this does not check it is well-formed...
def ListTree.listType [BEq α] : ListTree α  → List α
  | leaf l => l
  | parentNil l => l
  | parentCons _ siblings => listType siblings

def ListTree.mkParent {α : Type} [BEq α]
  (parent : List α) (children : List (ListTree α)) : Except String (ListTree α) := do
  let parentType := setJoin $ children.map ListTree.listType
  unless parentType.sublist parent do
    throw s!"Error appending. It has a non-sublist child."
  let parent := ListTree.parentNil parent
  return children.foldl (init := parent) λ sib ch => ListTree.parentCons ch sib

def Array.pmap : (α → β) → Array α → Array β
  | f, as =>
    let ts := as.map λ a => Task.spawn (λ _ => f a);
    let rs := ts.map Task.get;
    rs

open ListTree

def ListTree.elem [BEq α] :  List α → ListTree α → Bool
| val, leaf val' => val == val'
| val, parentNil val' => val == val'
| val, parentCons child siblings => elem val child || elem val siblings

instance [BEq α] : Membership (List α) (ListTree α) where
  mem lst tree := tree.elem lst = true

def ListTree.leaves [BEq α] :  ListTree α → List (List α)
  | leaf val => [val]
  | parentNil _ => []
  | parentCons child siblings => leaves child ++ leaves siblings

def ListTree.toList [BEq α] :  ListTree α → List (List α)
  | leaf val => [val]
  | parentNil val => [val]
  | parentCons child siblings => toList child ++ toList siblings

def ListTree.nodesAbove [BEq α] (lt :  ListTree α) (lst : List α) : List (List α) :=
  match lt with
  | leaf val => if List.sublist lst val then [val] else []
  | parentNil val => if List.sublist lst val then [val] else []
  | parentCons child siblings => (nodesAbove child lst) ++ (nodesAbove siblings lst)

def ListTree.nodesBelow [BEq α] (lt :  ListTree α) (lst : List α) : List (List α) :=
  match lt with
  | leaf val => if List.sublist val lst then [val] else []
  | parentNil val => if List.sublist val lst then [val] else []
  | parentCons child siblings => (nodesBelow child lst) ++ (nodesBelow siblings lst)

def ListTree.meet [BEq α] : ListTree α → α → α → Option (List α)
  | leaf val, a, b => if (val.elem a && val.elem b) then (some val) else none
  | parentNil val, a, b => if (val.elem a && val.elem b) then (some val) else none
  | parentCons child siblings, a, b =>
    let childRes := meet child a b
    match childRes with
      | res@(some _) => res
      | none => meet siblings a b

private def ListTree.toStringAux [BEq α] [ToString α] : ListTree α → String
   | .leaf val => String.intercalate ", " (val.map ToString.toString)
   | .parentNil val => String.intercalate ", " (val.map ToString.toString)
   | .parentCons child parent@(parentNil _) => "[" ++
     (toStringAux parent) ++ "] = [" ++ (toStringAux child) ++ "]"
   | .parentCons child siblings => (toStringAux siblings) ++ ", ["
     ++ (toStringAux child) ++ "]"

def ListTree.toString [BEq α] [ToString α] (lt : ListTree α) : String :=
  "[" ++ toStringAux lt ++ "]"

instance [BEq α] [ToString α] : ToString (ListTree α) where toString := ListTree.toString

/-
theorem List.sublist_trans [BEq α] (a b c : List α) : sublist a b → sublist b c → sublist a c := by
  intros hab hbc
  induction a <;> induction b <;> induction c <;> simp [List.sublist, List.all, List.foldr, List.elem] <;> try contradiction
  sorry  -- TODO
-/

def uncurry {α β γ : Type} (f : α → β → γ) (ab : α × β) : γ := match ab with | (a,b) => f a b

structure Triple (α β γ : Type) where
 fst : α
 snd : β
 trd : γ

notation "(" a "," b "," c ")t" => Triple.mk a b c

def TupleTuple2Triple {α β γ : Type} : (α × β) × γ → Triple α β γ
 | ((a, b), c) => (a,b,c)t

def TupleTuple'2Triple {α β γ : Type} : α × (β × γ) → Triple α β γ
 | (a, (b, c)) => (a,b,c)t

instance {α β γ : Type} : Coe ((α × β) × γ)  (Triple α β γ) where coe := TupleTuple2Triple
instance {α β γ : Type} : Coe (α × (β × γ))  (Triple α β γ) where coe := TupleTuple'2Triple
instance {α β γ : Type} [Inhabited α] [Inhabited β] [Inhabited γ] : Inhabited (Triple α β γ) where default:= (default,default,default)t
-- def ListTree.joinSub [BEq α] {l₁ l₂: List α} (h : List.sublist l₁ l₂) : ListTree α l₁ → ListTree α l₂ → ListTree α l₂
--   | (leaf l₁), (parentNil l₂) => parentCons (leaf l₁) (parentNil l₂) h
--   | (leaf l₁), (leaf l₂) => parentCons (leaf l₁) (parentNil l₂) h
--   | (leaf l₁), parentCons (children : ListTree α child) (siblings : ListTree α l₂) sub =>
--     let hchild := List.sublist_trans sub
--
--   parentCons (joinSub sub (leaf l₁) children) siblings sub

-- def ListTree.mkAux [BEq α] {l₁ l₂ : List α} : List (List α) → ListTree α l₁ → ListTree α l₂
--   --| lists, subtree => subtree
--   sorry
--
-- def ListTree.mk [BEq α] {l : List α} : List (List α) → ListTree α l
--   | lists =>
--     let sorted := lists.toArray.qsort List.sublist
--     match sorted.reverse.toList with
--       | [] => leaf []
--       | head :: rest => parentNil head

def exceptIO  {α : Type} [Inhabited α] : Except String α → IO α
  | exAlpha => match exAlpha with
    | Except.ok a => return a
    | Except.error msg => do
      IO.println msg
      return default

def selectLoop {α : Type 0} : String → (String → Except String α) → IO.FS.Stream → IO (Option α)
  | selectionString, selectFun, inputStream => do
  let mut selected := Except.error "unread"
  while !selected.isOk do
    IO.println selectionString
    let input <- inputStream.getLine
    if input.length == 0 then
      return none
    selected := selectFun input
    if let Except.error msg := selected then
      IO.println s!"Error: {msg}"
  match selected with
    | .ok a => return (some a)
    | _ => return none

def _root_.List.unique {α : Type 0} [BEq α] : List α → List α
  | [] => []
  | a :: as => if as.contains a then as.unique else (a :: as.unique)

def _root_.List.containsSet {α : Type 0} [BEq α] : List (List α) → List α → Bool
  | head::tail, l => head.setEq l || tail.containsSet l
  | [], _ => false

def _root_.List.uniqueSet {α : Type 0} [BEq α] : List (List α) → List (List α)
  | [] => []
  | a :: as => if as.containsSet a then as.uniqueSet else (a :: as.uniqueSet)

def _root_.List.revlookup [BEq β] : β → List (α × β) → Option α
  | _, []        => none
  | a, (k,b)::es => match a == b with
    | true  => some k
    | false => revlookup a es

structure ScopedBinaryRelation (α β : Type 0) [Hashable α] [BEq α] [Hashable β] [BEq β] where
  val : Std.HashMap (α × β × β) Bool
  defaultRes : Bool

variable {α β : Type} [Hashable α] [BEq α] [Hashable β] [BEq β]
def ScopedBinaryRelation.default : ScopedBinaryRelation α β := ScopedBinaryRelation.mk (Std.mkHashMap) false

instance : Inhabited (ScopedBinaryRelation α β) where default := ScopedBinaryRelation.default

def ScopedBinaryRelation.update : ScopedBinaryRelation α β → α → β → β → ScopedBinaryRelation α β
  | rel, s, x,y =>
    let val' := rel.val.insert (s,(x,y)) true
    { rel with val := val'}

def ScopedBinaryRelation.lookup : ScopedBinaryRelation α β → α → β → β → Bool
  | rel, s, x, y => match rel.val[(s,(x,y))] with
    | some res => res
    | none => rel.defaultRes

notation rel "[" s "," x "," y "]:=" val => ScopedBinaryRelation.update rel s x y val

def ltest := ListTree.leaf [1,2,4]
def ltest2 := ListTree.parentCons ltest (ListTree.parentNil [1,2,3,4])
def ltest3 := ListTree.parentCons (ListTree.leaf [1,4]) ltest2

#eval ListTree.elem [1,2,4] ltest
#eval ListTree.elem [1,2,4] ltest2
#eval ListTree.elem [1,2,3,4] ltest2
#eval ListTree.elem [1,3,4] ltest2
#eval ListTree.elem [1,3,4] ltest3
#eval ListTree.elem [1,2,4] ltest2
#eval ltest2.leaves
#eval ltest3.leaves
#eval ltest3.meet 1 2
#eval ltest3.meet 1 4
#eval ltest3.meet 1 3
#eval ltest3.meet 1 5

end Util


/-
Copyright 2022 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Authors: Moritz Firsching
-/
import tactic
import analysis.special_functions.pow
import topology.algebra.polynomial
open complex polynomial
open_locale classical
/-!
# The fundamental theorem of algebra

## TODO
  - compare analysis.complex.polynomial
  - statment
    - proof
      - (A)
      - (B)
      - (C)
    - Lemma
      - proof
-/
-- TODO: check if this definition already exists
def polynomial.constant (f : polynomial ℂ) : Prop := (0 < degree f)

theorem fundamental_theorem_of_algebra (f : polynomial ℂ) (h : polynomial.constant f) :
  ∃ z : ℂ, is_root f z :=
begin
  rw polynomial.constant at h,
  sorry,
end