/-
Copyright (c) 2021 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov
-/
import topology.metric_space.lipschitz
import analysis.special_functions.pow

/-!
# Hölder continuous functions

In this file we define Hölder continuity on a set and on the whole space. We also prove some basic
properties of Hölder continuous functions.

## Main definitions

* `holder_on_with`: `f : X → Y` is said to be *Hölder continuous* with constant `C : ℝ≥0` and
  exponent `r : ℝ≥0` on a set `s`, if `edist (f x) (f y) ≤ C * edist x y ^ r` for all `x y ∈ s`;
* `holder_with`: `f : X → Y` is said to be *Hölder continuous* with constant `C : ℝ≥0` and exponent
  `r : ℝ≥0`, if `edist (f x) (f y) ≤ C * edist x y ^ r` for all `x y : X`.

## Implementation notes

We use the type `ℝ≥0` (a.k.a. `nnreal`) for `C` because this type has coercion both to `ℝ` and
`ℝ≥0∞`, so it can be easily used both in inequalities about `dist` and `edist`. We also use `ℝ≥0`
for `r` to ensure that `d ^ r` is monotone in `d`. It might be a good idea to use
`ℝ>0` for `r` but we don't have this type in `mathlib` (yet).

## Tags

Hölder continuity, Lipschitz continuity

 -/

variables {X Y Z : Type*}

open filter set
open_locale nnreal ennreal topology

section emetric

variables [pseudo_emetric_space X] [pseudo_emetric_space Y] [pseudo_emetric_space Z]

/-- A function `f : X → Y` between two `pseudo_emetric_space`s is Hölder continuous with constant
`C : ℝ≥0` and exponent `r : ℝ≥0`, if `edist (f x) (f y) ≤ C * edist x y ^ r` for all `x y : X`. -/
def holder_with (C r : ℝ≥0) (f : X → Y) : Prop :=
∀ x y, edist (f x) (f y) ≤ C * edist x y ^ (r : ℝ)

/-- A function `f : X → Y` between two `pseudo_emeteric_space`s is Hölder continuous with constant
`C : ℝ≥0` and exponent `r : ℝ≥0` on a set `s : set X`, if `edist (f x) (f y) ≤ C * edist x y ^ r`
for all `x y ∈ s`. -/
def holder_on_with (C r : ℝ≥0) (f : X → Y) (s : set X) : Prop :=
∀ (x ∈ s) (y ∈ s), edist (f x) (f y) ≤ C * edist x y ^ (r : ℝ)

@[simp] lemma holder_on_with_empty (C r : ℝ≥0) (f : X → Y) : holder_on_with C r f ∅ :=
λ x hx, hx.elim

@[simp] lemma holder_on_with_singleton (C r : ℝ≥0) (f : X → Y) (x : X) : holder_on_with C r f {x} :=
by { rintro a (rfl : a = x) b (rfl : b = a), rw edist_self, exact zero_le _ }

lemma set.subsingleton.holder_on_with {s : set X} (hs : s.subsingleton) (C r : ℝ≥0) (f : X → Y) :
  holder_on_with C r f s :=
hs.induction_on (holder_on_with_empty C r f) (holder_on_with_singleton C r f)

lemma holder_on_with_univ {C r : ℝ≥0} {f : X → Y} :
  holder_on_with C r f univ ↔ holder_with C r f :=
by simp only [holder_on_with, holder_with, mem_univ, true_implies_iff]

@[simp] lemma holder_on_with_one {C : ℝ≥0} {f : X → Y} {s : set X} :
  holder_on_with C 1 f s ↔ lipschitz_on_with C f s :=
by simp only [holder_on_with, lipschitz_on_with, nnreal.coe_one, ennreal.rpow_one]

alias holder_on_with_one ↔ _ lipschitz_on_with.holder_on_with

@[simp] lemma holder_with_one {C : ℝ≥0} {f : X → Y} :
  holder_with C 1 f ↔ lipschitz_with C f :=
holder_on_with_univ.symm.trans $ holder_on_with_one.trans lipschitz_on_univ

alias holder_with_one ↔ _ lipschitz_with.holder_with

lemma holder_with_id : holder_with 1 1 (id : X → X) :=
lipschitz_with.id.holder_with

protected lemma holder_with.holder_on_with {C r : ℝ≥0} {f : X → Y} (h : holder_with C r f)
  (s : set X) :
  holder_on_with C r f s :=
λ x _ y _, h x y

namespace holder_on_with

variables {C r : ℝ≥0} {f : X → Y} {s t : set X}

lemma edist_le (h : holder_on_with C r f s) {x y : X} (hx : x ∈ s) (hy : y ∈ s) :
  edist (f x) (f y) ≤ C * edist x y ^ (r : ℝ) :=
h x hx y hy

lemma edist_le_of_le (h : holder_on_with C r f s) {x y : X} (hx : x ∈ s) (hy : y ∈ s)
  {d : ℝ≥0∞} (hd : edist x y ≤ d) :
  edist (f x) (f y) ≤ C * d ^ (r : ℝ) :=
(h.edist_le hx hy).trans (mul_le_mul_left' (ennreal.rpow_le_rpow hd r.coe_nonneg) _)

lemma comp {Cg rg : ℝ≥0} {g : Y → Z} {t : set Y} (hg : holder_on_with Cg rg g t)
  {Cf rf : ℝ≥0} {f : X → Y} (hf : holder_on_with Cf rf f s) (hst : maps_to f s t) :
  holder_on_with (Cg * Cf ^ (rg : ℝ)) (rg * rf) (g ∘ f) s :=
begin
  intros x hx y hy,
  rw [ennreal.coe_mul, mul_comm rg, nnreal.coe_mul, ennreal.rpow_mul, mul_assoc,
    ← ennreal.coe_rpow_of_nonneg _ rg.coe_nonneg, ← ennreal.mul_rpow_of_nonneg _ _ rg.coe_nonneg],
  exact hg.edist_le_of_le (hst hx) (hst hy) (hf.edist_le hx hy)
end

lemma comp_holder_with {Cg rg : ℝ≥0} {g : Y → Z} {t : set Y} (hg : holder_on_with Cg rg g t)
  {Cf rf : ℝ≥0} {f : X → Y} (hf : holder_with Cf rf f) (ht : ∀ x, f x ∈ t) :
  holder_with (Cg * Cf ^ (rg : ℝ)) (rg * rf) (g ∘ f) :=
holder_on_with_univ.mp $ hg.comp (hf.holder_on_with univ) (λ x _, ht x)

/-- A Hölder continuous function is uniformly continuous -/
protected lemma uniform_continuous_on (hf : holder_on_with C r f s) (h0 : 0 < r) :
  uniform_continuous_on f s :=
begin
  refine emetric.uniform_continuous_on_iff.2 (λε εpos, _),
  have : tendsto (λ d : ℝ≥0∞, (C : ℝ≥0∞) * d ^ (r : ℝ)) (𝓝 0) (𝓝 0),
    from ennreal.tendsto_const_mul_rpow_nhds_zero_of_pos ennreal.coe_ne_top h0,
  rcases ennreal.nhds_zero_basis.mem_iff.1 (this (gt_mem_nhds εpos)) with ⟨δ, δ0, H⟩,
  exact ⟨δ, δ0, λ x hx y hy h, (hf.edist_le hx hy).trans_lt (H h)⟩,
end

protected lemma continuous_on (hf : holder_on_with C r f s) (h0 : 0 < r) : continuous_on f s :=
(hf.uniform_continuous_on h0).continuous_on

protected lemma mono (hf : holder_on_with C r f s) (ht : t ⊆ s) : holder_on_with C r f t :=
λ x hx y hy, hf.edist_le (ht hx) (ht hy)

lemma ediam_image_le_of_le (hf : holder_on_with C r f s) {d : ℝ≥0∞} (hd : emetric.diam s ≤ d) :
  emetric.diam (f '' s) ≤ C * d ^ (r : ℝ) :=
emetric.diam_image_le_iff.2 $ λ x hx y hy, hf.edist_le_of_le hx hy $
  (emetric.edist_le_diam_of_mem hx hy).trans hd

lemma ediam_image_le (hf : holder_on_with C r f s) :
  emetric.diam (f '' s) ≤ C * emetric.diam s ^ (r : ℝ) :=
hf.ediam_image_le_of_le le_rfl

lemma ediam_image_le_of_subset (hf : holder_on_with C r f s) (ht : t ⊆ s) :
  emetric.diam (f '' t) ≤ C * emetric.diam t ^ (r : ℝ) :=
(hf.mono ht).ediam_image_le

lemma ediam_image_le_of_subset_of_le (hf : holder_on_with C r f s) (ht : t ⊆ s) {d : ℝ≥0∞}
  (hd : emetric.diam t ≤ d) :
  emetric.diam (f '' t) ≤ C * d ^ (r : ℝ) :=
(hf.mono ht).ediam_image_le_of_le hd

lemma ediam_image_inter_le_of_le (hf : holder_on_with C r f s) {d : ℝ≥0∞}
  (hd : emetric.diam t ≤ d) :
  emetric.diam (f '' (t ∩ s)) ≤ C * d ^ (r : ℝ) :=
hf.ediam_image_le_of_subset_of_le (inter_subset_right _ _) $
  (emetric.diam_mono $ inter_subset_left _ _).trans hd

lemma ediam_image_inter_le (hf : holder_on_with C r f s) (t : set X) :
  emetric.diam (f '' (t ∩ s)) ≤ C * emetric.diam t ^ (r : ℝ) :=
hf.ediam_image_inter_le_of_le le_rfl

end holder_on_with

namespace holder_with

variables {C r : ℝ≥0} {f : X → Y}

lemma edist_le (h : holder_with C r f) (x y : X) :
  edist (f x) (f y) ≤ C * edist x y ^ (r : ℝ) :=
h x y

lemma edist_le_of_le (h : holder_with C r f) {x y : X} {d : ℝ≥0∞} (hd : edist x y ≤ d) :
  edist (f x) (f y) ≤ C * d ^ (r : ℝ) :=
(h.holder_on_with univ).edist_le_of_le trivial trivial hd

lemma comp {Cg rg : ℝ≥0} {g : Y → Z} (hg : holder_with Cg rg g)
  {Cf rf : ℝ≥0} {f : X → Y} (hf : holder_with Cf rf f) :
  holder_with (Cg * Cf ^ (rg : ℝ)) (rg * rf) (g ∘ f) :=
(hg.holder_on_with univ).comp_holder_with hf (λ _, trivial)

lemma comp_holder_on_with {Cg rg : ℝ≥0} {g : Y → Z} (hg : holder_with Cg rg g)
  {Cf rf : ℝ≥0} {f : X → Y} {s : set X} (hf : holder_on_with Cf rf f s) :
  holder_on_with (Cg * Cf ^ (rg : ℝ)) (rg * rf) (g ∘ f) s :=
(hg.holder_on_with univ).comp hf (λ _ _, trivial)

/-- A Hölder continuous function is uniformly continuous -/
protected lemma uniform_continuous (hf : holder_with C r f) (h0 : 0 < r) : uniform_continuous f :=
uniform_continuous_on_univ.mp $ (hf.holder_on_with univ).uniform_continuous_on h0

protected lemma continuous (hf : holder_with C r f) (h0 : 0 < r) : continuous f :=
(hf.uniform_continuous h0).continuous

lemma ediam_image_le (hf : holder_with C r f) (s : set X) :
  emetric.diam (f '' s) ≤ C * emetric.diam s ^ (r : ℝ) :=
emetric.diam_image_le_iff.2 $ λ x hx y hy, hf.edist_le_of_le $ emetric.edist_le_diam_of_mem hx hy

end holder_with

end emetric

section metric

variables [pseudo_metric_space X] [pseudo_metric_space Y] {C r : ℝ≥0} {f : X → Y}

namespace holder_with

lemma nndist_le_of_le (hf : holder_with C r f) {x y : X} {d : ℝ≥0} (hd : nndist x y ≤ d) :
  nndist (f x) (f y) ≤ C * d ^ (r : ℝ) :=
begin
  rw [← ennreal.coe_le_coe, ← edist_nndist, ennreal.coe_mul,
    ← ennreal.coe_rpow_of_nonneg _ r.coe_nonneg],
  apply hf.edist_le_of_le,
  rwa [edist_nndist, ennreal.coe_le_coe],
end

lemma nndist_le (hf : holder_with C r f) (x y : X) :
  nndist (f x) (f y) ≤ C * nndist x y ^ (r : ℝ) :=
hf.nndist_le_of_le le_rfl

lemma dist_le_of_le (hf : holder_with C r f) {x y : X} {d : ℝ} (hd : dist x y ≤ d) :
  dist (f x) (f y) ≤ C * d ^ (r : ℝ) :=
begin
  lift d to ℝ≥0 using dist_nonneg.trans hd,
  rw dist_nndist at hd ⊢,
  norm_cast at hd ⊢,
  exact hf.nndist_le_of_le hd
end

lemma dist_le (hf : holder_with C r f) (x y : X) :
  dist (f x) (f y) ≤ C * dist x y ^ (r : ℝ) :=
hf.dist_le_of_le le_rfl

end holder_with

end metric


/-
Copyright (c) 2019 Seul Baek. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Seul Baek
-/

/-
Linear natural number arithmetic preformulas in pre-normalized preform.
-/

import tactic.omega.nat.preterm

namespace omega

namespace nat

/-- Intermediate shadow syntax for LNA formulas that includes unreified exprs -/
meta inductive exprform
| eq  : exprterm → exprterm → exprform
| le  : exprterm → exprterm → exprform
| not : exprform → exprform
| or  : exprform → exprform → exprform
| and : exprform → exprform → exprform

/-- Intermediate shadow syntax for LNA formulas that includes non-canonical terms -/
@[derive has_reflect, derive inhabited]
inductive preform
| eq  : preterm → preterm → preform
| le  : preterm → preterm → preform
| not : preform → preform
| or  : preform → preform → preform
| and : preform → preform → preform

localized "notation (name := preform.eq) x ` =* ` y := omega.nat.preform.eq x y" in omega.nat
localized "notation (name := preform.le) x ` ≤* ` y := omega.nat.preform.le x y" in omega.nat
localized "notation (name := preform.not) `¬* ` p    := omega.nat.preform.not p" in omega.nat
localized "notation (name := preform.or) p ` ∨* ` q := omega.nat.preform.or p q" in omega.nat
localized "notation (name := preform.and) p ` ∧* ` q := omega.nat.preform.and p q" in omega.nat

namespace preform

/-- Evaluate a preform into prop using the valuation `v`. -/
@[simp] def holds (v : nat → nat) : preform → Prop
| (t =* s) := t.val v = s.val v
| (t ≤* s) := t.val v ≤ s.val v
| (¬* p)   := ¬ p.holds
| (p ∨* q) := p.holds ∨ q.holds
| (p ∧* q) := p.holds ∧ q.holds

end preform

/-- `univ_close p n` := `p` closed by prepending `n` universal quantifiers -/
@[simp] def univ_close (p : preform) : (nat → nat) → nat → Prop
| v 0     := p.holds v
| v (k+1) := ∀ i : nat, univ_close (update_zero i v) k

namespace preform

/-- Argument is free of negations -/
def neg_free : preform → Prop
| (t =* s) := true
| (t ≤* s) := true
| (p ∨* q) := neg_free p ∧ neg_free q
| (p ∧* q) := neg_free p ∧ neg_free q
| _        := false

/-- Return expr of proof that argument is free of subtractions -/
def sub_free : preform → Prop
| (t =* s) := t.sub_free ∧ s.sub_free
| (t ≤* s) := t.sub_free ∧ s.sub_free
| (¬* p)   := p.sub_free
| (p ∨* q) := p.sub_free ∧ q.sub_free
| (p ∧* q) := p.sub_free ∧ q.sub_free

/-- Fresh de Brujin index not used by any variable in argument -/
def fresh_index : preform → nat
| (t =* s) := max t.fresh_index s.fresh_index
| (t ≤* s) := max t.fresh_index s.fresh_index
| (¬* p)   := p.fresh_index
| (p ∨* q) := max p.fresh_index q.fresh_index
| (p ∧* q) := max p.fresh_index q.fresh_index

lemma holds_constant {v w : nat → nat} :
  ∀ p : preform,
  ( (∀ x < p.fresh_index, v x = w x) →
    (p.holds v ↔ p.holds w) )
| (t =* s) h1 :=
  begin
    simp only [holds],
    apply pred_mono_2;
    apply preterm.val_constant;
    intros x h2; apply h1 _ (lt_of_lt_of_le h2 _),
    apply le_max_left, apply le_max_right
  end
| (t ≤* s) h1 :=
  begin
    simp only [holds],
    apply pred_mono_2;
    apply preterm.val_constant;
    intros x h2; apply h1 _ (lt_of_lt_of_le h2 _),
    apply le_max_left, apply le_max_right
  end
| (¬* p)   h1 :=
  begin
    apply not_iff_not_of_iff,
    apply holds_constant p h1
  end
| (p ∨* q) h1 :=
  begin
    simp only [holds],
    apply pred_mono_2';
    apply holds_constant;
    intros x h2; apply h1 _ (lt_of_lt_of_le h2 _),
    apply le_max_left, apply le_max_right
  end
| (p ∧* q) h1 :=
  begin
    simp only [holds],
    apply pred_mono_2';
    apply holds_constant;
    intros x h2; apply h1 _ (lt_of_lt_of_le h2 _),
    apply le_max_left, apply le_max_right
  end

/-- All valuations satisfy argument -/
def valid (p : preform) : Prop :=
∀ v, holds v p

/-- There exists some valuation that satisfies argument -/
def sat (p : preform) : Prop :=
∃ v, holds v p

/-- `implies p q` := under any valuation, `q` holds if `p` holds -/
def implies (p q : preform) : Prop :=
∀ v, (holds v p → holds v q)

/-- `equiv p q` := under any valuation, `p` holds iff `q` holds -/
def equiv (p q : preform) : Prop :=
∀ v, (holds v p ↔ holds v q)

lemma sat_of_implies_of_sat {p q : preform} :
  implies p q → sat p → sat q :=
begin intros h1 h2, apply exists_imp_exists h1 h2 end

lemma sat_or {p q : preform} :
  sat (p ∨* q) ↔ sat p ∨ sat q :=
begin
  constructor; intro h1,
  { cases h1 with v h1, cases h1 with h1 h1;
    [left,right]; refine ⟨v,_⟩; assumption },
  { cases h1 with h1 h1; cases h1 with v h1;
    refine ⟨v,_⟩; [left,right]; assumption }
end

/-- There does not exist any valuation that satisfies argument -/
def unsat (p : preform) : Prop := ¬ sat p

def repr : preform → string
| (t =* s) := "(" ++ t.repr ++ " = " ++ s.repr ++ ")"
| (t ≤* s) := "(" ++ t.repr ++ " ≤ " ++ s.repr ++ ")"
| (¬* p)   := "¬" ++ p.repr
| (p ∨* q) := "(" ++ p.repr ++ " ∨ " ++ q.repr ++ ")"
| (p ∧* q) := "(" ++ p.repr ++ " ∧ " ++ q.repr ++ ")"

instance has_repr : has_repr preform := ⟨repr⟩
meta instance has_to_format : has_to_format preform := ⟨λ x, x.repr⟩

end preform

lemma univ_close_of_valid {p : preform} :
  ∀ {m : nat} {v : nat → nat}, p.valid → univ_close p v m
| 0 v h1     := h1 _
| (m+1) v h1 := λ i, univ_close_of_valid h1

lemma valid_of_unsat_not {p : preform} : (¬*p).unsat → p.valid :=
begin
  simp only [preform.sat, preform.unsat, preform.valid, preform.holds],
  rw not_exists_not, intro h, assumption
end

/-- Tactic for setting up proof by induction over preforms. -/
meta def preform.induce (t : tactic unit := tactic.skip) : tactic unit :=
`[ intro p, induction p with t s t s p ih p q ihp ihq p q ihp ihq; t ]

end nat

end omega


/-
Copyright (c) 2022 Thomas Browning. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Thomas Browning

! This file was ported from Lean 3 source module group_theory.commuting_probability
! leanprover-community/mathlib commit 23aa88e32dcc9d2a24cca7bc23268567ed4cd7d6
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Group.ConjFinite
import Mathbin.GroupTheory.Abelianization
import Mathbin.GroupTheory.GroupAction.ConjAct
import Mathbin.GroupTheory.GroupAction.Quotient
import Mathbin.GroupTheory.Index

/-!
# Commuting Probability

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.
This file introduces the commuting probability of finite groups.

## Main definitions
* `comm_prob`: The commuting probability of a finite type with a multiplication operation.

## Todo
* Neumann's theorem.
-/

noncomputable section

open Classical

open BigOperators

open Fintype

variable (M : Type _) [Mul M]

#print commProb /-
/-- The commuting probability of a finite type with a multiplication operation -/
def commProb : ℚ :=
  Nat.card { p : M × M // p.1 * p.2 = p.2 * p.1 } / Nat.card M ^ 2
#align comm_prob commProb
-/

/- warning: comm_prob_def -> commProb_def is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) [_inst_1 : Mul.{u1} M], Eq.{1} Rat (commProb.{u1} M _inst_1) (HDiv.hDiv.{0, 0, 0} Rat Rat Rat (instHDiv.{0} Rat Rat.hasDiv) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) (Nat.card.{u1} (Subtype.{succ u1} (Prod.{u1, u1} M M) (fun (p : Prod.{u1, u1} M M) => Eq.{succ u1} M (HMul.hMul.{u1, u1, u1} M M M (instHMul.{u1} M _inst_1) (Prod.fst.{u1, u1} M M p) (Prod.snd.{u1, u1} M M p)) (HMul.hMul.{u1, u1, u1} M M M (instHMul.{u1} M _inst_1) (Prod.snd.{u1, u1} M M p) (Prod.fst.{u1, u1} M M p)))))) (HPow.hPow.{0, 0, 0} Rat Nat Rat (instHPow.{0, 0} Rat Nat (Monoid.Pow.{0} Rat Rat.monoid)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) (Nat.card.{u1} M)) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))))
but is expected to have type
  forall (M : Type.{u1}) [_inst_1 : Mul.{u1} M], Eq.{1} Rat (commProb.{u1} M _inst_1) (HDiv.hDiv.{0, 0, 0} Rat Rat Rat (instHDiv.{0} Rat Rat.instDivRat) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) (Nat.card.{u1} (Subtype.{succ u1} (Prod.{u1, u1} M M) (fun (p : Prod.{u1, u1} M M) => Eq.{succ u1} M (HMul.hMul.{u1, u1, u1} M M M (instHMul.{u1} M _inst_1) (Prod.fst.{u1, u1} M M p) (Prod.snd.{u1, u1} M M p)) (HMul.hMul.{u1, u1, u1} M M M (instHMul.{u1} M _inst_1) (Prod.snd.{u1, u1} M M p) (Prod.fst.{u1, u1} M M p)))))) (HPow.hPow.{0, 0, 0} Rat Nat Rat (instHPow.{0, 0} Rat Nat (Monoid.Pow.{0} Rat Rat.monoid)) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) (Nat.card.{u1} M)) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))))
Case conversion may be inaccurate. Consider using '#align comm_prob_def commProb_defₓ'. -/
theorem commProb_def :
    commProb M = Nat.card { p : M × M // p.1 * p.2 = p.2 * p.1 } / Nat.card M ^ 2 :=
  rfl
#align comm_prob_def commProb_def

variable [Finite M]

#print commProb_pos /-
theorem commProb_pos [h : Nonempty M] : 0 < commProb M :=
  h.elim fun x =>
    div_pos (Nat.cast_pos.mpr (Finite.card_pos_iff.mpr ⟨⟨(x, x), rfl⟩⟩))
      (pow_pos (Nat.cast_pos.mpr Finite.card_pos) 2)
#align comm_prob_pos commProb_pos
-/

/- warning: comm_prob_le_one -> commProb_le_one is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) [_inst_1 : Mul.{u1} M] [_inst_2 : Finite.{succ u1} M], LE.le.{0} Rat Rat.hasLe (commProb.{u1} M _inst_1) (OfNat.ofNat.{0} Rat 1 (OfNat.mk.{0} Rat 1 (One.one.{0} Rat Rat.hasOne)))
but is expected to have type
  forall (M : Type.{u1}) [_inst_1 : Mul.{u1} M] [_inst_2 : Finite.{succ u1} M], LE.le.{0} Rat Rat.instLERat (commProb.{u1} M _inst_1) (OfNat.ofNat.{0} Rat 1 (Rat.instOfNatRat 1))
Case conversion may be inaccurate. Consider using '#align comm_prob_le_one commProb_le_oneₓ'. -/
theorem commProb_le_one : commProb M ≤ 1 :=
  by
  refine' div_le_one_of_le _ (sq_nonneg (Nat.card M))
  rw [← Nat.cast_pow, Nat.cast_le, sq, ← Nat.card_prod]
  apply Finite.card_subtype_le
#align comm_prob_le_one commProb_le_one

variable {M}

#print commProb_eq_one_iff /-
theorem commProb_eq_one_iff [h : Nonempty M] : commProb M = 1 ↔ Commutative ((· * ·) : M → M → M) :=
  by
  haveI := Fintype.ofFinite M
  rw [commProb, ← Set.coe_setOf, Nat.card_eq_fintype_card, Nat.card_eq_fintype_card]
  rw [div_eq_one_iff_eq, ← Nat.cast_pow, Nat.cast_inj, sq, ← card_prod,
    set_fintype_card_eq_univ_iff, Set.eq_univ_iff_forall]
  · exact ⟨fun h x y => h (x, y), fun h x => h x.1 x.2⟩
  · exact pow_ne_zero 2 (nat.cast_ne_zero.mpr card_ne_zero)
#align comm_prob_eq_one_iff commProb_eq_one_iff
-/

variable (G : Type _) [Group G] [Finite G]

/- warning: card_comm_eq_card_conj_classes_mul_card -> card_comm_eq_card_conjClasses_mul_card is a dubious translation:
lean 3 declaration is
  forall (G : Type.{u1}) [_inst_3 : Group.{u1} G] [_inst_4 : Finite.{succ u1} G], Eq.{1} Nat (Nat.card.{u1} (Subtype.{succ u1} (Prod.{u1, u1} G G) (fun (p : Prod.{u1, u1} G 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_3))))) (Prod.fst.{u1, u1} G G p) (Prod.snd.{u1, u1} G G p)) (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_3))))) (Prod.snd.{u1, u1} G G p) (Prod.fst.{u1, u1} G G p))))) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) (Nat.card.{u1} (ConjClasses.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_3)))) (Nat.card.{u1} G))
but is expected to have type
  forall (G : Type.{u1}) [_inst_3 : Group.{u1} G] [_inst_4 : Finite.{succ u1} G], Eq.{1} Nat (Nat.card.{u1} (Subtype.{succ u1} (Prod.{u1, u1} G G) (fun (p : Prod.{u1, u1} G 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_3))))) (Prod.fst.{u1, u1} G G p) (Prod.snd.{u1, u1} G G p)) (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_3))))) (Prod.snd.{u1, u1} G G p) (Prod.fst.{u1, u1} G G p))))) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) (Nat.card.{u1} (ConjClasses.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_3)))) (Nat.card.{u1} G))
Case conversion may be inaccurate. Consider using '#align card_comm_eq_card_conj_classes_mul_card card_comm_eq_card_conjClasses_mul_cardₓ'. -/
theorem card_comm_eq_card_conjClasses_mul_card :
    Nat.card { p : G × G // p.1 * p.2 = p.2 * p.1 } = Nat.card (ConjClasses G) * Nat.card G :=
  by
  haveI := Fintype.ofFinite G
  simp only [Nat.card_eq_fintype_card]
  convert calc
      card { p : G × G // p.1 * p.2 = p.2 * p.1 } = card (Σg, { h // g * h = h * g }) :=
        card_congr (Equiv.subtypeProdEquivSigmaSubtype fun g h : G => g * h = h * g)
      _ = ∑ g, card { h // g * h = h * g } := (card_sigma _)
      _ = ∑ g, card (MulAction.fixedBy (ConjAct G) G g) :=
        (sum_equiv conj_act.to_conj_act.to_equiv _ _ fun g =>
          card_congr' <| congr_arg _ <| funext fun h => mul_inv_eq_iff_eq_mul.symm.to_eq)
      _ = card (Quotient (MulAction.orbitRel (ConjAct G) G)) * card G :=
        (MulAction.sum_card_fixedBy_eq_card_orbits_mul_card_group (ConjAct G) G)
      _ = card (Quotient (IsConj.setoid G)) * card G :=
        by
        have this : MulAction.orbitRel (ConjAct G) G = IsConj.setoid G :=
          Setoid.ext fun g h => (Setoid.comm' _).trans is_conj_iff.symm
        cc

#align card_comm_eq_card_conj_classes_mul_card card_comm_eq_card_conjClasses_mul_card

/- warning: comm_prob_def' -> commProb_def' is a dubious translation:
lean 3 declaration is
  forall (G : Type.{u1}) [_inst_3 : Group.{u1} G] [_inst_4 : Finite.{succ u1} G], Eq.{1} Rat (commProb.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_3))))) (HDiv.hDiv.{0, 0, 0} Rat Rat Rat (instHDiv.{0} Rat Rat.hasDiv) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) (Nat.card.{u1} (ConjClasses.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_3))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) (Nat.card.{u1} G)))
but is expected to have type
  forall (G : Type.{u1}) [_inst_3 : Group.{u1} G] [_inst_4 : Finite.{succ u1} G], Eq.{1} Rat (commProb.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_3))))) (HDiv.hDiv.{0, 0, 0} Rat Rat Rat (instHDiv.{0} Rat Rat.instDivRat) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) (Nat.card.{u1} (ConjClasses.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_3))))) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) (Nat.card.{u1} G)))
Case conversion may be inaccurate. Consider using '#align comm_prob_def' commProb_def'ₓ'. -/
theorem commProb_def' : commProb G = Nat.card (ConjClasses G) / Nat.card G :=
  by
  rw [commProb, card_comm_eq_card_conjClasses_mul_card, Nat.cast_mul, sq]
  exact mul_div_mul_right _ _ (nat.cast_ne_zero.mpr finite.card_pos.ne')
#align comm_prob_def' commProb_def'

variable {G} (H : Subgroup G)

/- warning: subgroup.comm_prob_subgroup_le -> Subgroup.commProb_subgroup_le is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_3 : Group.{u1} G] [_inst_4 : Finite.{succ u1} G] (H : Subgroup.{u1} G _inst_3), LE.le.{0} Rat Rat.hasLe (commProb.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_3) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_3) G (Subgroup.setLike.{u1} G _inst_3)) H) (Subgroup.mul.{u1} G _inst_3 H)) (HMul.hMul.{0, 0, 0} Rat Rat Rat (instHMul.{0} Rat Rat.hasMul) (commProb.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_3))))) (HPow.hPow.{0, 0, 0} Rat Nat Rat (instHPow.{0, 0} Rat Nat (Monoid.Pow.{0} Rat Rat.monoid)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) (Subgroup.index.{u1} G _inst_3 H)) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))))
but is expected to have type
  forall {G : Type.{u1}} [_inst_3 : Finite.{succ u1} G] [_inst_4 : Group.{u1} G] (H : Subgroup.{u1} G _inst_4), LE.le.{0} Rat Rat.instLERat (commProb.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_4) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_4) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_4)) x H)) (Subgroup.mul.{u1} G _inst_4 H)) (HMul.hMul.{0, 0, 0} Rat Rat Rat (instHMul.{0} Rat Rat.instMulRat) (commProb.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))))) (HPow.hPow.{0, 0, 0} Rat Nat Rat (instHPow.{0, 0} Rat Nat (Monoid.Pow.{0} Rat Rat.monoid)) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) (Subgroup.index.{u1} G _inst_4 H)) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))))
Case conversion may be inaccurate. Consider using '#align subgroup.comm_prob_subgroup_le Subgroup.commProb_subgroup_leₓ'. -/
theorem Subgroup.commProb_subgroup_le : commProb H ≤ commProb G * H.index ^ 2 :=
  by
  /- After rewriting with `comm_prob_def`, we reduce to showing that `G` has at least as many
      commuting pairs as `H`. -/
  rw [commProb_def, commProb_def, div_le_iff, mul_assoc, ← mul_pow, ← Nat.cast_mul,
    mul_comm H.index, H.card_mul_index, div_mul_cancel, Nat.cast_le]
  · refine' Finite.card_le_of_injective (fun p => ⟨⟨p.1.1, p.1.2⟩, subtype.ext_iff.mp p.2⟩) _
    exact fun p q h => by simpa only [Subtype.ext_iff, Prod.ext_iff] using h
  · exact pow_ne_zero 2 (nat.cast_ne_zero.mpr finite.card_pos.ne')
  · exact pow_pos (nat.cast_pos.mpr Finite.card_pos) 2
#align subgroup.comm_prob_subgroup_le Subgroup.commProb_subgroup_le

/- warning: subgroup.comm_prob_quotient_le -> Subgroup.commProb_quotient_le is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_3 : Group.{u1} G] [_inst_4 : Finite.{succ u1} G] (H : Subgroup.{u1} G _inst_3) [_inst_5 : Subgroup.Normal.{u1} G _inst_3 H], LE.le.{0} Rat Rat.hasLe (commProb.{u1} (HasQuotient.Quotient.{u1, u1} G (Subgroup.{u1} G _inst_3) (QuotientGroup.Subgroup.hasQuotient.{u1} G _inst_3) H) (MulOneClass.toHasMul.{u1} (HasQuotient.Quotient.{u1, u1} G (Subgroup.{u1} G _inst_3) (QuotientGroup.Subgroup.hasQuotient.{u1} G _inst_3) H) (Monoid.toMulOneClass.{u1} (HasQuotient.Quotient.{u1, u1} G (Subgroup.{u1} G _inst_3) (QuotientGroup.Subgroup.hasQuotient.{u1} G _inst_3) H) (DivInvMonoid.toMonoid.{u1} (HasQuotient.Quotient.{u1, u1} G (Subgroup.{u1} G _inst_3) (QuotientGroup.Subgroup.hasQuotient.{u1} G _inst_3) H) (Group.toDivInvMonoid.{u1} (HasQuotient.Quotient.{u1, u1} G (Subgroup.{u1} G _inst_3) (QuotientGroup.Subgroup.hasQuotient.{u1} G _inst_3) H) (QuotientGroup.Quotient.group.{u1} G _inst_3 H _inst_5)))))) (HMul.hMul.{0, 0, 0} Rat Rat Rat (instHMul.{0} Rat Rat.hasMul) (commProb.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_3))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) (Nat.card.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_3) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_3) G (Subgroup.setLike.{u1} G _inst_3)) H))))
but is expected to have type
  forall {G : Type.{u1}} [_inst_3 : Finite.{succ u1} G] [_inst_4 : Group.{u1} G] (H : Subgroup.{u1} G _inst_4) [_inst_5 : Subgroup.Normal.{u1} G _inst_4 H], LE.le.{0} Rat Rat.instLERat (commProb.{u1} (HasQuotient.Quotient.{u1, u1} G (Subgroup.{u1} G _inst_4) (QuotientGroup.instHasQuotientSubgroup.{u1} G _inst_4) H) (MulOneClass.toMul.{u1} (HasQuotient.Quotient.{u1, u1} G (Subgroup.{u1} G _inst_4) (QuotientGroup.instHasQuotientSubgroup.{u1} G _inst_4) H) (Monoid.toMulOneClass.{u1} (HasQuotient.Quotient.{u1, u1} G (Subgroup.{u1} G _inst_4) (QuotientGroup.instHasQuotientSubgroup.{u1} G _inst_4) H) (DivInvMonoid.toMonoid.{u1} (HasQuotient.Quotient.{u1, u1} G (Subgroup.{u1} G _inst_4) (QuotientGroup.instHasQuotientSubgroup.{u1} G _inst_4) H) (Group.toDivInvMonoid.{u1} (HasQuotient.Quotient.{u1, u1} G (Subgroup.{u1} G _inst_4) (QuotientGroup.instHasQuotientSubgroup.{u1} G _inst_4) H) (QuotientGroup.Quotient.group.{u1} G _inst_4 H _inst_5)))))) (HMul.hMul.{0, 0, 0} Rat Rat Rat (instHMul.{0} Rat Rat.instMulRat) (commProb.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))))) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) (Nat.card.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_4) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_4) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_4)) x H)))))
Case conversion may be inaccurate. Consider using '#align subgroup.comm_prob_quotient_le Subgroup.commProb_quotient_leₓ'. -/
theorem Subgroup.commProb_quotient_le [H.Normal] : commProb (G ⧸ H) ≤ commProb G * Nat.card H :=
  by
  /- After rewriting with `comm_prob_def'`, we reduce to showing that `G` has at least as many
      conjugacy classes as `G ⧸ H`. -/
  rw [commProb_def', commProb_def', div_le_iff, mul_assoc, ← Nat.cast_mul, ← Subgroup.index,
    H.card_mul_index, div_mul_cancel, Nat.cast_le]
  · apply Finite.card_le_of_surjective
    show Function.Surjective (ConjClasses.map (QuotientGroup.mk' H))
    exact ConjClasses.map_surjective Quotient.surjective_Quotient_mk''
  · exact nat.cast_ne_zero.mpr finite.card_pos.ne'
  · exact nat.cast_pos.mpr Finite.card_pos
#align subgroup.comm_prob_quotient_le Subgroup.commProb_quotient_le

variable (G)

/- warning: inv_card_commutator_le_comm_prob -> inv_card_commutator_le_commProb is a dubious translation:
lean 3 declaration is
  forall (G : Type.{u1}) [_inst_3 : Group.{u1} G] [_inst_4 : Finite.{succ u1} G], LE.le.{0} Rat Rat.hasLe (Inv.inv.{0} Rat Rat.hasInv ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) (Nat.card.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_3) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_3) G (Subgroup.setLike.{u1} G _inst_3)) (commutator.{u1} G _inst_3))))) (commProb.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_3)))))
but is expected to have type
  forall (G : Type.{u1}) [_inst_3 : Finite.{succ u1} G] [_inst_4 : Group.{u1} G], LE.le.{0} Rat Rat.instLERat (Inv.inv.{0} Rat Rat.instInvRat (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing))) (Nat.card.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_4) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_4) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_4)) x (commutator.{u1} G _inst_4)))))) (commProb.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4)))))
Case conversion may be inaccurate. Consider using '#align inv_card_commutator_le_comm_prob inv_card_commutator_le_commProbₓ'. -/
theorem inv_card_commutator_le_commProb : (↑(Nat.card (commutator G)))⁻¹ ≤ commProb G :=
  (inv_pos_le_iff_one_le_mul (nat.cast_pos.mpr Finite.card_pos)).mpr
    (le_trans (ge_of_eq (commProb_eq_one_iff.mpr (Abelianization.commGroup G).mul_comm))
      (commutator G).commProb_quotient_le)
#align inv_card_commutator_le_comm_prob inv_card_commutator_le_commProb