import .colimits
import .homeomorphism
import .quotient_space
import .sierpinski
import .subspace
import category_theory.pasting_pushouts

/-

We prove the following facts about pushouts in Top. Suppose given a
pushout square

      f
    A → B
  i ↓   ↓ j
    X → Y
      g

0. A subset of Y is closed (open) iff its preimages under g and j are.
1. If i is injective, then so is j.
2. If i is an embedding, then so is j.
3. If the image of i is closed (open), then so is the image of j.
4. In either case of 3, g : X → Y restricts to a homeomorphism
   g' : X - im i → Y - im j. Consequently, if k : C → X is another
   embedding whose image does not intersect the image of i, then
   g ∘ k is also an embedding.

In particular, we conclude that if i is a closed embedding, then so is
j, and in this case g induces a homeomorphism X - A → Y - B.

-/

open set

open category_theory
local notation f ` ∘ `:80 g:80 := g ≫ f

universe u

namespace homotopy_theory.topological_spaces
open homotopy_theory.topological_spaces.Top
local notation `Top` := Top.{u}

section
parameters {A B X Y : Top} {i : A ⟶ X} {f : A ⟶ B} {g : X ⟶ Y} {j : B ⟶ Y}
parameter (po : Is_pushout i f g j)
include po

def is_open_in_pushout (s : set Y) : is_open s ↔ is_open (g ⁻¹' s) ∧ is_open (j ⁻¹' s) :=
⟨λ hs, ⟨g.2 s hs, j.2 s hs⟩, λ ⟨gs, js⟩, begin
   let xs := (opens_equiv X) ⟨g ⁻¹' s, gs⟩,
   let bs := (opens_equiv B) ⟨j ⁻¹' s, js⟩,
   let ys := po.induced xs bs
     (by ext a; change (i ≫ g) a ∈ s ↔ (f ≫ j) a ∈ s; rw po.commutes),
   convert ((opens_equiv Y).symm ys).property,
   rw [←equiv.apply_eq_iff_eq (set_equiv Y), ←forget_open_map, equiv.apply_symm_apply],
   apply po.uniqueness; { rw [set_equiv_nat, ←category.assoc], simp, refl }
 end⟩

def is_closed_in_pushout (s : set Y) : is_closed s ↔ is_closed (g ⁻¹' s) ∧ is_closed (j ⁻¹' s) :=
is_open_in_pushout (sᶜ)

end

-- For facts 1 & 2, the strategy is to show that injections
-- (respectively, embeddings) are precisely the maps with the left
-- lifting property with respect to the indiscrete two-point space
-- (respectively, also the Sierpinski space) and then use the fact
-- that such a left lifting property is preserved by pushouts.

section llp
-- TODO: Generalize/merge this with left lifting property

def llp_obj {A X : Top} (i : A ⟶ X) (Z : Top) : Prop :=
∀ (h : A ⟶ Z), ∃ (l : X ⟶ Z), i ≫ l = h

lemma llp_of_pushout {A B X Y : Top} {i : A ⟶ X} {f : A ⟶ B} {g : X ⟶ Y} {j : B ⟶ Y}
  (po : Is_pushout i f g j) (Z : Top) (hf : llp_obj i Z) : llp_obj j Z :=
begin
  intro h,
  rcases hf (f ≫ h) with ⟨l, hl⟩,
  refine ⟨po.induced l h hl, _⟩,
  simp
end

end llp

section
parameters {A X : Top} {i : A ⟶ X}

lemma injective_iff_llp : function.injective i ↔ llp_obj i prop_indisc :=
begin
  split; intro H,
  { intro h,
    use set_equiv X (i '' (set_equiv A).symm h),
    rw [set_equiv_nat, ←equiv.eq_symm_apply],
    exact preimage_image_eq _ H },
  { intros a a' haa',
    -- Use lifting property to extend (λ x, x = a') to X.
    rcases H ⟨λ x, ulift.up (x = a'), continuous_top⟩ with ⟨l, hl⟩,
    have := Top.hom_congr hl a,
    change l (i a) = _ at this,
    rw haa' at this,
    change (l ∘ i) a' = _ at this,
    rw hl at this,
    replace := congr_arg ulift.down this,
    change (a' = a') = (a = a') at this,
    rw ←iff_eq_eq at this,
    simpa using this }
end

lemma induced_iff_llp : (A.str = X.str.induced i) ↔ llp_obj i sierpinski :=
begin
  split; intro H,
  { intro h,
    -- Idea: h : A → Zind encodes an open set of A. By definition of
    -- the induced topology, this open set is the preimage of an open
    -- set of X, which defines a map X → Zind extending h.
    let u : topological_space.opens A := (opens_equiv A).symm h,
    rcases is_open_induced_iff.mp (by convert ←u.property) with ⟨w, wo, uw⟩,
    use opens_equiv X ⟨w, wo⟩,
    rw [opens_equiv_nat, ←equiv.eq_symm_apply],
    apply subtype.eq,
    exact uw },
  { -- One inequality is automatic because i is continuous.
    refine le_antisymm (continuous_iff_le_induced.mp i.2) _,
    intros w wo,
    -- Now we reverse the above argument. w defines a continuous map A → Zind,
    -- which we extend to X using the lifting property to express w as the
    -- preimage of an open set of X.
    rcases H (opens_equiv A ⟨w, wo⟩) with ⟨l, hl⟩,
    apply is_open_induced_iff.mpr,
    use (opens_equiv X).symm l,
    refine ⟨((opens_equiv X).symm l).property, _⟩,
    funext a,
    exact (congr_arg ulift.down (Top.hom_congr hl a)) }
end

end

section
parameters {A B X Y : Top} {i : A ⟶ X} {f : A ⟶ B} {g : X ⟶ Y} {j : B ⟶ Y}
parameter (po : Is_pushout i f g j)
include po

lemma injective_j_of_injective_i (h : function.injective i) : function.injective j :=
begin
  rw injective_iff_llp at ⊢ h,
  exact llp_of_pushout po _ h
end

lemma embedding_j_of_embedding_i (h : embedding i) : embedding j :=
⟨⟨begin
   have := h.induced,
   erw induced_iff_llp at ⊢ this,
   exact llp_of_pushout po _ this
  end⟩,
 injective_j_of_injective_i h.inj⟩

end

namespace pushout_lemmas_private
section
parameters {A B X Y : Top} {i : A ⟶ X} {f : A ⟶ B} {g : X ⟶ Y} {j : B ⟶ Y}
parameter (po : Is_pushout i f g j)

/-
  Form the pushouts

               f
   A ⟶ *     A → B → *
  i↓   ↓a   i↓  j↓   ↓b
   X → X/A,  X → Y → Y/B
     qX        g   qY

  The outer rectangle on the right is then also a pushout, so
  comparison of these pushout squares yields a homeomorphism
  h : X/A ≅ Y/B carrying a to b.
-/

@[reducible] def YmodB := quotient_space j
local notation `Y/B` := YmodB
def qY : Y ⟶ Y/B := quotient_space.map j
def ptY : Y/B := quotient_space.pt j
def b : * ⟶ Y/B := Top.mk_hom (λ _, ptY) (by continuity)

def poY : Is_pushout j (Top.point_induced B) qY b := quotient_space.is_pushout j
def po' : Is_pushout i (Top.point_induced A) (qY ∘ g) b :=
Is_pushout_of_Is_pushout_of_Is_pushout po poY

@[reducible] def XmodA := quotient_space i
local notation `X/A` := XmodA
def qX : X ⟶ X/A := quotient_space.map i
def ptX : X/A := quotient_space.pt i
def a : * ⟶ X/A := Top.mk_hom (λ _, ptX) (by continuity)
def poX : Is_pushout i (Top.point_induced A) qX a := quotient_space.is_pushout i

def h : homeomorphism X/A Y/B := pushout.unique poX po'
lemma hab : h.hom ∘ a = b := by simp [h]
lemma hpt : h.hom ptX = ptY := Top.hom_congr hab punit.star
@[simp] lemma hpt' : h.hom ⁻¹' {ptY} = {ptX} :=
let h' := h.equiv in
begin
  ext p, simp [hpt.symm], change h' p = h' ptX ↔ p = ptX, simp
end

lemma pt_closed_iff : is_closed ({ptX} : set X/A) ↔ is_closed ({ptY} : set Y/B) :=
have _ := h.is_closed_iff ({ptY} : set Y/B),
by convert this.symm; rw [hpt']

lemma pt_open_iff : is_open ({ptX} : set X/A) ↔ is_open ({ptY} : set Y/B) :=
have _ := h.is_open_iff ({ptY} : set Y/B),
by convert this.symm; rw [hpt']

theorem range_i_closed_iff_range_j_closed : is_closed (range i) ↔ is_closed (range j) := calc
  is_closed (range i)
    ↔ is_closed ({ptX} : set X/A) : (quotient_space_pt_is_closed_iff i).symm
... ↔ is_closed ({ptY} : set Y/B) : pt_closed_iff
... ↔ is_closed (range j)         : quotient_space_pt_is_closed_iff j

theorem range_i_open_iff_range_j_open : is_open (range i) ↔ is_open (range j) := calc
  is_open (range i)
    ↔ is_open ({ptX} : set X/A) : (quotient_space_pt_is_open_iff i).symm
... ↔ is_open ({ptY} : set Y/B) : pt_open_iff
... ↔ is_open (range j)         : quotient_space_pt_is_open_iff j

-- Now we show that g : X → Y restricts to a homeomorphism g' : X - A → Y - B.

@[reducible] def XminusA := quotient_space.image_complement i
@[reducible] def XmodAminus := quotient_space.minus_base_point i
local notation `X-A` := XminusA
local notation `X/A₋` := XmodAminus

@[reducible] def YminusB := quotient_space.image_complement j
@[reducible] def YmodBminus := quotient_space.minus_base_point j
local notation `Y-B` := YminusB
local notation `Y/B₋` := YmodBminus

def h' : homeomorphism X/A₋ Y/B₋ :=
h.restrict $ calc
    _ = {ptX}ᶜ             : rfl
  ... = (h.hom ⁻¹' {ptY})ᶜ : by rw hpt'
  ... = h.hom ⁻¹' ({ptY}ᶜ) : set.preimage_compl
  ... = _                  : rfl

local notation a ` ≅ ` b := homeomorphism a b

parameter (hyp : is_closed (range i) ∨ is_open (range i))

def hyp' : is_closed (range j) ∨ is_open (range j) :=
(or_congr range_i_closed_iff_range_j_closed range_i_open_iff_range_j_open).mp hyp

def g' : X-A ≅ Y-B :=
calc
  X-A ≅ X/A₋ : quotient_space.homeomorphism_complement i hyp
  ... ≅ Y/B₋ : h'
  ... ≅ Y-B  : (quotient_space.homeomorphism_complement j hyp').symm

lemma g'_g : incl _ ∘ g'.hom = g ∘ incl _ :=
rfl

parameters {C : Top} {k : C ⟶ X} (hk : embedding k) (hik : range i ∩ range k = ∅)

def k' : C ⟶ X-A :=
factor_through_incl k _ (subset_compl_comm.mp $ subset_compl_iff_disjoint.mpr hik)

theorem comp_embedding_of_embedding_of_disjoint : embedding (g ∘ k) :=
show embedding (incl _ ∘ g'.hom ∘ k'), from
have embedding k', from embedding_of_embedding_comp (incl _) hk,
(embedding_incl _).comp (g'.embedding.comp this)

end

end pushout_lemmas_private

export pushout_lemmas_private
  (range_i_open_iff_range_j_open range_i_closed_iff_range_j_closed
   comp_embedding_of_embedding_of_disjoint)

section
parameters {A B X Y : Top} {i : A ⟶ X} {f : A ⟶ B} {g : X ⟶ Y} {j : B ⟶ Y}
parameter (po : Is_pushout i f g j)
parameter (hyp : is_closed (range i) ∨ is_open (range i))

def complement_homeomorphism :
  quotient_space.image_complement i ≅ quotient_space.image_complement j :=
pushout_lemmas_private.g' po hyp

lemma complement_homeomorphism_eq :
  complement_homeomorphism.hom ≫ incl _ = incl _ ≫ g :=
pushout_lemmas_private.g'_g po hyp

end

end homotopy_theory.topological_spaces


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Abhimanyu Pallavi Sudhir, Jean Lo, Calle Sönne

! This file was ported from Lean 3 source module analysis.special_functions.log.deriv
! leanprover-community/mathlib commit 8c8c544bf24ced19b1e76c34bb3262bdae620f82
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.SpecialFunctions.Log.Basic
import Mathbin.Analysis.SpecialFunctions.ExpDeriv

/-!
# Derivative and series expansion of real logarithm

In this file we prove that `real.log` is infinitely smooth at all nonzero `x : ℝ`. We also prove
that the series `∑' n : ℕ, x ^ (n + 1) / (n + 1)` converges to `(-real.log (1 - x))` for all
`x : ℝ`, `|x| < 1`.

## Tags

logarithm, derivative
-/

open Filter Finset Set

open Topology BigOperators

namespace Real

variable {x : ℝ}

theorem hasStrictDerivAt_log_of_pos (hx : 0 < x) : HasStrictDerivAt log x⁻¹ x :=
  by
  have : HasStrictDerivAt log (exp <| log x)⁻¹ x :=
    (hasStrictDerivAt_exp <| log x).of_local_left_inverse (continuousAt_log hx.ne')
        (ne_of_gt <| exp_pos _) <|
      Eventually.mono (lt_mem_nhds hx) @exp_log
  rwa [exp_log hx] at this
#align real.has_strict_deriv_at_log_of_pos Real.hasStrictDerivAt_log_of_pos

theorem hasStrictDerivAt_log (hx : x ≠ 0) : HasStrictDerivAt log x⁻¹ x :=
  by
  cases' hx.lt_or_lt with hx hx
  · convert(has_strict_deriv_at_log_of_pos (neg_pos.mpr hx)).comp x (hasStrictDerivAt_neg x)
    · ext y
      exact (log_neg_eq_log y).symm
    · field_simp [hx.ne]
  · exact has_strict_deriv_at_log_of_pos hx
#align real.has_strict_deriv_at_log Real.hasStrictDerivAt_log

theorem hasDerivAt_log (hx : x ≠ 0) : HasDerivAt log x⁻¹ x :=
  (hasStrictDerivAt_log hx).HasDerivAt
#align real.has_deriv_at_log Real.hasDerivAt_log

theorem differentiableAt_log (hx : x ≠ 0) : DifferentiableAt ℝ log x :=
  (hasDerivAt_log hx).DifferentiableAt
#align real.differentiable_at_log Real.differentiableAt_log

theorem differentiableOn_log : DifferentiableOn ℝ log ({0}ᶜ) := fun x hx =>
  (differentiableAt_log hx).DifferentiableWithinAt
#align real.differentiable_on_log Real.differentiableOn_log

@[simp]
theorem differentiableAt_log_iff : DifferentiableAt ℝ log x ↔ x ≠ 0 :=
  ⟨fun h => continuousAt_log_iff.1 h.ContinuousAt, differentiableAt_log⟩
#align real.differentiable_at_log_iff Real.differentiableAt_log_iff

theorem deriv_log (x : ℝ) : deriv log x = x⁻¹ :=
  if hx : x = 0 then by
    rw [deriv_zero_of_not_differentiableAt
        (mt differentiable_at_log_iff.1 (Classical.not_not.2 hx)),
      hx, inv_zero]
  else (hasDerivAt_log hx).deriv
#align real.deriv_log Real.deriv_log

@[simp]
theorem deriv_log' : deriv log = Inv.inv :=
  funext deriv_log
#align real.deriv_log' Real.deriv_log'

theorem contDiffOn_log {n : ℕ∞} : ContDiffOn ℝ n log ({0}ᶜ) :=
  by
  suffices : ContDiffOn ℝ ⊤ log ({0}ᶜ); exact this.of_le le_top
  refine' (contDiffOn_top_iff_deriv_of_open isOpen_compl_singleton).2 _
  simp [differentiable_on_log, contDiffOn_inv]
#align real.cont_diff_on_log Real.contDiffOn_log

theorem contDiffAt_log {n : ℕ∞} : ContDiffAt ℝ n log x ↔ x ≠ 0 :=
  ⟨fun h => continuousAt_log_iff.1 h.ContinuousAt, fun hx =>
    (contDiffOn_log x hx).ContDiffAt <| IsOpen.mem_nhds isOpen_compl_singleton hx⟩
#align real.cont_diff_at_log Real.contDiffAt_log

end Real

section LogDifferentiable

open Real

section deriv

variable {f : ℝ → ℝ} {x f' : ℝ} {s : Set ℝ}

theorem HasDerivWithinAt.log (hf : HasDerivWithinAt f f' s x) (hx : f x ≠ 0) :
    HasDerivWithinAt (fun y => log (f y)) (f' / f x) s x :=
  by
  rw [div_eq_inv_mul]
  exact (has_deriv_at_log hx).comp_hasDerivWithinAt x hf
#align has_deriv_within_at.log HasDerivWithinAt.log

theorem HasDerivAt.log (hf : HasDerivAt f f' x) (hx : f x ≠ 0) :
    HasDerivAt (fun y => log (f y)) (f' / f x) x :=
  by
  rw [← hasDerivWithinAt_univ] at *
  exact hf.log hx
#align has_deriv_at.log HasDerivAt.log

theorem HasStrictDerivAt.log (hf : HasStrictDerivAt f f' x) (hx : f x ≠ 0) :
    HasStrictDerivAt (fun y => log (f y)) (f' / f x) x :=
  by
  rw [div_eq_inv_mul]
  exact (has_strict_deriv_at_log hx).comp x hf
#align has_strict_deriv_at.log HasStrictDerivAt.log

theorem derivWithin.log (hf : DifferentiableWithinAt ℝ f s x) (hx : f x ≠ 0)
    (hxs : UniqueDiffWithinAt ℝ s x) :
    derivWithin (fun x => log (f x)) s x = derivWithin f s x / f x :=
  (hf.HasDerivWithinAt.log hx).derivWithin hxs
#align deriv_within.log derivWithin.log

@[simp]
theorem deriv.log (hf : DifferentiableAt ℝ f x) (hx : f x ≠ 0) :
    deriv (fun x => log (f x)) x = deriv f x / f x :=
  (hf.HasDerivAt.log hx).deriv
#align deriv.log deriv.log

end deriv

section fderiv

variable {E : Type _} [NormedAddCommGroup E] [NormedSpace ℝ E] {f : E → ℝ} {x : E} {f' : E →L[ℝ] ℝ}
  {s : Set E}

theorem HasFderivWithinAt.log (hf : HasFderivWithinAt f f' s x) (hx : f x ≠ 0) :
    HasFderivWithinAt (fun x => log (f x)) ((f x)⁻¹ • f') s x :=
  (hasDerivAt_log hx).comp_hasFderivWithinAt x hf
#align has_fderiv_within_at.log HasFderivWithinAt.log

theorem HasFderivAt.log (hf : HasFderivAt f f' x) (hx : f x ≠ 0) :
    HasFderivAt (fun x => log (f x)) ((f x)⁻¹ • f') x :=
  (hasDerivAt_log hx).comp_hasFderivAt x hf
#align has_fderiv_at.log HasFderivAt.log

theorem HasStrictFderivAt.log (hf : HasStrictFderivAt f f' x) (hx : f x ≠ 0) :
    HasStrictFderivAt (fun x => log (f x)) ((f x)⁻¹ • f') x :=
  (hasStrictDerivAt_log hx).comp_hasStrictFderivAt x hf
#align has_strict_fderiv_at.log HasStrictFderivAt.log

theorem DifferentiableWithinAt.log (hf : DifferentiableWithinAt ℝ f s x) (hx : f x ≠ 0) :
    DifferentiableWithinAt ℝ (fun x => log (f x)) s x :=
  (hf.HasFderivWithinAt.log hx).DifferentiableWithinAt
#align differentiable_within_at.log DifferentiableWithinAt.log

@[simp]
theorem DifferentiableAt.log (hf : DifferentiableAt ℝ f x) (hx : f x ≠ 0) :
    DifferentiableAt ℝ (fun x => log (f x)) x :=
  (hf.HasFderivAt.log hx).DifferentiableAt
#align differentiable_at.log DifferentiableAt.log

theorem ContDiffAt.log {n} (hf : ContDiffAt ℝ n f x) (hx : f x ≠ 0) :
    ContDiffAt ℝ n (fun x => log (f x)) x :=
  (contDiffAt_log.2 hx).comp x hf
#align cont_diff_at.log ContDiffAt.log

theorem ContDiffWithinAt.log {n} (hf : ContDiffWithinAt ℝ n f s x) (hx : f x ≠ 0) :
    ContDiffWithinAt ℝ n (fun x => log (f x)) s x :=
  (contDiffAt_log.2 hx).comp_contDiffWithinAt x hf
#align cont_diff_within_at.log ContDiffWithinAt.log

theorem ContDiffOn.log {n} (hf : ContDiffOn ℝ n f s) (hs : ∀ x ∈ s, f x ≠ 0) :
    ContDiffOn ℝ n (fun x => log (f x)) s := fun x hx => (hf x hx).log (hs x hx)
#align cont_diff_on.log ContDiffOn.log

theorem ContDiff.log {n} (hf : ContDiff ℝ n f) (h : ∀ x, f x ≠ 0) :
    ContDiff ℝ n fun x => log (f x) :=
  contDiff_iff_contDiffAt.2 fun x => hf.ContDiffAt.log (h x)
#align cont_diff.log ContDiff.log

theorem DifferentiableOn.log (hf : DifferentiableOn ℝ f s) (hx : ∀ x ∈ s, f x ≠ 0) :
    DifferentiableOn ℝ (fun x => log (f x)) s := fun x h => (hf x h).log (hx x h)
#align differentiable_on.log DifferentiableOn.log

@[simp]
theorem Differentiable.log (hf : Differentiable ℝ f) (hx : ∀ x, f x ≠ 0) :
    Differentiable ℝ fun x => log (f x) := fun x => (hf x).log (hx x)
#align differentiable.log Differentiable.log

theorem fderivWithin.log (hf : DifferentiableWithinAt ℝ f s x) (hx : f x ≠ 0)
    (hxs : UniqueDiffWithinAt ℝ s x) :
    fderivWithin ℝ (fun x => log (f x)) s x = (f x)⁻¹ • fderivWithin ℝ f s x :=
  (hf.HasFderivWithinAt.log hx).fderivWithin hxs
#align fderiv_within.log fderivWithin.log

@[simp]
theorem fderiv.log (hf : DifferentiableAt ℝ f x) (hx : f x ≠ 0) :
    fderiv ℝ (fun x => log (f x)) x = (f x)⁻¹ • fderiv ℝ f x :=
  (hf.HasFderivAt.log hx).fderiv
#align fderiv.log fderiv.log

end fderiv

end LogDifferentiable

namespace Real

/-- The function `x * log (1 + t / x)` tends to `t` at `+∞`. -/
theorem tendsto_mul_log_one_plus_div_atTop (t : ℝ) :
    Tendsto (fun x => x * log (1 + t / x)) atTop (𝓝 t) :=
  by
  have h₁ : tendsto (fun h => h⁻¹ * log (1 + t * h)) (𝓝[≠] 0) (𝓝 t) := by
    simpa [hasDerivAt_iff_tendsto_slope, slope_fun_def] using
      (((hasDerivAt_id (0 : ℝ)).const_mul t).const_add 1).log (by simp)
  have h₂ : tendsto (fun x : ℝ => x⁻¹) at_top (𝓝[≠] 0) :=
    tendsto_inv_at_top_zero'.mono_right (nhdsWithin_mono _ fun x hx => (set.mem_Ioi.mp hx).ne')
  simpa only [(· ∘ ·), inv_inv] using h₁.comp h₂
#align real.tendsto_mul_log_one_plus_div_at_top Real.tendsto_mul_log_one_plus_div_atTop

open BigOperators

/-- A crude lemma estimating the difference between `log (1-x)` and its Taylor series at `0`,
where the main point of the bound is that it tends to `0`. The goal is to deduce the series
expansion of the logarithm, in `has_sum_pow_div_log_of_abs_lt_1`.
-/
theorem abs_log_sub_add_sum_range_le {x : ℝ} (h : |x| < 1) (n : ℕ) :
    |(∑ i in range n, x ^ (i + 1) / (i + 1)) + log (1 - x)| ≤ |x| ^ (n + 1) / (1 - |x|) :=
  by
  /- For the proof, we show that the derivative of the function to be estimated is small,
    and then apply the mean value inequality. -/
  let F : ℝ → ℝ := fun x => (∑ i in range n, x ^ (i + 1) / (i + 1)) + log (1 - x)
  -- First step: compute the derivative of `F`
  have A : ∀ y ∈ Ioo (-1 : ℝ) 1, deriv F y = -y ^ n / (1 - y) :=
    by
    intro y hy
    have : (∑ i in range n, (↑i + 1) * y ^ i / (↑i + 1)) = ∑ i in range n, y ^ i :=
      by
      congr with i
      exact mul_div_cancel_left _ (Nat.cast_add_one_pos i).ne'
    field_simp [F, this, geom_sum_eq (ne_of_lt hy.2), sub_ne_zero_of_ne (ne_of_gt hy.2),
      sub_ne_zero_of_ne (ne_of_lt hy.2)]
    ring
  -- second step: show that the derivative of `F` is small
  have B : ∀ y ∈ Icc (-|x|) (|x|), |deriv F y| ≤ |x| ^ n / (1 - |x|) :=
    by
    intro y hy
    have : y ∈ Ioo (-(1 : ℝ)) 1 := ⟨lt_of_lt_of_le (neg_lt_neg h) hy.1, lt_of_le_of_lt hy.2 h⟩
    calc
      |deriv F y| = |-y ^ n / (1 - y)| := by rw [A y this]
      _ ≤ |x| ^ n / (1 - |x|) := by
        have : |y| ≤ |x| := abs_le.2 hy
        have : 0 < 1 - |x| := by linarith
        have : 1 - |x| ≤ |1 - y| := le_trans (by linarith [hy.2]) (le_abs_self _)
        simp only [← pow_abs, abs_div, abs_neg]
        apply_rules [div_le_div, pow_nonneg, abs_nonneg, pow_le_pow_of_le_left]

  -- third step: apply the mean value inequality
  have C : ‖F x - F 0‖ ≤ |x| ^ n / (1 - |x|) * ‖x - 0‖ :=
    by
    have : ∀ y ∈ Icc (-|x|) (|x|), DifferentiableAt ℝ F y :=
      by
      intro y hy
      have : 1 - y ≠ 0 := sub_ne_zero_of_ne (ne_of_gt (lt_of_le_of_lt hy.2 h))
      simp [F, this]
    apply Convex.norm_image_sub_le_of_norm_deriv_le this B (convex_Icc _ _) _ _
    · simp
    · simp [le_abs_self x, neg_le.mp (neg_le_abs_self x)]
  -- fourth step: conclude by massaging the inequality of the third step
  simpa [F, norm_eq_abs, div_mul_eq_mul_div, pow_succ'] using C
#align real.abs_log_sub_add_sum_range_le Real.abs_log_sub_add_sum_range_le

/-- Power series expansion of the logarithm around `1`. -/
theorem hasSum_pow_div_log_of_abs_lt_1 {x : ℝ} (h : |x| < 1) :
    HasSum (fun n : ℕ => x ^ (n + 1) / (n + 1)) (-log (1 - x)) :=
  by
  rw [Summable.hasSum_iff_tendsto_nat]
  show tendsto (fun n : ℕ => ∑ i : ℕ in range n, x ^ (i + 1) / (i + 1)) at_top (𝓝 (-log (1 - x)))
  · rw [tendsto_iff_norm_tendsto_zero]
    simp only [norm_eq_abs, sub_neg_eq_add]
    refine' squeeze_zero (fun n => abs_nonneg _) (abs_log_sub_add_sum_range_le h) _
    suffices tendsto (fun t : ℕ => |x| ^ (t + 1) / (1 - |x|)) at_top (𝓝 (|x| * 0 / (1 - |x|))) by
      simpa
    simp only [pow_succ]
    refine' (tendsto_const_nhds.mul _).div_const _
    exact tendsto_pow_atTop_nhds_0_of_lt_1 (abs_nonneg _) h
  show Summable fun n : ℕ => x ^ (n + 1) / (n + 1)
  · refine' summable_of_norm_bounded _ (summable_geometric_of_lt_1 (abs_nonneg _) h) fun i => _
    calc
      ‖x ^ (i + 1) / (i + 1)‖ = |x| ^ (i + 1) / (i + 1) :=
        by
        have : (0 : ℝ) ≤ i + 1 := le_of_lt (Nat.cast_add_one_pos i)
        rw [norm_eq_abs, abs_div, ← pow_abs, abs_of_nonneg this]
      _ ≤ |x| ^ (i + 1) / (0 + 1) :=
        by
        apply_rules [div_le_div_of_le_left, pow_nonneg, abs_nonneg, add_le_add_right, i.cast_nonneg]
        norm_num
      _ ≤ |x| ^ i := by
        simpa [pow_succ'] using mul_le_of_le_one_right (pow_nonneg (abs_nonneg x) i) (le_of_lt h)

#align real.has_sum_pow_div_log_of_abs_lt_1 Real.hasSum_pow_div_log_of_abs_lt_1

/-- Power series expansion of `log(1 + x) - log(1 - x)` for `|x| < 1`. -/
theorem hasSum_log_sub_log_of_abs_lt_1 {x : ℝ} (h : |x| < 1) :
    HasSum (fun k : ℕ => (2 : ℝ) * (1 / (2 * k + 1)) * x ^ (2 * k + 1))
      (log (1 + x) - log (1 - x)) :=
  by
  let term := fun n : ℕ => -1 * ((-x) ^ (n + 1) / ((n : ℝ) + 1)) + x ^ (n + 1) / (n + 1)
  have h_term_eq_goal : term ∘ (· * ·) 2 = fun k : ℕ => 2 * (1 / (2 * k + 1)) * x ^ (2 * k + 1) :=
    by
    ext n
    dsimp [term]
    rw [Odd.neg_pow (⟨n, rfl⟩ : Odd (2 * n + 1)) x]
    push_cast
    ring_nf
  rw [← h_term_eq_goal, (mul_right_injective₀ (two_ne_zero' ℕ)).hasSum_iff]
  · have h₁ := (has_sum_pow_div_log_of_abs_lt_1 (Eq.trans_lt (abs_neg x) h)).mul_left (-1)
    convert h₁.add (has_sum_pow_div_log_of_abs_lt_1 h)
    ring_nf
  · intro m hm
    rw [range_two_mul, Set.mem_setOf_eq, ← Nat.even_add_one] at hm
    dsimp [term]
    rw [Even.neg_pow hm, neg_one_mul, neg_add_self]
#align real.has_sum_log_sub_log_of_abs_lt_1 Real.hasSum_log_sub_log_of_abs_lt_1

/-- Expansion of `log (1 + a⁻¹)` as a series in powers of `1 / (2 * a + 1)`. -/
theorem hasSum_log_one_add_inv {a : ℝ} (h : 0 < a) :
    HasSum (fun k : ℕ => (2 : ℝ) * (1 / (2 * k + 1)) * (1 / (2 * a + 1)) ^ (2 * k + 1))
      (log (1 + a⁻¹)) :=
  by
  have h₁ : |1 / (2 * a + 1)| < 1 := by
    rw [abs_of_pos, div_lt_one]
    · linarith
    · linarith
    · exact div_pos one_pos (by linarith)
  convert has_sum_log_sub_log_of_abs_lt_1 h₁
  have h₂ : (2 : ℝ) * a + 1 ≠ 0 := by linarith
  have h₃ := h.ne'
  rw [← log_div]
  · congr
    field_simp
    linarith
  · field_simp
    linarith
  · field_simp
#align real.has_sum_log_one_add_inv Real.hasSum_log_one_add_inv

end Real


/-
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 measure_theory.group.measurable_equiv
import measure_theory.measure.regular
import dynamics.ergodic.measure_preserving
import dynamics.minimal

/-!
# Measures invariant under group actions

A measure `μ : measure α` is said to be *invariant* under an action of a group `G` if scalar
multiplication by `c : G` is a measure preserving map for all `c`. In this file we define a
typeclass for measures invariant under action of an (additive or multiplicative) group and prove
some basic properties of such measures.
-/

open_locale ennreal nnreal pointwise topological_space
open measure_theory measure_theory.measure set function

namespace measure_theory

variables {G M α : Type*}

/-- A measure `μ : measure α` is invariant under an additive action of `M` on `α` if for any
measurable set `s : set α` and `c : M`, the measure of its preimage under `λ x, c +ᵥ x` is equal to
the measure of `s`. -/
class vadd_invariant_measure (M α : Type*) [has_vadd M α] {_ : measurable_space α}
  (μ : measure α) : Prop :=
(measure_preimage_vadd [] : ∀ (c : M) ⦃s : set α⦄, measurable_set s → μ ((λ x, c +ᵥ x) ⁻¹' s) = μ s)

/-- A measure `μ : measure α` is invariant under a multiplicative action of `M` on `α` if for any
measurable set `s : set α` and `c : M`, the measure of its preimage under `λ x, c • x` is equal to
the measure of `s`. -/
@[to_additive] class smul_invariant_measure (M α : Type*) [has_smul M α] {_ : measurable_space α}
  (μ : measure α) : Prop :=
(measure_preimage_smul [] : ∀ (c : M) ⦃s : set α⦄, measurable_set s → μ ((λ x, c • x) ⁻¹' s) = μ s)

namespace smul_invariant_measure

@[to_additive] instance zero [measurable_space α] [has_smul M α] : smul_invariant_measure M α 0 :=
⟨λ _ _ _, rfl⟩

variables [has_smul M α] {m : measurable_space α} {μ ν : measure α}

@[to_additive] instance add [smul_invariant_measure M α μ] [smul_invariant_measure M α ν] :
  smul_invariant_measure M α (μ + ν) :=
⟨λ c s hs, show _ + _ = _ + _,
  from congr_arg2 (+) (measure_preimage_smul μ c hs) (measure_preimage_smul ν c hs)⟩

@[to_additive] instance smul [smul_invariant_measure M α μ] (c : ℝ≥0∞) :
  smul_invariant_measure M α (c • μ) :=
⟨λ a s hs, show c • _ = c • _, from congr_arg ((•) c) (measure_preimage_smul μ a hs)⟩

@[to_additive] instance smul_nnreal [smul_invariant_measure M α μ] (c : ℝ≥0) :
  smul_invariant_measure M α (c • μ) :=
smul_invariant_measure.smul c

end smul_invariant_measure

variables (G) {m : measurable_space α} [group G] [mul_action G α] [measurable_space G]
  [has_measurable_smul G α] (c : G) (μ : measure α)

/-- Equivalent definitions of a measure invariant under a multiplicative action of a group.

- 0: `smul_invariant_measure G α μ`;

- 1: for every `c : G` and a measurable set `s`, the measure of the preimage of `s` under scalar
     multiplication by `c` is equal to the measure of `s`;

- 2: for every `c : G` and a measurable set `s`, the measure of the image `c • s` of `s` under
     scalar multiplication by `c` is equal to the measure of `s`;

- 3, 4: properties 2, 3 for any set, including non-measurable ones;

- 5: for any `c : G`, scalar multiplication by `c` maps `μ` to `μ`;

- 6: for any `c : G`, scalar multiplication by `c` is a measure preserving map. -/
@[to_additive] lemma smul_invariant_measure_tfae :
  tfae [smul_invariant_measure G α μ,
    ∀ (c : G) s, measurable_set s → μ (((•) c) ⁻¹' s) = μ s,
    ∀ (c : G) s, measurable_set s → μ (c • s) = μ s,
    ∀ (c : G) s, μ (((•) c) ⁻¹' s) = μ s,
    ∀ (c : G) s, μ (c • s) = μ s,
    ∀ c : G, measure.map ((•) c) μ = μ,
    ∀ c : G, measure_preserving ((•) c) μ μ] :=
begin
  tfae_have : 1 ↔ 2, from ⟨λ h, h.1, λ h, ⟨h⟩⟩,
  tfae_have : 2 → 6,
    from λ H c, ext (λ s hs, by rw [map_apply (measurable_const_smul c) hs, H _ _ hs]),
  tfae_have : 6 → 7, from λ H c, ⟨measurable_const_smul c, H c⟩,
  tfae_have : 7 → 4, from λ H c, (H c).measure_preimage_emb (measurable_embedding_const_smul c),
  tfae_have : 4 → 5, from λ H c s, by { rw [← preimage_smul_inv], apply H },
  tfae_have : 5 → 3, from λ H c s hs, H c s,
  tfae_have : 3 → 2, { intros H c s hs, rw preimage_smul, exact H c⁻¹ s hs },
  tfae_finish
end

/-- Equivalent definitions of a measure invariant under an additive action of a group.

- 0: `vadd_invariant_measure G α μ`;

- 1: for every `c : G` and a measurable set `s`, the measure of the preimage of `s` under
     vector addition `(+ᵥ) c` is equal to the measure of `s`;

- 2: for every `c : G` and a measurable set `s`, the measure of the image `c +ᵥ s` of `s` under
     vector addition `(+ᵥ) c` is equal to the measure of `s`;

- 3, 4: properties 2, 3 for any set, including non-measurable ones;

- 5: for any `c : G`, vector addition of `c` maps `μ` to `μ`;

- 6: for any `c : G`, vector addition of `c` is a measure preserving map. -/
add_decl_doc vadd_invariant_measure_tfae

variables {G} [smul_invariant_measure G α μ]

@[to_additive] lemma measure_preserving_smul : measure_preserving ((•) c) μ μ :=
((smul_invariant_measure_tfae G μ).out 0 6).mp ‹_› c

@[simp, to_additive] lemma map_smul : map ((•) c) μ = μ :=
(measure_preserving_smul c μ).map_eq

@[simp, to_additive] lemma measure_preimage_smul (s : set α) : μ ((•) c ⁻¹' s) = μ s :=
((smul_invariant_measure_tfae G μ).out 0 3).mp ‹_› c s

@[simp, to_additive] lemma measure_smul_set (s : set α) : μ (c • s) = μ s :=
((smul_invariant_measure_tfae G μ).out 0 4).mp ‹_› c s

variable {μ}

@[to_additive] lemma null_measurable_set.smul {s} (hs : null_measurable_set s μ) (c : G) :
  null_measurable_set (c • s) μ :=
by simpa only [← preimage_smul_inv]
  using hs.preimage (measure_preserving_smul _ _).quasi_measure_preserving

section is_minimal

variables (G) [topological_space α] [has_continuous_const_smul G α] [mul_action.is_minimal G α]
  {K U : set α}

/-- If measure `μ` is invariant under a group action and is nonzero on a compact set `K`, then it is
positive on any nonempty open set. In case of a regular measure, one can assume `μ ≠ 0` instead of
`μ K ≠ 0`, see `measure_theory.measure_is_open_pos_of_smul_invariant_of_ne_zero`. -/
@[to_additive] lemma measure_is_open_pos_of_smul_invariant_of_compact_ne_zero (hK : is_compact K)
  (hμK : μ K ≠ 0) (hU : is_open U) (hne : U.nonempty) : 0 < μ U :=
let ⟨t, ht⟩ := hK.exists_finite_cover_smul G hU hne
in pos_iff_ne_zero.2 $ λ hμU, hμK $ measure_mono_null ht $
  (measure_bUnion_null_iff t.countable_to_set).2 $ λ _ _, by rwa measure_smul_set

/-- If measure `μ` is invariant under an additive group action and is nonzero on a compact set `K`,
then it is positive on any nonempty open set. In case of a regular measure, one can assume `μ ≠ 0`
instead of `μ K ≠ 0`, see `measure_theory.measure_is_open_pos_of_vadd_invariant_of_ne_zero`. -/
add_decl_doc measure_is_open_pos_of_vadd_invariant_of_compact_ne_zero

@[to_additive] lemma is_locally_finite_measure_of_smul_invariant (hU : is_open U) (hne : U.nonempty)
  (hμU : μ U ≠ ∞) : is_locally_finite_measure μ :=
⟨λ x, let ⟨g, hg⟩ := hU.exists_smul_mem G x hne in
  ⟨(•) g ⁻¹' U, (hU.preimage (continuous_id.const_smul _)).mem_nhds hg, ne.lt_top $
    by rwa [measure_preimage_smul]⟩⟩

variables [measure.regular μ]

@[to_additive] lemma measure_is_open_pos_of_smul_invariant_of_ne_zero (hμ : μ ≠ 0) (hU : is_open U)
  (hne : U.nonempty) : 0 < μ U :=
let ⟨K, hK, hμK⟩ := regular.exists_compact_not_null.mpr hμ
in measure_is_open_pos_of_smul_invariant_of_compact_ne_zero G hK hμK hU hne

@[to_additive] lemma measure_pos_iff_nonempty_of_smul_invariant (hμ : μ ≠ 0) (hU : is_open U) :
  0 < μ U ↔ U.nonempty :=
⟨λ h, nonempty_of_measure_ne_zero h.ne', measure_is_open_pos_of_smul_invariant_of_ne_zero G hμ hU⟩

include G

@[to_additive] lemma measure_eq_zero_iff_eq_empty_of_smul_invariant (hμ : μ ≠ 0) (hU : is_open U) :
  μ U = 0 ↔ U = ∅ :=
by rw [← not_iff_not, ← ne.def, ← pos_iff_ne_zero,
  measure_pos_iff_nonempty_of_smul_invariant G hμ hU, ← ne_empty_iff_nonempty]

end is_minimal

end measure_theory


--------------------------------------------------------------------------------
-- File     : PUZ031_1 : TPTP v7.3.0. Released v5.0.0.
-- Domain   : Puzzles
-- Problem  : Schubert's Steamroller
-- Version  : Especial.
-- English  : Wolves, foxes, birds, caterpillars, and snails are animals, and
--            there are some of each of them. Also there are some grains, and
--            grains are plants. Every animal either likes to eat all plants
--            or all animals much smaller than itself that like to eat some
--            plants. Caterpillars and snails are much smaller than birds,
--            which are much smaller than foxes, which in turn are much
--            smaller than wolves. Wolves do not like to eat foxes or grains,
--            while birds like to eat caterpillars but not snails.
--            Caterpillars and snails like to eat some plants. Therefore
--            there is an animal that likes to eat a grain eating animal.

-- Source   : [TPTP]
--------------------------------------------------------------------------------

variable animal: Type
variable wolf: Type
variable wolf_to_animal: wolf → animal
variable fox: Type
variable fox_to_animal: fox → animal
variable bird: Type
variable bird_to_animal: bird → animal
variable caterpillar: Type
variable caterpillar_to_animal: caterpillar → animal
variable snail: Type
variable snail_to_animal: snail → animal
variable plant: Type
variable grain: Type
variable grain_to_plant: grain → plant
variable edible: Type
variable animal_to_edible: animal → edible
variable plant_to_edible: plant → edible
variable eats: animal × edible → Prop
variable much_smaller: animal × animal → Prop

-- existence
variable existence : ∀ A : Type, A

-- axioms
variable pel47_7:
  ∀ (X: animal),
  (∀ (Y: plant), eats(X,plant_to_edible(Y)))
  ∨
  (∃ Y1: animal,
  (much_smaller(Y1,X) ∧ ∃ Z: plant, eats(Y1,plant_to_edible(Z)))
  → eats(X,animal_to_edible(Y1)))

variable pel47_8:
  ∀ (X: snail) (Y: bird),
  much_smaller(snail_to_animal(X),bird_to_animal(Y))

variable pel47_8a:
  ∀ (X: caterpillar) (Y: bird),
  much_smaller(caterpillar_to_animal(X),bird_to_animal(Y))

variable pel47_9:
  ∀ (X: bird) (Y: fox),
  much_smaller(bird_to_animal(X),fox_to_animal(Y))

variable pel47_10:
  ∀ (X: fox) (Y: wolf),
  much_smaller(fox_to_animal(X),wolf_to_animal(Y))

variable pel47_11:
  ∀ (X: wolf) (Y: fox),
  ¬ eats(wolf_to_animal(X),animal_to_edible(fox_to_animal(Y)))

variable pel47_11a:
  ∀ (X: wolf) (Y: grain),
  ¬ eats(wolf_to_animal(X),plant_to_edible(grain_to_plant(Y)))

variable pel47_12:
  ∀ (X: bird) (Y: caterpillar),
  eats(bird_to_animal(X),animal_to_edible(caterpillar_to_animal(Y)))

variable pel47_13:
  ∀ (X: bird) (Y: snail),
  ¬ eats(bird_to_animal(X),animal_to_edible(snail_to_animal(Y)))

variable pel47_14:
  ∀ (X: caterpillar),
  ∃ (Y: plant), eats(caterpillar_to_animal(X),plant_to_edible(Y))

variable pel47_14a:
  ∀ (X: snail),
  ∃ (Y: plant), eats(snail_to_animal(X),plant_to_edible(Y))

-- conjecture : pel47
include existence
include pel47_7 pel47_11a
include wolf wolf_to_animal caterpillar
include grain grain_to_plant
theorem pel47:
∃ (X: animal) (Y: animal) (Z: grain),
(eats(Y,plant_to_edible(grain_to_plant(Z))) ∧ eats(X,animal_to_edible(Y))) :=
  begin
    have ex_wolf : wolf, from existence _,
    cases pel47_7 (wolf_to_animal ex_wolf),
      have ex_grain : grain, from existence _,
      have wolf_eats_grain, from h (grain_to_plant ex_grain),
      have not_wolf_eats_grain, from (pel47_11a ex_wolf ex_grain),
      contradiction,

      exact sorry
  end


import EggTactic

set_option trace.EggTactic.egg true
theorem inv_inv
  {G: Type}
  (inv: G → G)
  (mul: G → G → G)
  (one: G)
  (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c))
  (invLeft: forall (a: G), mul (inv a) a = one)
  (oneMul: forall (a: G), mul one a = a)
  (mulOne: forall (a: G), mul a one = a)
  (invRight: forall (a: G), mul a (inv a) = one)
  (x: G)
  : (inv (inv x) = x) := by
  eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight]

theorem inv_mul_cancel_left
  {G: Type}
  (inv: G → G)
  (mul: G → G → G)
  (one: G)
  (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c))
  (invLeft: forall (a: G), mul (inv a) a = one)
  (mulOne: forall (a: G), mul a one = a)
  (oneMul: forall (a: G), mul one a = a)
  (invRight: forall (a: G), mul a (inv a) = one)
  (x y : G)
  : (mul (inv x) (mul x y)) = y := by
  eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight]

theorem mul_inv_cancel_left
  {G: Type}
  (inv: G → G)
  (mul: G → G → G)
  (one: G)
  (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c))
  (invLeft: forall (a: G), mul (inv a) a = one)
  (mulOne: forall (a: G), a = mul a one)
  (oneMul: forall (a: G), mul one a = a)
  (invRight: forall (a: G), one = mul a (inv a))
  (x y : G)
  : (mul x (mul (inv x) y)) = y := by
  eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight]

theorem inv_mul
  {G: Type}
  (inv: G → G)
  (mul: G → G → G)
  (one: G)
  (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c))
  (invLeft: forall (a: G), mul (inv a) a = one)
  (oneMul: forall (a: G), mul one a = a)
  (mulOne: forall (a: G), mul a one = a)
  (invRight: forall (a: G), mul a (inv a) = one)
  (x y : G)
  : (inv (mul x y)) = (mul (inv y) (inv x)) := by
  eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight] (timeLimit := 5)

#print inv_mul

theorem one_inv
  {G: Type}
  (inv: G → G)
  (mul: G → G → G)
  (one: G)
  (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c))
  (invLeft: forall (a: G), mul (inv a) a = one)
  (mulOne: forall (a: G), a = mul a one)
  (oneMul: forall (a: G), mul one a = a)
  (invRight: forall (a: G), one = mul a (inv a))
  (x y : G)
  : (inv one) = one := by
  eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight]

#print inv_mul_cancel_left

/-
theorem test_simplification
  {G: Type}
  (inv: G → G)
  (mul: G → G → G)
  (one: G)
  (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c))
  (invLeft: forall (a: G), mul (inv a) a = one)
  (mulOne: forall (a: G), a = mul a one)
  (oneMul: forall (a: G), mul one a = a)
  (invRight: forall (a: G), one = mul a (inv a))
  (x y : G)
  -- not equal terms, but simplies
  : (inv (mul x (inv y))) = one := by
  eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight]

#print test_simplification
-/