/-
Copyright (c) 2022 Matthias Uschold. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Matthias Uschold.
-/

import .def_amenable
import data.set        -- basics on sets
import data.set.finite -- basics on finite sets
import data.finset     -- type-level finite sets

import algebra.big_operators.basic
import algebra.big_operators.order

import .aux_lemmas_sums

/-!
# Finite groups are amenable

In this file, we show that finite groups are amenable.
We do this via an explicit construction of a left invariant mean.

## Main Definitions

- `inv_mean_of_fin`   : The explicitely constructed, left invariant mean

## Main Statements

- `amenable_of_finite`: Finite groups are amenable

## References
* [C. Löh, *Geometric Group Theory*, Example 9.1.2][loeh17]
* <https://en.wikipedia.org/wiki/Amenable_group>

## Tags

amenable finite groups, finite, amenable

-/

open_locale big_operators -- to enable ∑ notation

open classical

namespace amenable_finite

variables
{G:Type*}
[group G]
(G_fin: fintype G)

include G_fin

/-!The finset given by all elements in G-/
local notation `setG` := (@finset.univ G G_fin)

/--The averaging map, given by summing all values of f,
then dividing by the cardinality of G-/
noncomputable def avg_map
  : (bounded_continuous_function G ℝ) → ℝ
:= (λ f, (finset.card setG:ℝ)⁻¹ * ∑ x in setG, f x)

lemma avg_map_add'
  : ∀ f g, (avg_map G_fin) (f+g) = (avg_map G_fin) f + (avg_map G_fin) g
:= begin
  assume f g,
  calc  (avg_map G_fin) (f+g)
      = (finset.card setG :ℝ)⁻¹
            * ∑ x in setG, (f+g) x
        : by simp[avg_map]
  ... = (finset.card setG :ℝ)⁻¹
            * ∑ x in setG, (f x + g x)
        : by {
          congr' 1,
        }
  ... = (finset.card setG :ℝ)⁻¹ *
        (∑ x in setG, f x + ∑ x in setG, g x)
        : by simp [finset.sum_add_distrib]
  ... = (finset.card setG :ℝ)⁻¹ * ∑ x in setG, f x +
        (finset.card setG :ℝ)⁻¹ * ∑ x in setG, g x
        : by ring
  ... = (avg_map G_fin) f + (avg_map G_fin) g
        : by simp[avg_map],
end

lemma avg_map_smul'
  : ∀ (r:ℝ) f, (avg_map G_fin) (r•f) = r•((avg_map G_fin) f)
:= begin
  assume r f,
  calc  (avg_map G_fin) (r•f)
      = (finset.card setG :ℝ)⁻¹
            * ∑ x in setG, (r•f) x
        : by simp[avg_map]
  ... = (finset.card setG :ℝ)⁻¹
            * ∑ x in setG, r * f x
        : by simp
  ... = (finset.card setG :ℝ)⁻¹
            * ( r *  ∑ x in setG,  f x)
        : by simp [sum_scalar r]
  ... = r * ((finset.card setG :ℝ)⁻¹
            *  ∑ x in setG,  f x)
        : by ring
  ... = r * (avg_map G_fin) f
        : by simp[avg_map]
  ... = r•((avg_map G_fin) f)
        : by simp,
end

-- The averaging map is a linear map
noncomputable def avg_linmap
  : (bounded_continuous_function G ℝ) →ₗ[ℝ] ℝ
:= linear_map.mk (avg_map G_fin)
      (avg_map_add' G_fin) (avg_map_smul' G_fin)

lemma avg_map_norm
  : (avg_linmap G_fin) ((bounded_continuous_function.const G (1:ℝ))) = 1
:= begin
  -- later, we need that |G| ≠ 0
  have card_neq0 : finset.card setG ≠ 0,
  {
    have : setG ≠ ∅,
    { -- this is not the most straightforward way
      let x0 : G  := classical.choice has_one.nonempty,
      have : x0 ∈ setG := finset.mem_univ x0,
      exact finset.ne_empty_of_mem this,
    },
    finish,
  },

  calc  (avg_linmap G_fin) ((bounded_continuous_function.const G (1:ℝ)))
      = (finset.card setG :ℝ)⁻¹ * ∑ x in setG,
                      ((bounded_continuous_function.const G (1:ℝ)) x)
        : by simp[avg_linmap, avg_map]
  ... = (finset.card setG :ℝ)⁻¹ * ∑ x in setG, 1
        : by simp
  ... = (finset.card setG :ℝ)⁻¹ * ((finset.card setG) * (1:ℝ))
        : by simp
  ... = (finset.card setG :ℝ)⁻¹ * (finset.card setG)
        : by simp
  ... = (1:ℝ)
        : by simp [nat.cast_ne_zero.mpr card_neq0],
end

lemma avg_map_pos
  : ∀ (f : bounded_continuous_function G ℝ),
                    (∀ (x:G), f x ≥ 0) → (avg_linmap G_fin) f ≥ 0
:= begin
  assume f :bounded_continuous_function G ℝ,
  assume f_nonneg:  ∀ (x:G), f x ≥ 0,

  have : ∀ x ∈ setG, f x ≥ 0
    := by tauto,

  have sum_nonneg:  ∑ x in setG, f x ≥ 0,
  {
    calc (0:ℝ)
        = ∑ x in setG, (0:ℝ)
            : by simp
    ... ≤ ∑ x in setG, f x
            : finset.sum_le_sum this,
  },

  have card_nonneg : (finset.card setG:ℝ)⁻¹ ≥ 0
            := by simp[zero_le (finset.card setG)],

  calc (avg_linmap G_fin) f
      = (finset.card setG :ℝ)⁻¹ * ∑ x in setG, f x
        : by simp[avg_linmap, avg_map]
  ... ≥ 0
        : mul_nonneg card_nonneg sum_nonneg,
end

/--The excplicit mean on a finite group-/
noncomputable def mean_fin
  : mean G
:= mean.mk (avg_linmap G_fin) (avg_map_norm _) (avg_map_pos _)

omit G_fin

lemma perm_left_inverse {G:Type*} [group G]
  (g: G)
  : function.left_inverse (left_mul g⁻¹) (left_mul g)
:= begin
  unfold function.left_inverse,
  assume x:G,
  calc  left_mul g⁻¹ (left_mul g x)
      = left_mul g⁻¹ (g*x)
        : by simp [left_mul]
  ... = g⁻¹ * (g*x)
        : by simp [left_mul]
  ... = x
        :by group,
end

lemma perm_right_inverse {G:Type*} [group G]
  (g: G)
  : function.right_inverse (left_mul g⁻¹) (left_mul g)
:= begin
  unfold function.right_inverse,
  assume x:G,
  calc  left_mul g (left_mul g⁻¹ x)
      = left_mul g (g⁻¹*x)
        : by simp [left_mul]
  ... = g * (g⁻¹*x)
        : by simp [left_mul]
  ... = x
        :by group,
end

def left_mul_perm {G:Type*} [group G]
  (g: G)
  : equiv.perm G
:= equiv.mk (left_mul g) (left_mul g⁻¹)
          (perm_left_inverse g) (perm_right_inverse g)

include G_fin

/--This given mean is left invariant-/
lemma avg_map_left_inv
  : ∀(g:G), ∀(f: bounded_continuous_function G ℝ),
      (avg_linmap G_fin) (left_translate g f) = (avg_linmap G_fin) f
:= begin
	assume g : G,
	assume f,

	-- we first prove that the sums are equal
	have sums_eq: ∑ x in setG, f (g⁻¹*x)
							= ∑ x in setG, f x,
	{
		let σ : equiv.perm G := left_mul_perm g⁻¹,
		exact equiv.perm.sum_comp σ  setG _ (by norm_num),
	},

	calc  (avg_linmap G_fin) (left_translate g f)
	    = (finset.card setG :ℝ)⁻¹ * ∑ x in setG, (left_translate g f) x
			 	: by simp[avg_linmap, avg_map]
	... = (finset.card setG :ℝ)⁻¹ * ∑ x in setG, f (g⁻¹*x)
				: by congr';apply finset.sum_congr
	... = (finset.card setG :ℝ)⁻¹ * ∑ x in setG, f x
				: congr_arg (has_mul.mul (finset.card setG :ℝ)⁻¹) sums_eq
	... = (avg_linmap G_fin) f
				: by simp [avg_linmap, avg_map],
end

/--The explicit left invariant mean on a finite group-/
noncomputable def inv_mean_of_fin
  : left_invariant_mean G
:= left_invariant_mean.mk (mean_fin G_fin) (avg_map_left_inv G_fin)

omit G_fin

/--Finite groups are amenable-/
theorem amenable_of_finite
(G:Type*)
[group G]
[topological_space G]
[discrete_topology G]
(G_fin: fintype G)
: amenable G
:= amenable_of_invmean (inv_mean_of_fin G_fin)

end amenable_finite


-- ---------------------------------------------------------------------
-- Ejercicio. Abrir el espacion de nombres set.
-- ----------------------------------------------------------------------

import tactic

open set

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar ningún elemento pertenece al vacío.
-- ----------------------------------------------------------------------

example (x : ℕ) : x ∉ (∅ : set ℕ) :=
not_false

example (x : ℕ) (h : x ∈ (∅ : set ℕ)) : false :=
h

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar tofos los elementos pertenecen al universal.
-- ----------------------------------------------------------------------

example (x : ℕ) : x ∈ (univ : set ℕ) :=
trivial


--Rohan and Enrico
import data.set
namespace prop_logic

inductive fml
| atom (i : ℕ)
| imp (a b : fml)
| not (a : fml)
open fml

infixr ` →' `:50 := imp
local notation ` ¬' ` := fml.not

--CAN I MAKE THIS A FUNCTION INTO PROP INSTEAD OF TYPE????
inductive thm : fml → Prop
| axk (p q) : thm (p →' q →' p)
| axs (p q r) : thm $ (p →' q →' r) →' (p →' q) →' (p →' r)
| axn (p q) : thm $ (¬'q →' ¬'p) →' p →' q
| mp (p q) : thm p → thm (p →' q) → thm q

lemma p_of_p_of_p_of_p_of_p (p : fml) : thm ((p →' (p →' p)) →' (p →' p)) :=
thm.mp (p →' ((p →' p) →' p)) ((p →' (p →' p)) →' (p →' p)) (thm.axk p (p →' p)) (thm.axs p (p →' p) p)

lemma p_of_p (p : fml) : thm (p →' p) :=
thm.mp (p →' p →' p) (p →' p) (thm.axk p p) (p_of_p_of_p_of_p_of_p p)

lemma p_of_p' (p : fml) : thm (p →' p) :=
begin
    have lemma1 := p_of_p_of_p_of_p_of_p p,
    have lemma2 := thm.axk p p,
    exact thm.mp _ _ lemma2 lemma1,
end

inductive consequence (G : set fml) : fml → Prop
| axk (p q) : consequence (p →' q →' p)
| axs (p q r) : consequence $ (p →' q →' r) →' (p →' q) →' (p →' r)
| axn (p q) : consequence $ (¬'q →' ¬'p) →' p →' q
| mp (p q) : consequence p → consequence (p →' q) → consequence q
| of_G (g ∈ G) : consequence g

lemma consequence_of_thm (f : fml) (H : thm f) (G : set fml) : consequence G f :=
begin
  induction H,
  exact consequence.axk G H_p H_q,
  exact consequence.axs G H_p H_q H_r,
  exact consequence.axn G H_p H_q,
  exact consequence.mp H_p H_q H_ih_a H_ih_a_1,
end

lemma thm_of_consequence_null (f : fml) (H : consequence ∅ f) : thm f :=
begin
  induction H,
  exact thm.axk H_p H_q,
  exact thm.axs H_p H_q H_r,
  exact thm.axn H_p H_q,
  exact thm.mp H_p H_q H_ih_a H_ih_a_1,
  rw set.mem_empty_eq at H_H,
  contradiction,
end

theorem deduction (G : set fml) (p q : fml) (H : consequence (G ∪ {p}) q) : consequence G (p →' q) :=
begin
  induction H,
  have H1 := consequence.axk G H_p H_q,
  have H2 := consequence.axk G (H_p →' H_q →' H_p) p,
  exact consequence.mp _ _ H1 H2,
  have H6 := consequence.axs G H_p H_q H_r,
  have H7 := consequence.axk G ((H_p →' H_q →' H_r) →' (H_p →' H_q) →' H_p →' H_r) p,
  exact consequence.mp _ _ H6 H7,
  have H8 := consequence.axn G H_p H_q,
  have H9 := consequence.axk G ((¬' H_q →' ¬' H_p) →' H_p →' H_q) p,
  exact consequence.mp _ _ H8 H9,
  have H3 := consequence.axs G p H_p H_q,
  have H4 := consequence.mp _ _ H_ih_a_1 H3,
  exact consequence.mp _ _ H_ih_a H4,
  rw set.mem_union at H_H,
  cases H_H,
    have H51 := consequence.of_G H_g H_H,
    have H52 := consequence.axk G H_g p,
    exact consequence.mp _ _ H51 H52,
  rw set.mem_singleton_iff at H_H,
  rw H_H,
  exact consequence_of_thm _ (p_of_p p) G,
end

lemma part1 (p : fml) : consequence {¬' (¬' p)} p :=
begin
  have H1 := consequence.axk {¬' (¬' p)} p p,
  have H2 := consequence.axk {¬' (¬' p)} (¬' (¬' p)) (¬' (¬' (p →' p →' p))),
  have H3 := consequence.of_G (¬' (¬' p))(set.mem_singleton (¬' (¬' p))),
  have H4 := consequence.mp _ _ H3 H2,
  have H5 := consequence.axn {¬' (¬' p)} (¬' p) (¬' (p →' p →' p)),
  have H6 := consequence.mp _ _ H4 H5,
  have H7 := consequence.axn {¬' (¬' p)} (p →' p →' p) p,
  have H8 := consequence.mp _ _ H6 H7,
  exact consequence.mp _ _ H1 H8,
end

lemma p_of_not_not_p (p : fml) : thm ((¬' (¬' p)) →' p) :=
begin
  have H1 := deduction ∅ (¬' (¬' p)) p,
  rw set.empty_union at H1,
  have H2 := H1 (part1 p),
  exact thm_of_consequence_null (¬' (¬' p) →' p) H2,
end

theorem not_not_p_of_p (p : fml) : thm (p →' (¬' (¬' p))) :=
begin
  have H1 := thm.axn p (¬' (¬' p)),
  have H2 := p_of_not_not_p (¬' p),
  exact thm.mp _ _ H2 H1,
end

end prop_logic


import .list

namespace hidden

-- list is polymorphic
#check list nat         -- nat lives in Type
#check list bool        -- bool lives in Type
#check list Type        -- Type lives in Type 1
#check list (Type 1)    -- Type 1 lives in Type 2

/-
Some lists of nats
-/

#eval list.nil        -- can't infer α
#eval @list.nil nat   -- can't infer α
#eval (list.nil : list nat)

/-
Top-down (outside-in) refinement
-/

def lnat0 : list nat := list.nil

def lnat1 : list nat :=
  list.cons
    (_)
    (_)

def lstr1 : list string :=
  list.cons
    (_)
    (list.cons
      (_)
      (list.nil)
    )
/-
        cons
        /  \
      "hi" cons
           /  \
         "ho" ...
-/

-- FUNCTIONS

def head { α : Type } : list α → option α
| list.nil := none
| (list.cons h t) := some h

def tail { α : Type } : list α → option (list α)
| list.nil := none
| (list.cons h t) := some t

-- recursive definition of length (by cases)
def length {α : Type} : list α → nat
| list.nil := 0
| (list.cons h t) := (_) + 1   -- length in context

def append {α : Type} : list α → list α → list α
| list.nil         m := _
| (list.cons h t)  m := _

def pure {α : Type} : α → list α
| a := list.cons a list.nil

def reverse {α : Type} : list α → list α
| list.nil := list.nil
| (list.cons h t) :=  _

#eval reverse (list.cons 1 (list.cons 2 list.nil))

end hidden

/-
From here on, we use Lean's identical
definition of the list type, but we
gain the benefit of additional notation.
-/

/-
NOTATION
-/

#eval list.cons "hi" (list.cons "ho" list.nil)
#eval ["hi", "ho"]
#eval list.cons "hi" ["ho"]
#eval "hi"::["ho"]
#eval
  let s : string :=
    let a := ["hi","ho"]
    in
      (match a with
        | list.nil := "oops"
        | (h::t) := h
      end)
  in s ++ "!"

-- more examples
#eval [1,2,3,4,5]
#eval 1::[2,3,4,5]
#eval 1::2::3::4::5::list.nil

def head { α : Type } : list α → option α
| list.nil := none
| (h::t) := some h

def tail { α : Type } : list α → option (list α)
| list.nil := none
| (h::t) := some t

-- recursive definition of length (by cases)
def length {α : Type} : list α → nat
| list.nil := 0
| (h::t) := (_) + 1   -- length in context

#eval list.append ["Hi","Ho"] ["!"]
#eval ["Hi","Ho"] ++ ["!"]

#check @pure list
#eval (pure 5 : list nat)
#eval (pure 5 : option nat) -- whoa (later)

def reverse {α : Type} : list α → list α
| list.nil := list.nil
| (h::t) :=  _    -- use nice notation here

universe u

/-
HIGHER ORDER FUNCTIONS
-/

/-
Suppose we want to map a list of
strings to a list of bools, where
each bool is tt if the length of
the corresponding string is even,
and false otherwise.
-/

def strlist := ["Hello", "There", "Lean"]

def map_ev_string_bool :
  list string →
  list bool
| list.nil := list.nil
| (h::t) := (h.length%2=0)::_

def map_odd_string_bool :
  list string →
  list bool
| list.nil := list.nil
| (h::t) := (h.length%2=1)::_

def map_string_bang_string :
  list string →
  list string
| list.nil := list.nil
| (h::t) := (h++"!")::(map_string_bang_string t)

#eval map_string_bang_string strlist

/-
Dimensions of variation?
-- argument list element type, α
-- result list element type, β
-- function used to transform α → β
-/
def map_list {α β : Type u} :
    (α → β) → list α → list β
| f list.nil := list.nil
| f (h::t) := (f h)::(map_list f t)

#eval map_list
        (λ (s : string), s.length)
        (strlist)

#eval map_list
        (λ (s : string), (s.length%2=0:bool))
        (strlist)

def exclafy : list string → list string :=
  map_list (λ (s : string), s++"!") -- arg

#eval exclafy ["Whoa","That","Is","COOL"]

/-
Define reduce, or fold, over a list.

Let's start with an example. Suppose
we're given a list of strings and we
want to know if they are all of even
length. What we need is a function,
allEvenLength : list string → bool,
that returns tt iff all strings in
the list have even length.

Let's do an informal case analysis:

First we consider the case where the
given list of strings, l, is not empty.
In this case it has the form (h::t),
with (h : string) and (t : list string).
And in this case, every string in the
list will have even length iff (1) h
is of even length *AND* every string
in the tail, t, of l, also has even
length.

Now consider the case where l is nil.
What Boolean value do we return? One
might guess false ... incorrectly.

Suppose our function returned false
(ff) for the empty list. Consider a
case where l = ["Hi"]. That is, the
list, l = (list.cons "Hi" list.nil).

The list is non-empty, so the first
rule applies: return the AND of two
Boolean values: one for "Hi" and one
for the rest of the list. For "Hi"
we will get true, as "Hi" has even
length, so our result will have to
be (tt ∧ X), where X is the result
for the rest of the list. But here
the rest of the list is empty. So
what *must* R, the result for the
empty list, be for the function to
return the right result, tt (as all
strings in the list are even length)?

Clearly the answer must be true (tt).
What would happen if it were ff?

So now we have our algorithm!
-/

-- first a predicate function

def isEvenStr (s : string) : bool :=
  (s.length % 2 = 0)

def allEvenStr : list string → bool
| list.nil := tt
| (h::t) := band (isEvenStr h) (allEvenStr t)

#eval allEvenStr []
#eval allEvenStr ["Hi"]
#eval allEvenStr ["Hi!"]
#eval allEvenStr ["Hi","There!"]
#eval allEvenStr ["Hi","Ho!"]

/-
Now suppose we want to know if there
is *at least* one string of even length
in any given list of strings.

Again we do a case analysis.

There is at least one even-length
string in a non-empty list, l = h::t,
iff h is of even length OR there is at
least one even-lenght string in the
rest of the list.

Now suppose t is the empty string.
What result must be returned for the
function to work. Consider the case
where l = ["Hi!"] = (cons "Hi" nil).
The correct answer here is (ff ∨ X),
with the ff because "Hi!" is not of
even length, and X is the answer for
the empty list. To get the correct
answer, it must be that X = ff. So
now again we have our algorithm.
-/

/-
The key idea both here and in the
preceding example is that the answer
for the empty list must leave the
result for the preceding elements
unchanged. The result must be the
*identity element* for the Boolean
operator being used to combine the
values for the head and rest of the
list.

The identity element for Boolean and
is tt, and for  Boolean or it is ff.

∀ (b : bool), b ∧ tt = b
∀ (b : bool), b ∨ ff = b

We can see these propositions are true
by case analysis. Case 1: b = tt. In
this case b ∧ tt = tt and b = tt so
b ∧ t = b. Exercise: Finish up.
-/

def someEvenStr : list string → bool
| list.nil := ff
| (h::t) := bor (isEvenStr h) (someEvenStr t)

#eval someEvenStr []
#eval someEvenStr ["Hi"]
#eval someEvenStr ["Hi!"]
#eval someEvenStr ["Hello","Lean"]
#eval someEvenStr ["Hello","Lean!"]

/-
Now a key observation: Our two functions
are very similar, varying in only a few
dimensions. Let's factor dimensions of
variation into parameter to generalize
our definition.

First, we want to be able to operte on
lists of values of any type, not just
strings. Let call the list element type
α.

Second, in general, we want to reduce
a list (l : list α) to a value of some
other type, let's call it β, just as in
our examples we reduced lists of values
of type α = string to a final result of
type β = bool.

Third, we need a function to compute
the answer for a non-empty list (h::t)
where (1) (h : α), (2) the result for
the rest of the list is of type β, and
(3) the final combined result is of type
β. The type of this reducing function is
thus α → β → β.

Finally, we need a result value for the
empty list. This value obviously has to
be of type β.

Those are our parameters!

Given:
{ α : Type u}
{ β : Type u}
( f : α → β → β )
( id : β)
( l : list α)
Compute result : β
-/

-- By case analysis on the list l
def fold { α : Type u} { β : Type u} :
( α → β → β ) → β → list α → β
| f id list.nil := id
| f id (h::t) := f h (fold f id t)

-- allEven

def allStrCombine : string → bool → bool
| s b := band (isEvenStr s) b

def allEvenStr' (l : list string) : bool :=
  fold allStrCombine tt l

def someStrCombine : string → bool → bool
| s b := bor (isEvenStr s) b

def someEvenStr' (l : list string) : bool :=
  fold someStrCombine ff l

#eval allEvenStr' []
#eval allEvenStr' ["Hi"]
#eval allEvenStr' ["Hi!"]
#eval allEvenStr' ["Hi","There!"]
#eval allEvenStr' ["Hi","Ho!"]#eval someEvenStr []
#eval someEvenStr' ["Hi"]
#eval someEvenStr' ["Hi!"]
#eval someEvenStr' ["Hello","Lean"]
#eval someEvenStr' ["Hello","Lean!"]

#eval fold nat.add 0 [10,9,8,7,6,5,4,3,2,1]
#eval fold nat.mul 1 [10,9,8,7,6,5,4,3,2,1]

#eval fold string.append "" ["Hello, ", "Lean. ", "We ", "love ", "you!"]

/-
Given a predicate function on the type of
element in a list, return the sublist of
elements for which predicate is true.

Define filter by case analysis with one
case a base case and the other case using
"structural recursion" on list argument.

An opportunity to introduces if/then/else
expression in Lean.
-/

def filter' {α : Type u} :
  (α → bool) → list α → list α
| f list.nil := list.nil
| f (h::t) := if (f h)
              then h::(filter' f t)
              else (filter' f t)

/-
Take opportunity to introduce let
bindings. Binds name to result of
evaluation, then evaluates expr in
context of that additional binding.
-/
def filter {α : Type u} :
  (α → bool) → list α → list α
| f list.nil := list.nil
| f (h::t) := let restOfResult := (filter f t)
              in
                if (f h)
                then h::restOfResult
                else restOfResult

def isEven (n : nat) : bool := n%2 = 0

#eval filter isEven []
#eval filter isEven [0,1,2,3,5,6,7,9,10]

/-
Note: the body of a let can use another
let. That is, let bindings can be chained.

let x := 1 in
  let y := 2+3 in
    let z := 3*4 in
      x + y + z

To what value does this expression reduce?

-/


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import measure_theory.measure.complex_lebesgue
import measure_theory.integral.divergence_theorem
import measure_theory.integral.circle_integral
import analysis.calculus.dslope
import analysis.analytic.basic
import analysis.complex.re_im_topology
import data.real.cardinality

/-!
# Cauchy integral formula

In this file we prove Cauchy theorem and Cauchy integral formula for integrals over circles. Most
results are formulated for a function `f : ℂ → E` that takes values in a complex Banach space with
second countable topology.

## Main statements

In the following theorems, if the name ends with `off_countable`, then the actual theorem assumes
differentiability at all but countably many points of the set mentioned below.

* `complex.integral_boundary_rect_of_has_fderiv_within_at_real_off_countable`: If a function
  `f : ℂ → E` is continuous on a closed rectangle and *real* differentiable on its interior, then
  its integral over the boundary of this rectangle is equal to the integral of
  `I • f' (x + y * I) 1 - f' (x + y * I) I` over the rectangle, where `f' z w : E` is the derivative
  of `f` at `z` in the direction `w` and `I = complex.I` is the imaginary unit.

* `complex.integral_boundary_rect_eq_zero_of_differentiable_on_off_countable`: If a function
  `f : ℂ → E` is continuous on a closed rectangle and is *complex* differentiable on its interior,
  then its integral over the boundary of this rectangle is equal to zero.

* `complex.circle_integral_sub_center_inv_smul_eq_of_differentiable_on_annulus_off_countable`: If a
  function `f : ℂ → E` is continuous on a closed annulus `{z | r ≤ |z - c| ≤ R}` and is complex
  differentiable on its interior `{z | r < |z - c| < R}`, then the integrals of `(z - c)⁻¹ • f z`
  over the outer boundary and over the inner boundary are equal.

* `complex.circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable_of_tendsto`,
  `complex.circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable`:
  If a function `f : ℂ → E` is continuous on a punctured closed disc `{z | |z - c| ≤ R ∧ z ≠ c}`, is
  complex differentiable on the corresponding punctured open disc, and tends to `y` as `z → c`,
  `z ≠ c`, then the integral of `(z - c)⁻¹ • f z` over the circle `|z - c| = R` is equal to
  `2πiy`. In particular, if `f` is continuous on the whole closed disc and is complex differentiable
  on the corresponding open disc, then this integral is equal to `2πif(c)`.

* `complex.circle_integral_sub_inv_smul_of_differentiable_on_off_countable`,
  `complex.two_pi_I_inv_smul_circle_integral_sub_inv_smul_of_differentiable_on_off_countable`
  **Cauchy integral formula**: if `f : ℂ → E` is continuous on a closed disc of radius `R` and is
  complex differentiable on the corresponding open disc, then for any `w` in the corresponding open
  disc the integral of `(z - w)⁻¹ • f z` over the boundary of the disc is equal to `2πif(w)`.
  Two versions of the lemma put the multiplier `2πi` at the different sides of the equality.

* `complex.has_fpower_series_on_ball_of_differentiable_off_countable`: If `f : ℂ → E` is continuous
  on a closed disc of positive radius and is complex differentiable on the corresponding open disc,
  then it is analytic on the corresponding open disc, and the coefficients of the power series are
  given by Cauchy integral formulas.

* `differentiable_on.has_fpower_series_on_ball`: If `f : ℂ → E` is complex differentiable on a
  closed disc of positive radius, then it is analytic on the corresponding open disc, and the
  coefficients of the power series are given by Cauchy integral formulas.

* `differentiable_on.analytic_at`, `differentiable.analytic_at`: If `f : ℂ → E` is differentiable
  on a neighborhood of a point, then it is analytic at this point. In particular, if `f : ℂ → E`
  is differentiable on the whole `ℂ`, then it is analytic at every point `z : ℂ`.

## Implementation details

The proof of the Cauchy integral formula in this file is based on a very general version of the
divergence theorem, see `measure_theory.integral_divergence_of_has_fderiv_within_at_off_countable`
(a version for functions defined on `fin (n + 1) → ℝ`),
`measure_theory.integral_divergence_prod_Icc_of_has_fderiv_within_at_off_countable_of_le`, and
`measure_theory.integral2_divergence_prod_of_has_fderiv_within_at_off_countable` (versions for
functions defined on `ℝ × ℝ`).

Usually, the divergence theorem is formulated for a $C^1$ smooth function. The theorems formulated
above deal with a function that is

* continuous on a closed box/rectangle;
* differentiable at all but countably many points of its interior;
* have divergence integrable over the closed box/rectangle.

First, we reformulate the theorem for a *real*-differentiable map `ℂ → E`, and relate the integral
of `f` over the boundary of a rectangle in `ℂ` to the integral of the derivative
$\frac{\partial f}{\partial \bar z}$ over the interior of this box. In particular, for a *complex*
differentiable function, the latter derivative is zero, hence the integral over the boundary of a
rectangle is zero. Thus we get Cauchy theorem for a rectangle in `ℂ`.

Next, we apply the this theorem to the function $F(z)=f(c+e^{z})$ on the rectangle
$[\ln r, \ln R]\times [0, 2\pi]$ to prove that
$$
  \oint_{|z-c|=r}\frac{f(z)\,dz}{z-c}=\oint_{|z-c|=R}\frac{f(z)\,dz}{z-c}
$$
provided that `f` is continuous on the closed annulus `r ≤ |z - c| ≤ R` and is complex
differentiable on its interior `r < |z - c| < R` (possibly, at all but countably many points).

Here and below, we write $\frac{f(z)}{z-c}$ in the documentation while the actual lemmas use
`(z - c)⁻¹ • f z` because `f z` belongs to some Banach space over `ℂ` and `f z / (z - c)` is
undefined.

Taking the limit of this equality as `r` tends to `𝓝[>] 0`, we prove
$$
  \oint_{|z-c|=R}\frac{f(z)\,dz}{z-c}=2\pi if(c)
$$
provided that `f` is continuous on the closed disc `|z - c| ≤ R` and is differentiable at all but
countably many points of its interior. This is the Cauchy integral formula for the center of a
circle. In particular, if we apply this function to `F z = (z - c) • f z`, then we get
$$
  \oint_{|z-c|=R} f(z)\,dz=0.
$$

In order to deduce the Cauchy integral formula for any point `w`, `|w - c| < R`, we consider the
slope function `g : ℂ → E` given by `g z = (z - w)⁻¹ • (f z - f w)` if `z ≠ w` and `g w = f' w`.
This function satisfies assumptions of the previous theorem, so we have
$$
  \oint_{|z-c|=R} \frac{f(z)\,dz}{z-w}=\oint_{|z-c|=R} \frac{f(w)\,dz}{z-w}=
  \left(\oint_{|z-c|=R} \frac{dz}{z-w}\right)f(w).
$$
The latter integral was computed in `circle_integral.integral_sub_inv_of_mem_ball` and is equal to
`2 * π * complex.I`.

There is one more step in the actual proof. Since we allow `f` to be non-differentiable on a
countable set `s`, we cannot immediately claim that `g` is continuous at `w` if `w ∈ s`. So, we use
the proof outlined in the previous paragraph for `w ∉ s` (see
`complex.circle_integral_sub_inv_smul_of_differentiable_on_off_countable_aux`), then use continuity
of both sides of the formula and density of `sᶜ` to prove the formula for all points of the open
ball, see `complex.circle_integral_sub_inv_smul_of_differentiable_on_off_countable`.

Finally, we use the properties of the Cauchy integrals established elsewhere (see
`has_fpower_series_on_cauchy_integral`) and Cauchy integral formula to prove that the original
function is analytic on the open ball.

## Tags

Cauchy theorem, Cauchy integral formula
-/

open topological_space set measure_theory interval_integral metric filter function
open_locale interval real nnreal ennreal topological_space big_operators

noncomputable theory

universes u

variables {E : Type u} [normed_group E] [normed_space ℂ E] [measurable_space E] [borel_space E]
  [second_countable_topology E] [complete_space E]

namespace complex

/-- Suppose that a function `f : ℂ → E` is continuous on a closed rectangle with opposite corners at
`z w : ℂ`, is *real* differentiable at all but countably many points of the corresponding open
rectangle, and $\frac{\partial f}{\partial \bar z}$ is integrable on this rectangle. Then the
integral of `f` over the boundary of the rectangle is equal to the integral of
$2i\frac{\partial f}{\partial \bar z}=i\frac{\partial f}{\partial x}-\frac{\partial f}{\partial y}$
over the rectangle. -/
lemma integral_boundary_rect_of_has_fderiv_at_real_off_countable (f : ℂ → E)
  (f' : ℂ → ℂ →L[ℝ] E) (z w : ℂ) (s : set ℂ) (hs : countable s)
  (Hc : continuous_on f (re ⁻¹' [z.re, w.re] ∩ im ⁻¹' [z.im, w.im]))
  (Hd : ∀ x ∈ (re ⁻¹' (Ioo (min z.re w.re) (max z.re w.re)) ∩
    im ⁻¹' (Ioo (min z.im w.im) (max z.im w.im))) \ s, has_fderiv_at f (f' x) x)
  (Hi : integrable_on (λ z, I • f' z 1 - f' z I) (re ⁻¹' [z.re, w.re] ∩ im ⁻¹' [z.im, w.im])) :
  (∫ x : ℝ in z.re..w.re, f (x + z.im * I)) - (∫ x : ℝ in z.re..w.re, f (x + w.im * I)) +
    (I • ∫ y : ℝ in z.im..w.im, f (re w + y * I)) - I • ∫ y : ℝ in z.im..w.im, f (re z + y * I) =
    ∫ x : ℝ in z.re..w.re, ∫ y : ℝ in z.im..w.im, I • f' (x + y * I) 1 - f' (x + y * I) I :=
begin
  set e : (ℝ × ℝ) ≃L[ℝ] ℂ := equiv_real_prodₗ.symm,
  have he : ∀ x y : ℝ, ↑x + ↑y * I = e (x, y), from λ x y, (mk_eq_add_mul_I x y).symm,
  have he₁ : e (1, 0) = 1 := rfl, have he₂ : e (0, 1) = I := rfl,
  simp only [he] at *,
  set F : (ℝ × ℝ) → E := f ∘ e,
  set F' : (ℝ × ℝ) → (ℝ × ℝ) →L[ℝ] E := λ p, (f' (e p)).comp (e : (ℝ × ℝ) →L[ℝ] ℂ),
  have hF' : ∀ p : ℝ × ℝ, (-(I • F' p)) (1, 0) + F' p (0, 1) = -(I • f' (e p) 1 - f' (e p) I),
  { rintro ⟨x, y⟩, simp [F', he₁, he₂, ← sub_eq_neg_add], },
  set R : set (ℝ × ℝ) := [z.re, w.re] ×ˢ [w.im, z.im],
  set t : set (ℝ × ℝ) := e ⁻¹' s,
  rw [interval_swap z.im] at Hc Hi, rw [min_comm z.im, max_comm z.im] at Hd,
  have hR : e ⁻¹' (re ⁻¹' [z.re, w.re] ∩ im ⁻¹' [w.im, z.im]) = R := rfl,
  have htc : continuous_on F R, from Hc.comp e.continuous_on hR.ge,
  have htd : ∀ p ∈ Ioo (min z.re w.re) (max z.re w.re) ×ˢ Ioo (min w.im z.im) (max w.im z.im) \ t,
    has_fderiv_at F (F' p) p := λ p hp, (Hd (e p) hp).comp p e.has_fderiv_at,
  simp_rw [← interval_integral.integral_smul, interval_integral.integral_symm w.im z.im,
    ← interval_integral.integral_neg, ← hF'],
  refine (integral2_divergence_prod_of_has_fderiv_within_at_off_countable
      (λ p, -(I • F p)) F (λ p, - (I • F' p)) F' z.re w.im w.re z.im t (hs.preimage e.injective)
      (continuous_on_const.smul htc).neg htc (λ p hp, ((htd p hp).const_smul I).neg) htd _).symm,
  rw [← volume_preserving_equiv_real_prod.symm.integrable_on_comp_preimage
    (measurable_equiv.measurable_embedding _)] at Hi,
  simpa only [hF'] using Hi.neg
end

/-- Suppose that a function `f : ℂ → E` is continuous on a closed rectangle with opposite corners at
`z w : ℂ`, is *real* differentiable on the corresponding open rectangle, and
$\frac{\partial f}{\partial \bar z}$ is integrable on this rectangle. Then the integral of `f` over
the boundary of the rectangle is equal to the integral of
$2i\frac{\partial f}{\partial \bar z}=i\frac{\partial f}{\partial x}-\frac{\partial f}{\partial y}$
over the rectangle. -/
lemma integral_boundary_rect_of_continuous_on_of_has_fderiv_at_real (f : ℂ → E)
  (f' : ℂ → ℂ →L[ℝ] E) (z w : ℂ)
  (Hc : continuous_on f (re ⁻¹' [z.re, w.re] ∩ im ⁻¹' [z.im, w.im]))
  (Hd : ∀ x ∈ (re ⁻¹' (Ioo (min z.re w.re) (max z.re w.re)) ∩
    im ⁻¹' (Ioo (min z.im w.im) (max z.im w.im))), has_fderiv_at f (f' x) x)
  (Hi : integrable_on (λ z, I • f' z 1 - f' z I) (re ⁻¹' [z.re, w.re] ∩ im ⁻¹' [z.im, w.im])) :
  (∫ x : ℝ in z.re..w.re, f (x + z.im * I)) - (∫ x : ℝ in z.re..w.re, f (x + w.im * I)) +
    (I • ∫ y : ℝ in z.im..w.im, f (re w + y * I)) - I • ∫ y : ℝ in z.im..w.im, f (re z + y * I) =
    ∫ x : ℝ in z.re..w.re, ∫ y : ℝ in z.im..w.im, I • f' (x + y * I) 1 - f' (x + y * I) I :=
integral_boundary_rect_of_has_fderiv_at_real_off_countable f f' z w ∅ countable_empty Hc
  (λ x hx, Hd x hx.1) Hi

/-- Suppose that a function `f : ℂ → E` is *real* differentiable on a closed rectangle with opposite
corners at `z w : ℂ` and $\frac{\partial f}{\partial \bar z}$ is integrable on this rectangle. Then
the integral of `f` over the boundary of the rectangle is equal to the integral of
$2i\frac{\partial f}{\partial \bar z}=i\frac{\partial f}{\partial x}-\frac{\partial f}{\partial y}$
over the rectangle. -/
lemma integral_boundary_rect_of_differentiable_on_real (f : ℂ → E) (z w : ℂ)
  (Hd : differentiable_on ℝ f (re ⁻¹' [z.re, w.re] ∩ im ⁻¹' [z.im, w.im]))
  (Hi : integrable_on (λ z, I • fderiv ℝ f z 1 - fderiv ℝ f z I)
    (re ⁻¹' [z.re, w.re] ∩ im ⁻¹' [z.im, w.im])) :
  (∫ x : ℝ in z.re..w.re, f (x + z.im * I)) - (∫ x : ℝ in z.re..w.re, f (x + w.im * I)) +
    (I • ∫ y : ℝ in z.im..w.im, f (re w + y * I)) - I • ∫ y : ℝ in z.im..w.im, f (re z + y * I) =
    ∫ x : ℝ in z.re..w.re, ∫ y : ℝ in z.im..w.im,
      I • fderiv ℝ f (x + y * I) 1 - fderiv ℝ f (x + y * I) I :=
integral_boundary_rect_of_has_fderiv_at_real_off_countable f (fderiv ℝ f) z w ∅ countable_empty
  Hd.continuous_on
  (λ x hx, Hd.has_fderiv_at $ by simpa only [← mem_interior_iff_mem_nhds,
    interior_preimage_re_inter_preimage_im, interval, interior_Icc] using hx.1) Hi

/-- **Cauchy theorem**: the integral of a complex differentiable function over the boundary of a
rectangle equals zero. More precisely, if `f` is continuous on a closed rectangle and is complex
differentiable at all but countably many points of the corresponding open rectangle, then its
integral over the boundary of the rectangle equals zero. -/
lemma integral_boundary_rect_eq_zero_of_differentiable_on_off_countable (f : ℂ → E)
  (z w : ℂ) (s : set ℂ) (hs : countable s)
  (Hc : continuous_on f (re ⁻¹' [z.re, w.re] ∩ im ⁻¹' [z.im, w.im]))
  (Hd : ∀ x ∈ (re ⁻¹' (Ioo (min z.re w.re) (max z.re w.re)) ∩
    im ⁻¹' (Ioo (min z.im w.im) (max z.im w.im))) \ s, differentiable_at ℂ f x) :
  (∫ x : ℝ in z.re..w.re, f (x + z.im * I)) - (∫ x : ℝ in z.re..w.re, f (x + w.im * I)) +
    (I • ∫ y : ℝ in z.im..w.im, f (re w + y * I)) -
      I • ∫ y : ℝ in z.im..w.im, f (re z + y * I) = 0 :=
by refine (integral_boundary_rect_of_has_fderiv_at_real_off_countable f
  (λ z, (fderiv ℂ f z).restrict_scalars ℝ) z w s hs Hc
  (λ x hx, (Hd x hx).has_fderiv_at.restrict_scalars ℝ) _).trans _;
    simp [← continuous_linear_map.map_smul]

/-- **Cauchy theorem**: the integral of a complex differentiable function over the boundary of a
rectangle equals zero. More precisely, if `f` is continuous on a closed rectangle and is complex
differentiable on the corresponding open rectangle, then its integral over the boundary of the
rectangle equals zero. -/
lemma integral_boundary_rect_eq_zero_of_continuous_on_of_differentiable_on (f : ℂ → E) (z w : ℂ)
  (Hc : continuous_on f (re ⁻¹' [z.re, w.re] ∩ im ⁻¹' [z.im, w.im]))
  (Hd : differentiable_on ℂ f (re ⁻¹' (Ioo (min z.re w.re) (max z.re w.re)) ∩
    im ⁻¹' (Ioo (min z.im w.im) (max z.im w.im)))) :
  (∫ x : ℝ in z.re..w.re, f (x + z.im * I)) - (∫ x : ℝ in z.re..w.re, f (x + w.im * I)) +
    (I • ∫ y : ℝ in z.im..w.im, f (re w + y * I)) -
      I • ∫ y : ℝ in z.im..w.im, f (re z + y * I) = 0 :=
integral_boundary_rect_eq_zero_of_differentiable_on_off_countable f z w ∅ countable_empty
  Hc $ λ x hx, Hd.differentiable_at $ (is_open_Ioo.re_prod_im is_open_Ioo).mem_nhds hx.1

/-- **Cauchy theorem**: the integral of a complex differentiable function over the boundary of a
rectangle equals zero. More precisely, if `f` is complex differentiable on a closed rectangle, then
its integral over the boundary of the rectangle equals zero. -/

/-- If `f : ℂ → E` is continuous the closed annulus `r ≤ ∥z - c∥ ≤ R`, `0 < r ≤ R`, and is complex
differentiable at all but countably many points of its interior, then the integrals of
`f z / (z - c)` (formally, `(z - c)⁻¹ • f z`) over the circles `∥z - c∥ = r` and `∥z - c∥ = R` are
equal to each other. -/
lemma circle_integral_sub_center_inv_smul_eq_of_differentiable_on_annulus_off_countable
  {c : ℂ} {r R : ℝ} (h0 : 0 < r) (hle : r ≤ R) {f : ℂ → E} {s : set ℂ} (hs : countable s)
  (hc : continuous_on f (closed_ball c R \ ball c r))
  (hd : ∀ z ∈ ball c R \ closed_ball c r \ s, differentiable_at ℂ f z) :
  ∮ z in C(c, R), (z - c)⁻¹ • f z = ∮ z in C(c, r), (z - c)⁻¹ • f z :=
begin
  /- We apply the previous lemma to `λ z, f (c + exp z)` on the rectangle
  `[log r, log R] × [0, 2 * π]`. -/
  set A := closed_ball c R \ ball c r,
  obtain ⟨a, rfl⟩ : ∃ a, real.exp a = r, from ⟨real.log r, real.exp_log h0⟩,
  obtain ⟨b, rfl⟩ : ∃ b, real.exp b = R, from ⟨real.log R, real.exp_log (h0.trans_le hle)⟩,
  rw [real.exp_le_exp] at hle,
  -- Unfold definition of `circle_integral` and cancel some terms.
  suffices : ∫ θ in 0..2 * π, I • f (circle_map c (real.exp b) θ) =
    ∫ θ in 0..2 * π, I • f (circle_map c (real.exp a) θ),
    by simpa only [circle_integral, add_sub_cancel', of_real_exp, ← exp_add, smul_smul,
      ← div_eq_mul_inv, mul_div_cancel_left _ (circle_map_ne_center (real.exp_pos _).ne'),
      circle_map_sub_center, deriv_circle_map],
  set R := re ⁻¹' [a, b] ∩ im ⁻¹' [0, 2 * π],
  set g : ℂ → ℂ := (+) c ∘ exp,
  have hdg : differentiable ℂ g := differentiable_exp.const_add _,
  replace hs : countable (g ⁻¹' s) := (hs.preimage (add_right_injective c)).preimage_cexp,
  have h_maps : maps_to g R A,
  { rintro z ⟨h, -⟩, simpa [dist_eq, g, abs_exp, hle] using h.symm },
  replace hc : continuous_on (f ∘ g) R, from hc.comp hdg.continuous.continuous_on h_maps,
  replace hd : ∀ z ∈ re ⁻¹' (Ioo (min a b) (max a b)) ∩
    im ⁻¹' (Ioo (min 0 (2 * π)) (max 0 (2 * π))) \ g ⁻¹' s, differentiable_at ℂ (f ∘ g) z,
  { refine λ z hz, (hd (g z) ⟨_, hz.2⟩).comp z (hdg _),
    simpa [g, dist_eq, abs_exp, hle, and.comm] using hz.1.1 },
  simpa [g, circle_map, exp_periodic _, sub_eq_zero, ← exp_add]
    using integral_boundary_rect_eq_zero_of_differentiable_on_off_countable _ ⟨a, 0⟩ ⟨b, 2 * π⟩
      _ hs hc hd
end

/-- **Cauchy integral formula** for the value at the center of a disc. If `f` is continuous on a
punctured closed disc of radius `R`, is differentiable at all but countably many points of the
interior of this disc, and has a limit `y` at the center of the disc, then the integral
$\oint_{∥z-c∥=R} \frac{f(z)}{z-c}\,dz$ is equal to $2πiy`. -/
lemma circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable_of_tendsto
  {c : ℂ} {R : ℝ} (h0 : 0 < R) {f : ℂ → E} {y : E} {s : set ℂ} (hs : countable s)
  (hc : continuous_on f (closed_ball c R \ {c}))
  (hd : ∀ z ∈ ball c R \ {c} \ s, differentiable_at ℂ f z)
  (hy : tendsto f (𝓝[{c}ᶜ] c) (𝓝 y)) :
  ∮ z in C(c, R), (z - c)⁻¹ • f z = (2 * π * I : ℂ) • y :=
begin
  rw [← sub_eq_zero, ← norm_le_zero_iff],
  refine le_of_forall_le_of_dense (λ ε ε0, _),
  obtain ⟨δ, δ0, hδ⟩ :
    ∃ δ > (0 : ℝ), ∀ z ∈ closed_ball c δ \ {c}, dist (f z) y < ε / (2 * π),
    from ((nhds_within_has_basis nhds_basis_closed_ball _).tendsto_iff nhds_basis_ball).1 hy _
      (div_pos ε0 real.two_pi_pos),
  obtain ⟨r, hr0, hrδ, hrR⟩ : ∃ r, 0 < r ∧ r ≤ δ ∧ r ≤ R :=
    ⟨min δ R, lt_min δ0 h0, min_le_left _ _, min_le_right _ _⟩,
  have hsub : closed_ball c R \ ball c r ⊆ closed_ball c R \ {c},
    from diff_subset_diff_right (singleton_subset_iff.2 $ mem_ball_self hr0),
  have hsub' : ball c R \ closed_ball c r ⊆ ball c R \ {c},
    from diff_subset_diff_right (singleton_subset_iff.2 $ mem_closed_ball_self hr0.le),
  have hzne : ∀ z ∈ sphere c r, z ≠ c,
    from λ z hz, ne_of_mem_of_not_mem hz (λ h, hr0.ne' $ dist_self c ▸ eq.symm h),
  /- The integral `∮ z in C(c, r), f z / (z - c)` does not depend on `0 < r ≤ R` and tends to
  `2πIy` as `r → 0`. -/
  calc ∥(∮ z in C(c, R), (z - c)⁻¹ • f z) - (2 * ↑π * I) • y∥
      = ∥(∮ z in C(c, r), (z - c)⁻¹ • f z) - ∮ z in C(c, r), (z - c)⁻¹ • y∥ :
    begin
      congr' 2,
      { exact circle_integral_sub_center_inv_smul_eq_of_differentiable_on_annulus_off_countable
          hr0 hrR hs (hc.mono hsub) (λ z hz, hd z ⟨hsub' hz.1, hz.2⟩) },
      { simp [hr0.ne'] }
    end
  ... = ∥∮ z in C(c, r), (z - c)⁻¹ • (f z - y)∥ :
    begin
      simp only [smul_sub],
      have hc' : continuous_on (λ z, (z - c)⁻¹) (sphere c r),
        from (continuous_on_id.sub continuous_on_const).inv₀ (λ z hz, sub_ne_zero.2 $ hzne _ hz),
      rw circle_integral.integral_sub; refine (hc'.smul _).circle_integrable hr0.le,
      { exact hc.mono (subset_inter (sphere_subset_closed_ball.trans $
          closed_ball_subset_closed_ball hrR) hzne) },
      { exact continuous_on_const }
    end
  ... ≤ 2 * π * r * (r⁻¹ * (ε / (2 * π))) :
    begin
      refine circle_integral.norm_integral_le_of_norm_le_const hr0.le (λ z hz, _),
      specialize hzne z hz,
      rw [mem_sphere, dist_eq_norm] at hz,
      rw [norm_smul, normed_field.norm_inv, hz, ← dist_eq_norm],
      refine mul_le_mul_of_nonneg_left (hδ _ ⟨_, hzne⟩).le (inv_nonneg.2 hr0.le),
      rwa [mem_closed_ball_iff_norm, hz]
    end
  ... = ε : by { field_simp [hr0.ne', real.two_pi_pos.ne'], ac_refl }
end

/-- **Cauchy integral formula** for the value at the center of a disc. If `f : ℂ → E` is continuous
on a closed disc of radius `R` and is complex differentiable at all but countably many points of its
interior, then the integral $\oint_{|z-c|=R} \frac{f(z)}{z-c}\,dz$ is equal to $2πiy`. -/
lemma circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable {R : ℝ} (h0 : 0 < R)
  {f : ℂ → E} {c : ℂ} {s : set ℂ} (hs : countable s)
  (hc : continuous_on f (closed_ball c R)) (hd : ∀ z ∈ ball c R \ s, differentiable_at ℂ f z) :
  ∮ z in C(c, R), (z - c)⁻¹ • f z = (2 * π * I : ℂ) • f c :=
circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable_of_tendsto h0 hs
  (hc.mono $ diff_subset _ _) (λ z hz, hd z ⟨hz.1.1, hz.2⟩)
  (hc.continuous_at $ closed_ball_mem_nhds _ h0).continuous_within_at

/-- **Cauchy theorem**: if `f : ℂ → E` is continuous on a closed ball `{z | ∥z - c∥ ≤ R}` and is
complex differentiable at all but countably many points of its interior, then the integral
$\oint_{|z-c|=R}f(z)\,dz$ equals zero. -/
lemma circle_integral_eq_zero_of_differentiable_on_off_countable {R : ℝ} (h0 : 0 ≤ R) {f : ℂ → E}
  {c : ℂ} {s : set ℂ} (hs : countable s) (hc : continuous_on f (closed_ball c R))
  (hd : ∀ z ∈ ball c R \ s, differentiable_at ℂ f z) :
  ∮ z in C(c, R), f z = 0 :=
begin
  rcases h0.eq_or_lt with rfl|h0, { apply circle_integral.integral_radius_zero },
  calc ∮ z in C(c, R), f z = ∮ z in C(c, R), (z - c)⁻¹ • (z - c) • f z :
    begin
      refine circle_integral.integral_congr h0.le (λ z hz, (inv_smul_smul₀ (λ h₀, _) _).symm),
      rw [mem_sphere, dist_eq, h₀, abs_zero] at hz,
      exact h0.ne hz
    end
  ... = (2 * ↑π * I : ℂ) • (c - c) • f c :
    circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable h0 hs
      ((continuous_on_id.sub continuous_on_const).smul hc)
      (λ z hz, (differentiable_at_id.sub_const _).smul (hd z hz))
  ... = 0 : by rw [sub_self, zero_smul, smul_zero]
end

/-- An auxiliary lemma for
`complex.circle_integral_sub_inv_smul_of_differentiable_on_off_countable`. This lemma assumes
`w ∉ s` while the main lemma drops this assumption. -/
lemma circle_integral_sub_inv_smul_of_differentiable_on_off_countable_aux {R : ℝ} {c w : ℂ}
  {f : ℂ → E} {s : set ℂ} (hs : countable s) (hw : w ∈ ball c R \ s)
  (hc : continuous_on f (closed_ball c R)) (hd : ∀ x ∈ ball c R \ s, differentiable_at ℂ f x) :
  ∮ z in C(c, R), (z - w)⁻¹ • f z = (2 * π * I : ℂ) • f w :=
begin
  have hR : 0 < R := dist_nonneg.trans_lt hw.1,
  set F : ℂ → E := dslope f w,
  have hws : countable (insert w s) := hs.insert _,
  have hnhds : closed_ball c R ∈ 𝓝 w, from closed_ball_mem_nhds_of_mem hw.1,
  have hcF : continuous_on F (closed_ball c R),
    from (continuous_on_dslope $ closed_ball_mem_nhds_of_mem hw.1).2 ⟨hc, hd _ hw⟩,
  have hdF : ∀ z ∈ ball (c : ℂ) R \ (insert w s), differentiable_at ℂ F z,
    from λ z hz, (differentiable_at_dslope_of_ne
      (ne_of_mem_of_not_mem (mem_insert _ _) hz.2).symm).2
      (hd _ (diff_subset_diff_right (subset_insert _ _) hz)),
  have HI := circle_integral_eq_zero_of_differentiable_on_off_countable hR.le hws hcF hdF,
  have hne : ∀ z ∈ sphere c R, z ≠ w, from λ z hz, ne_of_mem_of_not_mem hz (ne_of_lt hw.1),
  have hFeq : eq_on F (λ z, (z - w)⁻¹ • f z - (z - w)⁻¹ • f w) (sphere c R),
  { intros z hz,
    calc F z = (z - w)⁻¹ • (f z - f w) : update_noteq (hne z hz) _ _
    ... = (z - w)⁻¹ • f z - (z - w)⁻¹ • f w : smul_sub _ _ _ },
  have hc' : continuous_on (λ z, (z - w)⁻¹) (sphere c R),
    from (continuous_on_id.sub continuous_on_const).inv₀ (λ z hz, sub_ne_zero.2 $ hne z hz),
  rw [← circle_integral.integral_sub_inv_of_mem_ball hw.1, ← circle_integral.integral_smul_const,
    ← sub_eq_zero, ← circle_integral.integral_sub, ← circle_integral.integral_congr hR.le hFeq, HI],
  exacts [(hc'.smul (hc.mono sphere_subset_closed_ball)).circle_integrable hR.le,
    (hc'.smul continuous_on_const).circle_integrable hR.le]
end

/-- **Cauchy integral formula**: if `f : ℂ → E` is continuous on a closed disc of radius `R` and is
complex differentiable at all but countably many points of its interior, then for any `w` in this
interior we have $\frac{1}{2πi}\oint_{|z-c|=R}(z-w)^{-1}f(z)\,dz=f(w)$.
-/
lemma two_pi_I_inv_smul_circle_integral_sub_inv_smul_of_differentiable_on_off_countable
  {R : ℝ} {c w : ℂ} {f : ℂ → E} {s : set ℂ} (hs : countable s) (hw : w ∈ ball c R)
  (hc : continuous_on f (closed_ball c R)) (hd : ∀ x ∈ ball c R \ s, differentiable_at ℂ f x) :
  (2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - w)⁻¹ • f z = f w :=
begin
  have hR : 0 < R := dist_nonneg.trans_lt hw,
  suffices : w ∈ closure (ball c R \ s),
  { lift R to ℝ≥0 using hR.le,
    have A : continuous_at (λ w, (2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - w)⁻¹ • f z) w,
    { have := has_fpower_series_on_cauchy_integral
      ((hc.mono sphere_subset_closed_ball).circle_integrable R.coe_nonneg) hR,
      refine this.continuous_on.continuous_at (emetric.is_open_ball.mem_nhds _),
      rwa metric.emetric_ball_nnreal },
    have B : continuous_at f w, from hc.continuous_at (closed_ball_mem_nhds_of_mem hw),
    refine tendsto_nhds_unique_of_frequently_eq A B ((mem_closure_iff_frequently.1 this).mono _),
    intros z hz,
    rw [circle_integral_sub_inv_smul_of_differentiable_on_off_countable_aux hs hz hc hd,
      inv_smul_smul₀],
    simp [real.pi_ne_zero, I_ne_zero] },
  refine mem_closure_iff_nhds.2 (λ t ht, _),
  -- TODO: generalize to any vector space over `ℝ`
  set g : ℝ → ℂ := λ x, w + x,
  have : tendsto g (𝓝 0) (𝓝 w),
    from (continuous_const.add continuous_of_real).tendsto' 0 w (add_zero _),
  rcases mem_nhds_iff_exists_Ioo_subset.1 (this $ inter_mem ht $ is_open_ball.mem_nhds hw)
    with ⟨l, u, hlu₀, hlu_sub⟩,
  obtain ⟨x, hx⟩ : (Ioo l u \ g ⁻¹' s).nonempty,
  { refine nonempty_diff.2 (λ hsub, _),
    have : countable (Ioo l u),
      from (hs.preimage ((add_right_injective w).comp of_real_injective)).mono hsub,
    rw [← cardinal.mk_set_le_omega, cardinal.mk_Ioo_real (hlu₀.1.trans hlu₀.2)] at this,
    exact this.not_lt cardinal.omega_lt_continuum },
  exact ⟨g x, (hlu_sub hx.1).1, (hlu_sub hx.1).2, hx.2⟩
end

/-- **Cauchy integral formula**: if `f : ℂ → E` is continuous on a closed disc of radius `R` and is
complex differentiable at all but countably many points of its interior, then for any `w` in this
interior we have $\oint_{|z-c|=R}(z-w)^{-1}f(z)\,dz=2πif(w)$.
-/
lemma circle_integral_sub_inv_smul_of_differentiable_on_off_countable
  {R : ℝ} {c w : ℂ} {f : ℂ → E} {s : set ℂ} (hs : countable s) (hw : w ∈ ball c R)
  (hc : continuous_on f (closed_ball c R)) (hd : ∀ x ∈ ball c R \ s, differentiable_at ℂ f x) :
  ∮ z in C(c, R), (z - w)⁻¹ • f z = (2 * π * I : ℂ) • f w :=
by { rw [← two_pi_I_inv_smul_circle_integral_sub_inv_smul_of_differentiable_on_off_countable
  hs hw hc hd, smul_inv_smul₀], simp [real.pi_ne_zero, I_ne_zero] }

/-- **Cauchy integral formula**: if `f : ℂ → E` is continuous on a closed disc of radius `R` and is
complex differentiable on its interior, then for any `w` in this interior we have
$\oint_{|z-c|=R}(z-w)^{-1}f(z)\,dz=2πif(w)$.
-/
lemma circle_integral_sub_inv_smul_of_continuous_on_of_differentiable_on
  {R : ℝ} {c w : ℂ} {f : ℂ → E} (hw : w ∈ ball c R)
  (hc : continuous_on f (closed_ball c R)) (hd : differentiable_on ℂ f (ball c R)) :
  ∮ z in C(c, R), (z - w)⁻¹ • f z = (2 * π * I : ℂ) • f w :=
circle_integral_sub_inv_smul_of_differentiable_on_off_countable countable_empty hw hc $ λ z hz,
  hd.differentiable_at (is_open_ball.mem_nhds hz.1)

/-- **Cauchy integral formula**: if `f : ℂ → E` is complex differentiable on a closed disc of radius
`R`, then for any `w` in its interior we have $\oint_{|z-c|=R}(z-w)^{-1}f(z)\,dz=2πif(w)$. -/
lemma circle_integral_sub_inv_smul_of_differentiable_on
  {R : ℝ} {c w : ℂ} {f : ℂ → E} (hw : w ∈ ball c R) (hd : differentiable_on ℂ f (closed_ball c R)) :
  ∮ z in C(c, R), (z - w)⁻¹ • f z = (2 * π * I : ℂ) • f w :=
circle_integral_sub_inv_smul_of_continuous_on_of_differentiable_on hw hd.continuous_on $
  hd.mono $ ball_subset_closed_ball

/-- **Cauchy integral formula**: if `f : ℂ → ℂ` is continuous on a closed disc of radius `R` and is
complex differentiable at all but countably many points of its interior, then for any `w` in this
interior we have $\oint_{|z-c|=R}\frac{f(z)}{z-w}dz=2\pi i\,f(w)$.
-/
lemma circle_integral_div_sub_of_differentiable_on_off_countable {R : ℝ} {c w : ℂ} {s : set ℂ}
  (hs : countable s) (hw : w ∈ ball c R) {f : ℂ → ℂ} (hc : continuous_on f (closed_ball c R))
  (hd : ∀ z ∈ ball c R \ s, differentiable_at ℂ f z) :
  ∮ z in C(c, R), f z / (z - w) = 2 * π * I * f w :=
by simpa only [smul_eq_mul, div_eq_inv_mul]
  using circle_integral_sub_inv_smul_of_differentiable_on_off_countable hs hw hc hd

/-- If `f : ℂ → E` is continuous on a closed ball of positive radius and is differentiable at all
but countably many points of the corresponding open ball, then it is analytic on the open ball with
coefficients of the power series given by Cauchy integral formulas. -/
lemma has_fpower_series_on_ball_of_differentiable_off_countable {R : ℝ≥0} {c : ℂ} {f : ℂ → E}
  {s : set ℂ} (hs : countable s) (hc : continuous_on f (closed_ball c R))
  (hd : ∀ z ∈ ball c R \ s, differentiable_at ℂ f z) (hR : 0 < R) :
  has_fpower_series_on_ball f (cauchy_power_series f c R) c R :=
{ r_le := le_radius_cauchy_power_series _ _ _,
  r_pos := ennreal.coe_pos.2 hR,
  has_sum := λ w hw,
    begin
      have hw' : c + w ∈ ball c R,
        by simpa only [add_mem_ball_iff_norm, ← coe_nnnorm, mem_emetric_ball_zero_iff,
          nnreal.coe_lt_coe, ennreal.coe_lt_coe] using hw,
      rw ← two_pi_I_inv_smul_circle_integral_sub_inv_smul_of_differentiable_on_off_countable hs
        hw' hc hd,
      exact (has_fpower_series_on_cauchy_integral
        ((hc.mono sphere_subset_closed_ball).circle_integrable R.2) hR).has_sum hw
    end }

/-- If `f : ℂ → E` is continuous on a closed ball of positive radius and is complex differentiable
on its interior, then it is analytic on the open ball with coefficients of the power series given by
Cauchy integral formulas. -/
lemma has_fpower_series_on_ball_of_continuous_on_of_differentiable_on {R : ℝ≥0} {c : ℂ} {f : ℂ → E}
  (hc : continuous_on f (closed_ball c R)) (hd : differentiable_on ℂ f (ball c R)) (hR : 0 < R) :
  has_fpower_series_on_ball f (cauchy_power_series f c R) c R :=
has_fpower_series_on_ball_of_differentiable_off_countable countable_empty hc
  (λ z hz, hd.differentiable_at $ is_open_ball.mem_nhds hz.1) hR

/-- If `f : ℂ → E` is complex differentiable on a closed disc of positive radius, then it is
analytic on the corresponding open disc, and the coefficients of the power series are given by
Cauchy integral formulas. See also
`complex.has_fpower_series_on_ball_of_differentiable_off_countable` for a version of this lemma with
weaker assumptions. -/
protected lemma _root_.differentiable_on.has_fpower_series_on_ball {R : ℝ≥0} {c : ℂ} {f : ℂ → E}
  (hd : differentiable_on ℂ f (closed_ball c R)) (hR : 0 < R) :
  has_fpower_series_on_ball f (cauchy_power_series f c R) c R :=
has_fpower_series_on_ball_of_continuous_on_of_differentiable_on hd.continuous_on
  (hd.mono ball_subset_closed_ball) hR

/-- If `f : ℂ → E` is complex differentiable on some set `s`, then it is analytic at any point `z`
such that `s ∈ 𝓝 z` (equivalently, `z ∈ interior s`). -/
protected lemma _root_.differentiable_on.analytic_at {s : set ℂ} {f : ℂ → E} {z : ℂ}
  (hd : differentiable_on ℂ f s) (hz : s ∈ 𝓝 z) : analytic_at ℂ f z :=
begin
  rcases nhds_basis_closed_ball.mem_iff.1 hz with ⟨R, hR0, hRs⟩,
  lift R to ℝ≥0 using hR0.le,
  exact ((hd.mono hRs).has_fpower_series_on_ball hR0).analytic_at
end

/-- A complex differentiable function `f : ℂ → E` is analytic at every point. -/
protected lemma _root_.differentiable.analytic_at {f : ℂ → E} (hf : differentiable ℂ f) (z : ℂ) :
  analytic_at ℂ f z :=
hf.differentiable_on.analytic_at univ_mem

end complex


/-
Copyright (c) 2021 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin

! This file was ported from Lean 3 source module group_theory.subgroup.saturated
! leanprover-community/mathlib commit fac369018417f980cec5fcdafc766a69f88d8cfe
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.GroupTheory.Subgroup.Basic

/-!
# Saturated subgroups

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

## Tags
subgroup, subgroups

-/

namespace Subgroup

variable {G : Type _} [Group G]

#print Subgroup.Saturated /-
/-- A subgroup `H` of `G` is *saturated* if for all `n : ℕ` and `g : G` with `g^n ∈ H`
we have `n = 0` or `g ∈ H`. -/
@[to_additive
      "An additive subgroup `H` of `G` is *saturated* if\nfor all `n : ℕ` and `g : G` with `n•g ∈ H` we have `n = 0` or `g ∈ H`."]
def Saturated (H : Subgroup G) : Prop :=
  ∀ ⦃n g⦄, g ^ n ∈ H → n = 0 ∨ g ∈ H
#align subgroup.saturated Subgroup.Saturated
#align add_subgroup.saturated AddSubgroup.Saturated
-/

/- warning: subgroup.saturated_iff_npow -> Subgroup.saturated_iff_npow is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {H : Subgroup.{u1} G _inst_1}, Iff (Subgroup.Saturated.{u1} G _inst_1 H) (forall (n : Nat) (g : G), (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)) (HPow.hPow.{u1, 0, u1} G Nat G (instHPow.{u1, 0} G Nat (Monoid.Pow.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) g n) H) -> (Or (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (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 H)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {H : Subgroup.{u1} G _inst_1}, Iff (Subgroup.Saturated.{u1} G _inst_1 H) (forall (n : Nat) (g : 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)) (HPow.hPow.{u1, 0, u1} G Nat G (instHPow.{u1, 0} G Nat (Monoid.Pow.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) g n) H) -> (Or (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (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 H)))
Case conversion may be inaccurate. Consider using '#align subgroup.saturated_iff_npow Subgroup.saturated_iff_npowₓ'. -/
@[to_additive]
theorem saturated_iff_npow {H : Subgroup G} :
    Saturated H ↔ ∀ (n : ℕ) (g : G), g ^ n ∈ H → n = 0 ∨ g ∈ H :=
  Iff.rfl
#align subgroup.saturated_iff_npow Subgroup.saturated_iff_npow
#align add_subgroup.saturated_iff_nsmul AddSubgroup.saturated_iff_nsmul

/- warning: subgroup.saturated_iff_zpow -> Subgroup.saturated_iff_zpow is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {H : Subgroup.{u1} G _inst_1}, Iff (Subgroup.Saturated.{u1} G _inst_1 H) (forall (n : Int) (g : G), (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)) (HPow.hPow.{u1, 0, u1} G Int G (instHPow.{u1, 0} G Int (DivInvMonoid.Pow.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) g n) H) -> (Or (Eq.{1} Int n (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero)))) (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 H)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {H : Subgroup.{u1} G _inst_1}, Iff (Subgroup.Saturated.{u1} G _inst_1 H) (forall (n : Int) (g : 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)) (HPow.hPow.{u1, 0, u1} G Int G (instHPow.{u1, 0} G Int (DivInvMonoid.Pow.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) g n) H) -> (Or (Eq.{1} Int n (OfNat.ofNat.{0} Int 0 (instOfNatInt 0))) (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 H)))
Case conversion may be inaccurate. Consider using '#align subgroup.saturated_iff_zpow Subgroup.saturated_iff_zpowₓ'. -/
@[to_additive]
theorem saturated_iff_zpow {H : Subgroup G} :
    Saturated H ↔ ∀ (n : ℤ) (g : G), g ^ n ∈ H → n = 0 ∨ g ∈ H :=
  by
  constructor
  · rintro hH ⟨n⟩ g hgn
    · simp only [Int.coe_nat_eq_zero, Int.ofNat_eq_coe, zpow_ofNat] at hgn⊢
      exact hH hgn
    · suffices g ^ (n + 1) ∈ H by
        refine' (hH this).imp _ id
        simp only [IsEmpty.forall_iff, Nat.succ_ne_zero]
      simpa only [inv_mem_iff, zpow_negSucc] using hgn
  · intro h n g hgn
    specialize h n g
    simp only [Int.coe_nat_eq_zero, zpow_ofNat] at h
    apply h hgn
#align subgroup.saturated_iff_zpow Subgroup.saturated_iff_zpow
#align add_subgroup.saturated_iff_zsmul AddSubgroup.saturated_iff_zsmul

end Subgroup

namespace AddSubgroup

/- warning: add_subgroup.ker_saturated -> AddSubgroup.ker_saturated is a dubious translation:
lean 3 declaration is
  forall {A₁ : Type.{u1}} {A₂ : Type.{u2}} [_inst_1 : AddCommGroup.{u1} A₁] [_inst_2 : AddCommGroup.{u2} A₂] [_inst_3 : NoZeroSMulDivisors.{0, u2} Nat A₂ Nat.hasZero (AddZeroClass.toHasZero.{u2} A₂ (AddMonoid.toAddZeroClass.{u2} A₂ (SubNegMonoid.toAddMonoid.{u2} A₂ (AddGroup.toSubNegMonoid.{u2} A₂ (AddCommGroup.toAddGroup.{u2} A₂ _inst_2))))) (AddMonoid.SMul.{u2} A₂ (SubNegMonoid.toAddMonoid.{u2} A₂ (AddGroup.toSubNegMonoid.{u2} A₂ (AddCommGroup.toAddGroup.{u2} A₂ _inst_2))))] (f : AddMonoidHom.{u1, u2} A₁ A₂ (AddMonoid.toAddZeroClass.{u1} A₁ (SubNegMonoid.toAddMonoid.{u1} A₁ (AddGroup.toSubNegMonoid.{u1} A₁ (AddCommGroup.toAddGroup.{u1} A₁ _inst_1)))) (AddMonoid.toAddZeroClass.{u2} A₂ (SubNegMonoid.toAddMonoid.{u2} A₂ (AddGroup.toSubNegMonoid.{u2} A₂ (AddCommGroup.toAddGroup.{u2} A₂ _inst_2))))), AddSubgroup.Saturated.{u1} A₁ (AddCommGroup.toAddGroup.{u1} A₁ _inst_1) (AddMonoidHom.ker.{u1, u2} A₁ (AddCommGroup.toAddGroup.{u1} A₁ _inst_1) A₂ (AddMonoid.toAddZeroClass.{u2} A₂ (SubNegMonoid.toAddMonoid.{u2} A₂ (AddGroup.toSubNegMonoid.{u2} A₂ (AddCommGroup.toAddGroup.{u2} A₂ _inst_2)))) f)
but is expected to have type
  forall {A₁ : Type.{u2}} {A₂ : Type.{u1}} [_inst_1 : AddCommGroup.{u2} A₁] [_inst_2 : AddCommGroup.{u1} A₂] [_inst_3 : NoZeroSMulDivisors.{0, u1} Nat A₂ (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (NegZeroClass.toZero.{u1} A₂ (SubNegZeroMonoid.toNegZeroClass.{u1} A₂ (SubtractionMonoid.toSubNegZeroMonoid.{u1} A₂ (SubtractionCommMonoid.toSubtractionMonoid.{u1} A₂ (AddCommGroup.toDivisionAddCommMonoid.{u1} A₂ _inst_2))))) (AddMonoid.SMul.{u1} A₂ (SubNegMonoid.toAddMonoid.{u1} A₂ (AddGroup.toSubNegMonoid.{u1} A₂ (AddCommGroup.toAddGroup.{u1} A₂ _inst_2))))] (f : AddMonoidHom.{u2, u1} A₁ A₂ (AddMonoid.toAddZeroClass.{u2} A₁ (SubNegMonoid.toAddMonoid.{u2} A₁ (AddGroup.toSubNegMonoid.{u2} A₁ (AddCommGroup.toAddGroup.{u2} A₁ _inst_1)))) (AddMonoid.toAddZeroClass.{u1} A₂ (SubNegMonoid.toAddMonoid.{u1} A₂ (AddGroup.toSubNegMonoid.{u1} A₂ (AddCommGroup.toAddGroup.{u1} A₂ _inst_2))))), AddSubgroup.Saturated.{u2} A₁ (AddCommGroup.toAddGroup.{u2} A₁ _inst_1) (AddMonoidHom.ker.{u2, u1} A₁ (AddCommGroup.toAddGroup.{u2} A₁ _inst_1) A₂ (AddMonoid.toAddZeroClass.{u1} A₂ (SubNegMonoid.toAddMonoid.{u1} A₂ (AddGroup.toSubNegMonoid.{u1} A₂ (AddCommGroup.toAddGroup.{u1} A₂ _inst_2)))) f)
Case conversion may be inaccurate. Consider using '#align add_subgroup.ker_saturated AddSubgroup.ker_saturatedₓ'. -/
theorem ker_saturated {A₁ A₂ : Type _} [AddCommGroup A₁] [AddCommGroup A₂] [NoZeroSMulDivisors ℕ A₂]
    (f : A₁ →+ A₂) : f.ker.Saturated := by
  intro n g hg
  simpa only [f.mem_ker, nsmul_eq_smul, f.map_nsmul, smul_eq_zero] using hg
#align add_subgroup.ker_saturated AddSubgroup.ker_saturated

end AddSubgroup


/-
Copyright (c) 2019 Zhouhang Zhou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Zhouhang Zhou, Sébastien Gouëzel, Frédéric Dupuis
-/
import algebra.direct_sum.module
import analysis.complex.basic
import analysis.convex.uniform
import analysis.normed_space.completion
import analysis.normed_space.bounded_linear_maps
import linear_algebra.bilinear_form

import analysis.inner_product_space.basic

/-!
# Orthogonal complements of submodules

In this file, the `orthogonal` complement of a submodule `K` is defined, and basic API established.
Some of the more subtle results about the orthogonal complement are delayed to
`analysis.inner_product_space.projection`.

## Notation

The orthogonal complement of a submodule `K` is denoted by `Kᗮ`.
-/

variables {𝕜 E F : Type*} [is_R_or_C 𝕜]
variables [normed_add_comm_group E] [inner_product_space 𝕜 E]
variables [normed_add_comm_group F] [inner_product_space ℝ F]
local notation `⟪`x`, `y`⟫` := @inner 𝕜 _ _ x y

namespace submodule

variables (K : submodule 𝕜 E)

/-- The subspace of vectors orthogonal to a given subspace. -/
def orthogonal : submodule 𝕜 E :=
{ carrier := {v | ∀ u ∈ K, ⟪u, v⟫ = 0},
  zero_mem' := λ _ _, inner_zero_right _,
  add_mem' := λ x y hx hy u hu, by rw [inner_add_right, hx u hu, hy u hu, add_zero],
  smul_mem' := λ c x hx u hu, by rw [inner_smul_right, hx u hu, mul_zero] }

notation K`ᗮ`:1200 := orthogonal K

/-- When a vector is in `Kᗮ`. -/
lemma mem_orthogonal (v : E) : v ∈ Kᗮ ↔ ∀ u ∈ K, ⟪u, v⟫ = 0 := iff.rfl

/-- When a vector is in `Kᗮ`, with the inner product the
other way round. -/
lemma mem_orthogonal' (v : E) : v ∈ Kᗮ ↔ ∀ u ∈ K, ⟪v, u⟫ = 0 :=
by simp_rw [mem_orthogonal, inner_eq_zero_symm]

variables {K}

/-- A vector in `K` is orthogonal to one in `Kᗮ`. -/
lemma inner_right_of_mem_orthogonal {u v : E} (hu : u ∈ K) (hv : v ∈ Kᗮ) : ⟪u, v⟫ = 0 :=
(K.mem_orthogonal v).1 hv u hu

/-- A vector in `Kᗮ` is orthogonal to one in `K`. -/
lemma inner_left_of_mem_orthogonal {u v : E} (hu : u ∈ K) (hv : v ∈ Kᗮ) : ⟪v, u⟫ = 0 :=
by rw [inner_eq_zero_symm]; exact inner_right_of_mem_orthogonal hu hv

/-- A vector is in `(𝕜 ∙ u)ᗮ` iff it is orthogonal to `u`. -/
lemma mem_orthogonal_singleton_iff_inner_right {u v : E} : v ∈ (𝕜 ∙ u)ᗮ ↔ ⟪u, v⟫ = 0 :=
begin
  refine ⟨inner_right_of_mem_orthogonal (mem_span_singleton_self u), _⟩,
  intros hv w hw,
  rw mem_span_singleton at hw,
  obtain ⟨c, rfl⟩ := hw,
  simp [inner_smul_left, hv],
end

/-- A vector in `(𝕜 ∙ u)ᗮ` is orthogonal to `u`. -/
lemma mem_orthogonal_singleton_iff_inner_left {u v : E} : v ∈ (𝕜 ∙ u)ᗮ ↔ ⟪v, u⟫ = 0 :=
by rw [mem_orthogonal_singleton_iff_inner_right, inner_eq_zero_symm]

lemma sub_mem_orthogonal_of_inner_left {x y : E}
  (h : ∀ (v : K), ⟪x, v⟫ = ⟪y, v⟫) : x - y ∈ Kᗮ :=
begin
  rw mem_orthogonal',
  intros u hu,
  rw [inner_sub_left, sub_eq_zero],
  exact h ⟨u, hu⟩,
end

lemma sub_mem_orthogonal_of_inner_right {x y : E}
  (h : ∀ (v : K), ⟪(v : E), x⟫ = ⟪(v : E), y⟫) : x - y ∈ Kᗮ :=
begin
  intros u hu,
  rw [inner_sub_right, sub_eq_zero],
  exact h ⟨u, hu⟩,
end

variables (K)

/-- `K` and `Kᗮ` have trivial intersection. -/
lemma inf_orthogonal_eq_bot : K ⊓ Kᗮ = ⊥ :=
begin
  rw eq_bot_iff,
  intros x,
  rw mem_inf,
  exact λ ⟨hx, ho⟩, inner_self_eq_zero.1 (ho x hx)
end

/-- `K` and `Kᗮ` have trivial intersection. -/
lemma orthogonal_disjoint : disjoint K Kᗮ :=
by simp [disjoint_iff, K.inf_orthogonal_eq_bot]

/-- `Kᗮ` can be characterized as the intersection of the kernels of the operations of
inner product with each of the elements of `K`. -/
lemma orthogonal_eq_inter : Kᗮ = ⨅ v : K, linear_map.ker (innerSL 𝕜 (v : E)) :=
begin
  apply le_antisymm,
  { rw le_infi_iff,
    rintros ⟨v, hv⟩ w hw,
    simpa using hw _ hv },
  { intros v hv w hw,
    simp only [mem_infi] at hv,
    exact hv ⟨w, hw⟩ }
end

/-- The orthogonal complement of any submodule `K` is closed. -/
lemma is_closed_orthogonal : is_closed (Kᗮ : set E) :=
begin
  rw orthogonal_eq_inter K,
  have := λ v : K, continuous_linear_map.is_closed_ker (innerSL 𝕜 (v : E)),
  convert is_closed_Inter this,
  simp only [infi_coe],
end

/-- In a complete space, the orthogonal complement of any submodule `K` is complete. -/
instance [complete_space E] : complete_space Kᗮ := K.is_closed_orthogonal.complete_space_coe

variables (𝕜 E)

/-- `orthogonal` gives a `galois_connection` between
`submodule 𝕜 E` and its `order_dual`. -/
lemma orthogonal_gc :
  @galois_connection (submodule 𝕜 E) (submodule 𝕜 E)ᵒᵈ _ _
    orthogonal orthogonal :=
λ K₁ K₂, ⟨λ h v hv u hu, inner_left_of_mem_orthogonal hv (h hu),
          λ h v hv u hu, inner_left_of_mem_orthogonal hv (h hu)⟩

variables {𝕜 E}

/-- `orthogonal` reverses the `≤` ordering of two
subspaces. -/
lemma orthogonal_le {K₁ K₂ : submodule 𝕜 E} (h : K₁ ≤ K₂) : K₂ᗮ ≤ K₁ᗮ :=
(orthogonal_gc 𝕜 E).monotone_l h

/-- `orthogonal.orthogonal` preserves the `≤` ordering of two
subspaces. -/
lemma orthogonal_orthogonal_monotone {K₁ K₂ : submodule 𝕜 E} (h : K₁ ≤ K₂) :
  K₁ᗮᗮ ≤ K₂ᗮᗮ :=
orthogonal_le (orthogonal_le h)

/-- `K` is contained in `Kᗮᗮ`. -/
lemma le_orthogonal_orthogonal : K ≤ Kᗮᗮ := (orthogonal_gc 𝕜 E).le_u_l _

/-- The inf of two orthogonal subspaces equals the subspace orthogonal
to the sup. -/
lemma inf_orthogonal (K₁ K₂ : submodule 𝕜 E) : K₁ᗮ ⊓ K₂ᗮ = (K₁ ⊔ K₂)ᗮ :=
(orthogonal_gc 𝕜 E).l_sup.symm

/-- The inf of an indexed family of orthogonal subspaces equals the
subspace orthogonal to the sup. -/
lemma infi_orthogonal {ι : Type*} (K : ι → submodule 𝕜 E) : (⨅ i, (K i)ᗮ) = (supr K)ᗮ :=
(orthogonal_gc 𝕜 E).l_supr.symm

/-- The inf of a set of orthogonal subspaces equals the subspace orthogonal to the sup. -/
lemma Inf_orthogonal (s : set $ submodule 𝕜 E) : (⨅ K ∈ s, Kᗮ) = (Sup s)ᗮ :=
(orthogonal_gc 𝕜 E).l_Sup.symm

@[simp] lemma top_orthogonal_eq_bot : (⊤ : submodule 𝕜 E)ᗮ = ⊥ :=
begin
  ext,
  rw [mem_bot, mem_orthogonal],
  exact ⟨λ h, inner_self_eq_zero.mp (h x mem_top), by { rintro rfl, simp }⟩
end

@[simp] lemma bot_orthogonal_eq_top : (⊥ : submodule 𝕜 E)ᗮ = ⊤ :=
begin
  rw [← top_orthogonal_eq_bot, eq_top_iff],
  exact le_orthogonal_orthogonal ⊤
end

@[simp] lemma orthogonal_eq_top_iff : Kᗮ = ⊤ ↔ K = ⊥ :=
begin
  refine ⟨_, by { rintro rfl, exact bot_orthogonal_eq_top }⟩,
  intro h,
  have : K ⊓ Kᗮ = ⊥ := K.orthogonal_disjoint.eq_bot,
  rwa [h, inf_comm, top_inf_eq] at this
end

lemma orthogonal_family_self :
  orthogonal_family 𝕜 (λ b, ↥(cond b K Kᗮ)) (λ b, (cond b K Kᗮ).subtypeₗᵢ)
| tt tt := absurd rfl
| tt ff := λ _ x y, inner_right_of_mem_orthogonal x.prop y.prop
| ff tt := λ _ x y, inner_left_of_mem_orthogonal y.prop x.prop
| ff ff := absurd rfl

end submodule


/-
Copyright (c) 2020 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/
import algebra.direct_limit
import field_theory.is_alg_closed.basic
/-!
# Algebraic Closure

In this file we construct the algebraic closure of a field

## Main Definitions

- `algebraic_closure k` is an algebraic closure of `k` (in the same universe).
  It is constructed by taking the polynomial ring generated by indeterminates `x_f`
  corresponding to monic irreducible polynomials `f` with coefficients in `k`, and quotienting
  out by a maximal ideal containing every `f(x_f)`, and then repeating this step countably
  many times. See Exercise 1.13 in Atiyah--Macdonald.

## Tags

algebraic closure, algebraically closed
-/

universes u v w
noncomputable theory
open_locale classical big_operators
open polynomial

variables (k : Type u) [field k]

namespace algebraic_closure

open mv_polynomial

/-- The subtype of monic irreducible polynomials -/
@[reducible] def monic_irreducible : Type u :=
{ f : polynomial k // monic f ∧ irreducible f }

/-- Sends a monic irreducible polynomial `f` to `f(x_f)` where `x_f` is a formal indeterminate. -/
def eval_X_self (f : monic_irreducible k) : mv_polynomial (monic_irreducible k) k :=
polynomial.eval₂ mv_polynomial.C (X f) f

/-- The span of `f(x_f)` across monic irreducible polynomials `f` where `x_f` is an
indeterminate. -/
def span_eval : ideal (mv_polynomial (monic_irreducible k) k) :=
ideal.span $ set.range $ eval_X_self k

/-- Given a finset of monic irreducible polynomials, construct an algebra homomorphism to the
splitting field of the product of the polynomials sending each indeterminate `x_f` represented by
the polynomial `f` in the finset to a root of `f`. -/
def to_splitting_field (s : finset (monic_irreducible k)) :
  mv_polynomial (monic_irreducible k) k →ₐ[k] splitting_field (∏ x in s, x : polynomial k) :=
mv_polynomial.aeval $ λ f,
  if hf : f ∈ s
  then root_of_splits _
    ((splits_prod_iff _ $ λ (j : monic_irreducible k) _, j.2.2.ne_zero).1
      (splitting_field.splits _) f hf)
    (mt is_unit_iff_degree_eq_zero.2 f.2.2.not_unit)
  else 37

theorem to_splitting_field_eval_X_self {s : finset (monic_irreducible k)} {f} (hf : f ∈ s) :
  to_splitting_field k s (eval_X_self k f) = 0 :=
by { rw [to_splitting_field, eval_X_self, ← alg_hom.coe_to_ring_hom, hom_eval₂,
         alg_hom.coe_to_ring_hom, mv_polynomial.aeval_X, dif_pos hf,
         ← algebra_map_eq, alg_hom.comp_algebra_map],
  exact map_root_of_splits _ _ _ }

theorem span_eval_ne_top : span_eval k ≠ ⊤ :=
begin
  rw [ideal.ne_top_iff_one, span_eval, ideal.span, ← set.image_univ,
    finsupp.mem_span_image_iff_total],
  rintros ⟨v, _, hv⟩,
  replace hv := congr_arg (to_splitting_field k v.support) hv,
  rw [alg_hom.map_one, finsupp.total_apply, finsupp.sum, alg_hom.map_sum, finset.sum_eq_zero] at hv,
  { exact zero_ne_one hv },
  intros j hj,
  rw [smul_eq_mul, alg_hom.map_mul, to_splitting_field_eval_X_self k hj, mul_zero]
end

/-- A random maximal ideal that contains `span_eval k` -/
def max_ideal : ideal (mv_polynomial (monic_irreducible k) k) :=
classical.some $ ideal.exists_le_maximal _ $ span_eval_ne_top k

instance max_ideal.is_maximal : (max_ideal k).is_maximal :=
(classical.some_spec $ ideal.exists_le_maximal _ $ span_eval_ne_top k).1

theorem le_max_ideal : span_eval k ≤ max_ideal k :=
(classical.some_spec $ ideal.exists_le_maximal _ $ span_eval_ne_top k).2

/-- The first step of constructing `algebraic_closure`: adjoin a root of all monic polynomials -/
def adjoin_monic : Type u :=
mv_polynomial (monic_irreducible k) k ⧸ max_ideal k

instance adjoin_monic.field : field (adjoin_monic k) :=
ideal.quotient.field _

instance adjoin_monic.inhabited : inhabited (adjoin_monic k) := ⟨37⟩

/-- The canonical ring homomorphism to `adjoin_monic k`. -/
def to_adjoin_monic : k →+* adjoin_monic k :=
(ideal.quotient.mk _).comp C

instance adjoin_monic.algebra : algebra k (adjoin_monic k) :=
(to_adjoin_monic k).to_algebra

theorem adjoin_monic.algebra_map : algebra_map k (adjoin_monic k) = (ideal.quotient.mk _).comp C :=
rfl

theorem adjoin_monic.is_integral (z : adjoin_monic k) : is_integral k z :=
let ⟨p, hp⟩ := ideal.quotient.mk_surjective z in hp ▸
mv_polynomial.induction_on p (λ x, is_integral_algebra_map) (λ p q, is_integral_add)
  (λ p f ih, @is_integral_mul _ _ _ _ _ _ (ideal.quotient.mk _ _) ih ⟨f, f.2.1,
    by { erw [adjoin_monic.algebra_map, ← hom_eval₂,
              ideal.quotient.eq_zero_iff_mem],
      exact le_max_ideal k (ideal.subset_span ⟨f, rfl⟩) }⟩)

theorem adjoin_monic.exists_root {f : polynomial k} (hfm : f.monic) (hfi : irreducible f) :
  ∃ x : adjoin_monic k, f.eval₂ (to_adjoin_monic k) x = 0 :=
⟨ideal.quotient.mk _ $ X (⟨f, hfm, hfi⟩ : monic_irreducible k),
 by { rw [to_adjoin_monic, ← hom_eval₂, ideal.quotient.eq_zero_iff_mem],
      exact le_max_ideal k (ideal.subset_span $ ⟨_, rfl⟩) }⟩

/-- The `n`th step of constructing `algebraic_closure`, together with its `field` instance. -/
def step_aux (n : ℕ) : Σ α : Type u, field α :=
nat.rec_on n ⟨k, infer_instance⟩ $ λ n ih, ⟨@adjoin_monic ih.1 ih.2, @adjoin_monic.field ih.1 ih.2⟩

/-- The `n`th step of constructing `algebraic_closure`. -/
def step (n : ℕ) : Type u :=
(step_aux k n).1

instance step.field (n : ℕ) : field (step k n) :=
(step_aux k n).2

instance step.inhabited (n) : inhabited (step k n) := ⟨37⟩

/-- The canonical inclusion to the `0`th step. -/
def to_step_zero : k →+* step k 0 :=
ring_hom.id k

/-- The canonical ring homomorphism to the next step. -/
def to_step_succ (n : ℕ) : step k n →+* step k (n + 1) :=
@to_adjoin_monic (step k n) (step.field k n)

instance step.algebra_succ (n) : algebra (step k n) (step k (n + 1)) :=
(to_step_succ k n).to_algebra

theorem to_step_succ.exists_root {n} {f : polynomial (step k n)}
  (hfm : f.monic) (hfi : irreducible f) :
  ∃ x : step k (n + 1), f.eval₂ (to_step_succ k n) x = 0 :=
@adjoin_monic.exists_root _ (step.field k n) _ hfm hfi

/-- The canonical ring homomorphism to a step with a greater index. -/
def to_step_of_le (m n : ℕ) (h : m ≤ n) : step k m →+* step k n :=
{ to_fun := nat.le_rec_on h (λ n, to_step_succ k n),
  map_one' := begin
    induction h with n h ih, { exact nat.le_rec_on_self 1 },
    rw [nat.le_rec_on_succ h, ih, ring_hom.map_one]
  end,
  map_mul' := λ x y, begin
    induction h with n h ih, { simp_rw nat.le_rec_on_self },
    simp_rw [nat.le_rec_on_succ h, ih, ring_hom.map_mul]
  end,
  map_zero' := begin
    induction h with n h ih, { exact nat.le_rec_on_self 0 },
    rw [nat.le_rec_on_succ h, ih, ring_hom.map_zero]
  end,
  map_add' := λ x y, begin
    induction h with n h ih, { simp_rw nat.le_rec_on_self },
    simp_rw [nat.le_rec_on_succ h, ih, ring_hom.map_add]
  end }

@[simp] lemma coe_to_step_of_le (m n : ℕ) (h : m ≤ n) :
  (to_step_of_le k m n h : step k m → step k n) = nat.le_rec_on h (λ n, to_step_succ k n) :=
rfl

instance step.algebra (n) : algebra k (step k n) :=
(to_step_of_le k 0 n n.zero_le).to_algebra

instance step.scalar_tower (n) : is_scalar_tower k (step k n) (step k (n + 1)) :=
is_scalar_tower.of_algebra_map_eq $ λ z,
  @nat.le_rec_on_succ (step k) 0 n n.zero_le (n + 1).zero_le (λ n, to_step_succ k n) z

theorem step.is_integral (n) : ∀ z : step k n, is_integral k z :=
nat.rec_on n (λ z, is_integral_algebra_map) $ λ n ih z,
  is_integral_trans ih _ (adjoin_monic.is_integral (step k n) z : _)

instance to_step_of_le.directed_system :
  directed_system (step k) (λ i j h, to_step_of_le k i j h) :=
⟨λ i x h, nat.le_rec_on_self x, λ i₁ i₂ i₃ h₁₂ h₂₃ x, (nat.le_rec_on_trans h₁₂ h₂₃ x).symm⟩

end algebraic_closure

/-- The canonical algebraic closure of a field, the direct limit of adding roots to the field for
each polynomial over the field. -/
def algebraic_closure : Type u :=
ring.direct_limit (algebraic_closure.step k) (λ i j h, algebraic_closure.to_step_of_le k i j h)

namespace algebraic_closure

instance : field (algebraic_closure k) :=
field.direct_limit.field _ _

instance : inhabited (algebraic_closure k) := ⟨37⟩

/-- The canonical ring embedding from the `n`th step to the algebraic closure. -/
def of_step (n : ℕ) : step k n →+* algebraic_closure k :=
ring.direct_limit.of _ _ _

instance algebra_of_step (n) : algebra (step k n) (algebraic_closure k) :=
(of_step k n).to_algebra

theorem of_step_succ (n : ℕ) : (of_step k (n + 1)).comp (to_step_succ k n) = of_step k n :=
ring_hom.ext $ λ x, show ring.direct_limit.of (step k) (λ i j h, to_step_of_le k i j h) _ _ = _,
  by { convert ring.direct_limit.of_f n.le_succ x, ext x, exact (nat.le_rec_on_succ' x).symm }

theorem exists_of_step (z : algebraic_closure k) : ∃ n x, of_step k n x = z :=
ring.direct_limit.exists_of z

-- slow
theorem exists_root {f : polynomial (algebraic_closure k)}
  (hfm : f.monic) (hfi : irreducible f) :
  ∃ x : algebraic_closure k, f.eval x = 0 :=
begin
  have : ∃ n p, polynomial.map (of_step k n) p = f,
  { convert ring.direct_limit.polynomial.exists_of f },
  unfreezingI { obtain ⟨n, p, rfl⟩ := this },
  rw monic_map_iff at hfm,
  have := hfm.irreducible_of_irreducible_map (of_step k n) p hfi,
  obtain ⟨x, hx⟩ := to_step_succ.exists_root k hfm this,
  refine ⟨of_step k (n + 1) x, _⟩,
  rw [← of_step_succ k n, eval_map, ← hom_eval₂, hx, ring_hom.map_zero]
end

instance : is_alg_closed (algebraic_closure k) :=
is_alg_closed.of_exists_root _ $ λ f, exists_root k

instance {R : Type*} [comm_semiring R] [alg : algebra R k] :
  algebra R (algebraic_closure k) :=
((of_step k 0).comp (@algebra_map _ _ _ _ alg)).to_algebra

lemma algebra_map_def {R : Type*} [comm_semiring R] [alg : algebra R k] :
  algebra_map R (algebraic_closure k) = ((of_step k 0 : k →+* _).comp (@algebra_map _ _ _ _ alg)) :=
rfl

instance {R S : Type*} [comm_semiring R] [comm_semiring S]
  [algebra R S] [algebra S k] [algebra R k] [is_scalar_tower R S k] :
  is_scalar_tower R S (algebraic_closure k) :=
is_scalar_tower.of_algebra_map_eq (λ x,
  ring_hom.congr_arg _ (is_scalar_tower.algebra_map_apply R S k x : _))

/-- Canonical algebra embedding from the `n`th step to the algebraic closure. -/
def of_step_hom (n) : step k n →ₐ[k] algebraic_closure k :=
{ commutes' := λ x, ring.direct_limit.of_f n.zero_le x,
  .. of_step k n }

theorem is_algebraic : algebra.is_algebraic k (algebraic_closure k) :=
λ z, (is_algebraic_iff_is_integral _).2 $ let ⟨n, x, hx⟩ := exists_of_step k z in
hx ▸ is_integral_alg_hom (of_step_hom k n) (step.is_integral k n x)

instance : is_alg_closure k (algebraic_closure k) :=
⟨algebraic_closure.is_alg_closed k, is_algebraic k⟩

end algebraic_closure